Dicas para jogar golfe em Haskell


73

Que dicas gerais você tem para jogar golfe em Haskell? Estou procurando idéias que possam ser aplicadas para codificar problemas de golfe em geral que sejam pelo menos um pouco específicos para Haskell. Poste apenas uma dica por resposta.


Se você é novo no golfe em Haskell, consulte o Guia de regras de golfe em Haskell . Há também uma sala de bate-papo exclusiva de Haskell: Of Monads and Men .


11
Vendo o número de respostas até agora, estou em dúvida se Haskell é mesmo uma boa linguagem para o código de golfe ou não?
Animesh 'the CODER'

10
Por que apenas uma dica por resposta? Além disso, todo idioma é um bom idioma para jogar golfe. Só nem sempre espere ganhar.
unclemeat

6
@unclemeat Desta forma, as pessoas poderiam votar de maneira positiva os bons até o topo sem votar nos ruins apenas porque foram escritos pelo mesmo cara na mesma resposta.
MasterMastic

3
Pedido especial, Compactação de strings.
precisa

Provavelmente não é adequado como resposta, mas ainda quero adicioná-lo aqui: wiki.haskell.org/Prime_numbers_misc
Miscellaneous#

Respostas:


45

Definir operadores de infix em vez de funções binárias

Isso economiza geralmente um ou dois espaços por definição ou chamada.

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

vs.

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

Os símbolos disponíveis para os operadores de um-byte são !, #, %, &, e ?. Todas as outras pontuações ASCII já estão definidas como um operador pelo Prelude (como $) ou têm um significado especial na sintaxe de Haskell (como @).

Se você precisar de mais de cinco operadores, poderá usar combinações dos itens acima, como !#certos caracteres de pontuação Unicode, como estes (todos os 2 bytes em UTF-8):

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

