Wskazówki do gry w golfa w Husk

15

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ź.

Pan Xcoder
źródło
1
@totallyhuman pierwsza odpowiedź Husk Nadal nie tak nowa
H.PWiz

Odpowiedzi:

10

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:

≠  Numbers: Absolute difference
   Chars:   Absolute difference of code points
   Lists:   First Index where the differ

Comparisons <, >, ≤, ≥:

For strict comparisons:
Numbers,Chars:  max 0 (the appropriate difference¹)
Lists: The first index where the comparison between the two lists is true

For non-strict comparisons:
Numbers,Chars: max 0 (the appropriate difference + 1)
Lists: Either the result of the strict comparison or, if they are equal,
       the length of the list + 1

ṗ  Index into the list of prime numbers

V  The index of the first element for which the condition is true

€  The first index of that element/substring in the list

£  Works like €

&  Given two arguments of the same type will return the second argument if false,
   otherwise will return the first argument

|  Given two arguments of the same type will return the second argument if true,
   otherwise will return the first argument

¦  Return the quotient if divisibility holds

Λ,E,Ë  Will all return length+1 in truthy cases

Char predicates:
□,±,√,D,½  will each return the codepoint of its argument on truthy cases

¹ 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

H.PWiz
źródło
7

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:

+----------+----------+
|Index     |Function  |
+----------+----------+
|1         |´ (argdup)|
+----------+----------+
|2         |` (flip)  |
+----------+----------+
|3         |m (map)   |
+----------+----------+
|4         |z (zip)   |
+----------+----------+
|5         |S (hook)  |
+----------+----------+

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.

Erik the Outgolfer
źródło
5

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

f
g

będzie odnosić się do funkcji g. Teraz, jeśli zawsze stosujesz dane wejściowe do funkcji g(i używasz jej wiele razy); coś takiego:

f₁⁰[...]g₁⁰[...]
h

Powinieneś wprowadzić lambda, ponieważ oszczędza to 1 bajt na każde dodatkowe użycie:

λf⁰[...]g⁰[...])h

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
5

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 Haskella

f (x : xs) = <something>
f [] = <something else>

gdzie <something>jest ekspresyjny zawierający x, xsi ewentualnie f. Istnieją 4 przeciążenia Γ, z których każdy działa nieco inaczej.

list

Pierwsze przeciążenie listprzyjmuje wartość ai funkcję binarną f. Zwraca nową funkcję, która pobiera listę, zwraca, ajeśli jest pusta, i wywołuje fgłowę i ogon , jeśli nie jest pusta . Na przykład Γ_1€pobiera listę, zwraca, -1jeśli jest pusta, a indeks pierwszego wystąpienia pierwszego elementu w ogonie, jeśli nie.

listN

Drugie przeciążenie, listNjest podobne do list, z tą różnicą, że ajest 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ą jest 0.

W praktyce listNjest 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ść ai funkcję fi zwraca nową funkcję, gktóra pobiera listę. Jednak ten czas fwymaga dodatkowego argumentu funkcji, który gsam 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, że listFimplementuje ogólny schemat rekurencji na listach. listFnie jest używane bardzo często, ponieważ wyraźna rekurencja z list/ listNma zwykle taką samą długość lub krótszą, jak w tej odpowiedzi .

listNF

listNF jest do listF tego, co listNjest list: dane wejściowe asą 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! Funkcja fjest jawną lambda λ·:o⁰↔, której argumentem jest cała funkcja. Za pomocą ftego 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.

Zgarb
źródło
3

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>Llista 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

\x -> α (\y -> β x y) x

§αβγ jest równa

\x -> α (\y -> β x y) (γ x)

i ~αβγjest równoważne z

\x y -> α (\z -> β x z) (γ y)

tak długo, jak typy są zgodne.

Jako kolejny konkretny przykład §►δṁ≠Pznajduje 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ę).

Zgarb
źródło
3

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ć lastelement pustej listy. Aby to osiągnąć, wykorzystuje predefiniowane wartości, oto lista wartości domyślnych, maksimów i minimów:

.------------------------------------.---------------.----------.-------.
|   Type (X and Y are placeholders)  | default (def) |    max   |  min  |
|------------------------------------|---------------|----------|-------|
|       Character (C)                |      ' '      | \1114111 | \NUL  |
|       Numbers   (N)                |       0       |   Inf    | -Inf  |
|       List of X (LX)               |      []       |  ∞ max   |   []  | *
|       Function :: X -> Y           | const (def Y) |   n/a    |  n/a  |
'------------------------------------'---------------'----------'-------'

* 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ład husk -u "▼" "[]:LLN"zwróci nieskończoną listę Inf), wartości domyślne są używane w kilku miejscach:

  • składanie pustych list bez samodzielnego podawania wartości ( Fi )
  • poprzedzająca wartość domyślna (z Θ)
  • gdy read ( r) kończy się niepowodzeniem
  • uzyskiwanie pierwszego / ostatniego elementu ( ←→) lub indeksowanie do jednego ( !)
  • dopasowywanie wzorców ( Γ) na pustych listach
  • za pomocą lub na pustych listach
ბიმო
źródło