Consigli per giocare a golf a Husk


15

Husk è un linguaggio golfistico abbastanza nuovo, creato dagli utenti PPCG Leo e Zgarb . Ha iniziato a essere sempre più competitivo, spesso restando vicino o addirittura battendo le lingue note per essere molto concise, come Jelly e 05AB1E.

Elenchiamo alcune delle tecniche di golf che sono in qualche modo specifiche per Husk. Come sempre, si prega di inviare un suggerimento per risposta.



1
@totallyhuman prima risposta Husk Ancora non così nuova
H.Pwiz

Risposte:


10

Utilizzare il valore restituito dai predicati

In Husk, le funzioni che testano i loro input per alcune proprietà di solito restituiscono un risultato significativo in casi veritieri, poiché qualsiasi numero intero positivo è veritiero.

Esempi:

≠  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

¹ differenza appropriata significa differenza di punti di codice per i caratteri. Si riferisce anche all'ordine degli argomenti. vale a dire <x y, sarebbex-y


7

Usa etichette con linee straripanti

Come forse già saprai, [₀-₉]+|[₀-₉]è regex per la sintassi di chiamare una linea diversa da quella in cui ti trovi attualmente.

Questo suggerimento è particolarmente utile se si desidera che una funzione definita su una riga specifica venga chiamata come argomento di più di una delle funzioni nella tabella seguente o come argomento per una o più delle funzioni sottostanti e da sola.

Tabella delle funzioni:

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

Le righe nel codice sono etichettate con i rispettivi indici basati su 0, dall'alto verso il basso. Se M <N , dove M è l'etichetta e N è il numero di linee in codice, l'etichetta rappresenta solo la funzione definita alla riga M . Se N ≤ M <N * 6 , rappresenta la funzione della tabella sopra all'indice ⌊M ÷ N⌋ con la funzione definita alla linea M mod N come primo argomento. Se N * 6 ≤ M , viene generato un errore di indice.


5

Le lambda possono essere più brevi delle nuove funzioni

Come probabilmente saprai se hai un programma multilinea, puoi fare riferimento alle righe con i pedici ₀…₉, ad esempio nel caso di

f
g

farà riferimento alla funzione g. Ora se applichi sempre gli input alla funzione g(e la usi più volte); qualcosa come questo:

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

Dovresti introdurre un lambda perché ti fa risparmiare 1 byte per ogni uso aggiuntivo:

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

Anche il contrario potrebbe essere vero

Nel caso di lambda autoreferenziali ( φχψ), c'è il caso speciale in cui si applicano gli input direttamente alla funzione ricorsiva, in questi casi è meglio usare il pedice invece di definire un nuovo lambda e usare .


5

Usi di Γ

L'uso principale del built-in Γ, noto come pattern matching su elenchi o decostruzione di elenchi , è quello di dividere un elenco in testa e coda e applicare una funzione binaria su di essi. Ciò corrisponde al linguaggio di corrispondenza del modello di Haskell

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

dove <something>è un'espressione contenente x, xse possibilmente f. Ci sono 4 sovraccarichi di Γ, ciascuno dei quali funziona in modo leggermente diverso.

list

Il primo sovraccarico,, listassume un valore ae una funzione binaria f. Restituisce una nuova funzione che accetta un elenco, restituisce ase è vuoto e chiama fin testa e in coda se è vuoto. Ad esempio, Γ_1€accetta un elenco, restituisce -1se è vuoto e in caso contrario l'indice di prima occorrenza del primo elemento nella coda.

listN

Il secondo sovraccarico, listNè simile a list, tranne che aviene omesso e al suo posto viene utilizzato il valore predefinito del tipo restituito. Ad esempio, Γ€è equivalente a Γ0€, poiché il valore numerico predefinito è 0.

In pratica, listNviene utilizzato più spesso di list, poiché il valore predefinito è irrilevante o esattamente ciò di cui hai bisogno. Un modello comune è Γ~αβγ, dove αβγsono tre funzioni; questo vale βper il primo elemento e γper la coda e combina i risultati con α. È stato utilizzato ad esempio in questa risposta . Altri modelli includono l' Γo:αapplicazione αsolo al primo elemento e l' Γ·:mαapplicazione αa tutti gli elementi tranne il primo. Quest'ultimo è stato utilizzato in questa risposta .