11
Nota: isso também pode ser usado para funções com três ou mais argumentos. (x!y)z=x+y*ze (x#y)z u=x*z+y*uambos funcionam como esperado.
Zgarb

3
Isso também pode ser usado para argumentos de função, por exemplo, em \f g(!)x y->f g!x yvez de\f g j x y->j(f g)(x y)
Esolanging Fruit

2
Às vezes é benéfico para alterar as funções unários para operadores binários se você caso contrário tem que usar parênteses - g x=…;g(f x)é maior do que_?x=…;0!f x
Angs

29

Use notação inútil (ou livre), quando apropriado

Frequentemente, uma função com um ou dois parâmetros pode ser escrita sem pontos.

Portanto, uma pesquisa por uma lista de tuplas cujos elementos são trocados é ingenuamente escrita como:

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

(o tipo existe apenas para ajudá-lo a entender o que está fazendo.)

para nossos propósitos, isso é muito melhor:

revlookup=(.map swap).lookup

28

Use a lista mônada

Uma rápida revisão:

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

Exemplos:

  • Repetindo uma lista duas vezes

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • Mais curta concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • Menor concatcompreensão da lista +

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

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • Lista de coordenadas em uma treliça

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

11
Outro uso que eu achei útil é em [0..b]>>[a]vez de replicate a b.
Wheat Wizard

3
@WheatWizard a<$[1..b]é ainda mais curto, por replicate.
Lynn

O uso =<<força você a importar Control.Monad. Se você não precisar disso por algum outro motivo, trocar os argumentos e usar >>=parece mais conciso.
Dfeuer

OTOH, se você precisar de Data.Traversablequalquer maneira, o exemplo do produto cartesiano pode ser abreviado para for["Hh","io",".!"]id.
Dfeuer

2
(=<<)está no Prelude , na verdade! Eu usei muito isso.
Lynn

28

Use proteções não condicionais:

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

Use ponto-e-vírgula e não recuos

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

Use expressões booleanas para funções booleanas

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

(SO está sendo uma dor ao me deixar publicá-las separadamente)


2
Além disso, use vários protetores em vez de &&quando estiver dentro de uma compreensão de lista.
John Dvorak

Boa Jan - você deve fazer isso em uma resposta, vou votar a favor
bazzargh

5
O primeiro exemplo pode ser abreviado ainda mais por True=>1>0
John Dvorak

11
no primeiro exemplo, eu suponho que você quer dizerf a=if a>0 then 3 else 7
Cyoce

O guarda até funciona se não houver argumento.
Akangka

24

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

As pessoas geralmente esquecem que essa função existe - ela pega todo o stdin e a aplica a uma função (pura). Costumo ver main-code ao longo das linhas de

main=getContents>>=print.foo

enquanto

main=interact$show.foo

é um pouco mais curto. É no Prelude, portanto não há necessidade de importação!


24

Use GHC 7.10

A primeira versão do GHC que continha esse material foi lançada em 27 de março de 2015 .

É a versão mais recente e o Prelude recebeu algumas novas adições úteis para jogar golfe:

Os operadores (<$>)e(<*>)

Esses operadores úteis Data.Applicativeconseguiram! <$>é apenas fmap, para que você possa substituir map f xe fmap f xpor f<$>xqualquer lugar e recuperar bytes. Além disso, <*>é útil na Applicativeinstância para listas:

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

O (<$)operador

x<$aé equivalente a fmap (const x) a; ou seja, substitua todos os elementos em um contêiner por x.

Geralmente, é uma boa alternativa para replicate: 4<$[1..n]é menor que replicate n 4.

A proposta dobrável / atravessável

As seguintes funções foram retiradas do trabalho em listas [a]para Foldabletipos gerais t a:

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

Isso significa que agora eles também trabalham Maybe a, onde se comportam exatamente como "listas com no máximo um elemento". Por exemplo,, null Nothing == Trueou sum (Just 3) == 3. Da mesma forma, lengthretorna 0 para Nothinge 1 para Justvalores. Em vez de escrever, x==Just yvocê pode escrever elem y x.

Você também pode aplicá-las em tuplas, o que funciona como se você tivesse chamado \(a, b) -> [b]primeiro. É quase completamente inútil, mas or :: (a, Bool) -> Boolé um personagem menor que snd, e elem bé menor que (==b).snd.

O Monoid funciona memptyemappend

Não é sempre um salva-vidas, mas se você pode inferir o tipo, memptyé um byte mais curto que Nothingisso, então é isso.


5
+1 É ótimo ouvir sobre '<$> `e <*>entrar no Prelude! Isso deve ser útil mesmo quando não é código de golfe (aplicativo é uma palavra tão longa).
Ankh-morpork 10/07/2015

Aviso sobre a substituição simples: se a versão do seu idioma for mais recente que o desafio, sua solução não será elegível para ganhar. Se você deseja atualizar suas respostas ou respostas existentes, não substitua sua solução existente. Escreva um apêndice.
John Dvorak

4
Engraçado lá [1..2]dentro. isso é apenas[1,2]
orgulhoso haskeller

2
Na mesma versão também tivemos <*de Applicative, que para listas é xs <* ys == concatMap (replicate (length ys)) xs. Isso é diferente xs >> ysou o xs *> ysque é concat (replicate (length ys)) xs. pureque é mais curto também returnveio neste ponto.
Angs

2
Agora você pode usar em <>vez de mappend, agora é (com GHC 8.4.1) parte do Prelude.
ბიმო

22

Use em 1<2vez de Truee em 1>2vez de False.

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

3
Isso não é realmente específico para Haskell: é aplicável a praticamente todos os idiomas que têm um tipo booleano, em oposição a valores verdadeiros e falsos de outros tipos.
Peter Taylor

Alguém pode explicar isso?
MasterMastic

2
este não é um bom exemplo, eu apenas jogaria isso como f=max 10.
haskeller orgulhoso

@ MasterMastic, isto é apenas escrita if(true)em outros idiomas. no prelúdio, caso contrário, é realmente o valor booleano True.
proud haskeller

2
@ PeterTaylor Acho que isso ainda é valioso para novos haskellianos (como eu), como eu aprendi a usar otherwise.
flawr

20

Use a compreensão da lista (de maneiras inteligentes)

Todo mundo sabe que é uma sintaxe útil, geralmente menor que map+ um lambda:

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

Ou filter(e opcionalmente um mapao mesmo tempo):

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

Mas existem alguns usos mais esquisitos que são úteis de vez em quando. Por um lado, a compreensão da lista não precisa conter nenhuma <-seta:

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

O que significa que, em vez de if p then[x]else[], você pode escrever [x|p]. Além disso, para contar o número de elementos de uma lista que satisfazem uma condição, você intuitivamente escreveria:

length$filter p x

Mas isso é mais curto:

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

Na verdade, eu usei tudo isso antes, mas não pensei em colocá-los aqui.
haskeller orgulhoso

18

Saber seu Prelude

Inicie o GHCi e role a documentação do Prelude . Sempre que você cruza uma função que tem um nome abreviado, pode ser útil procurar alguns casos em que possa ser útil.

Por exemplo, suponha que você deseje transformar uma string s = "abc\ndef\nghi"em uma que seja separada por espaço "abc def ghi". A maneira óbvia é:

unwords$lines s

Mas você pode fazer melhor se abusar maxe o fato de \n < space < printable ASCII:

max ' '<$>s

Outro exemplo é o lex :: String -> [(String, String)]que faz algo bastante misterioso:

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

Tente fst=<<lex sobter o primeiro token de uma string, pulando o espaço em branco. Aqui está uma solução inteligente da henkma que usa lex.showem Rationalvalores.


16

Corresponder a um valor constante

Uma compreensão de lista pode corresponder a um padrão em uma constante.


[0|0<-l]

Isso extrai os zeros de uma lista l, ou seja, faz uma lista de quantos zs existem l.


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

Isso faz uma lista de quantos 1são os elementos lque flevam para a lista vazia (usando <$>como infixo map). Aplique sumpara contar esses elementos.

Comparar:

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

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

Uma constante pode ser usada como parte de uma correspondência de padrão. Isso extrai as segundas entradas de todas as tuplas cuja primeira entrada é 0.


Observe que tudo isso exige um literal constante constante, não o valor de uma variável. Por exemplo, let x=1 in [1|x<-[1,2,3]]será exibido [1,1,1], não [1], porque a xligação externa é substituída.


14

Use em wordsvez de uma longa lista de strings. Isso não é realmente específico para Haskell, outros idiomas também têm truques semelhantes.

["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

Conheça suas funções monádicas

1)
simule funções monádicas usando mapM.

muitas vezes o código terá sequence(map f xs), mas pode ser substituído por mapM f xs. mesmo quando apenas usando sequencesozinho, é mais longo mapM id.

2)
combine funções usando (>>=)(ou (=<<))

