Husk to całkiem nowy język golfa, stworzony przez użytkowników PPCG Leo i Zgarba . Zaczęło być coraz bardziej konkurencyjne, często pozostając blisko, a nawet pokonując języki, które są bardzo zwięzłe, takie jak Jelly i 05AB1E.
Wymieńmy niektóre techniki gry w golfa, które są specyficzne dla Husk. Jak zawsze, napisz jedną wskazówkę na odpowiedź.
Odpowiedzi:
Użyj wartości zwracanej z predykatów
W Husk funkcje, które testują swoje dane wejściowe dla jakiejś właściwości, zwykle zwracają znaczący wynik w prawdziwych przypadkach, ponieważ jakakolwiek dodatnia liczba całkowita jest prawdziwa.
Przykłady:
¹ odpowiednia różnica oznacza różnicę punktów kodowych dla znaków. Odnosi się także do kolejności argumentów. to znaczy
<x y
, że byłobyx-y
źródło
Użyj przepełnionych etykiet linii
Jak być może już wiesz,
[₀-₉]+|[₀-₉]
regex dla składni wywołuje inną linię niż ta, w której aktualnie się znajdujesz.Ta wskazówka jest szczególnie przydatna, jeśli chcesz, aby funkcja zdefiniowana w konkretnym wierszu była wywoływana jako argument więcej niż jednej funkcji w poniższej tabeli lub jako argument jednej lub więcej funkcji poniżej.
Tabela funkcji:
Linie w kodzie są oznaczone odpowiednimi indeksami opartymi na 0, od góry do dołu. Jeśli M <N , gdzie M jest etykieta, a N jest liczbą wierszy w kodzie, etykieta tylko reprezentuje funkcję zdefiniowaną w linii M . Jeśli N ≤ M <N * 6 , reprezentuje funkcję z powyższej tabeli pod indeksem ⌊M ÷ N⌋ z funkcją zdefiniowaną w linii M mod N jako pierwszy argument. Jeśli N * 6 ≤ M , zgłaszany jest błąd indeksu.
źródło
Lambdy mogą być krótsze niż nowe funkcje
Jak zapewne wiesz, jeśli masz program wieloliniowy, możesz odwoływać się do wierszy z indeksami dolnymi
₀…₉
, na przykład w przypadku₁
będzie odnosić się do funkcjig
. Teraz, jeśli zawsze stosujesz dane wejściowe do funkcjig
(i używasz jej wiele razy); coś takiego:Powinieneś wprowadzić lambda, ponieważ oszczędza to 1 bajt na każde dodatkowe użycie:
Odwrotność może być również prawdą
W przypadku autoreferencyjnej lambdas (
φχψ
) istnieje szczególny przypadek, w którym dane wejściowe są stosowane bezpośrednio do funkcji rekurencyjnej, w takich przypadkach lepiej jest użyć indeksu dolnego₀
zamiast zdefiniować nową lambda i użyć⁰
.źródło
Zastosowania
Γ
Głównym zastosowaniem wbudowanego
Γ
, znanego jako dopasowanie wzorca do list lub dekonstrukcja listy , jest podzielenie listy na głowę i ogon i zastosowanie na nich funkcji binarnej. Odpowiada to idiomowi dopasowującemu wzór Haskellagdzie
<something>
jest ekspresyjny zawierającyx
,xs
i ewentualnief
. Istnieją 4 przeciążeniaΓ
, z których każdy działa nieco inaczej.list
Pierwsze przeciążenie
list
przyjmuje wartośća
i funkcję binarnąf
. Zwraca nową funkcję, która pobiera listę, zwraca,a
jeśli jest pusta, i wywołujef
głowę i ogon , jeśli nie jest pusta . Na przykładΓ_1€
pobiera listę, zwraca,-1
jeśli jest pusta, a indeks pierwszego wystąpienia pierwszego elementu w ogonie, jeśli nie.listN
Drugie przeciążenie,
listN
jest podobne dolist
, z tą różnicą, żea
jest pomijane i zamiast tego używana jest domyślna wartość typu zwracanego. Na przykładΓ€
jest równoważneΓ0€
, ponieważ domyślną wartością liczbową jest0
.W praktyce
listN
jest używany częściej niżlist
, ponieważ wartość domyślna jest albo nieistotna, albo dokładnie to, czego potrzebujesz. Częstym wzorem jestΓ~αβγ
, gdzieαβγ
są trzy funkcje; dotyczyβ
to pierwszego elementu iγ
ogona i łączy wyniki zα
. Użyto go np. W tej odpowiedzi . Inne wzorce obejmująΓo:α
zastosowanieα
tylko do pierwszego elementu iΓ·:mα
zastosowanieα
do wszystkich elementów oprócz pierwszego. To drugie zostało użyte w tej odpowiedzi .listF
Trzecie przeciążenie jest nieco bardziej zaangażowane. Podobnie
list
, przyjmuje wartośća
i funkcjęf
i zwraca nową funkcję,g
która pobiera listę. Jednak ten czasf
wymaga dodatkowego argumentu funkcji, któryg
sam w sobie, i może wywoływać go na dowolnej wartości (w tym między innymi na końcu listy wejściowej). Oznacza to, żelistF
implementuje ogólny schemat rekurencji na listach.listF
nie jest używane bardzo często, ponieważ wyraźna rekurencja zlist
/listN
ma zwykle taką samą długość lub krótszą, jak w tej odpowiedzi .listNF
listNF
jest dolistF
tego, colistN
jestlist
: dane wejściowea
są pomijane, a zamiast nich używana jest domyślna wartość typu zwracanego. W rzadkich przypadkach może być krótszy niż prawy pas, na przykład w tej odpowiedzi .Jako przykład rekurencyjnych wersji
Γ
funkcjaΓλ·:o⁰↔
przetasowuje listę w kolejności: pierwsza, ostatnia, druga, od drugiej do ostatniej, trzeciej, od trzeciej do ostatniej itd. Wypróbuj online! Funkcjaf
jest jawną lambdaλ·:o⁰↔
, której argumentem⁰
jest cała funkcja. Za pomocąf
tego można odwrócić ogon↔
, a następnie wywołać rekurencyjnie główną funkcjęo⁰
, a na koniec odrzucić głowę do tyłu·:
. Oczywiście,Γ·:o₀↔
bajt jest krótszy, ale nie działa, jeśli wiersz zawiera coś innego niż ta funkcja.źródło
Kombinatory można stosować do funkcji wyższego rzędu
Załóżmy, że masz listę liczb całkowitych X i chcesz policzyć całkowitą liczbę elementów X, które są większe niż długość (X) . Liczenie elementów, które spełniają predykat odbywa się za pomocą funkcji wyższego rzędu
#
, ale tutaj predykat (jest większa niż długość (X) ) zależy od X . Rozwiązaniem jest zastosowanie COMBINATORṠ
do#
i funkcjęo>L
, która sprawdza, czy lista jest krótsza niż liczby. W funkcji przekazywana jestṠ#o>L
lista Xo>L
, częściowo zastosowana funkcja jest przekazywana#
, a X jest podawany#
jako drugi argument.Ogólnie rzecz biorąc, jeśli
α
jest funkcją wyższego rzędu, funkcjaβ
binarna iγ
funkcja jednoargumentowaṠαβ
są równoważne pseudokodowi Haskell§αβγ
jest równai
~αβγ
jest równoważne ztak długo, jak typy są zgodne.
Jako kolejny konkretny przykład
§►δṁ≠P
znajduje permutację listy X, która maksymalizuje sumę różnic bezwzględnych do odpowiednich wartości X (δṁ≠
zamyka dwie listy przy użyciu różnicy bezwzględnej i pobiera sumę).źródło
Domyślne wartości łuski
Łuska nie jest tak surowa jak Haskell, w którym wpadasz w kłopoty, gdy na przykład próbujesz uzyskać
last
element pustej listy. Aby to osiągnąć, wykorzystuje predefiniowane wartości, oto lista wartości domyślnych, maksimów i minimów:* Tutaj ∞ powinna reprezentować nieskończoną listę odpowiedniego maksimum (patrz przykład poniżej)
Uwaga: W przypadku krotek (X, Y) użyje wartości dla każdego składnika osobno.
Kiedy są używane
Podczas gdy maksima i minima są używane tylko dla
▲▼
pustych list (na przykładhusk -u "▼" "[]:LLN"
zwróci nieskończoną listęInf
), wartości domyślne są używane w kilku miejscach:F
iḞ
)Θ
)r
) kończy się niepowodzeniem←→
) lub indeksowanie do jednego (!
)Γ
) na pustych listach►
lub◄
na pustych listachźródło