Conseils pour jouer au golf à Husk

15

Husk est un tout nouveau langage de golf, créé par les utilisateurs de PPCG Leo et Zgarb . Il a commencé à être de plus en plus compétitif, restant souvent proche ou même battant des langues connues pour être très laconiques, comme Jelly et 05AB1E.

Énumérons quelques-unes des techniques de golf qui sont quelque peu spécifiques à Husk. Comme toujours, veuillez poster un pourboire par réponse.

M. Xcoder
la source
1
@totallyhuman première réponse de Husk Toujours pas si nouvelle
H.PWiz

Réponses:

10

Utilisez la valeur de retour des prédicats

Dans Husk, les fonctions qui testent leurs entrées pour une propriété retournent généralement un résultat significatif dans les cas véridiques, car tout entier positif est véridique.

Exemples:

≠  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

¹ différence appropriée signifie différence de points de code pour les caractères. Il fait également référence à l'ordre des arguments. c'est-à-dire pour <x y, seraitx-y

H.PWiz
la source
7

Utiliser des étiquettes de ligne débordantes

Comme vous le savez peut-être déjà, il [₀-₉]+|[₀-₉]s'agit d'une expression régulière pour que la syntaxe appelle une ligne différente de celle dans laquelle vous vous trouvez actuellement.

Cette astuce est particulièrement utile si vous souhaitez qu'une fonction définie sur une ligne spécifique soit appelée en tant qu'argument de plusieurs des fonctions du tableau ci-dessous, ou en tant qu'argument d'une ou plusieurs des fonctions ci-dessous et par elle-même.

Table de fonction:

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

Les lignes de votre code sont étiquetées avec leurs indices de base respectifs, de haut en bas. Si M <N , où M est l'étiquette et N est le nombre de lignes dans votre code, l'étiquette ne représente que la fonction définie à la ligne M . Si N ≤ M <N * 6 , il représente la fonction du tableau ci-dessus à l'indice ⌊M ÷ N⌋ avec la fonction définie à la ligne M mod N comme premier argument. Si N * 6 ≤ M , une erreur d'index est déclenchée.

Erik le Outgolfer
la source
5

Les lambdas peuvent être plus courts que les nouvelles fonctions

Comme vous le savez probablement si vous avez un programme multi-lignes, vous pouvez vous référer aux lignes avec les indices ₀…₉, par exemple dans le cas de

f
g