a versão da função monad (>>=)é definida da seguinte forma:

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

pode ser útil para criar funções que não podem ser expressas como um pipeline. por exemplo, \x->x==nub xé maior que nub>>=(==)e \t->zip(tail t)té maior que tail>>=zip.


+1 - eu nem tinha percebido que havia uma instância de mônada para funções. isso poderia ser muito útil :) #
224 Jules

2
Nota lateral: Embora faça parte Applicativee não Monadexista a implementação pure, que é mais curta do que conste realmente me ajudou antes.
ბიმო

14

Os argumentos podem ser mais curtos que as definições

Acabei de ser enganado de maneira muito curiosa pelo henkma .

Se uma função auxiliar fna sua resposta usa um operador que não é usado em nenhum outro lugar da sua resposta e fé chamada uma vez, faça do operador um argumento f.

Este:

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

Tem dois bytes a mais que isso:

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

12

Use o operador contras (:)

ao concatenar listas, se o primeiro tiver o comprimento 1, use-o :.

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

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

4
Vale a pena notar que é associativo correto, para que você possa continuar usando-o para qualquer número de itens únicos no início da lista, por exemplo, em 1:2:3:xvez de [1,2,3]++x.
Jules

11

Não use backticks com muita frequência. Os backticks são uma ferramenta interessante para criar seções de funções de prefixo, mas às vezes podem ser mal utilizados.

