BrainF ** k, 396 391 byte
>+>>++++[-<++++++++>]->,----------[++++++++++.>>++++++++[-<++++<------>>]<.,----------]-<+[-<+]->>+[-<<<<<++++++++++.[-]>[-<+>>.<]<[->+<]>+>>>[[->+]->>+<<<+[-<+]->]>+[-<->[[->+]->+>>+<<<<+[-<+]->]<+>->+[->+]->>[->+<]>+>++++++++++>>-<<[-<-[>>]<]<->>>+[-<<<+>>>[-<->]<+++++++++>>>+]++++++++[-<++++<++++++>>]<<<[-<<<<+[-<+]-<+>>+[->+]->>>>+<]>.>.[-]<[-]<<<[->+<]<<+[-<+]>+]>>[-]<<<-<+[-<+]->>+]
Tôi không thể cưỡng lại sự cám dỗ để làm điều này. Ít nhất là tam giác là mặt nhọn xuống.
Đầu vào xuất hiện dưới dạng một chuỗi các ký tự số theo sau là một dòng mới.
Đầu ra sẽ chứa một khoảng trống duy nhất trên mỗi dòng.
Ví dụ:
$ bf sd.bf
010
0 1 0
1 1
2
$ bf sd.bf
123456
1 2 3 4 5 6
3 5 7 9 1
8 2 6 0
0 8 6
8 4
2
$ bf sd.bf
9245322
9 2 4 5 3 2 2
1 6 9 8 5 4
7 5 7 3 9
2 2 0 2
4 2 2
6 4
0
Giải trình
Vì khá khó để giải thích mã từ góc độ chức năng, thay vào đó chúng ta có thể xem xét nó từ góc độ trạng thái của băng ở nhiều thời điểm khác nhau. Ý tưởng cốt lõi ở đây là hình tam giác mà chúng ta tạo ra được khởi tạo dưới dạng một mảng được đóng gói chặt chẽ (đối với BF, dù sao), thu nhỏ kích thước 1 mỗi lần lặp của một vòng lặp. Một suy nghĩ quan trọng khác là chúng tôi sử dụng 255
để chỉ ra một "trình giữ chỗ" mà chúng tôi có thể tìm kiếm trên băng.
Khởi tạo
Đây là bước dễ nhất. Khi bắt đầu chương trình, chúng tôi thực hiện như sau:
>+>>++++[-<++++++++>]->
Điều này buộc băng vào trạng thái sau (trong đó >N<
chỉ ra vị trí của con trỏ trên băng)
[ 0 1 32 255 >0< 0 0 ...]
Số đầu tiên ở đây là một vị trí "đệm". Chúng tôi sẽ không sử dụng nó trên cơ sở lâu dài, nhưng nó hữu ích để làm cho các thao tác nhỏ đơn giản hơn và để sao chép dữ liệu xung quanh.
Số thứ hai là số khoảng trắng mà chúng ta sẽ xuất ra ở đầu mỗi dòng, bắt đầu sau dòng đầu tiên. Dòng đầu tiên sẽ không có không gian hàng đầu.
Số thứ ba là ký tự không gian chúng ta xuất ra.
Số thứ tư là một trình giữ chỗ 255, để chúng ta có thể quay lại vị trí này tương đối dễ dàng.
Đầu vào
Từ vị trí này, chúng tôi sẽ đọc trong tất cả các nhân vật. Ở cuối bước này, chúng tôi hy vọng sẽ ở trong tình huống sau:
[ 0 1 32 255 a b c d e f ... >255< 0 0 ... ]
Trong đó a b c d e f ...
chỉ ra chuỗi các ký tự số đã được nhập (không phải dòng mới).
Chúng tôi thực hiện điều này với những điều sau đây:
,----------[++++++++++.>>++++++++[-<++++<------>>]<.,----------]-
Có một số sắc thái cho điều này. Trước hết, chúng tôi sẽ xuất ra mỗi ký tự khi chúng tôi nhận được chúng, và sau đó xuất ra một khoảng trắng sau nó. Thứ hai, chúng tôi không muốn sao chép giá trị ASCII vào băng, chúng tôi muốn sao chép chữ số thực tế. Thứ ba, chúng tôi muốn dừng lại khi chúng tôi đạt được một dòng mới và để mình ở một nơi tốt vào thời điểm đó.
Nói đầu vào của chúng tôi là 6723
. Sau đó, khi đọc phần đầu tiên 6
, băng của chúng tôi trông như thế này:
[ 0 1 32 255 >54< 0 0 ...]
Chúng tôi kiểm tra xem giá trị này không bằng 10
(một dòng mới ASCII) với ,----------[++++++++++
. Sau đó, chúng tôi in ra giá trị và tiếp tục bằng cách trừ đồng thời 48 từ giá trị đầu vào và thêm 32 vào giá trị bên cạnh nó ( >>++++++++[-<++++<------>>]<
), để lại cho chúng tôi ở đây:
[ 0 1 32 255 6 >32< 0 ...]
Lưu ý rằng trong suốt quá trình này, chúng ta có thể giả sử rằng tất cả các chữ số ở bên phải đầu vào của chúng ta là 0 - điều này có nghĩa là chúng ta không có nguy cơ làm hỏng bất kỳ trạng thái nào trước đó nếu chúng ta sử dụng các giá trị ở bên phải để tính toán 6 * 8
và 4 * 8
.
Bây giờ chúng ta xuất ký tự khoảng trắng mà chúng ta vừa tạo và nhập một đầu vào mới, xóa khoảng trống chúng ta đã tính ở đó. Cuối cùng, đầu vào sẽ bị chấm dứt bởi một dòng mới và vòng lặp sẽ thoát ra, để lại một 255
dòng mới sẽ là ( ,----------]-
). Đây là nhân vật giữ chỗ thứ hai chúng tôi sẽ sử dụng để điều hướng băng. Tại thời điểm này trong kịch bản của chúng tôi, băng của chúng tôi chính xác là thế này:
[ 0 1 32 255 6 7 2 3 >255< 0 0 ... ]
Phép tính
Cách thức hoạt động này là danh sách các chữ số giữa các 255
trình giữ chỗ của chúng tôi sẽ bị thu hẹp bởi mỗi lần lặp của vòng lặp. Khi nó chỉ còn lại 1 chữ số, chúng tôi đã hoàn thành và tạm dừng ngay lập tức (Lưu ý rằng, tại thời điểm này, mọi chữ số trong danh sách đó đã được xuất ra, vì vậy chúng tôi không phải lo lắng về việc xuất lại lần nữa).
Bây giờ chúng tôi sử dụng thủ thuật này để điều hướng đến 255
trình giữ chỗ đầu tiên : <+[-<+]-
. Điều này có hiệu quả tìm kiếm băng bên trái cho a 255
, không thay đổi gì ở giữa. Bây giờ chúng ta đã di chuyển con trỏ, chúng ta có thể kiểm tra điều kiện thoát của chúng ta: nếu chỉ có một chữ số trong danh sách, thì ô hai ô bên phải sẽ giữ 255
. Vì vậy, chúng tôi kiểm tra chống lại điều đó và bắt đầu một vòng lặp:>>+[-<<
Bước đầu tiên trong vòng lặp của chúng tôi là xuất ra một dòng mới. Vì vậy, chúng tôi di chuyển đến ô đầu tiên (ô đệm của chúng tôi), thêm 10 vào ô đó và xuất ra. Bước tiếp theo là xuất tất cả các ký tự không gian hàng đầu. Sau khi xuất chúng, chúng tôi tăng số lượng không gian hàng đầu. Các bước này được thực hiện bằng cách sau:
-<<<<<++++++++++.[-]>[-<+>>.<]<[->+<]>+>>>
Điều này khiến chúng ta trong trạng thái này:
[ 0 2 32 255 >6< 7 2 3 255 0 0 0 0 0 0 ]
Bước tiếp theo của chúng tôi là sao chép giá trị đầu tiên trong danh sách, qua chỗ giữ chỗ thứ hai 255
:
[[->+]->>+<<<+[-<+]->]
Về cơ bản, chúng tôi thực hiện điều này bằng cách nhảy qua lại giữa các trình giữ chỗ 255
, để chúng tôi ở đây:
[ 0 2 32 255 >0< 7 2 3 255 0 6 0 0 ... ]
Bây giờ chúng tôi bắt đầu một vòng lặp, lặp qua phần còn lại của danh sách, dừng lại khi chúng tôi nhấn 255
:>+[-<
Tại thời điểm này, chữ số ở bên trái của chúng tôi luôn là 0. Vì vậy, vì chúng tôi yêu thích chúng, chúng tôi đưa một người giữ chỗ 255
ở đó để chúng tôi có thể quay lại vị trí của mình trong danh sách. Bước tiếp theo là di chuyển vị trí thứ hai trong danh sách đến các vị trí xung quanh nơi chúng tôi đã di chuyển vị trí đầu tiên đến, qua vị trí thứ hai 255
. Các bước này được thực hiện bằng cách sau:
->
[[->+]->+>>+<<<<+[-<+]->]
Để chúng tôi ở đây: [ 0 2 32 255 255 >0< 2 3 255 7 6 7 0 ]
Bây giờ, cả 6
và 7
đã được chuyển đến một vị trí nơi tính toán có thể xảy ra. Chúng tôi cần hai bản sao 7
vì số tiếp theo trong danh sách cũng sẽ cần nó. Các 7
ngay sau khi 255
phục vụ mục đích này, trong khi người kia 7
sẽ được tiêu thụ bởi các tính toán.
Đầu tiên, chúng tôi thêm hai chữ số:
<+>->+[->+]->>
[->+<]>
Để chúng tôi ở đây:
[ 0 2 32 255 0 255 2 3 255 7 0 >13< 0 ]
Sự kết hợp các bước tiếp theo là phức tạp nhất. Chúng ta cần xem con số chúng ta chỉ đến có lớn hơn 10 hay không và nếu có, chúng ta sẽ trừ đi 10
. Trong thực tế, những gì chúng ta làm là chúng ta trừ 10 từ nó và xem liệu nó có chạm 0
vào bất kỳ điểm nào trong phép trừ hay không. Nếu có, chúng tôi thêm 10
lại sau. Khi kết thúc điều này, chúng ta nên có tổng modulo 10.
Prepare a 10 to the right
+>++++++++++
Leave yet another 255 for a loop condition later
>>-<<
If the number is greater than 10 end up one space to the left
else one space to the right
[-<-[>>]<]<->
Check if the previous 255 is two spaces to the right and if it is
add 10 back to our sum--we've subtracted too much
>>+[-<<<+>>>[-<->]<+++++++++>>>+]
Tại thời điểm này, chúng tôi đã hoàn thành mục tiêu. Chúng ta có tổng modulo 10! Ngoài ra, cho dù số đó có lớn hơn 10 hay không, chúng tôi sẽ kết thúc tại đây:
[ 0 2 32 255 0 255 2 3 255 7 0 3 0 0 >0< ]
Mục tiêu tiếp theo của chúng tôi là đưa ra số tiền mới này, theo dõi nó với một khoảng trắng và đưa nó trở lại danh sách của chúng tôi. Chúng tôi làm tất cả điều này với các kỹ thuật trước đây của chúng tôi về việc 255
mua sắm và thêm 48
vào tổng của chúng tôi, vì vậy tôi sẽ không đề cập chi tiết.
++++++++[-<++++<++++++>>]
<<<[-<<<<+[-<+]-<+>>+[->+]->>>>+<]
>.>.
Và chúng tôi ở đây: [ 0 2 32 255 3 255 2 3 255 7 0 0 51 >32< ]
Lưu ý cách chúng tôi đặt một 255
trình giữ chỗ bổ sung sau khi chúng tôi mới được tiêm 3
để chúng tôi không bị mất vị trí trong danh sách. Tại thời điểm này, chúng ta đã xuất tổng của chúng ta và không gian của nó, vì vậy chúng ta cần dọn sạch và trở lại trạng thái nơi lần lặp tiếp theo của vòng lặp này sẽ hoạt động. Chúng ta cần hiểu rõ ràng của chúng tôi 51
và 32
các tế bào, di chuyển 7
một lần để bên phải, và điều hướng đến danh sách giữ chỗ của chúng tôi để chúng tôi có thể bắt đầu lại.
[-]<[-]<<<[->+<]<<+[-<+]
Bây giờ, chúng ta đang ở đây: [ 0 2 32 255 3 >0< 2 3 255 0 7 0 ... ]
Đó chính xác là nơi chúng ta muốn trở thành lần lặp tiếp theo. Vì vậy, kiểm tra 255 và di chuyển trên! ( >+]
)
Khi chúng tôi bị loại khỏi vòng lặp, chúng tôi sẽ có một danh sách hoàn toàn mới - bao gồm các khoản tiền từ danh sách trước đó. Lần đầu tiên, nó sẽ trông như thế này:
[ 0 2 32 255 3 9 5 0 >0< ]
Bây giờ chúng tôi muốn lặp lại toàn bộ quá trình đó trong danh sách mới của chúng tôi, vì vậy chúng tôi ngồi phịch 255
xuống bên trái và bắt đầu lại tất cả! Chúng tôi cần phải dọn dẹp một chút >>[-]<<
và sau đó bỏ giữ chỗ của chúng tôi với <-
. Sau đó, chúng tôi ở cùng một nơi với chúng tôi sau khi nhập liệu, vì vậy chúng tôi có thể thoát khỏi việc thực hiện các kiểm tra tương tự: <+[-<+]->>+
và bùng nổ! Chúng tôi đã có vòng lặp đầy đủ của chúng tôi! Tất cả những gì chúng ta cần là khung đóng cửa và khi nó kết thúc, chúng ta đã xuất tất cả mọi thứ, vậy là xong : ]
.