Dicas para jogar golfe no APL

28

Comecei um desafio de código de golfe recentemente e parece que o vencedor é o GolfScript (surpresa, surpresa!). O interessante é que havia outro concorrente muito forte que tinha todas as chances de conquistar o GolfScript. O nome dele é APL. Vejo muitas respostas escritas em APL aqui. Parece que essa linguagem é bastante eficiente para o código de golfe, por isso decido pedir dicas de código de golfe que você conhece para programas de APL. Sinta-se livre para postar alguns exemplos de código. Geralmente é muito interessante ver a linguagem em ação.

gthacoder
fonte

Respostas:

23

Edit : para as pessoas que lêem isso, que não conhecem o APL, mas querem adotá -lo, o Mastering Dyalog APL é um recurso muito bom.

  1. A avaliação é estritamente da direita para a esquerda. Isso inclui definir variáveis, portanto, tire proveito disso.

    2+a, 1+a←1 -> 3 4

    aé definido como 1, 1+aavalia 2, a,2avalia 1 2e 2+1 2avalia como 3 4.

  2. Como C, pode ser combinado com uma função, ie a +← 3. Ao contrário de C, isso é genérico: foo F← bardefine foocomo F bar. De maneira um pouco não intuitiva, como expressão isso retorna bar, não F bar.

    Também funciona com funções anônimas:

          a←0
          a+←3 ⋄ a
    3
          a+←3 ⋄ a
    6
          a { ⍵/'!' } ←4 ⋄ a
    !!!!
    
  3. Você pode atribuir a uma matriz:, A[3]←8como seria de esperar. Mas você também pode atribuir vários itens ao mesmo tempo: A[3 5 6]←9 1 4ou mesmo A[3 5 6]←9configurá-los para o mesmo item. Você pode, é claro, adicionar uma função ao aqui também. A função será aplicada a cada elemento separadamente, como se você o fizesse .

  4. é seu amigo, mesmo que ele não pareça muito feliz com isso.

    1. Se Ffor diádico, diádico alterna os argumentos: a F b<-> b F⍨ a. Isso é útil ao jogar golfe, pois pode evitar que você use chaves:

      (F G H x) K y      <->     y K⍨ F G H x
      

      Isso altera a ordem de avaliação, pois a mão direita é sempre avaliada antes da mão esquerda.

    2. Se Fé diádico, monádico aplica o mesmo argumento aos dois lados da função:

            5⍴5
      5 5 5 5 5
            ⍴⍨5
      5 5 5 5 5
      

      O argumento é avaliado apenas uma vez. Isso é particularmente útil com produtos externos, ou seja, para comparar cada valor em uma matriz com outros valores na mesma matriz, você pode usar em ∘.=⍨vez de precisar fazer isso x∘.=x←(whatever).

    3. Se Fé monádico, não faz nada, mas separa a função do argumento. Portanto, ele ainda pode economizar chaves se a função for complexa:

            {⍵+3}⍣5 6
            ∇{⍵+3}              
           ∇ ⍣ 5 6              
            ({⍵+3}⍣5)6
      21
            {⍵+3}⍣5⍨6
      21
      
  5. Aprenda os idiomas! Depois golf os idiomas. Por exemplo:

    ((((1↑⍴X),⍴Y)↑X)^.=Y)⌿X
    

    pode ser transformado mecanicamente em:

    X⌿⍨Y^.=⍨X↑⍨(1↑⍴X),⍴Y
    

    e depois para:

    X⌿⍨Y^.=⍨X↑⍨(⊃⍴X),⍴Y
    

    (primeiro) sendo equivalente a 1↑(pegue um) neste caso. E possivelmente:

    X⌿⍨Y^.=⍨X↑⍨(≢X),⍴Y
    

    (registro) sendo equivalente a ⊃⍴(o primeiro elemento da forma) para todos, exceto os escalares.