Uma vez vi alguém escrever esta subexpressão:

(x`v`)

Embora seja o mesmo que justo v x.

Outro exemplo é escrever (x+1)`div`y em oposição a div(x+1)y.

Eu vejo isso acontecer em torno dive elemmais frequentemente porque essas funções são geralmente utilizados como infix em código regular.


Você não quer dizer criar seções de funções de prefixo ?
Cyoce

@Cyoce Sim, é claro
proud haskeller 2/16

11

Use protetores de padrão

Eles são mais curtos que um letou um lambda que desconstrói os argumentos de uma função que você está definindo. Isso ajuda quando você precisa de algo como fromJusta partir de Data.Maybe:

f x=let Just c=… in c

é maior que

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

é maior que

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

é maior que

f x|Just c<-…=c

Na verdade, eles são mais curtos, mesmo quando vinculam um valor antigo simples em vez de desconstruir: veja a dica do xnor .


Bem, o lambda não precisa do sinal de dólar, e parece que esta mudança faz com que os comprimentos deste e do próximo trecho da mesma
haskeller orgulhoso

11
Estou assumindo que enão é realmente um token, mas uma expressão mais longa que precisa $antes dele, o que geralmente é o caso.
Lynn

11

Condicional mais curto

last$x:[y|b]

é equivalente a

if b then y else x

Veja como funciona:

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

Deveria ser if b then y else x?
Akangka

@ChristianIrwan Boa captura, sim.
Xnor

Wouldnt utilizando boolser mais curto que você não precisa de uma compreensão da lista
Potato44

@ Potato44 Está no Data.Bool, que custa bytes para importar.
xnor

11

Trabalhando com o sinal de menos

O sinal de menos -é uma exceção irritante para muitas regras de sintaxe. Esta dica lista algumas maneiras curtas de expressar negação e subtração em Haskell. Por favor, deixe-me saber se eu perdi alguma coisa.

Negação

  • Para negar uma expressão e, apenas faça -e. Por exemplo, -length[1,2]-2.
  • Se efor moderadamente complexo, você precisará de parênteses e, mas geralmente pode salvar um byte movendo-o: -length(take 3 x)é menor que -(length$take 3 x).
  • Se efor precedido por =ou um operador infix de fixidade menor que 6, você precisará de um espaço: f= -2define fe k< -2testa se ké menor que -2. Se a fixidez é de 6 ou maior, você precisa de parênteses: 2^^(-2)0.25. Geralmente, você pode reorganizar as coisas para se livrar delas: por exemplo, faça em -k>2vez de k< -2.
  • Da mesma forma, se !é um operador, então -a!bé analisado como (-a)!bse a fixidez de !fosse no máximo 6 (o que -1<1True) e o -(a!b)contrário (o que -[1,2]!!0-1). A fixidez padrão dos operadores definidos pelo usuário e das funções com reticulação é 9, portanto eles seguem a segunda regra.
  • Para ligar negação para uma função (para usar com mapetc), utilizar a secção (0-).

Subtração

  • Para obter uma função que subtrai k, use a seção (-k+)que adiciona -k. kpode até ser uma expressão bastante complexa: (-2*length x+)funciona como esperado.
  • Para subtrair 1, use em predvez disso, a menos que exija espaço nos dois lados. Isso é raro e geralmente ocorre com untilou uma função definida pelo usuário, pois map pred xpode ser substituída por pred<$>xe iterate pred xpor [x,x-1..]. E se você tem f pred xalgum lugar, provavelmente deve definir fcomo uma função infix de qualquer maneira. Veja esta dica .

