Haskell , 1080 1033 bajtów
;
f=
g
ij=f
a =hi
hi = g
hij= ij
g ' ' =0
g '"' =0;
g '$' =0;
g '&' =0-0
g '(' =0-0-0
g '*' =0-0-0;
g ',' =0-0-0;
g '.' =0-0-0-0
g '0' =0-0-0-0-0
g '2' =0-0-0-0-0;
g '4' =0-0-0-0-0;
g '6' =0; g '8' =0
g ':' =0; g '<' =0-0
g '>' =0; g '@' =0-0;
g 'B' =0; g 'D' =0-0;
g 'F' =0; g 'H' =0-0-0
g 'J' =0; g 'L' =0-0-0-0
g 'N' =0; g 'P' =0-0-0-0;
g 'R' =0; g 'T' =0-0-0-0;
g 'V' =0; g 'X' =0-0-0-0-0
g 'Z' =0; g '^' =0; g '`' =0
g 'b' =0; g 'd' =0; g 'f' =0;
g 'h' =0; g 'j' =0; g 'l' =0;
g 'n' =0; g 'p' =0; g 'r' =0-0
g 't' =0; g 'v' =0; g 'x' =0-0-0
g 'z' =0; g '\92' =0-0; g '|' =0;
g '~' =0; g y = 1 ;z=0; i(-0)z=z;
i m('\10':y ) ="y"; ; ; ; ; ; ; ;
i m(mnmnmnmnm:y ) = i(m - 1 ) y ; ;
i k m ="y"; ; k i [ ] =01<1010101010;
k m('\10':y ) = k(m + 1 )(i m y ) ; ;
k m y =01>10; m o = k 1$'\10':o ; ; ;
o i('\10':y ) = o i y ; ; ; ; ; ; ; ; ;
o i(k:y )|g k<i = o(1 - i ) y ; ; ; ; ; ;
o i(k:y )|g k>i = o(1 - i ) y ; ; ; ; ; ;
o i [ ] =01<10; o i y =01>10;v=01>10101010
s y|o 1 y = m y|o(-0) y = m y ; s y =v; ; ;
Wypróbuj online!
Wyjaśnienie
To było dość interesujące zadanie dla Haskell.
Parytet
Na początek potrzebujemy jakiegoś sposobu ustalenia, czy znak ma parzysty czy nieparzysty punkt kodowy. Normalnym sposobem, aby to zrobić, jest uzyskanie punktu kodowego i zmodyfikowanie go o 2. Jednak, jak wiadomo, uzyskanie punktu kodowego znaku wymaga importu, co z powodu ograniczenia źródła oznacza, że nie można używany. Bardziej doświadczony Haskeller pomyślałby o użyciu rekurencji. Charsą częścią tej Enumklasy, dzięki czemu możemy zdobyć ich poprzedników i następców. Jednak predi succto też zarówno bezużyteczny, ponieważ nie alternatywny bajtów parzystości.
Więc to nas mocno utknęło, że prawie nie możemy manipulować znakami. Rozwiązaniem tego jest zaszyfrowanie wszystkiego. Możemy reprezentować (większość) parzystych znaków jako literały, szanse, z którymi mamy problem, ponieważ 'są nieparzyste, więc nie może znajdować się obok samego znaku, co uniemożliwia dosłowne wyrażenie większości nieparzystych znaków. Więc na stałe kodujemy wszystkie parzyste bajty, a następnie dodajemy catch dla wszystkich nieparzystych bajtów na końcu.
Problem Bajty
Możesz zauważyć, że istnieją pewne nawet bajty, dla których literałów nie można utworzyć, zawijając je w cudzysłowy. Są to niedrukowalne, nowe linie i \. Nie musimy się martwić o materiały niedrukowalne, ponieważ dopóki nie użyjemy żadnego z nich, nie musimy weryfikować. W rzeczywistości nadal możemy używać dziwnych, niedrukowalnych elementów, takich jak tab, po prostu nie muszę tego robić. Newline można zignorować, ponieważ i tak zostanie on przycięty z programu. (Moglibyśmy dodać znak nowej linii, ponieważ jego kod jest raczej wygodny, ale nie musimy). To pozostawia \, teraz \ma kododpunkt 92, który dogodnie jest liczbą nieparzystą, po której następuje parzysta liczba, więc na \92przemian wyrównuje i wyrównuje, a zatem dosłownie'\92'jest całkowicie poprawny. Później, kiedy będziemy musieli reprezentować znak nowej linii, zauważymy, że na szczęście ma tę samą właściwość '\10'.
Problemy z odstępami
Teraz, aby rozpocząć pisanie rzeczywistego kodu, musimy umieć znaczną liczbę znaków w jednym wierszu. Aby to zrobić, napisałem cap:
;
f=
g
ij=f
a =hi
hi = g
hij= ij
Czapka nie robi nic oprócz ważnego Haskella. Początkowo miałem nadzieję stworzyć definicje, które pomogą nam w kodzie później, ale tak się nie stało. Są też łatwiejsze sposoby na dodanie znaku, na przykład białe znaki i średniki, ale nie oszczędzają w ten sposób bajtów, więc nie zadałem sobie trudu, aby go zmienić.
Hardcoder
Teraz, gdy mam już wystarczająco dużo miejsca w linii, zaczynam wartości na stałe. Jest to w większości dość nudne, ale jest kilka interesujących rzeczy. Po raz pierwszy linie stają się jeszcze dłuższe, możemy użyć ;do umieszczenia wielu deklaracji w linii, co oszczędza nam mnóstwo bajtów.
Po drugie, ponieważ nie zawsze możemy zaczynać linię od gtak często, musimy nieco wciąć linie. Teraz Haskell naprawdę dba o wcięcia, więc będzie na to narzekać. Jeśli jednak ostatnia linia przed linią wcięcia kończy się średnikiem, pozwoli na to. Dlaczego? Nie mam najsłabszego, ale działa. Musimy więc pamiętać o umieszczeniu średników na końcu linii.
Bloki konstrukcyjne funkcji
Po wykonaniu hardcodera płynne przejście do końca programu. Musimy zbudować kilka prostych funkcji. Najpierw buduję wersję o dropnazwie i. iróżni się od droptego, że jeśli spróbujemy przejść poza koniec łańcucha, po prostu zwraca "y". iróżni się od upuszczania również tym, że jeśli spróbuje upuścić nową linię, zwróci "y", Przydadzą się, ponieważ później, gdy sprawdzimy, czy program jest trójkątem, pozwoli nam to powrócić, Falsegdy ostatni wiersz nie jest kompletny, lub gdy linia kończy się wcześnie.
kknssTruen znaków z przodu. Następnie dzwonikn + 1False
Następnie zrobić alias k, m. mjest tylko kz 1pierwszym argumentem, a nowy wiersz jest dodawany do drugiego argumentu.
Następnie mamy o. obierze liczbę i ciąg znaków. Określa, czy bajty ciągu (ignorując znaki nowej linii) zmieniają się w parzystości (używając naszego g), zaczynając od numeru wejściowego.
Na koniec mamy, sktóry działa oz oboma 1i 0, jeśli któryś z nich się powiedzie, odracza się m. Jeśli zawiedzie oba, po prostu wraca False. To jest funkcja, której chcemy. Określa, że wejście jest trójkątne i naprzemienne.