Suggerimenti per il golf a Haskell


73

Quali consigli generali hai per giocare a golf a Haskell? Sto cercando idee che possano essere applicate ai problemi del codice golf in generale, almeno in qualche modo specifiche per Haskell. Si prega di inviare un solo suggerimento per risposta.


Se non conosci il golf a Haskell, dai un'occhiata alla Guida alle regole del golf a Haskell . C'è anche una chat room Haskell dedicata: Of Monads and Men .


1
Visto il numero di risposte fino ad ora, sono in dubbio sul fatto che Haskell sia anche una buona lingua per giocare a golf o no?
Animesh 'the CODER'

10
Perché solo un suggerimento per risposta? Inoltre, ogni lingua è una buona lingua per il golf. Non aspettarti sempre di vincere.
salato

6
@unclemeat In questo modo le persone possono votare i buoni verso l'alto senza votare quelli cattivi solo perché sono stati scritti dallo stesso ragazzo nella stessa risposta.
MasterMastic,

3
Richiesta speciale, compressione delle stringhe.
J Atkin,

Questo probabilmente non è adatto come risposta, ma voglio ancora aggiungerlo qui: wiki.haskell.org/Prime_numbers_miscellaneous#One-liners
flawr

Risposte:


45

Definire operatori infix anziché funzioni binarie

Ciò consente di risparmiare di solito uno o due spazi per definizione o chiamata.

0!(y:_)=y
x!(y:z)=(x-1)!z

vs.

f 0(y:_)=y
f x(y:z)=f(x-1)z

I simboli disponibili per gli operatori 1 byte sono !, #, %, &, e ?. Tutte le altre punteggiatura ASCII o sono già definite come operatore dal Preludio (come $) o hanno un significato speciale nella sintassi di Haskell (come @).

Se sono necessari più di cinque operatori, è possibile utilizzare combinazioni di quanto sopra, come !#, o alcuni caratteri di punteggiatura Unicode, come questi (tutti i 2 byte in UTF-8):

¡ ¢ £ ¤ ¥ ¦ § ¨ © ¬ ® ¯ ° ± ´ ¶ · ¸ ¿ × ÷

11
Nota: può essere utilizzato anche per funzioni con tre o più argomenti. (x!y)z=x+y*zed (x#y)z u=x*z+y*uentrambi funzionano come previsto.
Zgarb,

3
Questo può essere usato anche per argomenti di funzione, ad es. \f g(!)x y->f g!x yInvece di\f g j x y->j(f g)(x y)
Esolanging Fruit,

2
A volte è utile cambiare le funzioni unarie in operatori binari se altrimenti dovresti usare le parentesi - g x=…;g(f x)è più lungo di_?x=…;0!f x
Angs

29

Utilizzare la notazione inutile (o libera) ove appropriato

Spesso una funzione con uno o due parametri può essere scritta senza punti.

Quindi una ricerca di un elenco di tuple i cui elementi vengono scambiati è ingenuamente scritto come:

revlookup :: Eq b => b -> [(a, b)] -> Maybe a
revlookup e l=lookup e(map swap l)

(il tipo è lì solo per aiutarti a capire cosa sta facendo.)

per i nostri scopi è molto meglio:

revlookup=(.map swap).lookup

28

Usa la monade elenco

Una breve recensione:

xs >> ys        =  concat $ replicate (length xs) ys
xs >>= f        =  concatMap f xs
mapM id[a,b,c]  =  cartesian product of lists: a × b × c
mapM f[a,b,c]   =  cartesian product of lists: f a × f b × f c

Esempi:

  • Ripetendo un elenco due volte

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • Più breve concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • Più breve concat+ comprensione della lista

    Prelude> do x<-[1..5];[1..x]
    [1,1,2,1,2,3,1,2,3,4,1,2,3,4,5]
    
  • prodotto cartesiano

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • Elenco di coordinate su un reticolo

    Prelude> mapM(\x->[0..x])[3,2]
    [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2],[3,0],[3,1],[3,2]]
    

1
Un altro uso che ho trovato utile è [0..b]>>[a]invece di replicate a b.
Wheat Wizard

3
@WheatWizard a<$[1..b]è ancora più breve, per replicate.
Lynn,

L'uso =<<ti costringe a importare Control.Monad. Se non ne hai bisogno per qualche altro motivo, scambiare gli argomenti e usare >>=sembra più conciso.
decreto

