Consejos para jugar al golf en Haskell

73

¿Qué consejos generales tienes para jugar al golf en Haskell? Estoy buscando ideas que puedan aplicarse a los problemas de código de golf en general que sean al menos algo específicos de Haskell. Por favor, publique solo un consejo por respuesta.


Si eres nuevo en el golf en Haskell, echa un vistazo a la Guía de reglas de golf en Haskell . También hay una sala de chat dedicada a Haskell: Of Monads and Men .

Animesh 'el CODER'
fuente
1
Al ver el número de respuestas hasta ahora, tengo dudas sobre si Haskell es incluso un buen lenguaje para el golf de código o no.
Animesh 'the CODER'
10
¿Por qué solo un consejo por respuesta? Además, cada idioma es un buen idioma para el golf. Simplemente no siempre esperes ganar.
Unclemeat
66
@unclemeat De esta manera, la gente podría votar a los buenos hasta la cima sin votar a los malos solo porque fueron escritos por el mismo tipo en la misma respuesta.
MasterMastic
3
Solicitud especial, Compresión de cuerdas.
J Atkin
Probablemente esto no sea adecuado como respuesta, pero todavía quiero agregarlo aquí: wiki.haskell.org/Prime_numbers_miscellaneous#One-liners
flawr

Respuestas:

45

Definir operadores infijos en lugar de funciones binarias.

Esto generalmente ahorra uno o dos espacios por definición o llamada.

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

vs.

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

Los símbolos disponibles para los operadores de 1 byte son !, #, %, &, y ?. Todos los demás signos de puntuación ASCII ya están definidos como operador por el Preludio (como $) o tienen un significado especial en la sintaxis de Haskell (como @).

Si necesita más de cinco operadores, puede usar combinaciones de los anteriores, como !#, o ciertos caracteres de puntuación Unicode, como estos (los 2 bytes en UTF-8):

¡ ¢ £ ¤ ¥ ¦ § ¨ © ¬ ® ¯ ° ± ´ ¶ · ¸ ¿ × ÷
shiona
fuente
11
Nota: esto también se puede usar para funciones con tres o más argumentos. (x!y)z=x+y*zy (x#y)z u=x*z+y*uambos funcionan como se esperaba.
Zgarb
3
Esto también se puede usar para argumentos de función, por ejemplo, en \f g(!)x y->f g!x ylugar de\f g j x y->j(f g)(x y)
Esolanging Fruit
2
A veces es beneficioso cambiar las funciones unarias a operadores binarios si de lo contrario tuviera que usar paréntesis - g x=…;g(f x)es más largo que_?x=…;0!f x
Angs
29

Use notación sin sentido (o libre) cuando sea apropiado

A menudo, una función con uno o dos parámetros puede escribirse sin puntos.

Entonces, una búsqueda de una lista de tuplas cuyos elementos se intercambian ingenuamente se escribe como:

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

(el tipo está ahí solo para ayudarlo a comprender lo que está haciendo).

para nuestros propósitos esto es mucho mejor:

revlookup=(.map swap).lookup
shiona
fuente
28

Usa la lista mónada

Una revisión rápida:

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

Ejemplos:

  • Repetir una lista dos veces

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • Más corta concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • Menor concat+ comprensión de la lista

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

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • Lista de coordenadas en una red

    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]]
    
Lynn
fuente
1
Otro uso que encontré útil es en [0..b]>>[a]lugar de replicate a b.
Wheat Wizard
3
@WheatWizard a<$[1..b]es aún más corto, por replicate.
Lynn
Usar =<<te obliga a importar Control.Monad. Si no lo necesita por alguna otra razón, intercambiar los argumentos y usarlos >>=parece más conciso.
dfeuer
OTOH, si lo necesita de Data.Traversabletodos modos, el ejemplo del producto cartesiano se puede acortar for["Hh","io",".!"]id.
dfeuer
2
(=<<)está en Preludio , en realidad! Lo he usado mucho.
Lynn
28

Use guardias no condicionales:

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

Use punto y coma, no sangrías

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

Usar expresiones booleanas para funciones booleanas

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

(SO es una molestia por dejarme publicar esto por separado)