marinus
fonte
Existe uma maneira de obter uma licença gratuita, além de ter a versão raspberry pi?
Fabinout 27/01
Uma maneira legal de obtê-lo, obviamente.
Fabinout 27/01
2
@Fabinout: No dyalog.com você pode baixar uma versão gratuita do Windows. Clique em "Download Zone" e depois em "Unregistered Download". Ele irá incomodá-lo a se registrar, mas, caso contrário, é totalmente funcional, gratuito e legal. Se você é um estudante, pode obter a versão normal gratuitamente, preenchendo um formulário. Se você não mora em um país onde eles arruinam sua vida por pirataria, bem, você sabe o que fazer.
marinus
Há também o Nars2000, uma implementação de código aberto que possui muito mais recursos do que o Dyalog (e alguns bugs). Alguns de seus recursos são úteis para o golfe, como as funções de números primos ou os vários conjuntos.
Tobia
1
Existe o GNU APL.
M. Alaggan
14

Trens

A(f g h)B      ←→  (A f B)g A h B  ⍝ fork
 (f g h)B      ←→  (  f B)g   h B  ⍝ fork
A(  g h)B      ←→         g A h B  ⍝ atop
 (  g h)B      ←→         g   h B  ⍝ atop
 (A g h)       ←→  ({A} g h)       ⍝ "Agh" fork
 (f g h k)     ←→  (f (g h k))     ⍝ 4-train
 (f g h k l)   ←→  (f g (h k l))   ⍝ 5-train, etc
 (f g h k l m) ←→  (f(g h(k l m))) ⍝ groups of 3 from the right, last could be 2
  f∘g B        ←→    f g B         ⍝ "compose" operator, useful in trains
A f∘g B        ←→  A f g B
ngn
fonte
Isso significa que, para o bem dos futuros leitores, não devemos dizer a Oberon como reduzi-lo?
Adám 30/01/18
Não, faça como faria normalmente no PPCG. Retirei essa linha depois que a expressão atingir (o que eu acredito ser) a mais curta. É um exercício fácil - acho que você não se beneficiaria pessoalmente.
NGN
Posso reduzi-lo para 16, mas não estou usando nenhuma das suas dicas, então talvez esteja longe.
Adám 30/01/19
@ Adám bem, você está usando um trem :) o meu era semelhante, mas mais longo, porque eu não pensava em ML
ngn
Não é "grupos de 3 da direita "?
Adám 15/03/19
7

Truques para lidar com /e em trens

Ao usar trens, convém usar reduções f/como soma +/ou mesmo replicar a redução //. No entanto, se o seu trem tiver mais peças à esquerda da redução, você precisará de parênteses para criar um topo. Aqui estão alguns truques para salvar bytes.

Use em 1∊vez de matrizes monádicas ∨/ou ∨⌿booleanas

Tarefa: Dadas duas seqüências de tamanho igual A e B, retorne 2 se qualquer caractere correspondente de A e B for igual, 0 caso contrário. Por exemplo, A←'abc'e B←'def'0e A←'abc'e B←'dec'2.

Uma solução dfn pode ser, A{2×∨/⍺=⍵}Bmas você deseja reduzi-la, tornando-a tácita. A(2×∨/=)Bnão vai funcionar porque as regras de formação de trens analisam isso como 2 (× ∨/ =)você deseja 2 × (∨/=).

Observe que ∨/ou ∨⌿em um vetor booleano ( ∨/,ou ∨⌿,para matrizes de classificação mais alta) pergunta se existe algum 1 presente, ou seja 1∊, para que possamos escrever nosso trem como 2×1∊=.

Observe que o argumento correto é desviado, portanto você não pode usá-lo para reduzir cada linha ou coluna separadamente.

Use em 1⊥vez de monádico +/ou+⌿

Tarefa: Dada uma lista de listas L e um índice N, retorne três vezes a soma da enésima lista. Por exemplo, L←(3 1 4)(2 7)e N←124.

Uma solução dfn pode ser, N{3×+/⍺⊃⍵}Lmas você deseja reduzi-la, tornando-a tácita. N(3×+/⊃)Lnão vai funcionar porque as regras de formação de trens analisam isso como 3(× +/ ⊃)você deseja 3 × (+/⊃).

Observe que avaliar uma lista de números em unário (base 1) é equivalente a somar a lista porque a { a , b , c , d } =  a + b + c + d  = ( a × 1³) + ( b × 1²) ) + ( c × 1¹) + ( d × 1⁰). Portanto, +/a b c dé o mesmo que 1⊥a b c d, e podemos escrever nosso trem como 3×1⊥⊃.

Observe que, nos argumentos de classificação mais alta, 1⊥é equivalente a +⌿.

Use em f.gvez de f/gargumentos escalares e / ou vetoriais

