Consejos para jugar al golf en Husk

15

Husk es un lenguaje de golf bastante nuevo, creado por los usuarios de PPCG Leo y Zgarb . Ha comenzado a ser cada vez más competitivo, a menudo manteniéndose cerca o incluso superando idiomas conocidos como muy concisos, como Jelly y 05AB1E.

Hagamos una lista de algunas de las técnicas de golf que son algo específicas de Husk. Como siempre, publique un consejo por respuesta.

Sr. Xcoder
fuente
55
No diría que es nuevo ...
totalmente humano
1
@totallyhuman primera respuesta de Husk Todavía no es tan nuevo
H.PWiz

Respuestas:

10

Usa el valor de retorno de predicados

En Husk, las funciones que prueban sus entradas para alguna propiedad generalmente devolverán un resultado significativo en casos verdaderos, ya que cualquier número entero positivo es verdadero.

Ejemplos:

≠  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

¹ diferencia apropiada significa diferencia de puntos de código para caracteres. También se refiere al orden del argumento. es decir <x y, seríax-y

H.PWiz
fuente
7

Utilice etiquetas de líneas desbordantes

Como ya sabrás, [₀-₉]+|[₀-₉]es la expresión regular para que la sintaxis llame a una línea diferente a la que estás actualmente.

Este consejo es especialmente útil si desea que una función definida en una línea específica se llame como un argumento de más de una de las funciones en la tabla a continuación, o como un argumento para una o más de las funciones a continuación y por sí misma.

Tabla de funciones:

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

Las líneas en su código están etiquetadas con sus respectivos índices basados ​​en 0, de arriba a abajo. Si M <N , donde M es la etiqueta y N es el número de líneas en el código, la etiqueta justo representa la función definida en la línea M . Si N ≤ M <N * 6 , representa la función de la tabla anterior en el índice ⌊M ÷ N⌋ con la función definida en la línea M mod N como primer argumento. Si N * 6 ≤ M , se genera un error de índice.

Erik el Outgolfer
fuente
5

Las lambdas pueden ser más cortas que las nuevas funciones

Como probablemente sepa si tiene un programa de varias líneas, puede consultar las líneas con los subíndices ₀…₉, por ejemplo, en el caso de

f
g

se referirá a la función g. Ahora, si siempre aplica las entradas a la función g(y la usa varias veces); algo como esto:

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

Debe introducir una lambda porque le ahorra 1 byte por cada uso adicional:

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

Lo contrario también podría ser cierto

En el caso de lambdas autorreferenciales ( φχψ), existe el caso especial en el que aplica las entradas directamente a la función recursiva, en estos casos es mejor usar el subíndice en lugar de definir una nueva lambda y usarla .

ბიმო
fuente
5

Usos de Γ

El uso principal de la función incorporada Γ, conocida como coincidencia de patrones en listas o deconstrucción de listas , es dividir una lista en cabeza y cola, y aplicar una función binaria en ellas. Esto corresponde al patrón de coincidencia de patrones de Haskell

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

donde <something>es una expresión que contiene x, xsy posiblemente f. Hay 4 sobrecargas Γ, cada una de las cuales funciona un poco diferente.

list

La primera sobrecarga, listtoma un valor ay una función binaria f. Devuelve una nueva función que toma una lista, devuelve asi está vacía y llama fa la cabeza y la cola si no está vacía. Por ejemplo, Γ_1€toma una lista, devuelve -1si está vacía, y el índice de la primera aparición del primer elemento en la cola si no.

listN

La segunda sobrecarga, listNes similar a list, excepto que ase omite y en su lugar se usa el valor predeterminado del tipo de retorno. Por ejemplo, Γ€es equivalente a Γ0€, ya que el valor numérico predeterminado es 0.

