Tipps zum Golfen in Haskell


73

Welche allgemeinen Tipps haben Sie zum Golfen in Haskell? Ich bin auf der Suche nach Ideen, die auf Code-Golf-Probleme im Allgemeinen angewendet werden können, die zumindest etwas spezifisch für Haskell sind. Bitte posten Sie nur einen Tipp pro Antwort.


Wenn Sie neu im Golfsport in Haskell sind, schauen Sie sich bitte den Guide to Golfing Rules in Haskell an . Es gibt auch einen speziellen Haskell-Chatroom: Von Monaden und Männern .


1
Angesichts der Anzahl der bisherigen Antworten habe ich Zweifel, ob Haskell überhaupt eine gute Sprache für Code-Golf ist oder nicht.
Animesh 'der CODER'

10
Warum nur ein Tipp pro Antwort? Außerdem ist jede Sprache eine gute Sprache zum Golfen. Erwarten Sie nur nicht immer, zu gewinnen.
Unclemeat

6
@unclemeat Auf diese Weise konnten die Leute die Guten nach oben bringen, ohne die Schlechten zu stimmen, nur weil sie von demselben Mann in derselben Antwort geschrieben wurden.
MasterMastic

3
Sonderwunsch, String-Komprimierung.
J Atkin

Dies ist wahrscheinlich nicht als Antwort geeignet, aber ich möchte es trotzdem
flawr

Antworten:


45

Definieren Sie Infix-Operatoren anstelle von Binärfunktionen

Dies spart normalerweise ein oder zwei Leerzeichen pro Definition oder Aufruf.

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

gegen

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

Die verfügbaren Symbole für 1-Byte - Operatoren sind !, #, %, &, und ?. Alle anderen ASCII-Interpunktionen sind entweder bereits durch das Prelude (wie $) als Operator definiert oder haben in der Haskell-Syntax (wie @) eine besondere Bedeutung .

Wenn Sie mehr als fünf Operatoren benötigen, können Sie Kombinationen der oben genannten !#oder bestimmte Unicode-Interpunktionszeichen verwenden (alle 2 Bytes in UTF-8):

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

