BrainF ** k, 396 391 bajtów
>+>>++++[-<++++++++>]->,----------[++++++++++.>>++++++++[-<++++<------>>]<.,----------]-<+[-<+]->>+[-<<<<<++++++++++.[-]>[-<+>>.<]<[->+<]>+>>>[[->+]->>+<<<+[-<+]->]>+[-<->[[->+]->+>>+<<<<+[-<+]->]<+>->+[->+]->>[->+<]>+>++++++++++>>-<<[-<-[>>]<]<->>>+[-<<<+>>>[-<->]<+++++++++>>>+]++++++++[-<++++<++++++>>]<<<[-<<<<+[-<+]-<+>>+[->+]->>>>+<]>.>.[-]<[-]<<<[->+<]<<+[-<+]>+]>>[-]<<<-<+[-<+]->>+]
Nie mogłem oprzeć się pokusie zrobienia tego. Przynajmniej trójkąt jest spiczasty bokiem.
Wprowadzane dane są ciągiem znaków numerycznych, po których następuje pojedynczy znak nowej linii.
Dane wyjściowe będą zawierać pojedyncze końcowe miejsce w każdym wierszu.
Przykłady:
$ 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
Wyjaśnienie
Ponieważ trudno jest wyjaśnić kod z perspektywy funkcjonalnej, możemy zamiast tego spojrzeć na niego z perspektywy stanu taśmy w różnych momentach. Podstawową ideą jest to, że trójkąt, który wysyłamy, jest inicjalizowany jako ciasno upakowana (w każdym razie BF) tablica, która zmniejsza się o 1 przy każdej iteracji pętli. Inną ważną myślą jest to, że używamy 255
do wskazania „symbolu zastępczego”, który możemy wyszukać na taśmie.
Inicjalizacja
To najłatwiejszy krok. Na początku programu wykonujemy następujące czynności:
>+>>++++[-<++++++++>]->
Wymusza to przejście taśmy w następujący stan (gdzie >N<
wskazuje położenie wskaźnika na taśmie)
[ 0 1 32 255 >0< 0 0 ...]
Pierwszą liczbą tutaj jest lokalizacja „buforowa”. Nie będziemy go używać długoterminowo, ale przydatne jest uproszczenie niewielkich operacji i kopiowanie danych.
Druga liczba to liczba spacji, które będziemy wypisywać na początku każdej linii, zaczynając od pierwszej linii. Pierwsza linia nie będzie miała spacji wiodących.
Trzecia liczba to znak spacji, który wypisujemy.
Czwarta liczba to symbol zastępczy 255, dzięki czemu możemy stosunkowo łatwo wrócić do tej pozycji.
Wkład
Z tej pozycji będziemy czytać wszystkie postacie. Pod koniec tego kroku mamy nadzieję, że znajdziemy się w następującej sytuacji:
[ 0 1 32 255 a b c d e f ... >255< 0 0 ... ]
Gdzie a b c d e f ...
wskazuje ciąg znaków numerycznych, który został wprowadzony (nie nowy wiersz).
Osiągamy to poprzez:
,----------[++++++++++.>>++++++++[-<++++<------>>]<.,----------]-
Są w tym pewne niuanse. Po pierwsze, wypisujemy każdy znak, gdy go otrzymamy, a następnie wypisujemy po nim spację. Po drugie, nie chcemy kopiować wartości ASCII na taśmę, chcemy skopiować rzeczywistą cyfrę. Po trzecie, chcemy przestać, kiedy trafimy na nową linię i pozostawimy się w tym miejscu w dobrym miejscu.
Powiedz, że nasz wkład to 6723
. Następnie, po przeczytaniu pierwszego 6
, nasza taśma wygląda następująco:
[ 0 1 32 255 >54< 0 0 ...]
Sprawdzamy, czy ta wartość nie jest równa 10
(nowa linia ASCII) z ,----------[++++++++++
. Następnie drukujemy wartość i kontynuujemy odejmując jednocześnie 48 od wartości wejściowej i dodając 32 do wartości obok niej ( >>++++++++[-<++++<------>>]<
), pozostawiając nas tutaj:
[ 0 1 32 255 6 >32< 0 ...]
Zauważ, jak w tym procesie możemy założyć, że wszystkie cyfry po prawej stronie naszych danych wejściowych są równe 0 - oznacza to, że nie grozi nam zniszczenie jakiegokolwiek poprzedniego stanu, jeśli użyjemy wartości po prawej stronie do obliczenia 6 * 8
i 4 * 8
.
Teraz wypisujemy wygenerowany właśnie znak spacji i przyjmujemy nowe dane wejściowe, usuwając obliczone tam miejsce. Ostatecznie wejście zostanie zakończone nową linią i pętla wyjdzie, pozostawiając miejsce, w 255
którym byłaby nowa linia ( ,----------]-
). Jest to drugi znak zastępczy, którego użyjemy do nawigacji na taśmie. W tym momencie naszego scenariusza nasza taśma jest dokładnie taka:
[ 0 1 32 255 6 7 2 3 >255< 0 0 ... ]
Obliczenie
Działa to w ten sposób, że lista cyfr między naszymi 255
symbolami zastępczymi będzie się zmniejszać o jedną iterację pętli. Gdy pozostanie w nim tylko 1 cyfra, jesteśmy skończeni i powinniśmy natychmiast się zatrzymać (zwróć uwagę, że w tym momencie każda cyfra z tej listy została już wydrukowana, więc nie musimy się martwić o jej ponowne wysłanie).
Mamy teraz skorzystać z tej sztuczki, aby przejść do pierwszego 255
zastępczy: <+[-<+]-
. To skutecznie przeszukuje taśmę po lewej stronie w poszukiwaniu 255
, nie zmieniając niczego pomiędzy. Teraz, gdy przesunęliśmy wskaźnik, możemy sprawdzić nasz warunek wyjścia: jeśli na liście jest tylko jedna cyfra, to komórka pomieści dwie spacje po prawej stronie 255
. Sprawdzamy więc to i uruchamiamy pętlę:>>+[-<<
Pierwszym krokiem w naszej pętli jest wygenerowanie nowego wiersza. Przechodzimy więc do pierwszej komórki (naszej komórki buforowej), dodajemy do niej 10 i wyprowadzamy. Następnym krokiem jest wyprowadzenie wszystkich wiodących znaków spacji. Po ich wyliczeniu zwiększamy naszą liczbę o liczbę wiodących spacji. Kroki te są realizowane przez:
-<<<<<++++++++++.[-]>[-<+>>.<]<[->+<]>+>>>
Co pozostawia nas w tym stanie:
[ 0 2 32 255 >6< 7 2 3 255 0 0 0 0 0 0 ]
Naszym następnym krokiem jest skopiowanie pierwszej wartości z listy obok drugiego symbolu zastępczego 255
:
[[->+]->>+<<<+[-<+]->]
Zasadniczo robimy to, przeskakując tam iz powrotem między naszymi symbolami zastępczymi 255
, pozostawiając nas tutaj:
[ 0 2 32 255 >0< 7 2 3 255 0 6 0 0 ... ]
Rozpoczynamy teraz pętlę, iterując resztę listy, zatrzymując się po trafieniu 255
:>+[-<
W tym momencie cyfra po naszej lewej stronie wynosi zawsze 0. Tak więc, ponieważ je kochamy, wstawiamy 255
tam symbol zastępczy , abyśmy mogli wrócić do naszego miejsca na liście. Następnym krokiem jest przeniesienie drugiego miejsca na liście do lokalizacji, w których przenieśliśmy pierwsze miejsce, obok drugiego symbolu zastępczego 255
. Kroki te są realizowane przez:
->
[[->+]->+>>+<<<<+[-<+]->]
Zostawiając nas tutaj: [ 0 2 32 255 255 >0< 2 3 255 7 6 7 0 ]
Teraz zarówno 6
i , jak i 7
zostały przeniesione do miejsca, w którym mogą wystąpić obliczenia. Potrzebujemy dwóch kopii, 7
ponieważ następny numer na liście również będzie go potrzebował. 7
Natychmiast po 255
służy temu celowi, podczas gdy inne 7
będą spożywane przez obliczeniach.
Najpierw dodajemy dwie cyfry:
<+>->+[->+]->>
[->+<]>
Zostawiając nas tutaj:
[ 0 2 32 255 0 255 2 3 255 7 0 >13< 0 ]
Kolejna kombinacja kroków jest najbardziej skomplikowana. Musimy sprawdzić, czy wskazana liczba jest większa niż 10, a jeśli tak, to odejmujemy 10
. W rzeczywistości odejmujemy od niego 10 i sprawdzamy, czy trafi 0
w którymkolwiek punkcie odejmowania. Jeśli tak, dodajemy 10
później. Na koniec powinniśmy mieć sumę 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
>>+[-<<<+>>>[-<->]<+++++++++>>>+]
W tym momencie osiągnęliśmy cel. Mamy sumę modulo 10! Ponadto, niezależnie od tego, czy liczba była większa niż 10, skończymy tutaj:
[ 0 2 32 255 0 255 2 3 255 7 0 3 0 0 >0< ]
Naszym kolejnym celem jest wyprowadzenie tej nowej sumy, dodanie jej spacji i wprowadzenie jej z powrotem na naszą listę. Robimy to wszystko za pomocą naszych poprzednich technik 255
kupowania i dodawania 48
do naszej sumy, więc nie będę tego szczegółowo omawiać.
++++++++[-<++++<++++++>>]
<<<[-<<<<+[-<+]-<+>>+[->+]->>>>+<]
>.>.
I jesteśmy tutaj: [ 0 2 32 255 3 255 2 3 255 7 0 0 51 >32< ]
zauważ, jak 255
wstawiamy dodatkowy symbol zastępczy po naszym nowym wstrzyknięciu, 3
aby nie stracić miejsca na liście. W tym momencie wyprowadziliśmy naszą sumę i jej przestrzeń, więc musimy wyczyścić i powrócić do stanu, w którym zadziała kolejna iteracja tej pętli. Musimy wyczyścić nasze 51
i 32
komórki, przesunąć 7
raz w prawo i przejść do naszego symbolu zastępczego listy, abyśmy mogli zacząć od nowa.
[-]<[-]<<<[->+<]<<+[-<+]
Teraz jesteśmy tutaj: [ 0 2 32 255 3 >0< 2 3 255 0 7 0 ... ]
właśnie tam chcemy być na następnej iteracji. Więc sprawdź 255 i idź dalej! ( >+]
)
Kiedy zostaniemy wysadzeni z pętli, będziemy mieli zupełnie nową listę - złożoną z sum z poprzedniej listy. Za pierwszym razem będzie to wyglądać tak:
[ 0 2 32 255 3 9 5 0 >0< ]
Teraz chcemy powtórzyć cały proces na naszej nowej liście, więc przeskoczymy w 255
dół i zaczniemy od nowa! Musimy zrobić trochę czyszczenia >>[-]<<
, a następnie upuścić nasz symbol zastępczy <-
. Potem jesteśmy dokładnie w tym samym miejscu, w którym byliśmy po wejściu, więc możemy uniknąć tych samych kontroli: <+[-<+]->>+
i bum! Mamy pełną pętlę! Wszystko, czego potrzebujemy jest nawias zamykający, a kiedy kończy mamy już wyjścia wszystko, więc skończymy: ]
.