7 , 410 ký tự, 154 byte trong mã hóa 7, 0 chữ cái = điểm 154
55104010504200144434451510201304004220120504005434473340353241135014335450302052254241052253052244241052335452241114014241310052340435303052335442302052335500302052335430302052313340435303135014243241310335514052312241341351052302245341351525755102440304030434030421030442030424030455733413512410523142410523030523112411350143355142410523414252410523102410523002410523413342411145257551220304010420030455741403
Hãy thử trực tuyến!
Trong một thách thức không thích sử dụng các chữ cái, ngôn ngữ nào tốt hơn để sử dụng hơn một ngôn ngữ chỉ bao gồm các chữ số?
Đây là một chương trình đầy đủ thoát ra khỏi sự cố, do đó, có đầu ra không liên quan đến thiết bị lỗi chuẩn, nhưng thiết bị xuất chuẩn là chính xác.
Giải trình
Một chương trình 7, trong lần lặp đầu tiên của nó, chỉ cần đẩy một số phần tử vào ngăn xếp (vì trong số 12 lệnh tồn tại trong 7, chỉ có 8 trong số chúng có thể được biểu diễn trong một chương trình nguồn và 8 phần tử này chuyên dùng để viết mã để đẩy các cấu trúc dữ liệu cụ thể vào ngăn xếp). Chương trình này không sử dụng 6
lệnh (đây là cách đơn giản nhất để tạo các cấu trúc lồng nhau, nhưng mặt khác có xu hướng không xuất hiện theo nghĩa đen trong chương trình nguồn), do đó, đây chỉ là 7
các lệnh xác định cấu trúc; 7
đẩy một phần tử rỗng mới để phía trên cùng của ngăn xếp (trong khi 0
... 5
lệnh chỉ append để phía trên cùng của ngăn xếp). Do đó chúng ta có thể thêm khoảng trắng vào chương trình để hiển thị cấu trúc của nó:
551040105042001444344515102013040042201205040054344 7 7
33403532411350143354503020522542410522530522442410523354522411140142413100523
40435303052335442302052335500302052335430302052313340435303135014243241310335
514052312241341351052302245341351525 7 7
55102440304030434030421030442030424030455 7 7
33413512410523142410523030523112411350143355142410523414252410523102410523002
41052341334241114525 7
551220304010420030455 7
41403
Các phần tử gần cuối chương trình được đẩy cuối cùng, do đó, nằm trên đỉnh của ngăn xếp khi bắt đầu lần lặp thứ hai. Trong lần lặp này và tất cả các lần lặp lại trong tương lai, 7 trình thông dịch sẽ tự động tạo một bản sao của đỉnh ngăn xếp và diễn giải nó như một chương trình. Nghĩa đen 41403
đẩy (mã không chữ, mã sống) 47463
(7 có 12 lệnh nhưng chỉ có 8 lệnh có tên; vì vậy, tôi sử dụng chữ đậm để hiển thị mã và không in đậm để hiển thị nghĩa đen tạo ra mã đó, nghĩa là đó, ví dụ: 4
lệnh nối 4
vào phần tử ngăn xếp trên cùng). Vì vậy, chương trình chạy trên lần lặp thứ hai là 47463
. Đây là những gì nó làm:
47463
4 Hoán đổi hai phần tử ngăn xếp trên cùng, thêm một phần tử trống vào giữa
7 Thêm phần tử ngăn xếp trống vào đầu ngăn xếp
4 Hoán đổi hai phần tử ngăn xếp trên cùng, thêm một phần tử trống vào giữa
6 Làm việc ra lệnh nào sẽ tạo phần tử ngăn xếp trên cùng;
nối nó vào phần tử bên dưới (và bật đỉnh cũ của ngăn xếp)
3 Xuất phần tử ngăn xếp trên cùng, bật phần tử bên dưới
Điều này dễ hiểu hơn nếu chúng ta nhìn vào những gì xảy ra với ngăn xếp:
- ... d c b một
47463
(mã để chạy: 47463
)
- ... d c b trống một (mã để chạy: )
47463
7463
- ... d c b trống một trống (mã để chạy: )
47463
463
- ... d c b trống rỗng rỗng một (mã để chạy: )
47463
63
- ... d c b trống rỗng " một " (mã để chạy: )
47463
3
- ... d c b trống (mã để chạy: trống )
47463
Nói cách khác, chúng tôi lấy đầu ngăn xếp a , tìm ra mã nào có khả năng nhất đã tạo ra nó và xuất mã đó. Trình thông dịch 7 tự động bật các phần tử trống từ đỉnh ngăn xếp ở cuối vòng lặp, vì vậy chúng tôi kết thúc với mặt 47463
sau trên đỉnh của ngăn xếp, giống như trong chương trình gốc. Thật dễ dàng để xem điều gì sẽ xảy ra tiếp theo: cuối cùng chúng ta lần lượt lướt qua từng phần tử ngăn xếp, xuất ra tất cả, cho đến khi ngăn xếp tràn ra và chương trình gặp sự cố. Vì vậy, chúng tôi đã cơ bản tạo ra một vòng lặp đầu ra đơn giản mà vẻ tại của chương trình mã nguồn để xác định những gì để đầu ra (chúng tôi không xuất ra các cấu trúc dữ liệu mà là push vào stack bởi chúng tôi 0
...5
thay vào đó, chúng ta sẽ tạo lại các lệnh đã được sử dụng bằng cách xem xét các cấu trúc nào được tạo và xuất ra các cấu trúc đó). Do đó, phần đầu ra của dữ liệu là 551220304010420030455
(mã nguồn tạo ra phần tử ngăn xếp thứ hai từ trên xuống), phần thứ hai là 3341351…114525
(mã nguồn tạo ra phần tử ngăn xếp thứ ba từ trên xuống), v.v.
Rõ ràng, mặc dù, những đoạn mã nguồn này không được đầu ra không được mã hóa. 7 chứa một số ngôn ngữ dành riêng cho miền khác nhau để mã hóa đầu ra; một khi ngôn ngữ dành riêng cho tên miền được chọn, ngôn ngữ đó vẫn được sử dụng cho đến khi bị xóa rõ ràng, nhưng nếu chưa có ngôn ngữ nào được chọn, chữ số đầu tiên của mã là đầu ra sẽ xác định ngôn ngữ nào sẽ sử dụng. Trong chương trình này, chỉ có hai ngôn ngữ được sử dụng: 551
và 3
.
551
khá đơn giản: về cơ bản, đó là mã Baudot / teletype cũ được sử dụng để truyền các chữ cái qua teletypes, dưới dạng một bộ ký tự 5 bit, nhưng được sửa đổi để làm cho tất cả các chữ cái viết thường. Vì vậy, đoạn mã đầu tiên được giải mã như thế này:
551 22 03 04 01 04 20 03 04 55
c a SP e SP n a SP reset output format
Có thể thấy, chúng ta ghép từng ký tự thành hai chữ số bát phân, đây là một tỷ lệ nén khá tốt. Các cặp chữ số trong phạm vi 0-5 cung cấp cho chúng tôi 36 khả năng, trái ngược với 32 khả năng mà Baudot cần, do đó bốn số còn lại được sử dụng cho các lệnh đặc biệt; trong trường hợp này, 55
ở cuối sẽ xóa định dạng đầu ra được ghi nhớ, cho phép chúng tôi sử dụng một định dạng khác cho phần đầu ra tiếp theo mà chúng tôi sản xuất.
3
về mặt khái niệm thậm chí đơn giản hơn, nhưng với một twist. Ý tưởng cơ bản là lấy các nhóm gồm ba chữ số (một lần nữa, trong phạm vi 0-5, vì đó là những chữ số mà chúng tôi có thể đảm bảo rằng chúng tôi có thể tạo lại mã nguồn gốc từ đầu ra của nó), diễn giải chúng thành ba chữ số số trong cơ sở 6, và chỉ xuất nó dưới dạng một byte ở dạng nhị phân (do đó cho phép chúng ta xuất các ký tự đa dòng trong đầu ra mong muốn chỉ bằng cách xuất ra nhiều byte). Mặc dù vậy, sự vặn vẹo xuất phát từ thực tế là chỉ có 216 số có ba chữ số (với các số 0 đứng đầu có thể) trong cơ sở 6, nhưng 256 byte có thể. 7 làm tròn số này bằng cách liên kết các số từ 332₆ = 128₁₀ trở lên đến hai byte khác nhau; 332
có thể xuất ra byte 128 hoặc 192, 333
hoặc byte 129 hoặc 193, v.v., lên đến 515
byte đầu ra là byte 191 hoặc 255.
Làm thế nào để chương trình biết cái nào trong hai khả năng xuất ra? Bạn có thể sử dụng bộ ba chữ số 520
trở lên để kiểm soát điều này một cách rõ ràng, nhưng trong chương trình này, chúng tôi không phải: 7 mặc định là chọn tất cả các byte mơ hồ theo cách sao cho đầu ra là UTF-8 hợp lệ! Hóa ra là luôn có nhiều nhất một cách để làm điều này, miễn là chúng tôi muốn UTF-8 (và chúng tôi làm trong trường hợp này), chúng tôi chỉ có thể để nó mơ hồ và chương trình vẫn hoạt động.
Phần cuối của mỗi 3…
phần là 525
, đặt lại định dạng đầu ra, cho phép chúng ta quay lại 551
phần tiếp theo.
a
s - hoặc không, tùy thuộc vào số lượng các chữ cái sẽ mất, bởi vì 20 ký tự là một hình phạt thực sự lớn (mặc dù khi mọi thứ khác được ghi bằng byte, nó không được xác định rõ lắm ...)!