Dicas para jogar golfe em Husk

15

Husk é uma linguagem de golfe bastante nova, criada pelos usuários do PPCG Leo e Zgarb . Começou a ser cada vez mais competitivo, muitas vezes ficando próximo ou até superando idiomas conhecidos por serem muito concisos, como Jelly e 05AB1E.

Vamos listar algumas das técnicas de golfe que são um pouco específicas para Husk. Como sempre, poste uma dica por resposta.

Mr. Xcoder
fonte
1
@totallyhuman primeira resposta Husk Ainda sem esse novo
H.PWiz

Respostas:

10

Use o valor de retorno dos predicados

Em Husk, funções que testam suas entradas para alguma propriedade geralmente retornam um resultado significativo em casos reais, como qualquer número inteiro positivo é verdade.

Exemplos:

≠  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

Difference diferença apropriada significa diferença de pontos de código para caracteres. Também se refere à ordem dos argumentos. ou seja <x y, para , seriax-y

H.PWiz
fonte
7

Use etiquetas de linha transbordando

Como você já deve saber, [₀-₉]+|[₀-₉]é uma expressão regular para a sintaxe chamar uma linha diferente daquela em que você está atualmente.

Esta dica é especialmente útil se você quiser que uma função definida em uma linha específica seja chamada como argumento de mais de uma das funções na tabela abaixo ou como argumento para uma ou mais das funções abaixo e por si só.

Tabela de funções:

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

As linhas no seu código são rotuladas com seus respectivos índices baseados em 0, de cima para baixo. Se M <N , onde M é o rótulo e N é o número de linhas em seu código, o rótulo apenas representa a função definida na linha M . Se N ≤ M <N * 6 , representa a função da tabela acima no índice ⌊M ÷ N⌋ com a função definida na linha M mod N como seu primeiro argumento. Se N * 6 ≤ M , um erro de índice é gerado.

Erik, o Outgolfer
fonte
5

Lambdas podem ser mais curtas que novas funções

Como você provavelmente sabe se possui um programa de várias linhas, pode consultar as linhas com os subscritos ₀…₉, por exemplo, no caso de

f
g

irá se referir à função g. Agora, se você sempre aplicar as entradas à função g(e usá-la várias vezes); algo assim:

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

Você deve introduzir um lambda porque ele economiza 1 byte para cada uso adicional:

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

O inverso também pode ser verdadeiro

No caso de lambdas autorreferenciais ( φχψ), há o caso especial em que você aplica as entradas diretamente à função recursiva; nesses casos, é melhor usar o subscrito em vez de definir um novo lambda e usar .

ბიმო
fonte
5

Usos de Γ

O principal uso do built-in Γ, conhecido como correspondência de padrões em listas ou desconstrução de listas , é dividir uma lista em uma cabeça e cauda e aplicar uma função binária nelas. Corresponde ao idioma correspondente do padrão Haskell

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

onde <something>é uma expressão que contém x, xse possivelmente f. Existem 4 sobrecargas de Γ, cada uma das quais funciona um pouco diferente.

list

A primeira sobrecarga, listassume um valor ae uma função binária f. Ele retorna uma nova função que pega uma lista, retorna ase estiver vazia e chama fa cabeça e a cauda se não estiver vazia. Por exemplo, Γ_1€pega uma lista, retorna -1se estiver vazia e o índice da primeira ocorrência do primeiro elemento na cauda, ​​se não estiver.

listN

A segunda sobrecarga,, listNé semelhante a list, exceto que aé omitida e o valor padrão do tipo de retorno é usado. Por exemplo, Γ€é equivalente a Γ0€, já que o valor numérico padrão é 0.