Tarefa: Dada uma lista L e um número N, retornar o intervalo 1 completa o número de resto mínimos divisão quando os elementos de L são divididos por ELg L←31 41 59e N←71 2 3.

Uma solução dfn pode ser, N{⍳⌊/⍺|⍵}Lmas você deseja reduzi-la, tornando-a tácita. N(⍳⌊/|)Lnão vai funcionar porque as regras de formação de trens analisam isso como ⍳ (⌊/) |você deseja ⍳ (⌊/|).

O produto interno A f.g Bdas duas funções escalares quando os argumentos são escalares e / ou vetores é o mesmo que f/ A g Bambos são (A[1] g B[1]) f (A[2] g B[2]) f (A[3] g B[3])etc., para que possamos escrever nosso trem como ⍳⌊.|.

Observe que isso não funciona para matrizes de classificação mais alta.

Use em ∊⊆vez de /argumentos à esquerda booleanos e à direita de vetor simples

Tarefa: Dada uma lista L e um número N, filtre a lista para que apenas números maiores que N permaneçam. Por exemplo, L←3 1 4e N←13 4.

Uma solução dfn pode ser, N{(⍺<⍵)/⍵}Lmas você deseja reduzi-la, tornando-a tácita. N(</⊢)Lnão funcionará porque as regras de ligação analisarão isso como (</) ⊢mas você deseja /que a função seja replicada em vez de reduzir o operador .

Diádico com um argumento à esquerda booleano particiona o argumento à direita de acordo com execuções de 1s no argumento à esquerda, eliminando elementos indicados por 0s. Isso é quase o que queremos, exceto pelo particionamento indesejado. No entanto, podemos nos livrar da partição aplicando monádica . Assim {(⍺<⍵)/⍵}pode se tornar {∊(⍺<⍵)⊆⍵}e assim podemos escrever nosso trem como ∊<⊆⊢.

Observe que isso não funciona para matrizes de classificação mais alta.

Use em 0⊥vez de ⊢/ou ⊢⌿com argumentos numéricos

Tarefa: Dada uma lista L e um número N, multiplique N pelo elemento mais à direita de LEg L←3 1 4e N←28.

Uma solução dfn pode ser, N{⍺×⊢/⍵}Lmas você deseja reduzi-la, tornando-a tácita. N(⊣×⊢/⊢)Lnão vai funcionar porque as regras de formação de trens analisam isso como ⊣ (× ⊢/ ⊢)você deseja ⊣ × (⊢/⊢).

Observe que 0⊥em uma matriz numérica é o mesmo que ⊢⌿, para que possamos escrever nosso trem como ⊣×0⊥⊢.

Observe que isso seleciona a última célula principal de matrizes de classificação mais alta.

Adão
fonte
1
Talvez você possa adicionar esta resposta de bate-papo a esta?
J. Sallé
1
@ J.Sallé Adicionado.
Adám
7

Use para combinar multiplicação com adição

(a×b)+C  ->  a⊥b,C
(C)+a×b  ->  a⊥b,C
(a×b)-C  ->  a⊥b,-C

Suposições:

  • ae bsão termos que não exigem parênteses adicionais quando usados ​​como argumento à esquerda

  • C é uma expressão que pode precisar de parênteses quando usada como argumento à esquerda

  • a b C avaliar para escalares numéricos

ngn
fonte
5

Números complexos

Muitas vezes esquecidos, eles apresentam oportunidades maravilhosas para encurtar expressões que lidam com grades, labirintos, fractais ou geometria.

0j1⊥¨    0j1⊥   ⍝ pair(s) of reals -> complex
11 9∘○¨  11 9○  ⍝ complex -> pair(s) of reals
|z0-z1          ⍝ distance between two points
0j1×z   0j¯1×z  ⍝ rotate by ±90° around (0,0)
0j1*⍳4          ⍝ the four cardinal directions
+z       -+z    ⍝ reflect across x or y axis
+\0,z           ⍝ sequence of steps -> path
2-/z            ⍝ path -> sequence of steps
0j1⊥¨n-⍳2⍴1+2×n ⍝ lattice centred on (0,0)
ngn
fonte
4

Comprimento do vetor do módulo de indexação

⊃i⌽ageralmente é mais curto que o ingênuo ⊃a[(≢a)|i]ou a⊃⍨i|⍨≢a(onde aé um vetor e ié um número inteiro e ⎕ioé 0)