11

Tente reorganizar definições de função e / ou argumentos

Às vezes, você pode salvar alguns bytes alterando a ordem dos casos de correspondência de padrão em uma definição de função. Essas economias são baratas, mas fáceis de ignorar.

Como exemplo, considere a seguinte versão anterior (parte da) desta resposta :

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

Esta é uma definição recursiva de ?, com o caso base sendo a lista vazia. Como []não é um valor útil, devemos trocar as definições e substituí-lo pelo caractere curinga _ou argumento fictício y, salvando um byte:

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

Da mesma resposta, considere esta definição:

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

A lista vazia ocorre no valor de retorno, para que possamos salvar dois bytes trocando os casos:

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

Além disso, a ordem dos argumentos das funções às vezes pode fazer a diferença, permitindo remover espaços em branco desnecessários. Considere uma versão anterior desta resposta :

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

Há um espaço irritante de espaço em branco entre he pno primeiro ramo. Podemos nos livrar dele definindo em h a p qvez 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

Não desperdice a guarda "caso contrário"

Um guarda final que é genérico True(menor que 1>0) pode ser usado para vincular uma variável. Comparar:

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

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

Como a guarda é obrigatória e é desperdiçada, pouco é necessário para fazer isso valer a pena. Basta salvar um par de parênteses ou vincular uma expressão de comprimento 3 usada duas vezes. Às vezes, você pode negar guardas para tornar o caso final a expressão que melhor usa uma ligação.


10

Use em ,vez de &&em guardas

Múltiplas condições em um guarda que todos precisam manter podem ser combinadas em ,vez de &&.

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

2
Também não precisa ser condições, você pode fazer coisas como estas:f xs m | [x] <- xs, Just y <- m, x > 3 = y
BlackCap

10

Sintaxe mais curta para declarações locais

Às vezes, você precisa definir uma função ou operador local, mas custa muitos bytes para escrever whereou let…inou elevá-la ao nível superior, adicionando argumentos extras.

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

Felizmente, Haskell tem uma sintaxe confusa e raramente usada, mas razoavelmente concisa para declarações locais :

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

Nesse caso:

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

Você pode usar esta sintaxe com declarações com várias instruções ou várias declarações, e até aninha:

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

Ele também funciona para vincular variáveis ​​ou outros padrões, embora os protetores de padrões tendam a ser menores para isso, a menos que você também esteja vinculando funções.


3
Isso também funciona dentro compreensões lista: [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..]

Qualquer uma dessas quatro expressões produzirá uma lista infinita de n's.

É uma dica muito específica, mas pode economizar até 3 bytes!


4
Se nfor global, l=n:l;ltem o mesmo comprimento e funciona para (algumas) expressões mais longas. (Pode precisar de espaço em branco.) #
Ørjan Johansen 11/11

@ ØrjanJohansen Eu o incorporei ao post. Obrigado!
totallyhuman

10

Condicionais mais curtos quando um resultado é a lista vazia

Quando você precisa de um condicional que retorna a lista Aou a lista vazia, []dependendo de alguma condição C, existem algumas alternativas mais curtas às construções condicionais usuais:

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

Exemplos: 1 , 2


O segundo tem Ae []mudou.
Ørjan Johansen

@ ØrjanJohansen Corrigido, obrigado!
Laikoni

11
Aha! Mas *>tem fixidez maior do que >>(ainda um pouco baixo para o conforto.)
Ørjan Johansen

9

Regras de análise do Lambda

Uma expressão lambda na verdade não precisa de parênteses em torno dela - apenas pega avidamente tudo, de modo que a coisa toda ainda analisa, por exemplo, até

  • um ponto final - (foo$ \x -> succ x)
  • um em - let a = \x -> succ x in a 4
  • O fim da linha - main = getContents>>= \x -> head $ words x
  • etc ..