bazzargh
fuente
2
Además, use guardias múltiples en lugar de &&cuando esté dentro de una lista de comprensión.
John Dvorak
Buena Jan - deberías convertir eso en una respuesta, votaré por ello
bazzargh
55
El primer ejemplo se puede acortar aún más por True=>1>0
John Dvorak
1
en el primer ejemplo, supongo que quieres decirf a=if a>0 then 3 else 7
Cyoce
Guard incluso funciona si no hay argumento en él.
Akangka
24

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

La gente a menudo olvida que esta función existe: toma todos los stdin y los aplica a una función (pura). A menudo veo main-code a lo largo de las líneas de

main=getContents>>=print.foo

mientras

main=interact$show.foo

es bastante más corto Está en el preludio, por lo que no es necesario importar.

Flonk
fuente
24

Use GHC 7.10

La primera versión de GHC que contenía estas cosas fue lanzada el 27 de marzo de 2015 .

Es la última versión, y Prelude tiene algunas nuevas incorporaciones que son útiles para jugar al golf:

Los operadores (<$>)y(<*>)

¡Estos operadores útiles lo Data.Applicativelograron! <$>es justo fmap, para que pueda reemplazar map f xy fmap f xcon f<$>xtodas partes y recuperar bytes. Además, <*>es útil en la Applicativeinstancia para listas:

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

El (<$)operador

x<$aes equivalente a fmap (const x) a; es decir, reemplazar cada elemento en un contenedor por x.

Esta suele ser una buena alternativa a replicate: 4<$[1..n]es más corta que replicate n 4.

La propuesta plegable / transitable

Las siguientes funciones se eliminaron de trabajar en listas [a]a Foldabletipos generales t a:

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

Esto significa que ahora también trabajan Maybe a, donde se comportan como "listas con un elemento como máximo". Por ejemplo null Nothing == True, o sum (Just 3) == 3. Del mismo modo, lengthdevuelve 0 para Nothingy 1 para los Justvalores. En lugar de escribir x==Just ypuedes escribir elem y x.

También puede aplicarlos en tuplas, que funciona como si hubiera llamado \(a, b) -> [b]primero. Es casi completamente inútil, pero or :: (a, Bool) -> Booles un personaje más corto que snd, y elem bes más corto que (==b).snd.

Las funciones monoides memptyymappend

No suele ser un salvavidas, pero si puede inferir el tipo, memptyes un byte más corto que Nothing, así que ahí está.

Lynn
fuente
55
+1 ¡Es genial saber acerca de '<$> `y <*>convertirlo en Preludio! Eso debería ser útil incluso cuando no es código golf (aplicativo es una palabra muy larga).
ankh-morpork
Advertencia sobre el reemplazo plano: si su versión de idioma es más reciente que el desafío, su solución no es elegible para ganar. Si desea actualizar sus respuestas o respuestas existentes, no sobrescriba su solución existente. Escribe un apéndice.
John Dvorak
44
Es curioso que haya [1..2]allí. eso es solo[1,2]
orgulloso Haskeller
2
En la misma versión también nos dieron <*de Applicativeque para las listas es xs <* ys == concatMap (replicate (length ys)) xs. Esto es diferente xs >> yso lo xs *> ysque es concat (replicate (length ys)) xs. pureque es más corto returnllegó en este punto también.
Angs
2
Ahora puede usar en <>lugar de mappend, ahora es (con GHC 8.4.1) parte de Prelude.
ბიმო
22

Use en 1<2lugar de Truey en 1>2lugar de False.

g x|x<10=10|True=x
f x|x<10=10|1<2=x
Flonk
fuente
3
Esto no es realmente específico de Haskell: es aplicable a casi todos los idiomas que tienen un tipo booleano en oposición a los valores de verdad y falsedad de otros tipos.
Peter Taylor
¿Alguien puede explicar esto?
MasterMastic
2
este no es un buen ejemplo, simplemente jugaría golf como esto f=max 10.
orgulloso Haskeller
@MasterMastic esto es solo escribir if(true)en otros idiomas. en el preludio, de lo contrario es en realidad el valor booleano True.
orgulloso Haskeller
2
@PeterTaylor Creo que esto sigue siendo valioso para los nuevos haskellianos (como yo) como aprendí a usar otherwise.
flawr
20