11
Hinweis: Dies kann auch für Funktionen mit drei oder mehr Argumenten verwendet werden. (x!y)z=x+y*zund (x#y)z u=x*z+y*ubeide arbeiten wie erwartet.
Zgarb

3
Dies kann auch für Funktionsargumente verwendet werden, z. B. \f g(!)x y->f g!x yanstelle von\f g j x y->j(f g)(x y)
Esolanging Fruit

2
Manchmal ist es vorteilhaft, unäre Funktionen in binäre Operatoren umzuwandeln, wenn Sie ansonsten Klammern verwenden müssten - g x=…;g(f x)ist länger als_?x=…;0!f x
Angs

29

Verwenden Sie gegebenenfalls eine sinnlose (oder -freie) Notation

Oft kann eine Funktion mit einem oder zwei Parametern punktfrei geschrieben werden.

Eine Suche nach einer Liste von Tupeln, deren Elemente vertauscht sind, lautet also naiv:

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

(Der Typ ist nur da, um Ihnen zu helfen, zu verstehen, was er tut.)

für unsere zwecke ist das viel besser:

revlookup=(.map swap).lookup

28

Verwenden Sie die Listenmonade

Ein kurzer Rückblick:

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

Beispiele:

  • Eine Liste zweimal wiederholen

    Prelude> "aa">>[1..5]
    [1,2,3,4,5,1,2,3,4,5]
    
  • Kürzer concatMap

    Prelude> reverse=<<["Abc","Defgh","Ijkl"]
    "cbAhgfeDlkjI"
    
  • Kürzere concat+ Listenverständnis

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

    Prelude> mapM id["Hh","io",".!"]
    ["Hi.","Hi!","Ho.","Ho!","hi.","hi!","ho.","ho!"]
    
  • Liste der Koordinaten eines Gitters

    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
Eine andere Verwendung, die ich als nützlich empfand, findet [0..b]>>[a]statt replicate a b.
Wheat Wizard

3
@ WheatWizard a<$[1..b]ist noch kürzer, z replicate.
Lynn

Mit =<<erzwingen Sie den Import Control.Monad. Wenn Sie das aus einem anderen Grund nicht benötigen, können Sie die Argumente austauschen und >>=präziser verwenden.
dfeuer

OTOH, wenn Sie es Data.Traversabletrotzdem brauchen , kann das kartesische Produktbeispiel auf verkürzt werden for["Hh","io",".!"]id.
dfeuer

2
(=<<)ist eigentlich im Präludium ! Ich habe es oft benutzt.
Lynn

28

Verwenden Sie Wachen, nicht Bedingungen:

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

Verwenden Sie Semikolons ohne Einrückungen

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

Verwenden Sie boolesche Ausdrücke für boolesche Funktionen

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

(SO ist es ein Schmerz, mich diese separat posten zu lassen)


2
Verwenden Sie auch mehrere Wachen, anstatt sich &&innerhalb eines Listenverständnisses zu befinden.
John Dvorak

Guter Jan - du solltest das zu einer Antwort machen, ich werde dafür stimmen
bazzargh

5
Das erste Beispiel kann weiter verkürzt werden durch True=>1>0
John Dvorak

1
im ersten Beispiel nehme ich an, Sie meinenf a=if a>0 then 3 else 7
Cyoce

Wache funktioniert auch, wenn es kein Argument gibt.
Akangka

24

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

Die Leute vergessen oft, dass diese Funktion existiert - sie erfasst den gesamten Standard und wendet ihn auf eine (reine) Funktion an. Ich sehe oft main-code in Anlehnung an

main=getContents>>=print.foo

während

main=interact$show.foo

ist ziemlich viel kürzer. Es ist im Prelude also kein Import nötig!


24

Verwenden Sie GHC 7.10

Die erste Version von GHC, die dieses Zeug enthielt, wurde am 27. März 2015 veröffentlicht .

Es ist die neueste Version und Prelude hat einige neue Ergänzungen, die für das Golfen nützlich sind:

Die (<$>)und (<*>)Operatoren

Diese nützlichen Operatoren von Data.Applicativemade it in! <$>ist nur fmapso, dass Sie ersetzen map f xund fmap f xmit f<$>xüberall und Bytes zurückzugewinnen. Auch <*>ist in dem nützlichen ApplicativeBeispiel für Listen:

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

Der (<$)Betreiber

x<$aist gleichbedeutend mit fmap (const x) a; dh ersetzen Sie jedes Element in einem Container durch x.

Dies ist oft eine schöne Alternative zu replicate: 4<$[1..n]ist kürzer als replicate n 4.

Der faltbare / durchfahrbare Vorschlag

Die folgenden Funktionen wurden von der Arbeit an Listen [a]auf allgemeine FoldableTypen umgestellt t a:

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

Das heißt, sie arbeiten jetzt auch daran Maybe a, wo sie sich wie "Listen mit höchstens einem Element" verhalten. Zum Beispiel null Nothing == True, oder sum (Just 3) == 3. In ähnlicher Weise werden length0 für Nothingund 1 für JustWerte zurückgegeben. Anstatt zu schreiben x==Just y, können Sie schreiben elem y x.

Sie können sie auch auf Tupel anwenden, was so funktioniert, als hätten Sie \(a, b) -> [b]zuerst angerufen . Es ist fast völlig nutzlos, aber or :: (a, Bool) -> Boolein Zeichen ist kürzer als sndund elem bist kürzer als (==b).snd.

Das Monoid funktioniert memptyundmappend

Nicht oft ein Lebensretter, aber wenn Sie den Typ ableiten können, memptyist ein Byte kürzer als Nothing, also gibt es das.


5
+1 Es ist großartig, von '<$>' zu hören und <*>es ins Prelude zu schaffen! Das sollte auch dann nützlich sein, wenn es kein Code-Golf ist (Anwendbar ist so ein langes Wort).
Ankh-Morpork

Warnung vor Flat-Ersatz: Wenn Ihre Sprachversion neuer als die Challenge ist, ist Ihre Lösung für den Gewinn unerheblich. Wenn Sie Ihre vorhandenen Antworten oder Antworten aktualisieren möchten, überschreiben Sie Ihre vorhandene Lösung nicht. Schreiben Sie einen Anhang.
John Dvorak

4
Komisch ist [1..2]da drin . das ist nur[1,2]
stolzer haskeller

2
In der gleichen Version haben wir auch <*von bekommen Applicative, was für Listen steht xs <* ys == concatMap (replicate (length ys)) xs. Das ist anders als xs >> ysoder xs *> yswas ist concat (replicate (length ys)) xs. pureDas ist ein kürzerer returnkam an dieser Stelle auch.
Angs

2
Sie können jetzt <>anstelle von verwenden mappend, es ist jetzt (mit GHC 8.4.1) Teil der Prelude.
10.

22

Verwenden Sie 1<2anstelle von Trueund 1>2anstelle von False.

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

3
Dies ist nicht wirklich spezifisch für Haskell: Es gilt für fast jede Sprache, die einen Booleschen Typ hat, im Gegensatz zu wahrheitsgemäßen und falschen Werten anderer Typen.
Peter Taylor

Kann mir jemand das erklären?
MasterMastic

2
Das ist kein gutes Beispiel, ich würde einfach so golfen f=max 10.
stolzer Haskeller

@ MasterMastic dies ist nur if(true)in anderen Sprachen zu schreiben . im Auftakt ist sonst eigentlich der Boolesche Wert True.
stolzer Haskeller

2
@PeterTaylor Ich denke, dass dies für neue Haskellianer (wie mich) immer noch wertvoll ist, als ich das erste Mal gelernt habe, es zu benutzen otherwise.
Fehler

20

Verwenden Sie Listenverständnisse (auf clevere Weise)

Jeder weiß, dass sie nützliche Syntax sind, oft kürzer als map+ ein Lambda:

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

Oder filter(und optional mapgleichzeitig):

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

Aber es gibt einige seltsame Anwendungen, die sich ab und zu als nützlich erweisen. Zum einen muss ein Listenverständnis überhaupt keine <-Pfeile enthalten :

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

Was bedeutet if p then[x]else[], dass Sie stattdessen schreiben können [x|p]. Um die Anzahl der Elemente einer Liste zu zählen, die eine Bedingung erfüllen, schreiben Sie intuitiv:

length$filter p x

Das ist aber kürzer:

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

Ich habe eigentlich alle diese verwendet, aber ich habe nicht daran gedacht, sie hier zu platzieren.
stolzer Haskeller

18

Kenne dein Prelude

Starten Sie GHCi und scrollen Sie durch die Prelude-Dokumentation . Wann immer Sie eine Funktion mit einem Kurznamen kreuzen, kann es sich auszahlen, nach Fällen zu suchen, in denen dies nützlich sein könnte.

Angenommen, Sie möchten eine Zeichenfolge s = "abc\ndef\nghi"in eine durch Leerzeichen getrennte Zeichenfolge umwandeln "abc def ghi". Der naheliegende Weg ist:

unwords$lines s

Aber Sie können es besser machen, wenn Sie missbrauchen maxund die Tatsache, dass \n < space < printable ASCII:

max ' '<$>s

Ein anderes Beispiel ist lex :: String -> [(String, String)], das etwas ziemlich Geheimnisvolles tut:

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

Versuchen Sie fst=<<lex s, das erste Token aus einer Zeichenfolge abzurufen, indem Sie Leerzeichen überspringen. Hier ist eine clevere Lösung von henkma, die lex.showauf RationalWerten basiert .


16

Passen Sie einen konstanten Wert an

Ein Listenverständnis kann mit einer Konstanten übereinstimmen.


[0|0<-l]

Dadurch werden die Nullen einer Liste extrahiert l, dh es wird eine Liste mit so vielen Nullen erstellt, wie in enthalten sind l.


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

Dadurch wird eine Liste mit so vielen 1Elementen erstellt l, fwie in der leeren Liste enthalten sind ( <$>als Infix verwenden map). Wenden Sie suman, um diese Elemente zu zählen.

Vergleichen Sie:

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

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

Eine Konstante kann als Teil einer Musterübereinstimmung verwendet werden. Dies extrahiert die zweiten Einträge aller Tupel, deren erster Eintrag ist 0.


Beachten Sie, dass all dies ein tatsächliches konstantes Literal erfordert, nicht den Wert einer Variablen. Beispielsweise let x=1 in [1|x<-[1,2,3]]wird nicht ausgegeben [1,1,1], [1]da die äußere xBindung überschrieben wird.


14

Verwenden Sie wordsanstelle einer langen Liste von Zeichenfolgen. Dies ist nicht wirklich spezifisch für Haskell, andere Sprachen haben ähnliche Tricks.

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

Kennen Sie Ihre monadischen Funktionen

1)
simulieren Sie monadische Funktionen mit mapM.

