Suggerimenti per giocare a golf in APL


28

Ho iniziato una sfida di golf in codice di recente e sembra che il vincitore sia GolfScript (sorpresa, sorpresa!). La cosa interessante è che c'era un altro concorrente molto forte che aveva tutte le possibilità di vincere su GolfScript. Si chiama APL. Vedo molte risposte scritte in APL qui. Sembra che questa lingua sia abbastanza efficiente per il golf del codice, quindi decido di chiedere eventuali suggerimenti per il golf del codice che conosci per i programmi APL. Sentiti libero di pubblicare alcuni esempi di codice. Di solito è molto interessante vedere la lingua in azione.

Risposte:


23

Edit : per le persone che leggono questo che non conoscono APL a tutti, ma vogliono prendere in su, Mastering Dyalog APL è una molto buona risorsa.

  1. La valutazione è rigorosamente da destra a sinistra. Ciò include l'impostazione delle variabili, quindi approfittane.

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

    aè impostato su 1, 1+avaluta 2, a,2valuta 1 2e 2+1 2valuta 3 4.

  2. Come C, può essere combinato con una funzione, ad es a +← 3. A differenza di C, questo è generico: foo F← barimposta foosu F bar. Un po 'poco intuitivo, come espressione restituisce bar, no F bar.

    Funziona anche con funzioni anonime:

          a←0
          a+←3 ⋄ a
    3
          a+←3 ⋄ a
    6
          a { ⍵/'!' } ←4 ⋄ a
    !!!!
    
  3. Puoi assegnare a un array: A[3]←8come ti aspetteresti. Ma puoi anche assegnare più elementi contemporaneamente: A[3 5 6]←9 1 4o anche A[3 5 6]←9impostarli tutti sullo stesso oggetto. Ovviamente puoi aggiungere una funzione anche qui. La funzione verrà quindi applicata a ciascun elemento separatamente, come se lo fosse .

  4. è tuo amico, anche se non sembra molto contento.

    1. Se Fè diadico, diadico cambia gli argomenti: a F b<-> b F⍨ a. Questo è utile quando si gioca a golf perché può salvarti dall'uso delle parentesi graffe:

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

      Questo cambia l'ordine di valutazione, poiché la mano destra viene sempre valutata prima della mano sinistra.

    2. Se Fè diadico, monadic applica lo stesso argomento ad entrambi i lati della funzione:

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

      L'argomento viene valutato solo una volta. Ciò è particolarmente utile con i prodotti esterni, ovvero per confrontare ogni valore in un array con gli altri valori nello stesso array, è possibile utilizzare ∘.=⍨invece di doverlo fare x∘.=x←(whatever).

    3. Se Fè monadico, non fa nulla, ma separa la funzione dall'argomento. Quindi può ancora salvarti le parentesi graffe se la funzione è complessa:

            {⍵+3}⍣5 6
            ∇{⍵+3}              
           ∇ ⍣ 5 6              
            ({⍵+3}⍣5)6
      21
            {⍵+3}⍣5⍨6
      21
      
  5. Impara i modi di dire! Quindi golf gli idiomi. Per esempio:

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

    può essere trasformato meccanicamente in:

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

    e poi ulteriormente in:

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

    (primo) equivalente a 1↑(prendine uno) in questo caso. E possibilmente:

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

    (conteggio) equivalente a ⊃⍴(il primo elemento della forma) per tutti tranne gli scalari.


C'è un modo per ottenere una licenza gratuita oltre ad avere la versione raspberry pi?
Fabinout,

Un modo legale per ottenerlo, ovviamente.
Fabinout,

2
@Fabinout: su dyalog.com puoi scaricare una versione gratuita di Windows. Fai clic su "Download Zone" e quindi su "Download non registrato". Ti farà registrarti ma altrimenti è completamente funzionale, gratuito e legale. Se sei uno studente, puoi ottenere la versione normale gratuitamente compilando un modulo. Se non vivi in ​​un paese in cui ti rovinano la vita per pirateria, beh, sai cosa fare.
Marin il

C'è anche Nars2000, un'implementazione open source che ha molte più funzioni rispetto a Dyalog (e alcuni bug). Alcune delle sue funzionalità sono utili per giocare a golf, come le funzioni di numeri primi o multiset.
Tobia

1
C'è GNU APL.
M. Alaggan,

14

I treni

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

Ciò significa che per il bene dei futuri lettori, non dovremmo dire a Oberon come accorciarlo?
Adám,

No, fai come faresti normalmente su PPCG. Rimuoverò quella riga dopo che l'espressione raggiunge (quello che credo sia) il più breve. È un esercizio facile - non penso che tu ne trarrebbe beneficio personalmente.
ngn

Posso arrivare fino a 16, ma non sto usando nessuno dei tuoi consigli, quindi forse sono fuori strada.
Adám,

@Adám bene, stai usando un treno :) il mio era simile ma più lungo perché non pensavo a ⎕ML
ngn

Non sono "gruppi di 3 da destra "?
Adám,

7

Trucchi per affrontare /e in treno