OTOH, se necessario Data.Traversable, è possibile abbreviare l'esempio del prodotto cartesiano for["Hh","io",".!"]id.
decreto

2
(=<<)è in Preludio , in realtà! L'ho usato molto.
Lynn,

28

Usa protezioni non condizionali:

f a=if a>0 then 3 else 7
g a|a>0=3|True=7

Usa punto e virgola non rientri

f a=do
  this
  that
g a=do this;that

Usa espressioni booleane per funzioni booleane

f a=if zzz then True else f yyy
g a=zzz||f yyy

(SO è una seccatura nel lasciarmi pubblicare questi separatamente)


2
inoltre, utilizzare più protezioni anziché &&all'interno di una comprensione dell'elenco.
John Dvorak,

Buon gennaio - dovresti trasformarlo in una risposta, voterò per questo
bazzargh,

5
Il primo esempio può essere ulteriormente abbreviato da True=>1>0
John Dvorak il

1
nel primo esempio, suppongo che intendif a=if a>0 then 3 else 7
Cyoce

Guard funziona anche se non ci sono argomenti.
Akangka,

24

interact :: (String → String) → IO ()

Le persone spesso dimenticano che questa funzione esiste: prende tutto lo stdin e lo applica a una funzione (pura). Vedo spesso main-code sulla falsariga di

main=getContents>>=print.foo

mentre

main=interact$show.foo

è un po 'più breve. È nel preludio quindi non è necessario importare!


24

Usa GHC 7.10

La prima versione di GHC che conteneva questo materiale è stata rilasciata il 27 marzo 2015 .

È l'ultima versione e Prelude ha ottenuto alcune nuove aggiunte utili per il golf:

Gli operatori (<$>)e(<*>)

Questi utili operatori di ce l'hanno Data.Applicativefatta! <$>è giusto fmap, quindi è possibile sostituire map f xe fmap f xcon f<$>xovunque e riconquistare byte. Inoltre, <*>è utile Applicativenell'istanza per gli elenchi:

Prelude> (,)<$>[1..2]<*>"abcd"
[(1,'a'),(1,'b'),(1,'c'),(1,'d'),(2,'a'),(2,'b'),(2,'c'),(2,'d')]

L' (<$)operatore

x<$aè equivalente a fmap (const x) a; cioè sostituire ogni elemento in un contenitore con x.

Questa è spesso una bella alternativa a replicate: 4<$[1..n]è più breve di replicate n 4.

La proposta pieghevole / attraversabile

Le seguenti funzioni sono state portate dal lavoro sugli elenchi [a]ai Foldabletipi generali t a:

fold*, null, length, elem, maximum, minimum, sum, product
and, or, any, all, concat, concatMap

Ciò significa che ora funzionano anche su Maybe a, dove si comportano proprio come "elenchi con al massimo un elemento". Ad esempio null Nothing == True, o sum (Just 3) == 3. Allo stesso modo, lengthrestituisce 0 per Nothinge 1 per i Justvalori. Invece di scrivere x==Just ypuoi scrivere elem y x.

Puoi anche applicarli sulle tuple, che funzionano come se avessi chiamato per \(a, b) -> [b]primo. È quasi completamente inutile, ma or :: (a, Bool) -> Boolè un personaggio più corto di snd, ed elem bè più corto di (==b).snd.

Le funzioni Monoid memptyemappend

Non spesso salva la vita, ma se puoi inferire il tipo, memptyè un byte più corto di Nothing, quindi c'è quello.


5
+1 È fantastico conoscere '<$> `e <*>trasformarlo in Preludio! Ciò dovrebbe essere utile anche quando non si tratta di code golf (applicativo è una parola così lunga).
Ankh-Morpork,

Avvertenza sulla sostituzione semplice: se la versione della tua lingua è più recente della sfida, la tua soluzione non è vincente. Se si desidera aggiornare le risposte o le risposte esistenti, non sovrascrivere la soluzione esistente. Scrivi un'appendice.
John Dvorak,

4
Divertente c'è [1..2]dentro. questo è solo[1,2]
orgoglioso haskeller il

2
Nella stessa versione abbiamo anche ottenuto <*da Applicative, che per liste è xs <* ys == concatMap (replicate (length ys)) xs. Questo è diverso da xs >> yso xs *> ysquale è concat (replicate (length ys)) xs. pureche è più breve anche returna questo punto.
Angs,