Code wird häufig vorhanden sein sequence(map f xs), kann jedoch durch ersetzt werden mapM f xs. auch wenn es nur sequencealleine verwendet wird, ist es dann länger mapM id.

2)
kombiniere Funktionen mit (>>=)(oder (=<<))

Die Funktion monad version von (>>=)ist wie folgt definiert:

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

Es kann nützlich sein, um Funktionen zu erstellen, die nicht als Pipeline ausgedrückt werden können. zum Beispiel \x->x==nub xist länger als nub>>=(==)und \t->zip(tail t)tist länger als tail>>=zip.


+1 - Ich hatte nicht einmal bemerkt, dass es eine Monadeninstanz für Funktionen gibt. das könnte sehr praktisch sein :)
Jules

2
Randnotiz: Obwohl es Teil von ist Applicativeund nicht, Monadgibt es auch die Implementierung dafür, puredie kürzer ist als constund mir vorher tatsächlich geholfen hat.
5.

14

Argumente können kürzer als Definitionen sein

Henkma hat mich auf eine sehr merkwürdige Art und Weise ausgelacht .

Wenn eine Hilfsfunktion fin Ihrer Antwort einen Operator verwendet, der an keiner anderen Stelle in Ihrer Antwort verwendet wird und feinmal aufgerufen wird, machen Sie den Operator zu einem Argument von f.