Quando si utilizzano i treni, è possibile che si desideri utilizzare riduzioni f/come la somma +/o addirittura la riduzione della replica //. Tuttavia, se il tuo treno ha più parti a sinistra della riduzione, avrai bisogno di parentesi per crearne una in cima. Ecco alcuni trucchi per salvare i byte.

Utilizzare 1∊invece di monadici ∨/o ∨⌿su array booleani

Compito: date due stringhe di uguale lunghezza A e B, restituisce 2 se tutti i caratteri corrispondenti di A e B sono uguali, 0 altrimenti. Ad esempio A←'abc'e B←'def'0e A←'abc'e B←'dec'2.

Una soluzione dfn può essere A{2×∨/⍺=⍵}Bma vuoi accorciarla andando tacito. A(2×∨/=)Bnon funzionerà perché le regole della formazione del treno lo analizzano come 2 (× ∨/ =)ma tu vuoi 2 × (∨/=).

Osserva che ∨/o ∨⌿su un vettore booleano ( ∨/,o ∨⌿,per array di rango superiore) chiede se è presente 1, cioè 1∊, in modo da poter scrivere il nostro treno come 2×1∊=.

Tieni presente che corrobora l'argomento corretto, quindi non puoi usarlo per ridurre ogni riga o colonna separatamente.

Usa 1⊥invece di monadic +/o+⌿

Compito: dato un elenco di elenchi L e un indice N, restituisce tre volte la somma dell'ennesimo elenco. Ad esempio L←(3 1 4)(2 7)e N←124.

Una soluzione dfn può essere N{3×+/⍺⊃⍵}Lma vuoi accorciarla andando tacito. N(3×+/⊃)Lnon funzionerà perché le regole della formazione del treno lo analizzano come 3(× +/ ⊃)ma tu vuoi 3 × (+/⊃).

Osservare che valutare un elenco di numeri in unario (base-1) equivale a sommare l'elenco perché ∑ { a , b , c , d } =  a + b + c + d  = ( a × 1³) + ( b × 1² ) + ( c × 1¹) + ( d × 1⁰). Pertanto +/a b c dè lo stesso di 1⊥a b c de possiamo scrivere il nostro treno come 3×1⊥⊃.

Si noti che su argomenti di rango superiore, 1⊥è equivalente a +⌿.

Utilizzare f.ginvece di f/gargomenti scalari e / o vettoriali

Compito: dati un elenco L e un numero N, restituisce l'intervallo 1 completo del numero di resti minimi di divisione quando gli elementi di L sono divisi per NEg L←31 41 59e N←71 2 3.

Una soluzione dfn può essere N{⍳⌊/⍺|⍵}Lma vuoi accorciarla andando tacito. N(⍳⌊/|)Lnon funzionerà perché le regole della formazione del treno lo analizzano come ⍳ (⌊/) |ma tu vuoi ⍳ (⌊/|).

Il prodotto interno A f.g Bdi due funzioni scalari quando gli argomenti sono scalari e / o vettori è lo stesso di f/ A g Bperché entrambi sono (A[1] g B[1]) f (A[2] g B[2]) f (A[3] g B[3])ecc., Quindi possiamo scrivere il nostro treno come ⍳⌊.|.

Si noti che questo non funziona per array di livello superiore.

Utilizzare ∊⊆invece di /con argomenti booleani left e simple vector right

Attività: dati un elenco L e un numero N, filtrare l'elenco in modo che rimangano solo numeri maggiori di N. Ad esempio L←3 1 4e N←13 4.

Una soluzione dfn può essere N{(⍺<⍵)/⍵}Lma vuoi accorciarla andando tacito. N(</⊢)Lnon funzionerà perché le regole di associazione analizzeranno questo come, (</) ⊢ma si desidera /essere la funzione replicare anziché ridurre l'operatore .

Dyadic con un argomento booleano a sinistra suddivide l'argomento a destra in base alle esecuzioni di 1s nell'argomento a sinistra, facendo cadere gli elementi indicati da 0s. Questo è quasi ciò che vogliamo, a parte il partizionamento indesiderato. Tuttavia, possiamo sbarazzarci del partizionamento applicando monadic . Così {(⍺<⍵)/⍵}può diventare {∊(⍺<⍵)⊆⍵}e così possiamo scrivere il nostro treno come ∊<⊆⊢.

Si noti che questo non funziona per array di livello superiore.

Utilizzare al 0⊥posto di ⊢/o ⊢⌿con argomenti numerici

Compito: dati un elenco L e un numero N, moltiplica N con l'elemento più a destra di LEg L←3 1 4e N←28.

Una soluzione dfn può essere N{⍺×⊢/⍵}Lma vuoi accorciarla andando tacito. N(⊣×⊢/⊢)Lnon funzionerà perché le regole della formazione del treno lo analizzano come ⊣ (× ⊢/ ⊢)ma tu vuoi ⊣ × (⊢/⊢).

Osservare che 0⊥su un array numerico è uguale a ⊢⌿, quindi possiamo scrivere il nostro treno come ⊣×0⊥⊢.