Usar listas de comprensión (de manera inteligente)

Todos saben que son una sintaxis útil, a menudo más corta que map+ una lambda:

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

O filter(y opcionalmente mapa al mismo tiempo):

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

Pero hay algunos usos más extraños que son útiles de vez en cuando. Por un lado, una comprensión de la lista no necesita contener ninguna <-flecha en absoluto:

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

Lo que significa que en lugar de if p then[x]else[], puedes escribir [x|p]. Además, para contar el número de elementos de una lista que satisfacen una condición, intuitivamente escribiría:

length$filter p x

Pero esto es más corto:

sum[1|y<-x,p y]
Lynn
fuente
De hecho, usé todos estos antes, pero no pensé en ponerlos aquí.
orgulloso Haskeller
18

Conoce tu Prelude

Encienda GHCi y desplácese por la documentación de Prelude . Cada vez que cruza una función que tiene un nombre corto, puede ser útil buscar algunos casos en los que podría ser útil.

Por ejemplo, supongamos que se desea transformar una cadena s = "abc\ndef\nghi"en una que es separada por espacios, "abc def ghi". La forma obvia es:

unwords$lines s

Pero puede hacerlo mejor si abusa maxy el hecho de que \n < space < printable ASCII:

max ' '<$>s

Otro ejemplo es lex :: String -> [(String, String)], que hace algo bastante misterioso:

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

Intenta fst=<<lex sobtener el primer token de una cadena, saltando los espacios en blanco. Aquí hay una solución inteligente de henkma que utiliza lex.showen Rationalvalores.

Lynn
fuente
16

Hacer coincidir un valor constante

Una comprensión de la lista puede coincidir con un patrón en una constante.


[0|0<-l]

Esto extrae los 0 de una lista l, es decir, hace una lista de tantos 0 como hay l.


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

Esto hace una lista de tantos 1como hay elementos lque fllevan a la lista vacía (usando <$>como infijo map). Aplicar sumpara contar estos elementos.

Comparar:

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

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

Se puede usar una constante como parte de una coincidencia de patrón. Esto extrae las segundas entradas de todas las tuplas cuya primera entrada es 0.


Tenga en cuenta que todos estos requieren un literal constante real, no el valor de una variable. Por ejemplo, se let x=1 in [1|x<-[1,2,3]]generará [1,1,1], no [1], porque el xenlace externo se sobrescribe.

xnor
fuente
14

Use en wordslugar de una larga lista de cadenas. Esto no es realmente específico de Haskell, otros idiomas también tienen trucos similares.

["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
nyuszika7h
fuente
14

Conoce tus funciones monádicas

1)
simular funciones monádicas usando mapM.

muchas veces el código tendrá sequence(map f xs), pero puede ser reemplazado por mapM f xs. incluso cuando solo se usa sequencesolo es más largo entonces mapM id.

2)
combinar funciones usando (>>=)(o (=<<))

la versión de la función mónada (>>=)se define así:

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

Puede ser útil para crear funciones que no se pueden expresar como una canalización. por ejemplo, \x->x==nub xes más largo que nub>>=(==), y \t->zip(tail t)tes más largo que tail>>=zip.

orgulloso Haskeller
fuente
+1: ni siquiera me había dado cuenta de que había una instancia de mónada para las funciones. eso podría ser muy útil :)
Julio
2
Nota al margen: aunque es parte Applicativey no Monadexiste la implementación pure, que es más corta consty realmente me ayudó antes.
ბიმო
14

Los argumentos pueden ser más cortos que las definiciones.

Me acaban de outgolfed de una manera muy curiosa por henkma .

Si una función auxiliar fen su respuesta usa un operador que no se usa en otra parte de su respuesta, y fse llama una vez, haga que el operador sea un argumento de f.

Esta:

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

Es dos bytes más largo que esto:

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

Use el operador contras (:)

al concatenar listas, si la primera es de longitud 1, úsela :en su lugar.

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