Diese:

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

Ist zwei Bytes länger als das:

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

12

Benutze den cons Operator (:)

Wenn Sie Listen verketten und die erste Länge 1 hat, verwenden Sie :stattdessen.

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

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

4
Erwähnenswert ist, dass es richtig assoziativ ist, sodass Sie es für eine beliebige Anzahl von einzelnen Elementen am Anfang der Liste verwenden können, z . B. 1:2:3:xanstatt [1,2,3]++x.
Jules

11

Verwenden Sie Backticks nicht zu oft. Backticks sind ein cooles Tool zum Erstellen von Abschnitten mit Präfixfunktionen, können aber manchmal missbraucht werden.

Als ich jemanden sah, der diesen Unterausdruck schrieb:

(x`v`)

Obwohl es das gleiche ist wie gerade v x.

Ein anderes Beispiel ist das Schreiben (x+1)`div`y im Gegensatz zu div(x+1)y.

Ich sehe es passieren , um divund elemhäufiger , da diese Funktionen in der Regel als Infix in regelmäßigem Code verwendet werden.


Meinen Sie damit nicht, Abschnitte von Präfixfunktionen zu erstellen?
Cyoce

@Cyoce Ja, natürlich
stolzer Haskeller

11

Verwenden Sie Musterschutz

Sie sind kürzer als ein letoder ein Lambda, das die Argumente einer von Ihnen definierten Funktion dekonstruiert. Das hilft , wenn Sie so etwas wie müssen fromJustaus Data.Maybe:

f x=let Just c=… in c

ist länger als

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

ist länger als

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

ist länger als

f x|Just c<-…=c

Sie sind sogar dann kürzer, wenn Sie einen einfachen alten Wert binden, anstatt ihn zu dekonstruieren: siehe xnors Tipp .


Nun, der Lambda braucht kein Dollarzeichen und es scheint, dass diese Änderung die Länge dieses und des nächsten Snippets gleich macht
stolzer Haskeller

1
Ich gehe davon aus, dass ees sich nicht um einen einzigen Token handelt, sondern um einen längeren Ausdruck, der $davor benötigt wird, was normalerweise der Fall ist.
Lynn

11

Kürzere Bedingung