fera référence à la fonction g. Maintenant, si vous appliquez toujours les entrées à la fonction g(et que vous l'utilisez plusieurs fois); quelque chose comme ça:

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

Vous devez introduire un lambda car il vous fait économiser 1 octet pour chaque utilisation supplémentaire:

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

L'inverse pourrait aussi être vrai

Dans le cas des lambdas auto-référentiels ( φχψ), il y a le cas spécial où vous appliquez les entrées directement à la fonction récursive, dans ces cas, vous feriez mieux d'utiliser l'index au lieu de définir un nouveau lambda et d'utiliser .

ბიმო
la source
5

Utilisations de Γ

L'utilisation principale de la fonction intégrée Γ, connue sous le nom de correspondance de motifs sur des listes ou de déconstruction de listes , est de diviser une liste en une tête et une queue et d'y appliquer une fonction binaire. Cela correspond à l'idiome de correspondance du motif Haskell

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

<something>est une expression contenant x, xset éventuellement f. Il y a 4 surcharges de Γ, chacune fonctionnant un peu différemment.

list

La première surcharge,, listprend une valeur aet une fonction binaire f. Il retourne une nouvelle fonction qui prend une liste, retourne asi elle est vide et appelle fsur la tête et la queue si elle n'est pas vide. Par exemple, Γ_1€prend une liste, retourne -1si elle est vide et l'index de première occurrence du premier élément dans la queue sinon.

listN

La deuxième surcharge,, listNest similaire à list, sauf qu'elle aest omise et que la valeur par défaut du type de retour est utilisée à la place. Par exemple, Γ€est équivalent à Γ0€, car la valeur numérique par défaut est 0.

En pratique, listNest utilisé plus souvent que list, car la valeur par défaut n'est pas pertinente ou correspond exactement à ce dont vous avez besoin. Un modèle commun est Γ~αβγ, où αβγsont trois fonctions; cela s'applique βau premier élément et γà la queue, et combine les résultats avec α. Il a été utilisé par exemple dans cette réponse . D'autres modèles incluent l' Γo:αapplication αuniquement au premier élément et l' Γ·:mαapplication αà tous les éléments sauf le premier. Ce dernier a été utilisé dans cette réponse .

listF

La troisième surcharge est un peu plus compliquée. Comme listça, ça prend une valeur aet une fonctionf , et renvoie une nouvelle fonction gqui prend une liste. Cependant, cette fois fprend un argument de fonction supplémentaire, qui est glui - même, et peut l'appeler sur n'importe quelle valeur (y compris, mais sans s'y limiter, la queue de la liste d'entrée). Cela signifie que listFimplémente un schéma général de récursivité sur les listes. listFn'est pas utilisé très souvent, car la récursivité explicite avec list/ listNest généralement de la même longueur ou plus courte, comme dans cette réponse .

listNF

listNFest à listFce qui listNest à list: l'entrée aest omise et la valeur par défaut du type de retour est utilisée à la place. Dans de rares circonstances, il peut être plus court qu'un pli droit, par exemple dans cette réponse .

À titre d'exemple des versions récursives de Γ, la fonction Γλ·:o⁰↔mélange une liste dans l'ordre premier, dernier, deuxième, avant-dernier, troisième, avant-dernier, etc. Essayez-le en ligne! La fonction fest la lambda expliciteλ·:o⁰↔ , dont l'argument est la fonction entière. Ce qui fne fait qu'inverser la queue avec , puis appeler la fonction principale récursivement avec o⁰, et enfin clouer la tête en arrière avec ·:. Bien sûr, Γ·:o₀↔est un octet plus court, mais ne fonctionne pas si la ligne contient autre chose que cette fonction.

Zgarb
la source
3

Les combinateurs peuvent être appliqués à des fonctions d'ordre supérieur

Supposons que vous ayez une liste d'entiers X et que vous souhaitiez compter le nombre total d'éléments de X qui sont plus grands que la longueur (X) . Éléments de comptage qui satisfont un prédicat est fait avec la fonction d'ordre supérieur #, mais ici le prédicat (étant plus grande que la longueur (X) ) dépend de X . La solution consiste à appliquer le combinateur à #et la fonction o>Lqui vérifie si une liste est plus courte qu'un nombre. Dans la fonction Ṡ#o>L, la liste X est transmise o>L, la fonction partiellement appliquée est transmise #et X est donné #comme deuxième argument.

En général, si αest une fonction d'ordre supérieur, βune fonction binaire et γune fonction unaire, Ṡαβest équivalente au pseudocode Haskell

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

§αβγ est équivalent à

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

et ~αβγéquivaut à

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

tant que les types correspondent.

Comme autre exemple concret, §►δṁ≠Ptrouve une permutation d'une liste X qui maximise la somme des différences absolues aux valeurs correspondantes de X ( δṁ≠zippe deux listes en utilisant la différence absolue et prend la somme).

Zgarb
la source
3

Valeurs par défaut de Husk

Husk n'est pas aussi strict que Haskell où vous rencontrez des problèmes lorsque, par exemple, vous essayez d'obtenir l' lastélément d'une liste vide. Pour ce faire, il utilise des valeurs prédéfinies, voici une liste des valeurs par défaut, maxima et minima:

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

* Ici ∞ devrait représenter une liste infinie du maximum correspondant (voir ci-dessous pour un exemple)

Remarque: pour les tuples (X, Y), les valeurs de chaque composant seront utilisées séparément.


Quand ils sont utilisés

Alors que les maxima et les minima ne sont utilisés que ▲▼sur des listes vides (par exemple, husk -u "▼" "[]:LLN"ils renverront une liste infinie de Inf), les valeurs par défaut sont utilisées à quelques endroits:

  • replier des listes vides sans fournir de valeur vous-même ( Fet )
  • pré-valeur par défaut (avec Θ)
  • lorsque read ( r) échoue
  • obtenir le premier / dernier élément ( ←→) ou indexer en un (! )
  • correspondance de modèle ( Γ) sur des listes vides
  • en utilisant ou sur des listes vides
ბიმო
la source