Mini-Flak, chu kỳ 6851113
Chương trình (theo nghĩa đen)
Tôi biết hầu hết mọi người không mong đợi một quine Mini-Flak sẽ sử dụng các ký tự không thể in được và thậm chí các ký tự nhiều byte (làm cho mã hóa có liên quan). Tuy nhiên, quine này, và không thể in được, kết hợp với kích thước của quine (93919 ký tự được mã hóa thành 102646 byte UTF-8), khiến việc đặt chương trình vào bài đăng này khá khó khăn.
Tuy nhiên, chương trình này rất lặp đi lặp lại, và như vậy, nén thực sự tốt. Vì vậy, toàn bộ chương trình có sẵn theo nghĩa đen từ Stack Exchange, có một hệ số thập phân xxd
có thể đảo ngược của gzip
phiên bản được nén hoàn toàn của quine ẩn đằng sau bản thu gọn bên dưới:
00000000: 1f8b 0808 bea3 045c 0203 7175 696e 652e .......\..quine.
00000010: 6d69 6e69 666c 616b 00ed d9db 6a13 4118 miniflak....j.A.
00000020: 0060 2f8b f808 0d64 a1c1 1dc8 4202 c973 .`/....d....B..s
00000030: 4829 4524 0409 22e2 5529 a194 1242 1129 H)E$..".U)...B.)
00000040: d2d7 ca93 f9cf 4c4c d45b 9536 e6db 6967 ......LL.[.6..ig
00000050: 770e 3bc9 ffed eca9 edb7 b1a4 9ad2 6a1d w.;...........j.
00000060: bfab 75db c6c6 6c5f 3d4f a5a6 8da6 dcd8 ..u...l_=O......
00000070: 465b d4a5 5a28 4bd9 719d 727b aa79 f9c9 F[..Z(K.q.r{.y..
00000080: 43b6 b9d7 8b17 cd45 7f79 d3f4 fb65 7519 C......E.y...eu.
00000090: 59ac 9a65 bfdf 8f86 e6b2 69a2 bc5c 4675 Y..e......i..\Fu
000000a0: d4e4 bcd9 5637 17b9 7099 9b73 7dd3 fcb2 ....V7..p..s}...
000000b0: 4773 b9bc e9bd b9ba 3eed 9df7 aeaf 229d Gs......>.....".
000000c0: e6ed 5eae 3aef 9d46 21b2 5e4d bd28 942e ..^.:..F!.^M.(..
000000d0: 6917 d71f a6bf 348c 819f 6260 dfd9 77fe i.....4...b`..w.
000000e0: df86 3e84 74e4 e19b b70e 9af0 111c fa0d ..>.t...........
000000f0: d29c 75ab 21e3 71d7 77f6 9d8f f902 6db2 ..u.!.q.w.....m.
00000100: b8e1 0adf e9e0 9009 1f81 f011 18d8 1b33 ...............3
00000110: 72af 762e aac2 4760 6003 1bd8 698c c043 r.v...G``...i..C
00000120: 8879 6bde 9245 207c 04ae 5ce6 2d02 e1bb .yk..E |..\.-...
00000130: 7291 4540 57f8 fe0d 6546 f89b a70b 8da9 r.E@W...eF......
00000140: f5e7 03ff 8b8f 3ad6 a367 d60b f980 679d ......:..g....g.
00000150: d3d6 1c16 f2ff a767 e608 57c8 c27d c697 .......g..W..}..
00000160: 4207 c140 9e47 9d57 2e50 6e8e c215 b270 B..@.G.W.Pn....p
00000170: bdf6 9926 9e47 9d05 ce02 0ff0 5ea7 109a ...&.G......^...
00000180: 8ba6 b5db 880b 970b 9749 2864 47d8 1b92 .........I(dG...
00000190: 39e7 9aec 8f0e 9e93 117a 6773 b710 ae53 9........zgs...S
000001a0: cd01 17ee b30e d9c1 15e6 6186 7a5c dc26 ..........a.z\.&
000001b0: 9750 1d51 610a d594 10ea f3be 4b7a 2c37 .P.Qa.......Kz,7
000001c0: 2f85 7a14 8fc4 a696 304d 4bdf c143 8db3 /.z.....0MK..C..
000001d0: d785 8a96 3085 2acc 274a a358 c635 8d37 ....0.*.'J.X.5.7
000001e0: 5f37 0f25 8ff5 6854 4a1f f6ad 1fc7 dbba _7.%..hTJ.......
000001f0: 51ed 517b 8da2 4b34 8d77 e5b2 ec46 7a18 Q.Q{..K4.w...Fz.
00000200: ffe8 3ade 6fed b2f2 99a3 bae3 c949 9ab5 ..:.o........I..
00000210: ab75 d897 d53c b258 a555 1b07 63d6 a679 .u...<.X.U..c..y
00000220: 4a51 5ead a23a 6a72 9eb6 d569 960b f3dc JQ^..:jr...i....
00000230: 9ceb 53fa 658f 345f ad07 6f6f efce 06ef ..S.e.4_..oo....
00000240: 0677 b791 cef2 f620 57bd 1b9c 4521 b241 .w..... W...E!.A
00000250: 4d83 2894 2eaf a140 8102 050a 1428 50a0 M.(....@.....(P.
00000260: 4081 0205 0a14 2850 a040 8102 050a 1428 @.....(P.@.....(
00000270: 50a0 4081 0205 0a14 2850 a040 8102 050a P.@.....(P.@....
00000280: 1428 50a0 4081 0205 0a14 2850 a040 8102 .(P.@.....(P.@..
00000290: 050a 1428 50a0 4081 0205 0a14 2850 a040 ...(P.@.....(P.@
000002a0: 8102 050a 1428 50a0 4081 0205 0a14 2850 .....(P.@.....(P
000002b0: a040 8102 050a 1428 50a0 4081 0205 0a14 .@.....(P.@.....
000002c0: 2850 a040 8102 050a 1428 50a0 4081 0205 (P.@.....(P.@...
000002d0: 0a14 2850 a040 8102 050a 1428 50a0 4081 ..(P.@.....(P.@.
000002e0: 0205 0a14 2850 a040 8102 050a 1428 50a0 ....(P.@.....(P.
000002f0: 4081 0205 0a14 2850 a040 8102 050a 1428 @.....(P.@.....(
00000300: 50a0 4081 0205 0a14 2850 a040 8102 050a P.@.....(P.@....
00000310: 1428 50a0 4081 0205 0a14 2850 a040 8102 .(P.@.....(P.@..
00000320: 050a 1428 50a0 4081 0205 0a14 2850 a040 ...(P.@.....(P.@
00000330: 8102 050a 1428 50a0 4081 0205 0a14 2850 .....(P.@.....(P
00000340: a040 8102 050a 1428 50a0 4081 0205 0a14 .@.....(P.@.....
00000350: 2850 a040 8102 050a 1428 50a0 4081 0205 (P.@.....(P.@...
00000360: 0a14 2850 a040 8102 050a 1428 50a0 4081 ..(P.@.....(P.@.
00000370: 0205 0a14 2850 a01c 14ca 7012 cbb4 a6e9 ....(P....p.....
00000380: e6db e6b1 e4b1 9e4c 4ae9 d3be f5f3 745b .......LJ.....t[
00000390: 37a9 3d6a af49 7489 a6e9 ae5c 96dd 488f 7.=j.It....\..H.
000003a0: d31f 5da7 fbad 5d56 3e73 5277 7cf5 aa7b ..]...]V>sRw|..{
000003b0: 3fbc df7c e986 c3ba 5ee4 3c6f 74f7 c3e1 ?..|....^.<ot...
000003c0: 301a bb45 d795 9afb fbdc 1495 65d5 6d9b 0..E........e.m.
000003d0: baf7 a5b4 a87d 4a5b d7fd b667 b788 ec27 .....}J[...g...'
000003e0: c5d8 28bc b96a 9eda 7a50 524d 290a a5cb ..(..j..zPRM)...
000003f0: cbef 38cb c3ad f690 0100 ..8.......
(Vâng, nó lặp đi lặp lại đến mức bạn thậm chí có thể thấy các lần lặp lại sau khi nó được nén).
Câu hỏi cho biết "Tôi cũng rất khuyến nghị không chạy chương trình của bạn trong TIO. Không chỉ TIO chậm hơn trình thông dịch trên máy tính để bàn, mà còn hết thời gian trong khoảng một phút. Sẽ rất ấn tượng nếu ai đó đạt được điểm thấp để chạy chương trình của họ trước khi TIO hết thời gian. " Tôi có thể làm điều đó! Mất khoảng 20 giây để chạy trên TIO, sử dụng trình thông dịch Ruby: Dùng thử trực tuyến!
Chương trình (dễ đọc)
Bây giờ tôi đã đưa ra một phiên bản chương trình mà máy tính có thể đọc, hãy thử phiên bản mà con người có thể đọc. Tôi đã chuyển đổi các byte tạo ra quine thành codepage 437 (nếu chúng có tập bit cao) hoặc hình ảnh điều khiển Unicode (nếu chúng là mã điều khiển ASCII), thêm khoảng trắng (mọi khoảng trắng có sẵn đã được chuyển đổi thành hình ảnh kiểm soát ), được mã hóa theo chiều dài chạy bằng cú pháp «string×length»
và một số bit nặng dữ liệu được tách ra:
␠
(((()()()()){}))
{{}
(({})[(()()()())])
(({})(
{{}{}((()[()]))}{}
(((((((({})){}){}{})){}{}){}){}())
{
({}(
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45»
… much more data encoded the same way …
(«()×117»(«()×115»(«()×117»
«000010101011┬â┬ … many more comment characters … ┬â0┬â┬à00␈␈
)[({})(
([({})]({}{}))
{
((()[()]))
}{}
{
{
({}(((({}())[()])))[{}()])
}{}
(({}))
((()[()]))
}{}
)]{}
%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'×almost 241»
,444454545455┬ç┬ … many more comment characters … -a--┬ü␡┬ü-a␡┬ü
)[{}()])
}{}
{}({}())
)[{}])
(({})(()()()()){})
}{}{}␊
("Gần như 241" là do bản sao thứ 241 bị thiếu dấu '
, nhưng khác với 240 khác.)
Giải trình
Về ý kiến
Điều đầu tiên cần giải thích là, những gì với các ký tự không thể in được và các thứ linh tinh khác không phải là lệnh Mini-Flak? Bạn có thể nghĩ rằng việc thêm ý kiến vào quine chỉ khiến mọi việc trở nên khó khăn hơn, nhưng đây là một cuộc thi tốc độ (không phải là một cuộc thi quy mô), có nghĩa là các bình luận không làm tổn hại đến tốc độ của chương trình. Trong khi đó, Brain-Flak, và do đó là Mini-Flak, chỉ cần đổ nội dung của ngăn xếp vào đầu ra tiêu chuẩn; nếu bạn phải đảm bảo rằng ngăn xếp chỉ chứacác ký tự tạo nên các lệnh của chương trình của bạn, bạn sẽ phải dành chu kỳ để dọn dẹp ngăn xếp. Như vậy, Brain-Flak bỏ qua hầu hết các ký tự, miễn là chúng tôi đảm bảo rằng các thành phần ngăn xếp rác không hợp lệ với các lệnh Brain-Flak (biến nó thành một polyglot Brain-Flak / Mini-Flak) và không âm tính hoặc bên ngoài phạm vi Unicode, chúng ta có thể để chúng trên ngăn xếp, cho phép chúng xuất ra và đặt cùng một ký tự trong chương trình của chúng ta tại cùng một vị trí để giữ lại thuộc tính quine.
Có một cách đặc biệt quan trọng chúng ta có thể tận dụng điều này. Quine hoạt động bằng cách sử dụng một chuỗi dữ liệu dài và về cơ bản, tất cả đầu ra từ quine được tạo ra bằng cách định dạng chuỗi dữ liệu theo nhiều cách khác nhau. Chỉ có một chuỗi dữ liệu, mặc dù thực tế là chương trình có nhiều phần; vì vậy chúng ta cần có khả năng sử dụng cùng một chuỗi dữ liệu để in các phần khác nhau của chương trình. Thủ thuật "dữ liệu rác không quan trọng" cho phép chúng tôi thực hiện việc này một cách rất đơn giản; chúng tôi lưu trữ các ký tự tạo nên chương trình trong chuỗi dữ liệu bằng cách thêm hoặc trừ một giá trị vào hoặc từ mã ASCII của chúng. Cụ thể, các ký tự tạo nên bắt đầu chương trình được lưu trữ dưới dạng mã ASCII + 4 của chúng, các ký tự tạo thành phần được lặp lại gần 241 lần dưới dạng mã ASCII của chúng - 4,mỗi ký tự của chuỗi dữ liệu có phần bù; ví dụ, nếu chúng ta in nó với 4 được thêm vào mỗi mã ký tự, chúng ta sẽ nhận được một lần lặp lại của phần lặp lại, với một số nhận xét trước và sau. (Những nhận xét đó chỉ đơn giản là các phần khác của chương trình, với các mã ký tự được dịch chuyển để chúng không tạo thành bất kỳ lệnh Brain-Flak hợp lệ nào, bởi vì phần bù sai đã được thêm vào. Chúng ta phải tránh các lệnh Brain-Flak, không chỉ Mini- Các lệnh Flak, để tránh vi phạm phần nguồn bị hạn chế của câu hỏi, sự lựa chọn bù đắp được thiết kế để đảm bảo điều này.)
Do thủ thuật nhận xét này, chúng tôi thực sự chỉ cần có thể xuất chuỗi dữ liệu được định dạng theo hai cách khác nhau: a) được mã hóa theo cùng một cách như trong nguồn, b) dưới dạng mã ký tự với phần bù được chỉ định được thêm vào mỗi mã. Đó là một sự đơn giản hóa lớn làm cho chiều dài thêm vào hoàn toàn xứng đáng.
Cấu trúc chương trình
Chương trình này bao gồm bốn phần: phần giới thiệu, chuỗi dữ liệu, bộ định dạng chuỗi dữ liệu và phần ngoài. Giới thiệu và hướng ngoại về cơ bản chịu trách nhiệm chạy chuỗi dữ liệu và bộ định dạng của nó trong một vòng lặp, chỉ định định dạng phù hợp mỗi lần (nghĩa là mã hóa hay bù và sử dụng phần bù nào). Chuỗi dữ liệu chỉ là dữ liệu và là phần duy nhất của quine mà các ký tự tạo nên nó không được chỉ định theo nghĩa đen trong chuỗi dữ liệu (việc đó rõ ràng là không thể, vì nó sẽ phải dài hơn chính nó); do đó nó được viết theo cách đặc biệt dễ dàng để tái sinh từ chính nó. Trình định dạng chuỗi dữ liệu được tạo thành từ 241 phần gần như giống hệt nhau, mỗi phần định dạng một mốc dữ liệu cụ thể trong số 241 trong chuỗi dữ liệu.
Mỗi phần của chương trình có thể được tạo ra thông qua chuỗi dữ liệu và định dạng của nó như sau:
- Để tạo ra outro, định dạng chuỗi dữ liệu với offset +8
- Để tạo bộ định dạng chuỗi dữ liệu, định dạng chuỗi dữ liệu với offset +4, 241 lần
- Để tạo chuỗi dữ liệu, định dạng chuỗi dữ liệu thông qua mã hóa thành định dạng nguồn
- Để tạo phần giới thiệu, định dạng chuỗi dữ liệu với offset -4
Vì vậy, tất cả những gì chúng ta phải làm là xem xét cách thức các phần này của chương trình hoạt động.
Chuỗi dữ liệu
(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45» …
Chúng ta cần một mã hóa đơn giản cho chuỗi dữ liệu vì chúng ta phải có khả năng đảo ngược mã hóa trong mã Mini-Flak. Bạn không thể đơn giản hơn thế này nhiều!
Ý tưởng chính đằng sau câu hỏi này (ngoài thủ thuật nhận xét) là lưu ý rằng về cơ bản chỉ có một nơi chúng ta có thể lưu trữ một lượng lớn dữ liệu: "tổng các giá trị trả về lệnh" trong các mức lồng nhau khác nhau của nguồn chương trình. (Điều này thường được gọi là ngăn xếp thứ ba, mặc dù Mini-Flak không có ngăn xếp thứ hai, vì vậy "ngăn xếp hoạt động" có thể là một tên tốt hơn trong bối cảnh Mini-Flak.) Các khả năng khác để lưu trữ dữ liệu sẽ là ngăn xếp chính / đầu tiên (không hoạt động bởi vì đó là nơi đầu ra của chúng ta phải đi và chúng ta không thể di chuyển đầu ra qua bộ lưu trữ theo cách hiệu quả từ xa) và được mã hóa thành một khối trong một phần tử ngăn xếp duy nhất (không phù hợp với vấn đề này vì nó mất thời gian theo cấp số nhân trích xuất dữ liệu từ nó); khi bạn loại bỏ chúng, ngăn xếp làm việc là vị trí duy nhất còn lại.
Để "lưu trữ" dữ liệu trên ngăn xếp này, chúng tôi sử dụng các lệnh không cân bằng (trong trường hợp này là nửa đầu của (…)
lệnh), sẽ được cân bằng trong bộ định dạng chuỗi dữ liệu sau này. Mỗi lần chúng ta đóng một trong các lệnh này trong bộ định dạng, nó sẽ đẩy tổng số dữ liệu được lấy từ chuỗi dữ liệu và các giá trị trả về của tất cả các lệnh ở mức lồng nhau trong bộ định dạng; chúng ta có thể đảm bảo rằng cái sau thêm vào 0, vì vậy trình định dạng chỉ đơn giản nhìn thấy các giá trị đơn lẻ được lấy từ chuỗi dữ liệu.
Định dạng rất đơn giản : (
, theo sau là n bản sao của ()
, trong đó n là số chúng tôi muốn lưu trữ. (Lưu ý rằng điều này có nghĩa là chúng tôi chỉ có thể lưu trữ các số không âm và phần tử cuối cùng của chuỗi dữ liệu cần phải dương.)
Một điểm hơi không trực quan về chuỗi dữ liệu là thứ tự của nó. "Bắt đầu" của chuỗi dữ liệu là kết thúc gần hơn khi bắt đầu chương trình, tức là mức lồng nhau ngoài cùng; phần này được định dạng cuối cùng (vì trình định dạng chạy từ mức trong cùng đến mức lồng ngoài cùng). Tuy nhiên, mặc dù được định dạng lần cuối, nó sẽ được in trước, bởi vì các giá trị được đẩy lên ngăn xếp trước được in bởi trình thông dịch Mini-Flak. Nguyên tắc tương tự áp dụng cho toàn bộ chương trình; chúng ta cần định dạng outro trước, sau đó là định dạng chuỗi dữ liệu, sau đó là chuỗi dữ liệu, sau đó là intro, tức là đảo ngược thứ tự mà chúng được lưu trữ trong chương trình.
Trình định dạng chuỗi dữ liệu
)[({})(
([({})]({}{}))
{
((()[()]))
}{}
{
{
({}(((({}())[()])))[{}()])
}{}
(({}))
((()[()]))
}{}
)]{}
Trình định dạng chuỗi dữ liệu được tạo thành từ 241 phần, mỗi phần có mã giống nhau (một phần có một nhận xét khác nhau), mỗi phần định dạng một ký tự cụ thể của chuỗi dữ liệu. (Chúng ta không thể sử dụng một vòng lặp ở đây: chúng ta cần một chuỗi không cân bằng )
để đọc chuỗi dữ liệu thông qua khớp với chuỗi không cân bằng (
và chúng ta không thể đặt một trong số chúng vào trong một{…}
vòng lặp, dạng vòng lặp duy nhất tồn tại. Thay vào đó, chúng ta " hủy đăng ký "bộ định dạng và chỉ cần lấy phần giới thiệu / đầu ra để xuất chuỗi dữ liệu với phần bù của bộ định dạng 241 lần.)
)[({})( … )]{}
Phần ngoài cùng của một phần tử định dạng đọc một phần tử của chuỗi dữ liệu; sự đơn giản của mã hóa chuỗi dữ liệu dẫn đến một chút phức tạp trong việc đọc nó. Chúng tôi bắt đầu bằng cách đóng giá trị chưa từng có (…)
trong chuỗi dữ liệu, sau đó phủ định ( […]
) hai giá trị: mốc thời gian chúng tôi vừa đọc từ chuỗi dữ liệu ( ({})
) và giá trị trả về của phần còn lại của chương trình. Chúng tôi sao chép giá trị trả về của phần còn lại của phần tử định dạng (…)
và thêm bản sao vào phiên bản phủ định với {}
. Kết quả cuối cùng là giá trị trả về của phần tử chuỗi dữ liệu và phần tử định dạng cùng nhau là mốc chuẩn trừ đi phần tử trừ đi giá trị trả về cộng với giá trị trả về hoặc 0; điều này là cần thiết để làm cho phần tử chuỗi dữ liệu tiếp theo tạo ra giá trị chính xác.
([({})]({}{}))
Trình định dạng sử dụng phần tử ngăn xếp trên cùng để biết nó ở chế độ nào (0 = định dạng trong định dạng chuỗi dữ liệu, bất kỳ giá trị nào khác = phần bù cho đầu ra với). Tuy nhiên, chỉ cần đọc chuỗi dữ liệu, mốc chuẩn nằm trên cùng của định dạng trên ngăn xếp và chúng tôi muốn chúng theo cách khác. Mã này là một biến thể ngắn hơn của mã hoán đổi Brain-Flak, lấy một trên b để b trên một + b ; Nó không chỉ ngắn hơn mà còn (trong trường hợp cụ thể này) hữu ích hơn, bởi vì tác dụng phụ của việc thêm b vào a không có vấn đề gì khi b bằng 0 và khi b không bằng 0, nó thực hiện phép tính bù cho chúng tôi.
{
((()[()]))
}{}
{
…
((()[()]))
}{}
Brain-Flak chỉ có một cấu trúc luồng điều khiển, vì vậy nếu chúng ta muốn bất cứ thứ gì ngoài while
vòng lặp, nó sẽ mất một chút công việc. Đây là một cấu trúc "phủ định"; nếu có 0 trên đỉnh của ngăn xếp, nó sẽ loại bỏ nó, nếu không, nó đặt 0 trên đỉnh của ngăn xếp. (Nó hoạt động khá đơn giản: miễn là không có 0 trên đỉnh của ngăn xếp, đẩy 1 - 1 lên ngăn xếp hai lần; khi bạn hoàn thành, hãy bật phần tử ngăn xếp trên cùng.)
Có thể đặt mã bên trong một cấu trúc phủ định, như được thấy ở đây. Mã sẽ chỉ chạy nếu đỉnh của ngăn xếp là khác không; vì vậy nếu chúng ta có hai cấu trúc phủ định, giả sử hai phần tử ngăn xếp trên cùng không cùng không bằng 0, chúng sẽ triệt tiêu lẫn nhau, nhưng bất kỳ mã nào trong cấu trúc đầu tiên sẽ chỉ chạy nếu phần tử ngăn xếp trên cùng là khác không và mã bên trong cấu trúc thứ hai sẽ chỉ chạy nếu phần tử ngăn xếp trên cùng bằng không. Nói cách khác, đây là tương đương với một câu lệnh if-then-other.
Trong mệnh đề "then", chạy nếu định dạng khác không, chúng ta thực sự không có gì để làm; những gì chúng tôi muốn là đẩy dữ liệu + offset vào ngăn xếp chính (để nó có thể là đầu ra ở cuối chương trình), nhưng nó đã có sẵn. Vì vậy, chúng ta chỉ phải đối phó với trường hợp mã hóa thành phần chuỗi dữ liệu ở dạng nguồn.
{
({}(((({}())[()])))[{}()])
}{}
(({}))
Đây là cách chúng tôi làm điều đó. Các {({}( … )[{}()])}{}
cấu trúc nên quen thuộc như một vòng lặp với một số cụ thể của sự lặp lại (mà hoạt động bằng cách di chuyển các vòng lặp ngược lại với chồng làm việc và giữ nó ở đó, nó sẽ được an toàn từ bất kỳ mã khác, bởi vì truy cập vào các ngăn xếp làm việc được gắn với mức độ lồng của chương trình). Phần thân của vòng lặp là ((({}())[()]))
, tạo ra ba bản sao của phần tử ngăn xếp trên cùng và thêm 1 vào mức thấp nhất. Nói cách khác, nó biến đổi một số 40 trên đỉnh của ngăn xếp thành 40 trên 40 trên 41 hoặc được xem là ASCII, (
thành (()
; chạy này nhiều lần sẽ làm cho (
thành (()
vào (()()
vào (()()()
và như vậy, và do đó là một cách đơn giản để tạo ra chuỗi dữ liệu của chúng tôi (giả định rằng có một (
trên đỉnh của ngăn xếp đã được).
Khi chúng ta đã thực hiện xong vòng lặp, (({}))
sao chép đỉnh của ngăn xếp (để bây giờ nó bắt đầu ((()…
thay vì (()…
. Phần đầu (
sẽ được sử dụng bởi bản sao tiếp theo của trình định dạng chuỗi dữ liệu để định dạng ký tự tiếp theo (nó sẽ mở rộng nó thành (()(()…
sau đó (()()(()…
, v.v., do đó, điều này tạo ra sự phân tách (
trong chuỗi dữ liệu).
%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'
Có một chút quan tâm cuối cùng trong bộ định dạng chuỗi dữ liệu. OK, vì vậy, hầu hết đây chỉ là 4 điểm mã hướng ra ngoài; tuy nhiên, dấu nháy đơn đó ở cuối có thể không phù hợp. '
(codepoint 39) sẽ chuyển sang +
(codepoint 43), đây không phải là lệnh Brain-Flak, vì vậy bạn có thể đoán rằng nó ở đó cho một số mục đích khác.
Lý do là ở đây là vì trình định dạng chuỗi dữ liệu dự kiến đã có sẵn (
trên ngăn xếp (nó không chứa 40 chữ ở bất cứ đâu). Các'
thực ra là ở đầu khối được lặp lại để tạo thành bộ định dạng chuỗi dữ liệu, không phải là kết thúc, vì vậy sau khi các ký tự của bộ định dạng chuỗi dữ liệu đã được đẩy lên ngăn xếp (và mã sắp chuyển sang in chuỗi dữ liệu chính nó), outro điều chỉnh 39 trên đỉnh của ngăn xếp thành 40, sẵn sàng cho bộ định dạng (chính bộ định dạng đang chạy lần này, không phải là đại diện của nó trong nguồn) để sử dụng nó. Đó là lý do tại sao chúng tôi có "gần 241" bản sao của bộ định dạng; bản sao đầu tiên bị thiếu ký tự đầu tiên. Và ký tự đó, dấu nháy đơn, là một trong ba ký tự trong chuỗi dữ liệu không tương ứng với mã Mini-Flak ở đâu đó trong chương trình; nó hoàn toàn là một phương pháp cung cấp một hằng số.
Giới thiệu và hướng ngoại
(((()()()()){}))
{{}
(({})[(()()()())])
(({})(
{{}{}((()[()]))}{}
(((((((({})){}){}{})){}{}){}){}())
{
({}(
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
…
)[{}()])
}{}
{}({}())
)[{}])
(({})(()()()()){})
}{}{}␊
Giới thiệu và hướng ngoại về mặt khái niệm là cùng một phần của chương trình; lý do duy nhất chúng ta rút ra một điểm khác biệt là outro cần được xuất ra trước chuỗi dữ liệu và định dạng của nó (để nó in ra sau chúng), trong khi phần giới thiệu cần được xuất sau chúng (in trước chúng).
(((()()()()){}))
Chúng tôi bắt đầu bằng cách đặt hai bản sao 8 trên ngăn xếp. Đây là phần bù cho lần lặp đầu tiên. Bản sao thứ hai là bởi vì vòng lặp chính dự kiến sẽ có một phần tử rác ở trên cùng của ngăn xếp phía trên phần bù, bị bỏ lại phía sau bài kiểm tra quyết định có tồn tại vòng lặp chính hay không, và vì vậy chúng ta cần đặt một phần tử rác ở đó để nó không vứt bỏ yếu tố chúng ta thực sự muốn; một bản sao là cách nhanh nhất (do đó nhanh nhất để xuất) để làm điều đó.
Có những đại diện khác của số 8 không dài hơn số này. Tuy nhiên, khi đi mã nhanh nhất, đây chắc chắn là lựa chọn tốt nhất. Đối với một điều, sử dụng ()()()()
là nhanh hơn, nói, (()()){}
bởi vì mặc dù cả hai đều dài 8 ký tự, trước đây là một chu kỳ nhanh hơn, bởi vì (…)
được tính là 2 chu kỳ, nhưng ()
chỉ là một. Tuy nhiên, việc lưu một chu kỳ là không đáng kể so với việc xem xét lớn hơn nhiều đối với một quine : (
và )
có các điểm mã thấp hơn nhiều so với {
và }
do đó, việc tạo ra đoạn dữ liệu cho chúng sẽ nhanh hơn nhiều (và đoạn dữ liệu sẽ chiếm ít không gian hơn trong mã, quá).
{{} … }{}{}
Vòng lặp chính. Điều này không tính các lần lặp (đó là một while
vòng lặp, không phải là một for
vòng lặp và sử dụng một bài kiểm tra để thoát ra). Khi nó thoát, chúng tôi loại bỏ hai phần tử ngăn xếp trên cùng; phần tử trên cùng là 0 vô hại, nhưng phần tử bên dưới sẽ là "định dạng để sử dụng cho lần lặp tiếp theo", phần này (là phần bù âm) là một số âm và nếu có bất kỳ số âm nào trong ngăn xếp khi Mini Chương trình -Flak thoát, trình thông dịch gặp sự cố khi cố gắng xuất chúng.
Vì vòng lặp này sử dụng một thử nghiệm rõ ràng để thoát ra, kết quả của thử nghiệm đó sẽ được để lại trên ngăn xếp, vì vậy chúng tôi loại bỏ nó như là điều đầu tiên chúng tôi làm (giá trị của nó không hữu ích).
(({})[(()()()())])
Mã này đẩy 4 và f - 4 lên trên một phần tử stack f , trong khi giữ nguyên phần tử đó. Chúng tôi đang tính toán định dạng cho lần lặp tiếp theo trước (trong khi chúng tôi có 4 liên tục tiện dụng) và đồng thời sắp xếp ngăn xếp theo đúng thứ tự cho một số phần tiếp theo của chương trình: chúng tôi sẽ sử dụng f làm định dạng cho lần lặp này và 4 là cần thiết trước đó.
(({})( … )[{}])
Điều này lưu một bản sao f - 4 trên ngăn xếp làm việc, để chúng ta có thể sử dụng nó cho lần lặp tiếp theo. (Giá trị của f sẽ vẫn có mặt tại thời điểm đó, nhưng nó sẽ được ở một nơi vụng về trên stack, và thậm chí nếu chúng ta có thể cơ động nó vào địa điểm chính xác, chúng tôi phải bỏ ra chu kỳ trừ đi 4 từ nó, và chu trình in mã để thực hiện phép trừ đó. Đơn giản hơn rất nhiều để lưu trữ mã ngay bây giờ.)
{{}{}((()[()]))}{}
Một thử nghiệm để xem nếu độ lệch là 4 (tức là f - 4 là 0). Nếu đúng như vậy, chúng tôi đang in bộ định dạng chuỗi dữ liệu, vì vậy chúng tôi cần chạy chuỗi dữ liệu và bộ định dạng của nó 240 lần thay vì chỉ một lần ở phần bù này. Mã này khá đơn giản: nếu f - 4 không khác, hãy thay thế f - 4 và 4 bằng một cặp số không; sau đó trong cả hai trường hợp, bật phần tử ngăn xếp trên cùng. Bây giờ chúng ta có một số trên f trên ngăn xếp, là 4 (nếu chúng ta muốn in lần lặp này tới 241 lần) hoặc 0 (nếu chúng ta chỉ muốn in nó một lần).
(
((((((({})){}){}{})){}{}){}){}
()
)
Đây là một loại thú vị của hằng số Brain-Flak / Mini-Flak; dòng dài ở đây đại diện cho số 60. Bạn có thể bị nhầm lẫn ở chỗ thiếu ()
, thường là ở khắp mọi nơi trong các hằng số Brain-Flak; đây không phải là một số thông thường, mà là một số của Giáo hội, diễn giải các số như là một phép toán trùng lặp. Ví dụ, chữ số Church cho 60, được thấy ở đây, tạo ra 60 bản sao đầu vào của nó và kết hợp tất cả chúng lại với nhau thành một giá trị duy nhất; trong Brain-Flak, những thứ duy nhất chúng ta có thể kết hợp là những con số thông thường, ngoài ra, vì vậy chúng ta cuối cùng đã thêm 60 bản sao của đỉnh ngăn xếp và do đó nhân số đỉnh của ngăn xếp với 60.
Là một lưu ý phụ, bạn có thể sử dụng công cụ tìm số Underload , tạo ra các số Church trong cú pháp Underload, để tìm số thích hợp trong Mini-Flak. Các chữ số dưới tải (khác 0) sử dụng các thao tác "phần tử ngăn xếp trên cùng trùng lặp" :
và "kết hợp hai phần tử ngăn xếp trên cùng" *
; cả hai thao tác này đều tồn tại trong Brain-Flak, vì vậy bạn chỉ cần dịch :
sang )
, *
nạp {}
trước {}
và thêm (
vào lúc bắt đầu để cân bằng (điều này là sử dụng hỗn hợp kỳ lạ của ngăn xếp chính và ngăn xếp hoạt động, nhưng nó hoạt động).
Đoạn mã cụ thể này sử dụng số 60 của nhà thờ (có hiệu quả là một đoạn "nhân với 60"), cùng với số gia, để tạo biểu thức 60 x + 1. Vì vậy, nếu chúng ta có 4 từ bước trước, điều này mang lại cho chúng ta một giá trị là 241 hoặc nếu chúng ta có 0, chúng ta chỉ nhận được giá trị là 1, tức là điều này sẽ tính toán chính xác số lần lặp mà chúng ta cần.
Sự lựa chọn của 241 không phải là ngẫu nhiên; nó là một giá trị được chọn là a) xấp xỉ độ dài mà chương trình sẽ kết thúc bằng mọi cách và b) 1 hơn 4 lần một số tròn. Số tròn, 60 trong trường hợp này, có xu hướng có các biểu diễn ngắn hơn là số của Giáo hội vì bạn có sự linh hoạt hơn trong các yếu tố để sao chép. Chương trình có chứa phần đệm sau này để mang lại độ dài chính xác lên tới 241.
{
({}(
…
)[{}()])
}{}
Đây là một vòng lặp for, giống như vòng lặp đã thấy trước đó, chỉ đơn giản là chạy mã bên trong nó một số lần bằng với đỉnh của ngăn xếp chính (mà nó tiêu thụ; chính bộ đếm vòng lặp được lưu trữ trên ngăn xếp làm việc, nhưng khả năng hiển thị của được gắn với mức lồng nhau của chương trình và do đó không thể có bất cứ thứ gì ngoại trừ chính vòng lặp for tương tác với nó). Điều này thực sự chạy chuỗi dữ liệu và trình định dạng của nó 1 hoặc 241 lần và vì hiện tại chúng tôi đã bật tất cả các giá trị mà chúng tôi đang sử dụng để tính toán luồng điều khiển từ ngăn xếp chính, chúng tôi có định dạng để sử dụng trên đầu trang, sẵn sàng cho các định dạng để sử dụng.
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
Nhận xét ở đây không hoàn toàn không có hứng thú. Đối với một điều, có một vài lệnh Brain-Flak; phần )
cuối được tạo ra một cách tự nhiên như là một tác dụng phụ của cách chuyển đổi giữa các phân đoạn khác nhau của chương trình, do đó, (
khi bắt đầu được thêm thủ công để cân bằng nó (và mặc dù độ dài của nhận xét bên trong, đặt một nhận xét bên trong một ()
lệnh vẫn là một ()
lệnh, vì vậy tất cả những gì nó làm là thêm 1 vào giá trị trả về của chuỗi dữ liệu và định dạng của nó, một cái gì đó mà vòng lặp for hoàn toàn bỏ qua).
Đáng chú ý hơn, những ký tự NUL khi bắt đầu bình luận rõ ràng không bù đắp cho bất cứ điều gì (ngay cả sự khác biệt giữa +8 và -4 cũng không đủ để biến (
NUL thành NUL). Đó là những phần đệm thuần túy để đưa chuỗi dữ liệu 239 phần tử lên tới 241 phần tử (dễ dàng tự trả tiền: sẽ mất nhiều hơn hai byte để tạo 1 so với 239 thay vì 1 so với 241 khi tính toán số lần lặp cần thiết ). NUL được sử dụng làm ký tự đệm bởi vì nó có mật mã thấp nhất có thể (làm cho mã nguồn cho chuỗi dữ liệu ngắn hơn và do đó nhanh hơn để xuất ra).
{}({}())
Bỏ phần tử ngăn xếp trên cùng (định dạng chúng tôi đang sử dụng), thêm 1 vào phần tiếp theo (ký tự cuối cùng được xuất, tức là ký tự đầu tiên được in, của phần chương trình chúng tôi vừa định dạng). Chúng tôi không cần định dạng cũ nữa (định dạng mới đang ẩn trên ngăn xếp hoạt động); và phần tăng là vô hại trong hầu hết các trường hợp và thay đổi '
ở một đầu của biểu diễn nguồn của bộ định dạng chuỗi dữ liệu thành một (
(yêu cầu trên ngăn xếp cho lần tiếp theo chúng ta chạy bộ định dạng, để định dạng chính chuỗi dữ liệu). Chúng ta cần một phép biến đổi như thế trong phần ngoài hoặc phần giới thiệu, bởi vì việc bắt buộc mỗi phần tử định dạng chuỗi dữ liệu bắt đầu (
sẽ làm cho nó phức tạp hơn một chút (vì chúng ta cần phải đóng (
và sau đó hoàn tác lại hiệu ứng của nó) vàbằng cách nào đó chúng ta cần phải tạo thêm (
ở đâu đó bởi vì chúng ta chỉ có gần 241 bản sao của bộ định dạng, không phải tất cả là 241 (vì vậy tốt nhất là một nhân vật vô hại như '
là một nhân vật bị thiếu).
(({})(()()()()){})
Cuối cùng, kiểm tra thoát vòng lặp. Đỉnh hiện tại của ngăn xếp chính là định dạng chúng ta cần cho lần lặp tiếp theo (vừa quay trở lại ngăn xếp hoạt động). Điều này sao chép nó và thêm 8 vào bản sao; giá trị kết quả sẽ bị loại bỏ trong lần tiếp theo vòng lặp. Tuy nhiên, nếu chúng ta chỉ in phần giới thiệu, phần bù là -4 nên phần bù cho "lần lặp tiếp theo" sẽ là -8; -8 + 8 là 0, do đó vòng lặp sẽ thoát thay vì tiếp tục lặp lại sau đó.