En la práctica, listNse usa con más frecuencia list, ya que el valor predeterminado es irrelevante o exactamente lo que necesita. Un patrón común es Γ~αβγ, donde αβγhay tres funciones; esto se aplica βal primer elemento y γa la cola, y combina los resultados con α. Se usó, por ejemplo, en esta respuesta . Otros patrones incluyen Γo:αpara aplicar αsolo al primer elemento y Γ·:mαpara aplicar αa todos los elementos excepto el primero. Este último se usó en esta respuesta .

listF

La tercera sobrecarga es un poco más complicada. Al igual list, toma un valor ay una función f, y devuelve una nueva función gque toma una lista. Sin embargo, esta vez ftoma un argumento de función adicional, que es en gsí mismo, y puede invocarlo en cualquier valor (incluido, entre otros, el final de la lista de entrada). Esto significa que listFimplementa un esquema de recursión general en las listas. listFno se usa con mucha frecuencia, ya que la recursividad explícita con list/ listNsuele ser de la misma longitud o más corta, como en esta respuesta .

listNF

listNFes a listFlo que listNes list: ase omite la entrada y en su lugar se usa el valor predeterminado del tipo de retorno. En raras circunstancias, puede ser más corto que un pliegue derecho, por ejemplo en esta respuesta .

Como ejemplo de las versiones recursivas de Γ, la función Γλ·:o⁰↔baraja una lista en el orden primero, último, segundo, penúltimo, tercero, penúltimo, y así sucesivamente. Pruébalo en línea! La función fes la lambda explícita λ·:o⁰↔, cuyo argumento es la función completa. Lo que fhace es invertir la cola con , luego llamar a la función principal de forma recursiva o⁰y finalmente clavar la cabeza hacia atrás con ·:. Por supuesto, Γ·:o₀↔es un byte más corto, pero no funciona si la línea contiene algo más que esta función.

Zgarb
fuente
3

Los combinadores se pueden aplicar a funciones de orden superior

Supongamos que tiene una lista de enteros X y desea contar el número total de elementos de X que son mayores que la longitud (X) . Elementos de recuento que satisfacen un predicado se hace con la función de orden superior #, pero aquí el predicado (que es mayor que la longitud (X) ) depende de X . La solución es aplicar el combinador a #y la función o>Lque comprueba si una lista es más corto que un número. En la función Ṡ#o>L, se pasa la lista X, se pasa o>Lla función parcialmente aplicada #y se da X# como segundo argumento.

En general, si αes una función de orden superior, βuna función binaria y γuna función unaria, Ṡαβes equivalente al pseudocódigo de Haskell

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

§αβγ es equivalente a

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

y ~αβγes equivalente a

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

siempre y cuando los tipos coincidan.

Como otro ejemplo concreto, §►δṁ≠Pencuentra una permutación de una lista X que maximiza la suma de las diferencias absolutas a los valores correspondientes de X (δṁ≠ comprime dos listas usando la diferencia absoluta y toma la suma).

Zgarb
fuente
3

Valores predeterminados de Husk

Husk no es tan estricto como Haskell, donde te encuentras con problemas cuando, por ejemplo, intentas obtener el lastelemento de una lista vacía. Para lograr esto, utiliza valores predefinidos, aquí hay una lista de los valores predeterminados, máximos y mínimos:

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

* Aquí ∞ debe representar una lista infinita del máximo correspondiente (vea a continuación un ejemplo)

Nota: Para las tuplas (X, Y) usará los valores para cada componente por separado.


Cuando se usan

Mientras que los máximos y mínimos solo se usan ▲▼en listas vacías (por ejemplo husk -u "▼" "[]:LLN", devolverá una lista infinita de Inf) los valores predeterminados se usan en algunos lugares:

  • doblar listas vacías sin proporcionar un valor usted mismo ( Fy )
  • valor predeterminado anterior (con Θ)
  • cuando read ( r) falla
  • obteniendo el primer / último elemento (←→ ) o indexar en uno ( !)
  • la coincidencia de patrones (Γ ) en listas vacías
  • usando o en listas vacías
ბიმო
fuente