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 .


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):

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

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

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

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)


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.


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á.


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

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]

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.


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.


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

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.


+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

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

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.


¿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 .


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

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 .

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

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.


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 = ...

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.


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!


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


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']

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


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).


¿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.


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í .


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])[]

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.