[3]++l
3:l    -- three characters shorter
orgulloso Haskeller
fuente
44
Vale la pena señalar que es correcto asociativo, por lo que puede seguir usándolo para cualquier número de elementos individuales al comienzo de la lista, por ejemplo, en 1:2:3:xlugar de [1,2,3]++x.
Julio
11

No use backticks con demasiada frecuencia. Los backticks son una herramienta genial para crear secciones de funciones de prefijo, pero a veces se pueden usar de forma incorrecta.

Una vez que vi a alguien escribir esta subexpresión:

(x`v`)

Aunque es lo mismo que justo v x.

Otro ejemplo es escribir (x+1)`div`y en lugar de div(x+1)y.

Veo que ocurre alrededor divy con elemmayor frecuencia porque estas funciones generalmente se usan como infijo en el código regular.

orgulloso Haskeller
fuente
¿No te refieres a hacer secciones de funciones de prefijo ?
Cyoce
@Cyoce Sí, por supuesto
haskeller orgulloso
11

Usar protectores de patrones

Son más cortas que una leto una lambda que deconstruye los argumentos de una función que está definiendo. Esto ayuda cuando se necesita algo así como fromJustdesde Data.Maybe:

f x=let Just c=… in c

es más largo que

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

es más largo que

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

es más largo que

f x|Just c<-…=c

De hecho, son más cortos incluso cuando se vincula un valor antiguo simple en lugar de deconstruir: vea la sugerencia de xnor .

Lynn
fuente
Bueno, el lambda no necesita el signo del dólar, y parece que este cambio hace que la longitud de este y el próximo fragmento sean iguales
orgulloso Haskeller
1
Supongo que en erealidad no es un token, sino una expresión más larga que se necesita $antes, que suele ser el caso.
Lynn
11

Condicional más corto

last$x:[y|b]

es equivalente a

if b then y else x

Así es como funciona:

             [y|b]   x:[y|b]   last$x:[y|b]
if...      +--------------------------------
b == False | []      [x]       x
b == True  | [y]     [x,y]     y
xnor
fuente
Debe ser if b then y else x?
Akangka
@ChristianIrwan Buena captura, sí.
xnor
No boolsería más corto ya que no necesita una lista de comprensión
Papa44
@ Potato44 Eso está en Data.Bool, que cuesta bytes para importar.
xnor
11

Trabajando con el signo menos

El signo menos -es una molesta excepción a muchas reglas de sintaxis. Este consejo enumera algunas formas cortas de expresar negación y sustracción en Haskell. Avísame si me he perdido algo.

Negación

  • Para negar una expresión e, solo hazlo -e. Por ejemplo, -length[1,2]da -2.
  • Si ees incluso moderadamente complejo, necesitará paréntesis e, pero generalmente puede guardar un byte moviéndolos: -length(take 3 x)es más corto que -(length$take 3 x).
  • Si eestá precedido por =un operador infijo de fijación menor que 6, necesita un espacio: f= -2define fy k< -2prueba si kes menor que -2. Si la fijación es 6 o mayor, necesita parens: 2^^(-2)da 0.25. Por lo general, puede reorganizar las cosas para deshacerse de estos: por ejemplo, hacer en -k>2lugar de k< -2.
  • Del mismo modo, si !es un operador, entonces -a!bse analiza como (-a)!bsi la fijeza de !a lo sumo sea 6 (por lo que -1<1da True), y de lo -(a!b)contrario (por lo que -[1,2]!!0da -1). La fijación predeterminada de los operadores definidos por el usuario y las funciones de retroceso es 9, por lo que siguen la segunda regla.
  • Para convertir la negación en una función (para usar con mapetc.), use la sección (0-).

Sustracción

  • Para obtener una función que resta k, use la sección (-k+), que suma -k. kIncluso puede ser una expresión bastante compleja: (-2*length x+)funciona como se esperaba.
  • Para restar 1, use preden su lugar, a menos que requiera un espacio en ambos lados. Esto es raro y generalmente ocurre con untiluna función definida por el usuario, ya que map pred xpuede ser reemplazada por pred<$>xy iterate pred xpor [x,x-1..]. Y si tiene f pred xalgún lugar, probablemente debería definirlo fcomo una función infija de todos modos. Mira este consejo .
