Conseils pour jouer au golf à Haskell


73

Quels conseils généraux avez-vous pour jouer au golf à Haskell? Je cherche des idées qui puissent être appliquées aux problèmes de code de golf en général et qui sont au moins quelque peu spécifiques à Haskell. Merci de ne publier qu'un pourboire par réponse.


Si vous débutez dans le golf à Haskell, veuillez consulter le Guide des règles de golf à Haskell . Il existe également une salle de discussion dédiée à Haskell: Of Monads and Men .


1
Vu le nombre de réponses à ce jour, je ne sais pas si Haskell est un bon langage pour coder le golf ou pas?
Animesh 'the CODER' le

10
Pourquoi un seul conseil par réponse? En outre, chaque langue est une bonne langue pour le golf. Mais ne vous attendez pas toujours à gagner.
Unmemeat

6
@unclemeat De cette façon, les gens pourraient voter en priorité vers les meilleurs sans relancer les mauvais uniquement parce qu'ils ont été écrits par le même type dans la même réponse.
MasterMastic

3
Demande spéciale, compression de chaîne.
J Atkin

Cela ne convient probablement pas en tant que réponse, mais je veux tout de même l'ajouter ici: wiki.haskell.org/Prime_numbers_miscellaneous#One-liners
flawr

Réponses:


45

Définir des opérateurs infixes au lieu de fonctions binaires

Cela économise généralement un ou deux espaces par définition ou appel.

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

contre.

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

Les symboles disponibles pour les opérateurs de 1 octet sont !, #, %, &et ?. Toute autre ponctuation ASCII est déjà définie en tant qu'opérateur par Prelude (tel que $) ou a une signification spéciale dans la syntaxe de Haskell (telle que @).

Si vous avez besoin de plus de cinq opérateurs, vous pouvez utiliser des combinaisons de ce qui précède, telles que !#, ou certains caractères de ponctuation Unicode, tels que ceux-ci (tous les 2 octets en UTF-8):

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