2
Ora puoi usare <>invece di mappend, ora è (con GHC 8.4.1) parte del Prelude.
ბიმო

22

Utilizzare 1<2invece di Truee 1>2invece di False.

g x|x<10=10|True=x
f x|x<10=10|1<2=x

3
Questo non è proprio specifico di Haskell: è applicabile a quasi tutte le lingue che hanno un tipo booleano al contrario di valori di verità e falsità di altri tipi.
Peter Taylor,

Qualcuno può spiegare questo?
MasterMastic

2
questo non è un buon esempio, vorrei solo giocare a golf come f=max 10.
orgoglioso haskeller il

@MasterMastic questo è solo scrivere if(true)in altre lingue. nel preludio, altrimenti è in realtà il valore booleano True.
orgoglioso haskeller il

2
@PeterTaylor Penso che questo sia ancora prezioso per i nuovi haskelliani (come me) come ho imparato a usare per la prima volta otherwise.
flawr,

20

Usa la comprensione dell'elenco (in modi intelligenti)

Tutti sanno che sono utili sintassi, spesso più brevi di map+ a lambda:

Prelude> [[1..x]>>show x|x<-[1..9]]
["1","22","333","4444","55555","666666","7777777","88888888","999999999"]

Oppure filter(e facoltativamente mapa allo stesso tempo):

Prelude> [show x|x<-[1..60],mod 60x<1]
["1","2","3","4","5","6","10","12","15","20","30","60"]

Ma ci sono alcuni usi più strani che tornano utili di tanto in tanto. Per uno, una comprensione dell'elenco non deve contenere alcuna <-freccia:

Prelude> [1|False]
[]
Prelude> [1|True]
[1]

Il che significa invece if p then[x]else[]che puoi scrivere [x|p]. Inoltre, per contare il numero di elementi di un elenco che soddisfano una condizione, intuitivamente scrivere:

length$filter p x

Ma questo è più breve:

sum[1|y<-x,p y]

In realtà ho usato tutti questi prima, ma non pensavo di metterli qui.
orgoglioso haskeller il

18

Conosci il tuo Prelude

Avvia GHCi e scorri la documentazione Prelude . Ogni volta che attraversi una funzione che ha un nome breve, può pagare per cercare alcuni casi in cui potrebbe essere utile.

Per esempio, supponiamo che si desidera trasformare una stringa s = "abc\ndef\nghi"in uno che è separato da spazi, "abc def ghi". Il modo ovvio è:

unwords$lines s

Ma puoi fare di meglio se abusi maxe il fatto che \n < space < printable ASCII:

max ' '<$>s

Un altro esempio è lex :: String -> [(String, String)]che fa qualcosa di abbastanza misterioso:

Prelude> lex "   some string of Haskell tokens  123  "
[("some"," string of Haskell tokens  123  ")]

Prova fst=<<lex sa ottenere il primo token da una stringa, saltando gli spazi bianchi. Qui è una soluzione intelligente per henkma che utilizza lex.showsu Rationalvalori.


16

Abbina un valore costante

Una comprensione dell'elenco può modellare la corrispondenza su una costante.


[0|0<-l]

Questo estrae gli 0 di un elenco l, ovvero crea un elenco di tutti gli 0 presenti l.


[1|[]<-f<$>l] 

Questo crea un elenco di quanti 1sono gli elementi lche fportano alla lista vuota (usando <$>come infix map). Fai domanda sumper contare questi elementi.

Confrontare:

[1|[]<-f<$>l]
[1|x<-l,f x==[]]

[x|(0,x)<-l]

Una costante può essere utilizzata come parte di una corrispondenza del modello. Questo estrae le seconde voci di tutte le tuple la cui prima voce è 0.


Si noti che tutti questi richiedono un valore letterale costante effettivo, non il valore di una variabile. Ad esempio, let x=1 in [1|x<-[1,2,3]]verrà generato [1,1,1], non [1]perché il xbinding esterno viene sovrascritto.


14

Utilizzare wordsinvece di un lungo elenco di stringhe. Questo non è proprio specifico di Haskell, anche altre lingue hanno trucchi simili.

["foo","bar"]
words"foo bar"  -- 1 byte longer
["foo","bar","baz"]
words"foo bar baz"  -- 1 byte shorter
["foo","bar","baz","qux"]
words"foo bar baz qux"    -- 3 bytes shorter