Na prática, listNé usado com mais frequência do que list, uma vez que o valor padrão é irrelevante ou exatamente o que você precisa. Um padrão comum é Γ~αβγonde αβγestão três funções; isso se aplica βao primeiro elemento e γà cauda e combina os resultados com α. Foi usado, por exemplo, nesta resposta . Outros padrões incluem Γo:αa aplicação αapenas ao primeiro elemento e Γ·:mαa aplicação αa todos os elementos, exceto o primeiro. O último foi usado nesta resposta .

listF

A terceira sobrecarga é um pouco mais envolvida. Como list, é preciso um valor ae uma função fe retorna uma nova função gque leva uma lista. No entanto, esse tempo fexige um argumento de função extra, que é gele próprio, e pode chamá-lo com qualquer valor (incluindo, mas não limitado a, final da lista de entrada). Isso significa que listFimplementa um esquema geral de recursão nas listas. listFnão é usado com muita frequência, pois a recursão explícita com list/ listNgeralmente é do mesmo tamanho ou menor, como nesta resposta .

listNF

listNFé para o listFque listNé list: a entrada aé omitida e o valor padrão do tipo de retorno é usado. Em raras circunstâncias, pode ser menor que uma dobra à direita, por exemplo, nesta resposta .

Como exemplo das versões recursivas de Γ, a função Γλ·:o⁰↔embaralha uma lista na ordem primeiro, último, segundo, penúltimo, terceiro, penúltimo e assim por diante. Experimente online! A função fé a lambda explícita λ·:o⁰↔, cujo argumento é a função inteira. O que ffaz é inverter a cauda com , em seguida, chame a função principal recursivamente com o⁰e finalmente enfie a cabeça para trás ·:. Obviamente, Γ·:o₀↔é um byte mais curto, mas não funciona se a linha contiver algo além dessa função.

Zgarb
fonte
3

Os combinadores podem ser aplicados a funções de ordem superior

Suponha que você tenha uma lista de números inteiros X e queira contar o número total de elementos de X maiores que o comprimento (X) . Elementos de contagem que satisfazem um predicado é feito com a função de ordem superior #, mas aqui o predicado (sendo maior do que o comprimento (X) ) depende de X . A solução é aplicar a combinator para #ea função o>Lque verifica se a lista é mais curto do que um número. Na função Ṡ#o>L, a lista X é passada para o>L, a função parcialmente aplicada é passada #e X é fornecido #como o segundo argumento.

Em geral, se αé uma função de ordem superior, βuma função binária e γuma função unária, Ṡαβé equivalente ao pseudocódigo de Haskell

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

§αβγ é equivalente a

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

e ~αβγé equivalente a

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

desde que os tipos correspondam.

Como outro exemplo concreto, §►δṁ≠Pencontra uma permutação de uma lista X que maximiza a soma das diferenças absolutas para os valores correspondentes de X ( δṁ≠fecha duas listas usando a diferença absoluta e recebe a soma).

Zgarb
fonte
3

Valores padrão do Husk

Husk não é tão rigoroso quanto Haskell, onde você encontra problemas quando, por exemplo, tenta obter o lastelemento de uma lista vazia. Para conseguir isso, ele usa valores predefinidos, aqui está uma lista dos valores padrão, máximos e 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  |
'------------------------------------'---------------'----------'-------'

* Aqui ∞ deve representar uma lista infinita do máximo correspondente (veja um exemplo abaixo)

Nota: Para tuplas (X, Y), os valores de cada componente serão utilizados separadamente.


Quando eles são usados

Enquanto o máximo e o mínimo são usados ​​apenas ▲▼em listas vazias (por exemplo husk -u "▼" "[]:LLN", retornará uma lista infinita de Inf), os valores padrão são usados ​​em alguns lugares:

  • dobrar listas vazias sem fornecer um valor a si mesmo ( Fe )
  • preceder o valor padrão (com Θ)
  • quando read ( r) falha
  • obtendo o primeiro / último elemento ( ←→) ou indexando em um ( !)
  • correspondência de padrões ( Γ) em listas vazias
  • usando ou em listas vazias
ბიმო
fonte