Uma variação útil disso (obrigado EriktheOutgolfer por apontar) é: I↑Y⌽⍨I×Xonde Yestá a concatenação de alguns Ivetores de comprimento e Xé o índice daquele que queremos escolher, por exemplo:3↑'JanFeb...Dec'⌽⍨3×month

ngn
fonte
3

Funções constantes

=⍨e ≠⍨graças a ngn.

Às vezes, você só precisa de um único valor para cada elemento de uma lista. Embora você seja tentado a usar {value}¨, é mais curto, value⊣¨ mas para alguns valores comuns, você pode ficar ainda mais baixo (usando ⎕IO←0):

¯1s com ⍬⍸list

0s com ⍬⍳list

1s com ⍬⍷list

Observe que eles funcionam apenas em listas (embora possam estar aninhados). Para matrizes de classificação mais alta, você pode usar o seguinte para obter todos os 0s e todos os 1s:

1s com =⍨

0s com ≠⍨

Se você definir ⎕ML←0, todos os números podem ser transformados em zeros (como se ) com:

Se você precisar apenas de um único número, poderá usar monádico para obter 1 ou 0 em vez de usar 1⊣ou 0⊣.

Adão
fonte
" Às vezes, você só precisa de um único valor para cada elemento de uma lista. " - Pode ser digno de nota: quando esse valor é o primeiro elemento da lista, você pode usar⊣\
ngn
@ngn eu diria isso e com /e mérito um post próprio.
Adám 13/02/19
2

Usar

Evite parênteses

(Comutar) pode economizar bytes, evitando parênteses. Sempre que você tiver uma função em que o argumento da esquerda precise ser parênteses e o argumento da direita não, você poderá salvar um byte, por exemplo, (A<B)÷CC÷⍨A<B.

Matrizes duplas

Para anexar uma cópia de uma matriz ao final, use ,⍨Aou ⍪⍨A.

Números duplos

Em vez de usar 2∘×para dobrar, você pode usar, +⍨pois ele adiciona o argumento a si mesmo: 1+2∘×1++⍨.

Números quadrados

Em vez de usar o 2*⍨Yquadrado, você pode usar, ×⍨Ypois ele multiplica o argumento consigo mesmo: 2*⍨A+B×⍨A+B.

Permutação aleatória

?⍨Nlhe dará uma permutação aleatória de comprimento N.

Auto-classificação

Encontre os índices da primeira ocorrência de cada célula principal com ⍳⍨A

Contagem 1s à direita em um vetor booleano

Em vez de +/∧\⌽Bcontar quantos 1s finais existem, Nvocê pode usar ⊥⍨.

Composição reversa

A f∘g Bé A f g B, mas se você quiser (g A) f B, use f⍨∘g⍨.

Redução reversa

f/ a1 a2 a3é a1 f (a2 f a3). Se você quiser (a1 f a2) f a3, use f⍨/⌽.

Reverse scan

f\ A B Cé
A (A f B) (A f (B f C)).

f⍨/∘⌽¨,\ A B Cé
A (A f B) ((A f B) f C).

f⍨\⌽ A B Cé
((A f B) f C) (B f C) C.

⌽f/∘⌽¨,\⌽ A B C. é
(A f (B f C)) (B f C) C.

Adão
fonte
2

Enumere os caracteres em uma sequência sem ⍳≢

Tarefa: Dadas duas cadeias, S e T, liste os índices de sua concatenação. Por exemplo, S←'abcd'e T←'xyz'1 2 3 4 5 6 7.

Uma solução dfn pode ser, S{⍳≢⍺,⍵}Tmas você deseja reduzi-la, tornando-a tácita. ⍳≢,não funcionará porque as regras de análise de trem analisarão isso como (⍳)≢(,)você desejar (⍳≢),.

Diádico com um argumento vazio à esquerda classifica matrizes simples de caracteres de acordo com a ordem atual, que é a mesma que ⍳≢. Assim {⍳≢⍺,⍵} pode se tornar {⍬⍋⍺,⍵}, para que possamos escrever nosso trem como ⍬⍋,.

Observe que isso não funciona para matrizes numéricas ou mistas.

Adão
fonte
Uau, não sabia que isso era uma coisa.
Zachary