Liczba bajtów zakłada kodowanie ISO 8859-1.
+%`\B
¶$`:
1
Wypróbuj online!
Alternatywne rozwiązanie:
+1`\B
:$`:
1
Wyjaśnienie
Prawdopodobnie łatwiej będzie to wyjaśnić na podstawie mojej starej, mniej golfowej wersji, a następnie pokazać, jak ją skróciłem. Kiedyś konwertowałem dane binarne na dziesiętne w następujący sposób:
^
,
+`,(.)
$`$1,
1
Jedynym sensownym sposobem skonstruowania liczby dziesiętnej w Retinie jest zliczanie rzeczy (ponieważ Retina ma kilka funkcji, które pozwalają wydrukować liczbę dziesiętną reprezentującą ilość). Tak więc naprawdę jedynym możliwym podejściem jest konwersja binarnego na jednoargumentowy, a następnie policzenie liczby jednoznacznych cyfr. Liczy się ostatni wiersz, więc pierwsze cztery konwertują binarne na jednoargumentowe.
Jak to zrobimy? Ogólnie rzecz biorąc, aby przekonwertować z listy bitów na liczbę całkowitą, inicjalizujemy wynik, 0
a następnie przechodzimy przez bity od najbardziej do najmniej znaczącej, podwajamy wartość, którą już mamy, i dodajemy bieżący bit. Np. Jeśli liczba binarna jest 1011
, naprawdę obliczymy:
(((0 * 2 + 1) * 2 + 0) * 2 + 1) * 2 + 1 = 11
^ ^ ^ ^
Gdzie zaznaczyłem poszczególne bity dla przejrzystości.
Sztuczka polegająca na robieniu tego pojedynczo polega na tym, że a) podwojenie oznacza po prostu powtórzenie liczby ib) ponieważ liczymy 1
s na końcu, nie musimy nawet rozróżniać 0
s i 1
s w tym procesie. To stanie się wyraźniejsze za sekundę.
To, co robi program, polega na tym, że najpierw dodaje przecinek na początku jako znacznik ilości danych wejściowych, które już przetworzyliśmy:
^
,
Po lewej stronie znacznika będziemy mieć wartość, którą kumulujemy (która jest poprawnie zainicjalizowana do jednostkowej reprezentacji zera), a po prawej stronie wartości będzie następny bit do przetworzenia. Teraz stosujemy następujące podstawienie w pętli:
,(.)
$`$1,
Wystarczy spojrzeć na ,(.)
i i $1,
za każdym razem przesuwa znacznik nieco w prawo. Ale wstawiamy także $`
, czyli wszystko przed znacznikiem, tj. Bieżącą wartość, którą podwajamy. Oto poszczególne kroki przetwarzania danych wejściowych 1011
, w których zaznaczyłem wynik wstawiania$`
powyżej każdej linii (dla pierwszego kroku jest pusty):
,1011
1,011
_
110,11
___
1101101,1
_______
110110111011011,
Zobaczysz, że zachowaliśmy i podwoiliśmy zero wraz ze wszystkim innym, ale ponieważ pomijamy je na końcu, nie ma znaczenia, jak często je podwajaliśmy, o ile liczba 1
s poprawny. Jeśli je policzysz, jest 11
ich tylko to, czego potrzebujemy.
Pozostawia to pytanie, jak zagrać w golfa do 12 bajtów. Najdroższa część 18-bajtowej wersji wymaga użycia znacznika. Celem jest pozbycie się tego. Naprawdę chcemy podwoić każdy prefiks, więc pierwszy pomysł może być następujący:
.
$`$&
Problem polega na tym, że te zamiany odbywają się jednocześnie, więc pierwszy bit nie jest podwajany dla każdego bitu, ale jest po prostu kopiowany za każdym razem. Do wprowadzenia 1011
otrzymamy (oznaczenie wstawionego $`
):
_ __ ___
1101011011
Nadal musimy rekurencyjnie przetwarzać dane wejściowe, aby podwojony pierwszy prefiks został ponownie podwojony przez drugi i tak dalej. Jednym z pomysłów jest wstawianie znaczników wszędzie i wielokrotne zastępowanie ich przedrostkiem:
\B
,
+%`,
¶$`
Po pierwszym zastąpieniu każdego znacznika prefiksem musimy pamiętać, gdzie był początek danych wejściowych, więc wstawiamy również linie i używamy %
opcji, aby upewnić się, że następny $`
odbierze tylko najbliższe źródło.
To działa, ale wciąż jest za długie (16 bajtów, licząc 1
s na końcu). Co powiesz na to, żeby się odwrócić? Miejsca, w których chcemy wstawić znaczniki, są oznaczone \B
(pozycja między dwiema cyframi). Dlaczego po prostu nie wstawiamy prefiksów w tych pozycjach? To prawie działa, ale różnica polega na tym, że w poprzednim rozwiązaniu faktycznie usuwaliśmy jeden marker w każdej zamianie, i to ważne, aby proces został zakończony. Jednak\B
są to jednak postacie, tylko pozycje, więc nic nie zostanie usunięte. My może jednak zatrzymać\B
od dopasowania, zamiast tego wstawiając znak niecyfrowy w to miejsce. To zamienia granicę niebędącą słowem w granicę słowa, co jest równoważne wcześniejszemu usunięciu znaku znacznika. I tak działa 12-bajtowe rozwiązanie:
+%`\B
¶$`:
Dla kompletności, oto poszczególne etapy przetwarzania 1011
, z pustym wierszem po każdym kroku:
1
1:0
10:1
101:1
1
1:0
1
1:0:1
1
1:0
10:1:1
1
1:0
1
1:0:1
1
1:0
1
1:0:1:1
Ponownie okaże się, że ostatni wynik zawiera dokładnie 11 1
sekund.
Czy jako ćwiczenie dla czytelnika możesz zobaczyć, jak dość łatwo generalizuje się to do innych baz (dla kilku dodatkowych bajtów na przyrost w bazie)?