last$x:[y|b]

ist äquivalent zu

if b then y else x

So funktioniert das:

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

Sollte es sein if b then y else x?
Akangka

@ChristianIrwan Guter Fang, ja.
Xnor

Wäre die Verwendung boolnicht kürzer, da Sie kein Listenverständnis benötigen
Potato44

@ Potato44 Das ist in Data.Bool, dessen Import Byte kostet.
20.

11

Arbeiten mit dem Minuszeichen

Das Minuszeichen -ist eine lästige Ausnahme zu vielen Syntaxregeln. Dieser Tipp listet einige kurze Möglichkeiten auf, Negation und Subtraktion in Haskell auszudrücken. Bitte lassen Sie mich wissen, wenn ich etwas verpasst habe.

Negation

  • Um einen Ausdruck zu negieren e, machen Sie einfach -e. Zum Beispiel -length[1,2]gibt -2.
  • Wenn ees auch nur mäßig komplex ist, benötigen Sie Klammern e, aber Sie können in der Regel ein Byte speichern, indem Sie sie verschieben: -length(take 3 x)ist kürzer als -(length$take 3 x).
  • Wenn evor =oder nach einem Infix-Operator mit einer Fixität von weniger als 6 ein Leerzeichen steht, wird f= -2definiert fund k< -2geprüft, ob kkleiner als ist -2. Wenn die Fixität 6 oder höher ist, benötigen Sie parens: 2^^(-2)gives 0.25. Normalerweise können Sie Dinge neu anordnen, um diese loszuwerden: zum Beispiel tun -k>2statt k< -2.
  • In ähnlicher Weise wird, wenn !es sich um einen Operator handelt, -a!banalysiert, als (-a)!bob die Fixität von !höchstens 6 (so -1<1ergibt sich True) und -(a!b)andernfalls (so -[1,2]!!0ergibt sich -1) beträgt . Standardmäßig sind benutzerdefinierte Operatoren und Backticked-Funktionen auf 9 festgelegt, sodass sie der zweiten Regel folgen.
  • mapVerwenden Sie den Abschnitt, um Negation in eine Funktion zu verwandeln (zur Verwendung mit usw.) (0-).

Subtraktion

  • kVerwenden Sie den Abschnitt (-k+), der addiert, um eine Funktion zu erhalten, die subtrahiert -k. kkann sogar ein ziemlich komplexer Ausdruck sein: (-2*length x+)funktioniert wie erwartet.
  • Verwenden Sie predstattdessen, um 1 zu subtrahieren , es sei denn, Sie benötigen auf beiden Seiten ein Leerzeichen. Dies ist selten und tritt normalerweise mit untiloder einer benutzerdefinierten Funktion auf, da map pred xdiese nach pred<$>xund iterate pred xnach ersetzt werden kann [x,x-1..]. Und wenn du f pred xirgendwo hast, solltest du wahrscheinlich fsowieso eine Infix-Funktion definieren . Siehe diesen Tipp .

11

Versuchen Sie, Funktionsdefinitionen und / oder Argumente neu anzuordnen

Sie können manchmal einige Bytes sparen, indem Sie die Reihenfolge der Mustervergleichsfälle in einer Funktionsdefinition ändern. Diese Einsparungen sind billig, aber leicht zu übersehen.

Betrachten Sie als Beispiel die folgende frühere Version (eines Teils) dieser Antwort :

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

Dies ist eine rekursive Definition von ?, wobei der Basisfall die leere Liste ist. Da dies []kein nützlicher Wert ist, sollten wir die Definitionen austauschen und durch den Platzhalter _oder ein Dummy-Argument ersetzen, wobei yein Byte gespeichert wird:

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

Betrachten Sie aus der gleichen Antwort die folgende Definition:

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

Die leere Liste erscheint im Rückgabewert, so dass wir durch Vertauschen der Fälle zwei Bytes sparen können:

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

Auch die Reihenfolge der Funktionsargumente kann manchmal einen Unterschied bewirken, da Sie unnötige Leerzeichen entfernen können. Betrachten Sie eine frühere Version dieser Antwort :

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