14

Conosci le tue funzioni monadiche

1)
simulare le funzioni monadiche usando mapM.

molte volte il codice avrà sequence(map f xs), ma può essere sostituito con mapM f xs. anche quando si usa sequenceda solo è più lungo mapM id.

2)
combina le funzioni usando (>>=)(o (=<<))

la versione monade della funzione (>>=)è definita come segue:

(f >>= g) x = g (f x) x 

può essere utile per creare funzioni che non possono essere espresse come pipeline. ad esempio, \x->x==nub xè più lungo di nub>>=(==)ed \t->zip(tail t)tè più lungo di tail>>=zip.


+1 - Non avevo nemmeno capito che esisteva un'istanza monade per le funzioni. potrebbe essere molto utile :)
Jules il

2
Nota a margine: anche se fa parte Applicativee non Monadc'è anche l'implementazione pure, che è più breve di conste in realtà mi ha aiutato prima.
ბიმო

14

Gli argomenti possono essere più brevi delle definizioni

Sono appena stato superato in modo molto curioso da Henkma .

Se una funzione ausiliaria fnella tua risposta utilizza un operatore che non è utilizzato altrove nella tua risposta e fviene chiamata una volta, trasforma l'operatore in un argomento f.

Questo:

(!)=take
f a=5!a++3!a
reverse.f

È due byte più lungo di questo:

f(!)a=5!a++3!a
reverse.f take

12

Usa l'operatore contro (:)

quando si concatenano elenchi, se il primo è di lunghezza 1, utilizzare :invece.

a++" "++b
a++' ':b  -- one character shorter

[3]++l
3:l    -- three characters shorter

4
Vale la pena notare che è giusto associativo, quindi puoi continuare a usarlo per qualsiasi numero di singoli elementi all'inizio dell'elenco, ad esempio 1:2:3:xanziché [1,2,3]++x.
Jules,

11

Non usare i backtick troppo spesso. I backtick sono uno strumento interessante per creare sezioni di funzioni prefisso, ma a volte possono essere utilizzate in modo improprio.

Una volta ho visto qualcuno scrivere questa sottoespressione:

(x`v`)

Anche se è lo stesso di solo v x.

Un altro esempio è la scrittura (x+1)`div`y al contrario div(x+1)y.

Vedo accadere in giro dive elempiù spesso perché queste funzioni sono solitamente utilizzate come infisso nel codice normale.


Non intendi creare sezioni di funzioni prefisso ?
Cyoce,

@Cyoce Sì, certo
orgoglioso haskeller il

11

Usa protezioni di motivi

Sono più brevi di una leto una lambda che decostruisce gli argomenti di una funzione che stai definendo. Questo aiuta quando hai bisogno di qualcosa di simile fromJustda Data.Maybe:

f x=let Just c=… in c

è più lungo di

f x=(\(Just c)->c)$…

è più lungo di

m(Just c)=c;f x=m$…

è più lungo di

f x|Just c<-…=c

In effetti, sono più brevi anche quando si associa un semplice vecchio valore invece di decostruire: vedere il suggerimento di xnor .


Bene, quello lambda non ha bisogno del simbolo del dollaro, e sembra che questo cambiamento renda la stessa lunghezza di questo e dello snippet successivo
orgoglioso haskeller il

1
Suppongo che in erealtà non sia un token ma un'espressione più lunga che ha bisogno $prima di esso, che di solito è il caso.
Lynn,

11

Condizionale più breve

last$x:[y|b]

è equivalente a

if b then y else x

Ecco come funziona:

             [y|b]   x:[y|b]   last$x:[y|b]
if...      +--------------------------------
b == False | []      [x]       x
b == True  | [y]     [x,y]     y

Dovrebbe essere if b then y else x?
Akangka,

@ChristianIrwan Buona cattura, sì.
xnor

Usando non boolsarebbe più breve in quanto non è necessaria una comprensione dell'elenco
Potato44

@ Potato44 È in Data.Bool, che costa i byte da importare.
xnor

11

Lavorare con il segno meno

Il segno meno -è una fastidiosa eccezione a molte regole di sintassi. Questo suggerimento elenca alcuni modi brevi per esprimere negazione e sottrazione in Haskell. Per favore fatemi sapere se ho perso qualcosa.