listF

Il terzo sovraccarico è un po 'più coinvolto. Ad esempio list, accetta un valore ae una funzione fe restituisce una nuova funzione gche accetta un elenco. Tuttavia, questa volta faccetta un argomento di funzione extra, che è esso gstesso, e può chiamarlo su qualsiasi valore (incluso, ma non limitato a, la coda dell'elenco di input). Ciò significa che listFimplementa uno schema di ricorsione generale negli elenchi. listFnon viene usato molto spesso, poiché la ricorsione esplicita con list/ listNè solitamente della stessa lunghezza o più corta, come in questa risposta .

listNF

listNFè listFciò che listNè list: l'ingresso aè omesso e al suo posto viene utilizzato il valore predefinito del tipo restituito. In rare circostanze, può essere più breve di una piega a destra, ad esempio in questa risposta .

Come esempio delle versioni ricorsive di Γ, la funzione Γλ·:o⁰↔mescola un elenco nell'ordine primo, ultimo, secondo, penultimo, terzo, terzo penultimo e così via. Provalo online! La funzione fè la lambda esplicita λ·:o⁰↔, il cui argomento è l'intera funzione. Ciò che ffa è invertire la coda con , quindi chiamare la funzione principale in modo ricorsivo con o⁰, e infine virare la testa indietro con ·:. Certo, Γ·:o₀↔è un byte più breve, ma non funziona se la linea contiene qualcosa di diverso da questa funzione.


3

I combinatori possono essere applicati a funzioni di ordine superiore

Supponiamo di avere un elenco di numeri interi X e di voler contare il numero totale di elementi di X che sono più grandi della lunghezza (X) . Elementi di conteggio che soddisfano un predicato avviene con la funzione di ordine superiore #, ma qui il predicato (essendo maggiore lunghezza (X) ) dipende X . La soluzione è quella di applicare il combinatore per #e la funzione o>Lche controlla se l'elenco è più breve di un numero. Nella funzione Ṡ#o>L, l'elenco X viene passato o>L, la funzione parzialmente applicata viene passata #e X viene assegnato #come secondo argomento.

In generale, se αè una funzione di ordine superiore, βuna funzione binaria e γuna funzione unaria, Ṡαβè equivalente allo pseudocodice di Haskell

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

§αβγ è equivalente a

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

ed ~αβγè equivalente a

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

purché i tipi corrispondano.

Come altro esempio concreto, §►δṁ≠Ptrova una permutazione di una lista X che massimizza la somma delle differenze assolute ai corrispondenti valori di X ( δṁ≠comprime due liste usando la differenza assoluta e prende la somma).


3

Valori predefiniti di Husk

Husk non è così rigoroso come Haskell quando si verificano problemi quando, ad esempio, si tenta di ottenere l' lastelemento di un elenco vuoto. Per raggiungere questo obiettivo utilizza valori predefiniti, ecco un elenco di valori predefiniti, massimi e minimi:

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

* Qui ∞ dovrebbe rappresentare un elenco infinito del massimo corrispondente (vedi sotto per un esempio)

Nota: per le tuple (X, Y) utilizzerà i valori per ciascun componente separatamente.


Quando vengono utilizzati

Mentre i massimi e i minimi vengono utilizzati solo per ▲▼gli elenchi vuoti (ad esempio husk -u "▼" "[]:LLN"restituiranno un elenco infinito di Inf), i valori predefiniti vengono utilizzati in alcuni punti:

  • ripiegare gli elenchi vuoti senza fornire un valore ( Fe )
  • anteposta valore predefinito (con Θ)
  • quando read ( r) non riesce
  • ottenere il primo / ultimo elemento ( ←→) o indicizzare in uno ( !)
  • pattern matching ( Γ) su liste vuote
  • usando o su liste vuote
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.