Zwischen hund pim ersten Zweig befindet sich ein nerviges Leerzeichen . Wir können durch die Definition es loszuwerden h a p qstatt 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

Verschwenden Sie nicht die "sonst" Wache

Ein letzter Schutz, der ein Allheilmittel ist True(kürzer als 1>0), kann verwendet werden, um eine Variable zu binden. Vergleichen Sie:

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

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

Da die Bewachung obligatorisch ist und ansonsten verschwendet wird, wird wenig benötigt, um dies wert zu machen. Es reicht aus, ein Paar Parens zu speichern oder einen Ausdruck der Länge 3 zu binden, der zweimal verwendet wird. Manchmal können Sie Wachen negieren, um den endgültigen Fall zum Ausdruck zu machen, der am besten eine Bindung verwendet.


10

Verwenden Sie ,statt &&in Wachen

Mehrere Bedingungen in einer Wache, die alle halten müssen, können mit ,statt kombiniert werden &&.

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

2
Es muss auch keine Bedingung sein, so etwas kann man machen:f xs m | [x] <- xs, Just y <- m, x > 3 = y
BlackCap

10

Kürzere Syntax für lokale Deklarationen

Manchmal müssen Sie eine lokale Funktion oder Operator definieren, aber es kostet viel Bytes zu schreiben whereoder let…inoder auf der obersten Ebene zu heben durch zusätzliche Argumente hinzufügen.

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

Glücklicherweise hat Haskell eine verwirrende und selten verwendete, aber einigermaßen knappe Syntax für lokale Deklarationen :

fun1 pattern1 | let fun2 pattern2 = expr2 = expr1

In diesem Fall:

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

Sie können diese Syntax mit Mehrfachanweisungsdeklarationen oder Mehrfachdeklarationen verwenden und sie verschachtelt sogar:

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

Es funktioniert auch für das Binden von Variablen oder anderen Mustern, obwohl die Pattern Guards in der Regel kürzer sind, es sei denn, Sie sind auch Bindungsfunktionen.


3
Dies funktioniert auch innerhalb Listenkomprehensionen: [f 1|let f x=x+1].
Laikoni

10

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

Jeder dieser vier Ausdrücke erzeugt eine unendliche Liste von n's.

Dies ist ein sehr spezieller Tipp, der jedoch bis zu 3 Bytes einsparen kann !


4
If nist global, l=n:l;lhat dieselbe Länge und funktioniert für (einige) längere Ausdrücke. (Kann allerdings Leerzeichen benötigen.)
Ørjan Johansen

@ ØrjanJohansen Ich habe es in den Beitrag aufgenommen. Vielen Dank!
Totalhuman

10

Kürzere Bedingungen, wenn ein Ergebnis die leere Liste ist

Wenn Sie eine Bedingung benötigen, die die Liste Aoder die leere Liste []abhängig von einer Bedingung zurückgibt C, gibt es einige kürzere Alternativen zu den üblichen bedingten Konstrukten:

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

Beispiele: 1 , 2


Der zweite hat Aund []geschaltet.
Ørjan Johansen

@ ØrjanJohansen Korrigiert, danke!
Laikoni

1
Aha! Aber *>hat eine höhere Festigkeit als >>(immer noch ein bisschen niedrig für den Komfort.)
Ørjan Johansen

9

Lambda-Parsing-Regeln

Ein Lambda-Ausdruck braucht eigentlich keine runden Klammern - er greift nur gierig nach allem, damit das Ganze noch analysiert wird, zB bis

  • eine Abschlussfeier - (foo$ \x -> succ x)
  • ein in - let a = \x -> succ x in a 4
  • das Ende der Linie - main = getContents>>= \x -> head $ words x
  • usw..

wird angetroffen, und es gibt einige merkwürdige Randfälle, in denen Sie ein oder zwei Bytes sparen können. Ich glaube, man \kann auch Operatoren definieren. Wenn Sie dies ausnutzen, benötigen Sie ein Leerzeichen, wenn Sie ein Lambda direkt nach einem Operator schreiben (wie im dritten Beispiel).

Hier ist ein Beispiel dafür, wo die Verwendung eines Lambdas das kürzeste war, was ich herausfinden konnte. Der Code sieht im Grunde so aus:

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