é encontrado e existem alguns casos estranhos em que isso pode economizar um ou dois bytes. Eu acredito \que também pode ser usado para definir operadores, portanto, ao explorar isso, você precisará de um espaço ao escrever um lambda diretamente após um operador (como no terceiro exemplo).

Aqui está um exemplo de onde o uso de um lambda foi a coisa mais curta que consegui descobrir. O código basicamente se parece com:

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

9

Substituir let por lambda

Isso geralmente pode reduzir uma definição auxiliar isolada que não pode ser vinculada a um guarda ou definida globalmente por algum motivo. Por exemplo, substitua

let c=foo a in bar

pelos 3 bytes mais curtos

(\c->bar)$foo a

Para várias definições auxiliares, o ganho é provavelmente menor, dependendo do número de definições.

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 algumas das definições se referirem às outras, é ainda mais difícil salvar bytes desta maneira:

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

A principal ressalva disso é que letpermite definir variáveis ​​polimórficas, mas as lambdas não, como observado por @ChristianSievers. Por exemplo,

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

resulta em (1,1), mas

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

dá um erro de tipo.


11
Raramente importa, mas "semanticamente equivalente" promete um pouco demais. Temos polimórficos let, então podemos fazer let f=id in (f 0,f True). Se tentarmos reescrever isso com o lambda, não digite check.
Christian Sievers

@ChristianSievers Isso é verdade, obrigado pela observação. Eu editei em.
Zgarb 18/01/17

8

Vincular usando proteções

Ao definir uma função nomeada, você pode vincular uma expressão a uma variável em um guarda. Por exemplo,

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

faz o mesmo que

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

Use isso para economizar em expressões repetidas. Quando a expressão é usada duas vezes, ela atinge o comprimento 6, embora os problemas de espaçamento e precedência possam mudar isso.

(No exemplo, se a variável original snão for usada, será mais curto

g w=...
f=g.words

mas isso não é verdade para vincular expressões mais complexas.)


Esse não é um caso duplicado / especial dessa resposta ?
Lynn

@ Lynn Olhando para trás, é um caso especial, mas quando li essa resposta, o Justexemplo me fez pensar que é para correspondência de padrões extrair de um contêiner, em vez de armazenar em uma expressão.
Xnor

8

Use em (0<$)vez de lengthpara comparações

Ao testar se uma lista aé maior que uma lista b, normalmente se escreve

length a>length b

No entanto, a substituição de cada elemento de ambas as listas pelo mesmo valor, por exemplo 0, e a comparação dessas duas listas podem ser mais curtas:

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

Experimente online!

O parêntese são necessários porque <$e os operadores de comparação ( ==, >, <=, ...) têm o mesmo nível de precedência 4, embora em alguns outros casos, pode não ser necessário, economizando ainda mais bytes.


8

Mais curta transpose

Para usar a transposefunção Data.Listprecisa ser importada. Se esta é a única função que precisa da importação, é possível salvar um byte usando a seguinte foldrdefinição de transpose:

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

Observe que o comportamento é idêntico apenas para uma lista de listas com o mesmo comprimento.

Eu usei isso aqui com sucesso .


8

Obter sufixos

Use scanr(:)[]para obter os sufixos de uma lista:

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

Isso é muito mais curto do que tailsdepois import Data.List. Você pode fazer prefixos com scanr(\_->init)=<<id(encontrado por Ørjan Johansen).

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

Isso economiza um byte

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

Talvez scanl(flip(:))[] "abc"= ["","a","ba","cba"]também mereça ser mencionado - algumas vezes os prefixos que estão ao contrário não importam.
Lynn

3
Ørjan Johansen encontrou uma alternativa mais curta para um prefixo de um byte:scanr(\_->init)=<<id
Laikoni
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.