Negazione

  • Per negare un'espressione e, basta fare -e. Ad esempio, -length[1,2]-2.
  • Se eè anche moderatamente complesso, avrai bisogno delle parentesi e, ma di solito puoi salvare un byte spostandole: -length(take 3 x)è più breve di -(length$take 3 x).
  • Se eè preceduto da =o un operatore infisso di fissità inferiore a 6, è necessario uno spazio: f= -2definisce fe k< -2verifica se kè minore di -2. Se la fissità è 6 o maggiore, hai bisogno di parentesi: 2^^(-2)0.25. Di solito puoi riorganizzare le cose per sbarazzartene: per esempio, fai -k>2invece di k< -2.
  • Allo stesso modo, se !è un operatore, -a!bviene analizzato come (-a)!bse la fissità di !fosse al massimo 6 (quindi -1<1True), e -(a!b)altrimenti (così -[1,2]!!0-1). La fissità predefinita degli operatori definiti dall'utente e delle funzioni backticked è 9, quindi seguono la seconda regola.
  • Per trasformare la negazione in una funzione (da usare con mapecc.), Usa la sezione (0-).

Sottrazione

  • Per ottenere una funzione che sottrae k, utilizzare la sezione (-k+), che aggiunge -k. kpuò anche essere un'espressione piuttosto complessa: (-2*length x+)funziona come previsto.
  • Per sottrarre 1, utilizzare predinvece, a meno che non richiederebbe uno spazio su entrambi i lati. Questo è raro e di solito accade con untilo una funzione definita dall'utente, poiché map pred xpuò essere sostituita da pred<$>xe iterate pred xda [x,x-1..]. E se hai da f pred xqualche parte, dovresti probabilmente definire fcomunque una funzione infix. Vedi questo consiglio .

11

Prova a riorganizzare le definizioni delle funzioni e / o gli argomenti

A volte è possibile salvare un paio di byte modificando l'ordine dei casi di corrispondenza dei modelli in una definizione di funzione. Questi risparmi sono economici, ma facili da trascurare.

Ad esempio, considera la seguente versione precedente di (una parte di) questa risposta :

(g?x)[]=x
(g?x)(a:b)=g(g?x$b)a

Questa è una definizione ricorsiva di ?, con il caso base essendo l'elenco vuoto. Poiché []non è un valore utile, dovremmo scambiare le definizioni e sostituirlo con il carattere jolly _o un argomento fittizio y, salvando un byte:

(g?x)(a:b)=g(g?x$b)a
(g?x)y=x

Dalla stessa risposta, considera questa definizione:

f#[]=[]
f#(a:b)=f a:f#b

L'elenco vuoto si trova nel valore restituito, quindi possiamo salvare due byte scambiando i casi:

f#(a:b)=f a:f#b
f#x=x

Inoltre, l'ordine degli argomenti delle funzioni può talvolta fare la differenza consentendo di rimuovere spazi bianchi non necessari. Considera una versione precedente di questa risposta :

h p q a|a>z=0:h p(q+2)(a-1%q)|1<2=1:h(p+2)q(a+1%p)

C'è un fastidioso pezzo di spazio bianco tra he pnel primo ramo. Possiamo liberarcene definendo h a p qinvece di h p q a:

h a p q|a>z=0:h(a-1%q)p(q+2)|1<2=1:h(a+1%p)(p+2)q

10

Non sprecare la protezione "altrimenti"

Una protezione finale che è un catch-all True(più breve come 1>0) può essere utilizzata per associare una variabile. Confrontare:

... |1>0=1/(x+y)
... |z<-x+y=1/z

... |1>0=sum l-sum m
... |s<-sum=s l-s m

Dal momento che la guardia è obbligatoria ed è altrimenti sprecata, poco è necessario per far valere la pena. È sufficiente salvare una coppia di parentesi o associare un'espressione di lunghezza 3 utilizzata due volte. A volte è possibile negare le protezioni per rendere il caso finale l'espressione che utilizza meglio un'associazione.


10

Utilizzare ,invece di &&protezioni

Al ,posto di possono essere combinate più condizioni in una guardia che tutti devono tenere &&.

f a b | a/=5 && b/=7 = ...
f a b | a/=5 ,  b/=7 = ...

2
Non devono essere nemmeno condizioni, puoi fare cose del genere:f xs m | [x] <- xs, Just y <- m, x > 3 = y
BlackCap

10