11
Remarque: ceci peut également être utilisé pour des fonctions avec trois arguments ou plus. (x!y)z=x+y*zet les (x#y)z u=x*z+y*udeux fonctionnent comme prévu.
Zgarb

3
Cela peut également être utilisé pour les arguments de fonction, par exemple à la \f g(!)x y->f g!x yplace de\f g j x y->j(f g)(x y)
Esolanging Fruit

2
Parfois, il est avantageux de remplacer les fonctions unaires par des opérateurs binaires si vous deviez sinon utiliser des parenthèses - g x=…;g(f x)est plus long que_?x=…;0!f x
Angs

29

Utilisez des notations inutiles (ou libres) le cas échéant

Souvent, une fonction avec un ou deux paramètres peut être écrite sans point.

Ainsi, une recherche d'une liste de n-uplets dont les éléments sont échangés s'écrit naïvement comme suit:

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

(le type est là juste pour vous aider à comprendre ce qu'il fait.)

pour nos besoins c'est beaucoup mieux:

revlookup=(.map swap).lookup

28

Utilisez la liste monade

Un examen rapide:

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

Exemples:

  • Répéter une liste deux fois

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • Plus court concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • concatCompréhension plus courte + liste

    Prelude> do x<-[1..5];[1..x]
    [1,1,2,1,2,3,1,2,3,4,1,2,3,4,5]
    
  • produit cartésien

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • Liste de coordonnées sur un réseau

    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
Une autre utilisation que j’ai trouvée utile est [0..b]>>[a]au lieu de replicate a b.
Wheat Wizard

3
@ WheatWizard a<$[1..b]est encore plus court, pour replicate.
Lynn le

Utiliser des =<<forces pour importer Control.Monad. Si vous n'en avez pas besoin pour une autre raison, échanger les arguments et utiliser >>=semble plus concis.
dfeuer

OTOH, si vous en avez besoin de Data.Traversabletoute façon, l'exemple de produit cartésien peut être raccourci for["Hh","io",".!"]id.
dfeuer

2
(=<<)est en prélude , en fait! Je l'ai beaucoup utilisé.
Lynn

28

Utilisez des gardes pas conditionnels:

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

Utilisez des points-virgules et non des tirets

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

Utiliser des expressions booléennes pour les fonctions booléennes

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

(SO est une douleur de me laisser poster ces séparément)


2
aussi, utilisez plusieurs gardes au lieu de &&dans une liste de compréhension.
John Dvorak

Bon un janvier - vous devriez en faire une réponse, je vais voter pour elle
bazzargh

5
Le premier exemple peut être encore raccourci de True=>1>0
John Dvorak le

1
dans le premier exemple, je suppose que vous voulez diref a=if a>0 then 3 else 7
Cyoce

Guard fonctionne même s'il n'y a pas de discussion.
Akangka

24

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

Les gens oublient souvent que cette fonction existe - elle saisit tout le stdin et l'applique à une fonction (pure). Je vois souvent main-code le long des lignes de

main=getContents>>=print.foo

tandis que

main=interact$show.foo

est un peu plus court. C'est dans le prélude donc pas besoin d'importations!


24

Utilisez GHC 7.10

La première version de GHC contenant ces éléments a été publiée le 27 mars 2015 .

C'est la dernière version, et Prelude a de nouveaux ajouts utiles pour le golf:

Les (<$>)et (<*>)opérateurs

Ces opérateurs utiles de Data.Applicativemade in! <$>est juste fmap, donc vous pouvez remplacer map f xet fmap f xavec f<$>xpartout et récupérer des octets. Aussi, <*>est utile dans l' Applicativeinstance pour les listes:

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

L' (<$)opérateur

x<$aest équivalent à fmap (const x) a; c'est-à-dire remplacer chaque élément d'un conteneur par x.

C'est souvent une bonne alternative à replicate: 4<$[1..n]est plus courte que replicate n 4.

La proposition pliable / transversale

Les fonctions suivantes ont été levées du travail sur les listes [a]aux Foldabletypes généraux t a:

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

Cela signifie qu'ils travaillent maintenant également Maybe a, où ils se comportent exactement comme des "listes comportant au plus un élément". Par exemple null Nothing == True, ou sum (Just 3) == 3. De même, lengthrenvoie 0 pour Nothinget 1 pour les Justvaleurs. Au lieu d'écrire, x==Just yvous pouvez écrire elem y x.

Vous pouvez également les appliquer sur des tuples, ce qui fonctionne comme si vous aviez appelé en \(a, b) -> [b]premier. C'est presque complètement inutile, mais or :: (a, Bool) -> Boolest un personnage plus court que snd, et elem best plus court que (==b).snd.

Le monoïde fonctionne memptyetmappend

Pas souvent un épargnant de vie, mais si vous pouvez en déduire le type, memptyun octet est plus court que Nothing, alors voilà.


5
+1 Il est bon d'entendre parler de '<$> `et de le <*>transformer en prélude! Cela devrait être utile même quand ce n'est pas du code golf (applicatif est un mot tellement long).
ankh-morpork

Avertissement concernant le remplacement à plat: si votre version linguistique est plus récente que le défi, votre solution ne peut pas être gagnée. Si vous souhaitez mettre à jour vos réponses existantes, n'écrasez pas votre solution existante. Ecrire une annexe.
John Dvorak

4
C'est drôle là- [1..2]dedans. c'est juste[1,2]
fier haskeller

2
Dans la même version , nous avons également obtenu à <*partir Applicative, ce qui est pour les listes xs <* ys == concatMap (replicate (length ys)) xs. Ceci est différent de xs >> ysou xs *> ysqui est concat (replicate (length ys)) xs. purequi est un plus court returnest venu à ce stade aussi.
Angs

2
Vous pouvez maintenant utiliser <>au lieu de mappend, c’est maintenant (avec GHC 8.4.1) une partie de Prelude.
ბიმო

22

Utilisez 1<2au lieu de Trueet 1>2au lieu de False.

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

3
Ce n’est pas vraiment spécifique à Haskell: il s’applique à peu près à toutes les langues qui ont un type booléen, par opposition aux valeurs de vérité et de fausseté des autres types.
Peter Taylor

Quelqu'un peut-il expliquer cela?
MasterMastic

2
ce n'est pas un bon exemple, je voudrais juste jouer au golf comme ça f=max 10.
fier haskeller

@MasterMastic c'est juste écrire if(true)dans d'autres langues. dans le prélude, sinon est en réalité la valeur booléenne True.
fier haskeller

2
@PeterTaylor Je pense que cela reste utile pour les nouveaux haskelliens (comme moi), comme je l'ai appris à utiliser pour la première fois otherwise.
flawr

20

Utiliser les compréhensions de liste (de manière intelligente)

Tout le monde sait que leur syntaxe est utile, souvent plus courte que map+ un lambda:

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

Ou filter(et éventuellement mapen même temps):

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

Mais il y a des utilisations plus étranges qui sont utiles de temps en temps. D'une part, une compréhension de liste n'a pas besoin de contenir de <-flèches du tout:

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

Ce qui signifie qu'au lieu de if p then[x]else[], vous pouvez écrire [x|p]. De plus, pour compter le nombre d'éléments d'une liste satisfaisant une condition, vous écririez intuitivement:

length$filter p x

Mais c'est plus court:

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

En fait, j'ai déjà utilisé tous ces outils auparavant, mais je n'ai pas pensé à les mettre ici.
fier haskeller

18

Connaître ton Prelude

Lancez GHCi et parcourez la documentation Prelude . Chaque fois que vous croisez une fonction dont le nom est court, il peut être utile de rechercher des cas où cela pourrait être utile.

Par exemple, supposons que vous souhaitiez transformer une chaîne s = "abc\ndef\nghi"en chaînes séparées par des espaces "abc def ghi". La manière évidente est:

unwords$lines s

Mais vous pouvez faire mieux si vous abusez maxet le fait que \n < space < printable ASCII:

max ' '<$>s

Un autre exemple est lex :: String -> [(String, String)], qui fait quelque chose d'assez mystérieux:

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

Essayez fst=<<lex sd’obtenir le premier jeton d’une chaîne en évitant les espaces blancs. Voici une solution intelligente par henkma qui utilise lex.showsur des Rationalvaleurs.


16

Correspondre à une valeur constante

Une compréhension de liste peut correspondre à un motif sur une constante.


[0|0<-l]

Ceci extrait les 0 d’une liste l, c’est-à-dire crée une liste contenant autant de 0 qu’il y en a l.


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

Cela fait une liste de autant 1de éléments qu'il y a d'éléments lqui fprend la liste vide (en utilisant <$>infix map). Appliquez sumpour compter ces éléments.

Comparer:

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

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

Une constante peut être utilisée dans le cadre d'une correspondance de modèle. Ceci extrait les secondes entrées de tous les tuples dont la première est 0.


Notez que tous ces éléments nécessitent un littéral constant réel et non la valeur d'une variable. Par exemple, let x=1 in [1|x<-[1,2,3]]affichera [1,1,1], pas [1]parce que la xliaison externe est écrasée.


14

Utilisez wordsau lieu d'une longue liste de chaînes. Ce n'est pas vraiment spécifique à Haskell, d'autres langues ont aussi des astuces similaires.

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

Connaissez vos fonctions monadiques

1)
simuler des fonctions monadiques en utilisant mapM.

beaucoup de fois le code aura sequence(map f xs), mais il peut être remplacé par mapM f xs. même si vous utilisez seulement sequenceseul, il est plus long mapM id.

2)
combiner des fonctions en utilisant (>>=)(ou (=<<))

la fonction monad version de (>>=)est définie comme suit:

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

cela peut être utile pour créer des fonctions qui ne peuvent pas être exprimées sous forme de pipeline. par exemple, \x->x==nub xest plus long que nub>>=(==), et \t->zip(tail t)test plus long que tail>>=zip.


+1 - Je n'avais même pas réalisé qu'il existait une instance de monade pour les fonctions. cela pourrait être très pratique :)
Jules

2
Note latérale: Bien que cela fasse partie Applicativeet non Monadde la mise en œuvre, pureelle est plus courte constet m'a effectivement aidée auparavant.
ბიმო

14

Les arguments peuvent être plus courts que les définitions

Je viens de me outgolfed d'une manière très curieuse par henkma .

Si une fonction auxiliaire fde votre réponse utilise un opérateur qui n'est pas utilisé ailleurs dans votre réponse et fest appelée une fois, définissez-le comme argument f.

Cette:

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

Est-ce que deux octets sont plus longs que cela:

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

12

Utilisez le contre opérateur (:)

lors de la concaténation de listes, si la première est de longueur 1, utilisez :plutôt.

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

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

4
Il est à noter que c'est juste associatif, donc vous pouvez continuer à l'utiliser pour n'importe quel nombre d'éléments simples au début de la liste, par exemple 1:2:3:xplutôt que [1,2,3]++x.
Jules

11

Ne pas utiliser les backticks trop souvent. Les backticks sont un outil intéressant pour créer des sections de fonctions de préfixe, mais ils peuvent parfois être mal utilisés.

Une fois que j'ai vu quelqu'un écrire cette sous-expression:

(x`v`)

Bien que ce soit la même chose que juste v x.

Un autre exemple est l'écriture (x+1)`div`y par opposition à div(x+1)y.

Je le vois souvent divet elemplus souvent parce que ces fonctions sont généralement utilisées comme infixes dans le code normal.


Ne voulez-vous pas dire créer des sections de fonctions de préfixe ?
Cyoce

@Cyoce Oui, bien sûr
fier haskeller

11

Utiliser des gardes de modèle

Ils sont plus courts qu'un letou un lambda qui déconstruit les arguments d'une fonction que vous définissez. Cela aide lorsque vous avez besoin de quelque chose comme fromJustde Data.Maybe:

f x=let Just c=… in c

est plus long que

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

est plus long que

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

est plus long que

f x|Just c<-…=c

En fait, ils sont plus courts même lorsqu'ils lient une ancienne valeur ordinaire au lieu de la déconstruire: voir le conseil de xnor .


Eh bien, le lambda n'a pas besoin du signe dollar, et il semble que ce changement rend la longueur de cet extrait et du prochain extrait identiques
fier haskeller

1
Je suppose que ce en'est pas réellement un jeton mais une expression plus longue qui doit $être précédée, ce qui est généralement le cas.
Lynn

11

Conditionnel plus court

last$x:[y|b]

est équivalent à

if b then y else x

Voilà comment cela fonctionne:

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

Cela devrait-il être if b then y else x?
Akangka

@ChristianIrwan Bonne prise, oui.
xnor

La consommation ne boolserait pas plus courte, car vous n'avez pas besoin d'une liste de compréhension
Potato44

@ Potato44 C'est dans Data.Bool, ce qui coûte des octets à importer.
xnor

11

Travailler avec le signe moins

Le signe moins -est une exception ennuyeuse à de nombreuses règles de syntaxe. Cette astuce répertorie quelques manières d’exprimer la négation et la soustraction en Haskell. S'il vous plaît laissez-moi savoir si j'ai manqué quelque chose.

Négation

  • Pour nier une expression e, il suffit de faire -e. Par exemple, -length[1,2]donne -2.
  • Si elle eest même moyennement complexe, vous aurez besoin de parenthèses e, mais vous pouvez généralement enregistrer un octet en les déplaçant: -length(take 3 x)est plus court que -(length$take 3 x).
  • Si eest précédé de =ou par un opérateur infixe de fixité inférieur à 6, vous avez besoin d'un espace: f= -2définit fet k< -2teste si kest inférieur à -2. Si la fixité est de 6 ou plus, vous avez besoin de parens: 2^^(-2)donne 0.25. Vous pouvez généralement réorganiser les éléments pour vous en débarrasser: par exemple, faites à la -k>2place de k< -2.
  • De même, si !est un opérateur, alors -a!best analysé comme (-a)!bsi la fixité de !était au plus 6 ( -1<1donne donc True), et -(a!b)sinon ( -[1,2]!!0donnait ainsi -1). La fixité par défaut des opérateurs définis par l'utilisateur et des fonctions associées à un point arrière est de 9; ils respectent donc la deuxième règle.
  • Pour transformer la négation en une fonction (à utiliser avec mapetc.), utilisez la section (0-).

Soustraction

  • Pour obtenir une fonction qui soustrait k, utilisez la section (-k+)qui ajoute -k. kpeut même être une expression assez complexe: (-2*length x+)fonctionne comme prévu.
  • Pour soustraire 1, utilisez predplutôt, à moins que cela ne nécessite un espace des deux côtés. Cela est rare et se produit généralement avec untilune fonction définie par l'utilisateur, car elle map pred xpeut être remplacée par pred<$>xet iterate pred xpar [x,x-1..]. Et si vous avez f pred xquelque part, vous devriez probablement définir fcomme une fonction infixe de toute façon. Voir cette astuce .

11

Essayez de réorganiser les définitions de fonctions et / ou les arguments

Vous pouvez parfois enregistrer quelques octets en modifiant l'ordre des cas de correspondance de modèle dans une définition de fonction. Ces économies sont bon marché, mais faciles à négliger.

Par exemple, considérons la version antérieure suivante de (une partie de) cette réponse :

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

C'est une définition récursive de ?, le cas de base étant la liste vide. Comme ce []n'est pas une valeur utile, nous devrions échanger les définitions et les remplacer par le caractère générique _ou un argument factice y, en sauvegardant un octet:

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

De la même réponse, considérons cette définition:

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

La liste vide apparaît dans la valeur de retour, nous pouvons donc économiser deux octets en échangeant les cas:

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

En outre, l'ordre des arguments de fonction peut parfois faire une différence en vous permettant de supprimer les espaces inutiles. Considérons une version antérieure de cette réponse :

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

Il y a un espace blanc ennuyeux entre het pdans la première branche. On peut s'en débarrasser en définissant à la h a p qplace 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

Ne perdez pas le garde "autrement"

Un garde final qui est un fourre-tout True(plus court que 1>0) peut être utilisé pour lier une variable. Comparer:

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

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

Comme la garde est obligatoire et qu'elle est autrement perdue, il en faut peu pour que cela en vaille la peine. Il suffit d'enregistrer une paire de parens ou de lier une expression de longueur 3 utilisée deux fois. Parfois, vous pouvez nier les gardes pour que le cas final soit l'expression qui utilise le mieux la liaison.


10

Utilisez ,au lieu de &&dans les gardes

Plusieurs conditions dans un garde qui doivent toutes tenir peuvent être combinés avec au ,lieu de &&.

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

2
Il n'est pas nécessaire que ce soit des conditions non plus, vous pouvez faire des choses comme ceci:f xs m | [x] <- xs, Just y <- m, x > 3 = y
BlackCap

10

Syntaxe plus courte pour les déclarations locales

Parfois, il est nécessaire de définir une fonction ou un opérateur local, mais écrire whereou let…inrelever le niveau supérieur en ajoutant des arguments supplémentaires coûte cher .

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

Heureusement, Haskell utilise une syntaxe déroutante et rarement utilisée mais raisonnablement succincte pour les déclarations locales :

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

Dans ce cas:

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

Vous pouvez utiliser cette syntaxe avec des déclarations multi-déclarations ou des déclarations multiples, et cela même s'imbrique:

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

Cela fonctionne également pour les variables de liaison ou d'autres modèles, bien que les gardes de modèle aient tendance à être plus courts pour cela, sauf si vous utilisez également des fonctions de liaison.


3
Cela fonctionne aussi à l' intérieur liste compréhensions: [f 1|let f x=x+1].
Laikoni

10

Éviter 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..]

Chacune de ces quatre expressions produira une liste infinie de n's.

C'est un conseil très spécifique, mais vous pouvez économiser jusqu'à 3 octets!


4
Si nest global, a l=n:l;lla même longueur et fonctionne pour (certaines) expressions plus longues. (Peut-être besoin d'espaces cependant.)
Ørjan Johansen

@ ØrjanJohansen je l'ai incorporé dans le post. Merci!
totalement humain

10

Conditions plus courtes lorsqu'un résultat est la liste vide

Lorsque vous avez besoin d'un conditionnel qui renvoie la liste Aou la liste vide en []fonction de certaines conditions C, il existe des alternatives plus courtes aux constructions conditionnelles habituelles:

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

Exemples: 1 , 2


Le second a Aet []changé.
Ørjan Johansen

@ ØrjanJohansen Corrigé, merci!
Laikoni

1
Aha! Mais *>a plus de fixité que >>(toujours un peu faible pour le confort.)
Ørjan Johansen

9

Règles d'analyse Lambda

Une expression lambda n’a pas vraiment besoin de parenthèses; elle saisit tout plutôt goulûment pour que tout continue à être analysé, par exemple jusqu’à ce que

  • un paren de clôture - (foo$ \x -> succ x)
  • un dans - let a = \x -> succ x in a 4
  • La fin de la ligne - main = getContents>>= \x -> head $ words x
  • etc..

est rencontré, et il y a quelques cas de bord étranges où cela peut vous sauver un octet ou deux. Je pense que cela \peut également être utilisé pour définir des opérateurs, donc lors de l’exploitation, vous aurez besoin d’un espace pour écrire un lambda directement après un opérateur (comme dans le troisième exemple).

Voici un exemple où lambda était la chose la plus courte que je pouvais comprendre. Le code ressemble en gros à ceci:

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

9

Remplacer letpar lambda

Cela peut généralement raccourcir une définition auxiliaire unique qui ne peut pas être liée à une garde ou définie globalement pour une raison quelconque. Par exemple, remplacez

let c=foo a in bar

par les 3 octets plus court

(\c->bar)$foo a

Pour plusieurs définitions auxiliaires, le gain est probablement plus petit, en fonction du nombre de définitions.

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 certaines définitions se réfèrent aux autres, il est encore plus difficile de sauvegarder des octets de cette façon:

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

L'avertissement principal avec ceci est que letvous permet de définir des variables polymorphes, mais pas les lambdas, comme l'a noté @ChristianSievers. Par exemple,

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

résultats en (1,1), mais

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

donne une erreur de type.


1
Cela compte rarement, mais "sémantiquement équivalent" en promet un peu trop. Nous avons polymorphe let, alors nous pouvons le faire let f=id in (f 0,f True). Si nous essayons de réécrire cela avec lambda, il ne tapez pas check.
Christian Sievers

@ChristianSievers C'est vrai, merci pour la note. Je l'ai édité.
Zgarb

8

Lier en utilisant des gardes

Lorsque vous définissez une fonction nommée, vous pouvez lier une expression à une variable dans une garde. Par exemple,

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

fait la même chose que

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

Utilisez cette option pour enregistrer les expressions répétées. Lorsque l'expression est utilisée deux fois, elle dépasse même la longueur 6, bien que des problèmes d'espacement et de priorité puissent changer cela.

(Dans l'exemple, si la variable d'origine sn'est pas utilisée, il est plus court

g w=...
f=g.words

mais ce n'est pas vrai pour la liaison d'expressions plus complexes.)


N'est-ce pas une sorte de duplicata / cas particulier de cette réponse ?
Lynn

@ Lynn En regardant en arrière, c'est un cas spécial, mais quand j'ai lu cette réponse, l' Justexemple m'a fait penser que c'était pour la correspondance de motif extraire d'un conteneur, plutôt que de stocker sur une expression.
xnor

8

Utiliser (0<$)au lieu de lengthpour les comparaisons

Lorsqu’on vérifie si une liste aest plus longue qu’une liste b, on écrit généralement

length a>length b

Cependant, remplacer chaque élément des deux listes par la même valeur, par exemple 0, puis comparer ces deux listes peut être plus court:

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

Essayez-le en ligne!

La parenthèse est nécessaire du fait <$et les opérateurs de comparaison ( ==, >, <=, ...) ont le même niveau de priorité 4, bien que dans certains autres cas , ils pourraient ne pas être nécessaire, sauver octets encore plus.


8

Plus court transpose

Pour utiliser la transposefonction Data.Listdoit être importé. S'il s'agit de la seule fonction nécessitant une importation, vous pouvez enregistrer un octet en utilisant la foldrdéfinition suivante de transpose:

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

Notez que le comportement n'est identique que pour une liste de listes de même longueur.

Je l'ai utilisé avec succès ici .


8

Obtenir des suffixes

Utilisez scanr(:)[]pour obtenir les suffixes d'une liste:

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

C'est beaucoup plus court tailsqu'après import Data.List. Vous pouvez faire des préfixes avec scanr(\_->init)=<<id(trouvé par Ørjan Johansen).

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

Cela économise un octet sur

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

Peut-être que scanl(flip(:))[] "abc"= ["","a","ba","cba"]mérite également d'être mentionné - parfois, les préfixes inversés importent peu.
Lynn

3
Ørjan Johansen a trouvé une alternative plus courte à un octet pour les préfixes:scanr(\_->init)=<<id
Laikoni
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.