Zgarb
fuente
11

Intente reorganizar definiciones y / o argumentos de funciones

A veces puede guardar un par de bytes cambiando el orden de los casos de coincidencia de patrones en una definición de función. Estos ahorros son baratos, pero fáciles de pasar por alto.

Como ejemplo, considere la siguiente versión anterior de (una parte de) esta respuesta :

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

Esta es una definición recursiva de ?, con el caso base como la lista vacía. Como []no es un valor útil, debemos intercambiar las definiciones y reemplazarlo con el comodín _o un argumento ficticio y, guardando un byte:

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

De la misma respuesta, considere esta definición:

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

La lista vacía aparece en el valor de retorno, por lo que podemos guardar dos bytes intercambiando los casos:

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

Además, el orden de los argumentos de la función a veces puede marcar la diferencia al permitirle eliminar espacios en blanco innecesarios. Considere una versión anterior de esta respuesta :

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

Hay un molesto espacio en blanco entre hy pen la primera rama. Podemos deshacernos de él definiendo en h a p qlugar de 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
Zgarb
fuente
10

No malgastes la guardia "de lo contrario"

Un guardia final que es un comodín True(más corto como 1>0) puede usarse para unir una variable. Comparar:

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

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

Dado que la guardia es obligatoria y de lo contrario se desperdicia, se necesita poco para que valga la pena. Es suficiente para guardar un par de parens o vincular una expresión de longitud 3 que se usa dos veces. A veces puede negar guardias para hacer que el caso final sea la expresión que mejor utiliza un enlace.

xnor
fuente
10

Usar en ,lugar de &&en guardias

Se pueden combinar múltiples condiciones en un guardia que todos deben tener en ,lugar de &&.

f a b | a/=5 && b/=7 = ...
f a b | a/=5 ,  b/=7 = ...
nimi
fuente
2
No tiene que ser condiciones tampoco, puede hacer cosas como esta:f xs m | [x] <- xs, Just y <- m, x > 3 = y
BlackCap
10

Sintaxis más corta para declaraciones locales

A veces necesita definir una función u operador local, pero cuesta muchos bytes escribir whereo let…inelevarlo al nivel superior agregando argumentos adicionales.

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

Afortunadamente, Haskell tiene una sintaxis confusa y poco utilizada pero razonablemente breve para las declaraciones locales :

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

En este caso:

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

Puede usar esta sintaxis con declaraciones de múltiples declaraciones o declaraciones múltiples, e incluso anida:

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

También funciona para vincular variables u otros patrones, aunque los protectores de patrones tienden a ser más cortos para eso a menos que también esté vinculando funciones.

Anders Kaseorg
fuente
3
Esto también funciona dentro de las listas por comprensión: [f 1|let f x=x+1].
Laikoni
10

Evitar 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..]

Cualquiera de esas cuatro expresiones producirá una lista infinita de n's.

Es un consejo muy específico, ¡pero puede ahorrar hasta 3 bytes!

totalmente humano
fuente
44
Si nes global, l=n:l;ltiene la misma longitud y funciona para (algunas) expresiones más largas. (Aunque puede necesitar espacios en blanco).
Ørjan Johansen
@ ØrjanJohansen Lo incorporé a la publicación. ¡Gracias!
totalmente humano
10

Condicionales más cortos cuando un resultado es la lista vacía

Cuando necesita un condicional que devuelve la lista Ao la lista vacía []según alguna condición C, existen algunas alternativas más cortas a las construcciones condicionales habituales:

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

Ejemplos: 1 , 2

Laikoni
fuente
El segundo tiene Ay []cambió.
Ørjan Johansen
@ ØrjanJohansen Corregido, ¡gracias!
Laikoni
1
¡Ajá! Pero *>tiene una mayor fijación que >>(todavía un poco baja para la comodidad.)
Ørjan Johansen
9

Reglas de análisis de Lambda