Sintassi più breve per le dichiarazioni locali

A volte è necessario definire una funzione locale o un operatore, ma costa un sacco di byte da scrivere whereo let…ino di sollevarlo a livello superiore con l'aggiunta di argomenti extra.

g~(a:b)=2!g b where k!l=k:take(a-1)l++(k+1)!drop(a-1)l
g~(a:b)=let k!l=k:take(a-1)l++(k+1)!drop(a-1)l in 2!g b
g~(a:b)=2!g b$a;(k!l)a=k:take(a-1)l++((k+1)!drop(a-1)l)a

Fortunatamente, Haskell ha una sintassi confusa e poco usata ma ragionevolmente concisa per le dichiarazioni locali :

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

In questo caso:

g~(a:b)|let k!l=k:take(a-1)l++(k+1)!drop(a-1)l=2!g b

È possibile utilizzare questa sintassi con dichiarazioni a più istruzioni o dichiarazioni multiple e nidifica anche:

fun1 pattern1 | let fun2 pattern2 = expr2; fun2 pattern2' = expr2' = expr1
fun1 pattern1 | let fun2 pattern2 = expr2; fun3 pattern3 = expr3 = expr1
fun1 pattern1 | let fun2 pattern2 | let fun3 pattern3 = expr3 = expr2 = expr1

Funziona anche con variabili vincolanti o altri motivi, sebbene le protezioni dei motivi tendano ad essere più brevi per questo a meno che tu non sia anche vincolante funzioni.


3
Questo funziona anche all'interno list comprehension: [f 1|let f x=x+1].
Laikoni,

10

Evitare repeat n

-- 8 bytes, whitespace might be needed before and after
repeat n

-- 8 bytes, whitespace might be needed before
cycle[n]

-- 7 bytes, whitespace might be needed before and after, can be reused,
-- needs an assignment, n needs to be global
l=n:l;l

-- 7 bytes, never needs whitespace, n needs to derive from Enum,
-- n has to be short enough to be repeated twice
[n,n..]

Ognuna di queste quattro espressioni produrrà un elenco infinito di n's.

È un suggerimento molto specifico ma può salvare fino a 3 byte!


4
Se nè globale, ha l=n:l;lla stessa lunghezza e funziona per (alcune) espressioni più lunghe. (Potrebbe aver bisogno di spazi bianchi.)
Ørjan Johansen,

@ ØrjanJohansen l'ho incorporato nel post. Grazie!
totalmente umano il

10

Condizionatori più corti quando un risultato è l'elenco vuoto

Quando è necessario un condizionale che restituisce l'elenco Ao l'elenco vuoto a []seconda di una condizione C, allora esistono alternative più brevi ai soliti costrutti condizionali:

if(C)then(A)else[] -- the normal conditional
last$[]:[A|C]      -- the golfy all-round-conditional
concat[A|C]        -- shorter and works when surrounded by infix operator
id=<<[A|C]         -- even shorter but might conflict with other infix operators
[x|C,x<-A]         -- same length and no-conflict-guarantee™
[0|C]>>A           -- shortest way, but needs surrounding parenthesis more often than not

Esempi: 1 , 2


Il secondo ha Ae []acceso.
Ørjan Johansen,

@ ØrjanJohansen Corretto, grazie!
Laikoni,