Si noti che questo seleziona l'ultima cella principale di array di livello superiore.


1
Forse potresti aggiungere questa risposta alla chat a questa?
J. Sallé,

1
@ J.Sallé Aggiunto.
Adám,

7

Utilizzare per combinare la moltiplicazione con l'aggiunta

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

ipotesi:

  • ae bsono termini che non richiedono ulteriori parentesi se usati come argomento sinistro

  • C è un'espressione che può richiedere parentesi se usata come argomento sinistro

  • a b C valutare su scalari numerici


5

Numeri complessi

Spesso trascurati, offrono meravigliose opportunità per abbreviare le espressioni affrontando griglie, labirinti, frattali o 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)

4

Lunghezza del vettore del modulo di indicizzazione

⊃i⌽aè spesso più breve dell'ingenuo ⊃a[(≢a)|i]o a⊃⍨i|⍨≢a(dove aè un vettore ed iè un numero intero, ed ⎕ioè 0)

una utile variazione su questo (grazie a EriktheOutgolfer per la precisazione) è: I↑Y⌽⍨I×Xdov'è Yla concatenazione di alcuni Ivettori di lunghezza ed Xè l'indice di quello che vogliamo scegliere, ad esempio:3↑'JanFeb...Dec'⌽⍨3×month


3

Funzioni costanti

=⍨e ≠⍨grazie a ngn.

A volte hai solo bisogno di un singolo valore per ogni elemento di un elenco. Mentre potresti essere tentato di usare {value}¨, è più breve da usare value⊣¨ ma per alcuni valori comuni, puoi ottenere anche più breve (usando ⎕IO←0):

¯1s con ⍬⍸list

0s con ⍬⍳list

1s con ⍬⍷list

Nota che funzionano solo sugli elenchi (sebbene possano essere nidificati). Per array di livello superiore, è possibile utilizzare quanto segue per ottenere tutti gli 0 e tutti gli 1:

1s con =⍨

0s con ≠⍨

Se si imposta ⎕ML←0, tutti i numeri possono essere trasformati in zeri (come se ) con:

Se hai bisogno di un solo numero, potresti essere in grado di usare monadic per ottenere 1 o 0 invece di usare 1⊣o 0⊣.


" A volte hai solo bisogno di un singolo valore per ogni elemento di un elenco. " - Potrebbe essere degno di nota: quando quel valore è il primo elemento dell'elenco, puoi usare⊣\
ngn

@ngn Direi che e con /e merito un post tutto loro.
Adám,

2

Uso

Evita le parentesi

(Commute) può farti risparmiare byte evitando le parentesi. Ogni volta che si ha una funzione in cui l'argomento sinistro deve essere tra parentesi e l'argomento destro no, è possibile salvare un byte, ad es . (A<B)÷CC÷⍨A<B.

Matrici doppie

Per aggiungere una copia di un array alla sua fine, utilizzare ,⍨Ao ⍪⍨A.

Numeri doppi

Invece di usare 2∘×raddoppiare, puoi usare +⍨poiché aggiunge l'argomento a se stesso: 1+2∘×1++⍨.

Numeri quadrati

Invece di usare 2*⍨Yto square, puoi usare ×⍨Ypoiché moltiplica l'argomento con se stesso: 2*⍨A+B×⍨A+B.

Permutazione casuale

?⍨Nti darà una permutazione casuale di lunghezza N.

Self-classificare

Trova gli indici della prima occorrenza di ciascuna cella principale con ⍳⍨A

Contare 1s finali in un vettore booleano

Invece di +/∧\⌽Bcontare quanti 1 finali ci sono in Nte, puoi usare ⊥⍨.

Composizione inversa

A f∘g Bè A f g B, ma se vuoi (g A) f B, usa f⍨∘g⍨.

Riduzione inversa

f/ a1 a2 a3lo è a1 f (a2 f a3). Se vuoi (a1 f a2) f a3, usa f⍨/⌽.

Scansione inversa

f\ A B Clo è
A (A f B) (A f (B f C)).

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

f⍨\⌽ A B Clo è
((A f B) f C) (B f C) C.

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


2

Enumera i caratteri in una stringa senza ⍳≢

Compito: date due stringhe, S e T, elencano gli indici della loro concatenazione. Ad esempio S←'abcd'e T←'xyz'1 2 3 4 5 6 7.

Una soluzione dfn può essere S{⍳≢⍺,⍵}Tma vuoi accorciarla andando tacito. ⍳≢,non funzionerà perché le regole di analisi del treno lo analizzeranno come (⍳)≢(,)desiderato (⍳≢),.

Dyadic con un argomento sinistro vuoto classifica le matrici di caratteri semplici in base al loro ordine attuale, che è lo stesso di ⍳≢. Così {⍳≢⍺,⍵} può diventare {⍬⍋⍺,⍵}, così possiamo scrivere il nostro treno come ⍬⍋,.

Si noti che questo non funziona con array numerici o misti.


Wow, non sapevo che fosse una cosa.
Zacharý,
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.