Una expresión lambda en realidad no necesita paréntesis a su alrededor, simplemente toma con avidez todo para que todo se analice, por ejemplo, hasta

  • un par de cierre (foo$ \x -> succ x)
  • una en - let a = \x -> succ x in a 4
  • el fin de la línea - main = getContents>>= \x -> head $ words x
  • etc.

se encuentra, y hay algunos casos extraños en los que esto puede ahorrarle un byte o dos. Creo que \también se puede usar para definir operadores, por lo que al explotar esto necesitará un espacio al escribir una lambda directamente después de un operador (como en el tercer ejemplo).

Aquí hay un ejemplo de dónde usar una lambda fue lo más corto que pude resolver. El código básicamente se ve así:

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

Reemplazar letpor lambda

Esto generalmente puede acortar una definición auxiliar solitaria que no puede vincularse con una protección o definirse globalmente por alguna razón. Por ejemplo, reemplazar

let c=foo a in bar

por los 3 bytes más cortos

(\c->bar)$foo a

Para múltiples definiciones auxiliares, la ganancia es probablemente menor, dependiendo del número de definiciones.

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

Si algunas de las definiciones se refieren a las demás, es aún más difícil guardar bytes de esta manera:

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

La advertencia principal con esto es que le letpermite definir variables polimórficas, pero las lambdas no lo hacen, como lo señaló @ChristianSievers. Por ejemplo,

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

resulta en (1,1), pero

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

da un error de tipo

Zgarb
fuente
1
Raramente importa, pero "semánticamente equivalente" promete demasiado. Tenemos polimórficos let, por lo que podemos hacer let f=id in (f 0,f True). Si intentamos reescribir esto con lambda, no escribe check.
Christian Sievers
@ChristianSievers Es cierto, gracias por la nota. Lo
edité
8

Atar usando guardias

Al definir una función con nombre, puede vincular una expresión a una variable en un resguardo. Por ejemplo,

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

hace lo mismo que

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

Use esto para guardar en expresiones repetidas. Cuando la expresión se usa dos veces, se rompe incluso en la longitud 6, aunque los problemas de espacio y precedencia pueden cambiar eso.

(En el ejemplo, si sno se usa la variable original , es más corto hacerlo

g w=...
f=g.words

pero eso no es cierto para vincular expresiones más complejas).

xnor
fuente
¿No es este un caso duplicado / especial de esta respuesta ?
Lynn el
@Lynn Mirando hacia atrás, es un caso especial, pero cuando leí esa respuesta, el Justejemplo me hizo pensar que es para la coincidencia de patrones extraer de un contenedor, en lugar de almacenarlo en una expresión.
xnor
8

Usar en (0<$)lugar de lengthpara comparaciones

Cuando se prueba si una lista aes más larga que una lista b, generalmente se escribiría

length a>length b

Sin embargo, reemplazar cada elemento de ambas listas con el mismo valor, por ejemplo 0, y luego comparar esas dos listas puede ser más corto:

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

Pruébalo en línea!

El paréntesis son necesarios porque <$ya los operadores de comparación ( ==, >, <=, ...) tienen el mismo nivel de precedencia 4, aunque en algunos otros casos que podrían no ser necesarios, ahorrando aún más bytes.

Laikoni
fuente
8

Más corta transpose

Para usar la transposefunción Data.Listhay que importarla. Si esta es la única función que necesita la importación, se puede guardar un byte utilizando la siguiente foldrdefinición de transpose:

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

Tenga en cuenta que el comportamiento solo es idéntico para una lista de listas con la misma longitud.

Utilicé esto con éxito aquí .

Laikoni
fuente
8

Consigue sufijos

Use scanr(:)[]para obtener los sufijos de una lista:

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

Esto es mucho más corto que tailsdespués import Data.List. Puedes hacer prefijos con scanr(\_->init)=<<id(encontrado por Ørjan Johansen).

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

Esto ahorra un byte sobre

scanl(\s c->s++[c])[]
xnor
fuente
Quizás scanl(flip(:))[] "abc"= ["","a","ba","cba"]también vale la pena mencionarlo: a veces los prefijos al revés no importan.
Lynn
3
Ørjan Johansen encontró una alternativa más corta de un byte para los prefijos:scanr(\_->init)=<<id
Laikoni