9

letDurch Lambda ersetzen

Dies kann normalerweise eine einzelne Hilfsdefinition verkürzen, die aus irgendeinem Grund nicht an einen Guard gebunden oder global definiert werden kann. Zum Beispiel ersetzen

let c=foo a in bar

um die 3 Bytes kürzer

(\c->bar)$foo a

Bei mehreren Hilfsdefinitionen ist die Verstärkung abhängig von der Anzahl der Definitionen wahrscheinlich geringer.

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

Wenn sich einige der Definitionen auf die anderen beziehen, ist es noch schwieriger, Bytes auf diese Weise zu speichern:

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

Die wichtigste Einschränkung dabei ist, letdass Sie polymorphe Variablen definieren können, Lambdas jedoch nicht, wie von @ChristianSievers angegeben. Zum Beispiel,

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

Ergebnisse in (1,1), aber

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

gibt einen Tippfehler.


1
Es kommt selten darauf an, aber "semantisch äquivalent" verspricht etwas zu viel. Wir haben Polymorphe let, also können wir es tun let f=id in (f 0,f True). Wenn wir versuchen, dies mit Lambda umzuschreiben, wird check nicht eingegeben.
Christian Sievers

@ ChristianSievers Das stimmt, danke für den Hinweis. Ich habe es in bearbeitet.
Zgarb

8

Mit Wachen binden

Beim Definieren einer benannten Funktion können Sie einen Ausdruck an eine Variable in einem Guard binden. Zum Beispiel,

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

macht das selbe wie

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

Verwenden Sie diese Option, um wiederholte Ausdrücke zu speichern. Wenn der Ausdruck zweimal verwendet wird, wird die Gewinnschwelle bei Länge 6 überschritten, obwohl sich dies durch Abstände und Prioritäten ändern kann.

(Wenn im Beispiel die ursprüngliche Variable snicht verwendet wird, ist sie kürzer

g w=...
f=g.words

Dies gilt jedoch nicht für die Bindung komplexerer Ausdrücke.)


Ist das nicht ein Duplikat / Sonderfall dieser Antwort ?
Lynn

@Lynn Wenn ich zurückblicke, ist dies ein Sonderfall, aber als ich diese Antwort las, Justkam mir das Beispiel vor, dass der Mustervergleich aus einem Container extrahiert und nicht in einem Ausdruck gespeichert werden soll.
xnor

8

Verwenden Sie (0<$)statt lengthfür Vergleiche

Wenn man prüft, ob eine Liste alänger als eine Liste ist b, schreibt man normalerweise

length a>length b

Es 0kann jedoch kürzer sein , jedes Element beider Listen durch denselben Wert zu ersetzen, z. B. und dann diese beiden Listen zu vergleichen:

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

Probieren Sie es online!

Die Klammern werden , da benötigt <$und die Vergleichsoperatoren ( ==, >, <=, ...) die gleiche Prioritätsstufe 4, obwohl in einigen anderen Fällen sie nicht benötigt werden könnten, spart noch mehr Bytes.


8

Kürzer transpose

Um die transposeFunktion nutzen zu können, Data.Listmuss sie importiert werden. Wenn dies die einzige Funktion ist, die importiert werden muss, kann ein Byte mit der folgenden foldrDefinition von gespeichert werden transpose:

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

Beachten Sie, dass das Verhalten nur für eine Liste von Listen mit derselben Länge identisch ist.

Ich habe das hier erfolgreich genutzt .


8

Suffixe abrufen

Verwenden Sie scanr(:)[]diese Option , um die Suffixe einer Liste abzurufen:

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

Das ist viel kürzer als tailsdanach import Data.List. Sie können Präfixe mit scanr(\_->init)=<<id(gefunden von Ørjan Johansen) machen.

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

Das spart ein Byte mehr

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

Vielleicht ist scanl(flip(:))[] "abc"= ["","a","ba","cba"]auch erwähnenswert - manchmal spielt es keine Rolle, ob die Präfixe rückwärts sind.
Lynn

3
Ørjan Johansen hat eine um ein Byte kürzere Alternative für Präfixe gefunden:scanr(\_->init)=<<id
Laikoni
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.