1
Aha! Ma *>ha una fissità più alta di >>(ancora un po 'bassa per il comfort.)
Ørjan Johansen

9

Regole di analisi lambda

Un'espressione lambda in realtà non ha bisogno di parentesi attorno ad esso - prende piuttosto avidamente tutto in modo che tutto sia ancora analizzato, ad esempio fino a quando

  • una parentesi di chiusura - (foo$ \x -> succ x)
  • un in - let a = \x -> succ x in a 4
  • La fine della linea - main = getContents>>= \x -> head $ words x
  • eccetera..

viene rilevato e ci sono alcuni casi limite strani in cui questo può farti risparmiare uno o due byte. Credo che \possa anche essere usato per definire gli operatori, quindi quando lo sfruttate avrai bisogno di uno spazio quando scrivete un lambda direttamente dopo un operatore (come nel terzo esempio).

Ecco un esempio di dove usare una lambda era la cosa più breve che potessi capire. Il codice appare sostanzialmente come:

a%f=...
f t=sortBy(% \c->...)['A'..'Z']

9

Sostituisci letcon lambda

Questo di solito può abbreviare una definizione ausiliaria solitaria che non può essere vincolata con una guardia o definita globalmente per qualche motivo. Ad esempio, sostituire

let c=foo a in bar

dai 3 byte più corti

(\c->bar)$foo a

Per più definizioni ausiliarie, il guadagno è probabilmente inferiore, a seconda del numero di definizioni.

let{c=foo a;n=bar a}in baz
(\c n->baz)(foo a)$bar a

let{c=foo a;n=bar a;m=baz a}in qux
(\c n m->qux)(foo a)(bar a)$baz a

let{c=foo a;n=bar a;m=baz a;l=qux a}in quux
(\c n m l->quux)(foo a)(bar a)(baz a)$qux a

Se alcune delle definizioni si riferiscono alle altre, è ancora più difficile salvare i byte in questo modo:

let{c=foo a;n=bar c}in baz
(\c->(\n->baz)$bar c)$foo a

La principale avvertenza è che letconsente di definire variabili polimorfiche, ma le lambda no, come notato da @ChristianSievers. Per esempio,

let f=length in(f["True"],f[True])

risulta (1,1), ma

(\f->(f["True"],f[True]))length

dà un errore di tipo.


1
Raramente importa, ma "semanticamente equivalente" promette un po 'troppo. Abbiamo polimorfico let, quindi possiamo fare let f=id in (f 0,f True). Se proviamo a riscriverlo con lambda non digita check.
Christian Sievers,

@ChristianSievers È vero, grazie per la nota. L'ho modificato.
Zgarb,

8

Associa usando le protezioni

Quando si definisce una funzione con nome, è possibile associare un'espressione a una variabile in una guardia. Per esempio,

f s|w<-words s=...

fa lo stesso di

f s=let w=words s in ...
f s=(\w->...)$words s

Usa questo per salvare su espressioni ripetute. Quando l'espressione viene usata due volte, si interrompe anche alla lunghezza 6, sebbene i problemi di spaziatura e precedenza possano cambiarla.

(Nell'esempio, se la variabile originale snon viene utilizzata, è più breve da fare

g w=...
f=g.words

ma questo non è vero per legare espressioni più complesse.)


Questo tipo di duplicazione / caso speciale di questa risposta non è ?
Lynn,

@Lynn Guardando indietro, è un caso speciale, ma quando ho letto quella risposta, l' Justesempio mi ha fatto pensare che il pattern-matching sia estratto da un contenitore, piuttosto che archiviare su un'espressione.
xnor

8

Utilizzare (0<$)invece che lengthper i confronti

Quando si verifica se un elenco aè più lungo di un elenco b, di solito si scrive

length a>length b

Tuttavia, sostituire ciascun elemento di entrambi gli elenchi con lo stesso valore, ad esempio 0, e quindi confrontare questi due elenchi può essere più breve:

(0<$a)>(0<$b)

Provalo online!

La parentesi sono necessarie perché <$e gli operatori di confronto ( ==, >, <=, ...) hanno lo stesso livello di precedenza 4, anche se in alcuni altri casi potrebbero non essere necessari, risparmiando ancora di più byte.


8

Più breve transpose

Per utilizzare la transposefunzione Data.Listdeve essere importato. Se questa è l'unica funzione che richiede l'importazione, è possibile salvare un byte usando la seguente foldrdefinizione di transpose:

import Data.List;transpose
e=[]:e;foldr(zipWith(:))e

Si noti che il comportamento è identico solo per un elenco di elenchi con la stessa lunghezza.

L'ho usato con successo qui .


8

Ottieni suffissi

Utilizzare scanr(:)[]per ottenere i suffissi di un elenco:

λ scanr(:)[] "abc"
["abc","bc","c",""]

Questo è molto più breve di tailsdopo import Data.List. Puoi fare prefissi con scanr(\_->init)=<<id(trovato da Ørjan Johansen).

λ  scanr(\_->init)=<<id $ "abc"
["","a","ab","abc"]

Ciò consente di risparmiare un byte

scanl(\s c->s++[c])[]

Forse scanl(flip(:))[] "abc"= ["","a","ba","cba"]vale anche la pena menzionare - a volte i prefissi che sono all'indietro non contano.
Lynn,

3
Ørjan Johansen ha trovato un'alternativa più corta di un byte per i prefissi:scanr(\_->init)=<<id
Laikoni
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.