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 .
Antworten:
Definieren Sie Infix-Operatoren anstelle von Binärfunktionen
Dies spart normalerweise ein oder zwei Leerzeichen pro Definition oder Aufruf.
gegen
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):quelle
(x!y)z=x+y*z
und(x#y)z u=x*z+y*u
beide arbeiten wie erwartet.\f g(!)x y->f g!x y
anstelle von\f g j x y->j(f g)(x y)
g x=…;g(f x)
ist länger als_?x=…;0!f x
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:
(Der Typ ist nur da, um Ihnen zu helfen, zu verstehen, was er tut.)
für unsere zwecke ist das viel besser:
quelle
Verwenden Sie die Listenmonade
Ein kurzer Rückblick:
Beispiele:
Eine Liste zweimal wiederholen
Kürzer
concatMap
Kürzere
concat
+ Listenverständniskartesisches Produkt
Liste der Koordinaten eines Gitters
quelle
[0..b]>>[a]
stattreplicate a b
.a<$[1..b]
ist noch kürzer, zreplicate
.=<<
erzwingen Sie den ImportControl.Monad
. Wenn Sie das aus einem anderen Grund nicht benötigen, können Sie die Argumente austauschen und>>=
präziser verwenden.Data.Traversable
trotzdem brauchen , kann das kartesische Produktbeispiel auf verkürzt werdenfor["Hh","io",".!"]id
.(=<<)
ist eigentlich im Präludium ! Ich habe es oft benutzt.Verwenden Sie Wachen, nicht Bedingungen:
Verwenden Sie Semikolons ohne Einrückungen
Verwenden Sie boolesche Ausdrücke für boolesche Funktionen
(SO ist es ein Schmerz, mich diese separat posten zu lassen)
quelle
&&
innerhalb eines Listenverständnisses zu befinden.True
=>1>0
f a=if a>0 then 3 else 7
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 anwährend
ist ziemlich viel kürzer. Es ist im Prelude also kein Import nötig!
quelle
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(<*>)
OperatorenDiese nützlichen Operatoren von
Data.Applicative
made it in!<$>
ist nurfmap
so, dass Sie ersetzenmap f x
undfmap f x
mitf<$>x
überall und Bytes zurückzugewinnen. Auch<*>
ist in dem nützlichenApplicative
Beispiel für Listen:Der
(<$)
Betreiberx<$a
ist gleichbedeutend mitfmap (const x) a
; dh ersetzen Sie jedes Element in einem Container durchx
.Dies ist oft eine schöne Alternative zu
replicate
:4<$[1..n]
ist kürzer alsreplicate n 4
.Der faltbare / durchfahrbare Vorschlag
Die folgenden Funktionen wurden von der Arbeit an Listen
[a]
auf allgemeineFoldable
Typen umgestelltt a
:Das heißt, sie arbeiten jetzt auch daran
Maybe a
, wo sie sich wie "Listen mit höchstens einem Element" verhalten. Zum Beispielnull Nothing == True
, odersum (Just 3) == 3
. In ähnlicher Weise werdenlength
0 fürNothing
und 1 fürJust
Werte zurückgegeben. Anstatt zu schreibenx==Just y
, können Sie schreibenelem 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, aberor :: (a, Bool) -> Bool
ein Zeichen ist kürzer alssnd
undelem b
ist kürzer als(==b).snd
.Das Monoid funktioniert
mempty
undmappend
Nicht oft ein Lebensretter, aber wenn Sie den Typ ableiten können,
mempty
ist ein Byte kürzer alsNothing
, also gibt es das.quelle
<*>
es ins Prelude zu schaffen! Das sollte auch dann nützlich sein, wenn es kein Code-Golf ist (Anwendbar ist so ein langes Wort).[1..2]
da drin . das ist nur[1,2]
<*
von bekommenApplicative
, was für Listen stehtxs <* ys == concatMap (replicate (length ys)) xs
. Das ist anders alsxs >> ys
oderxs *> ys
was istconcat (replicate (length ys)) xs
.pure
Das ist ein kürzererreturn
kam an dieser Stelle auch.<>
anstelle von verwendenmappend
, es ist jetzt (mit GHC 8.4.1) Teil derPrelude
.Verwenden Sie
1<2
anstelle vonTrue
und1>2
anstelle vonFalse
.quelle
f=max 10
.if(true)
in anderen Sprachen zu schreiben . im Auftakt ist sonst eigentlich der Boolesche WertTrue
.otherwise
.Verwenden Sie Listenverständnisse (auf clevere Weise)
Jeder weiß, dass sie nützliche Syntax sind, oft kürzer als
map
+ ein Lambda:Oder
filter
(und optionalmap
gleichzeitig):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 :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:Das ist aber kürzer:
quelle
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:Aber Sie können es besser machen, wenn Sie missbrauchen
max
und die Tatsache, dass\n < space < printable ASCII
:Ein anderes Beispiel ist
lex :: String -> [(String, String)]
, das etwas ziemlich Geheimnisvolles tut:Versuchen Sie
fst=<<lex s
, das erste Token aus einer Zeichenfolge abzurufen, indem Sie Leerzeichen überspringen. Hier ist eine clevere Lösung von henkma, dielex.show
aufRational
Werten basiert .quelle
Passen Sie einen konstanten Wert an
Ein Listenverständnis kann mit einer Konstanten übereinstimmen.
Dadurch werden die Nullen einer Liste extrahiert
l
, dh es wird eine Liste mit so vielen Nullen erstellt, wie in enthalten sindl
.Dadurch wird eine Liste mit so vielen
1
Elementen erstelltl
,f
wie in der leeren Liste enthalten sind (<$>
als Infix verwendenmap
). Wenden Siesum
an, um diese Elemente zu zählen.Vergleichen Sie:
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ßerex
Bindung überschrieben wird.quelle
Verwenden Sie
words
anstelle einer langen Liste von Zeichenfolgen. Dies ist nicht wirklich spezifisch für Haskell, andere Sprachen haben ähnliche Tricks.quelle
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 werdenmapM f xs
. auch wenn es nursequence
alleine verwendet wird, ist es dann längermapM id
.2)
kombiniere Funktionen mit
(>>=)
(oder(=<<)
)Die Funktion monad version von
(>>=)
ist wie folgt definiert:Es kann nützlich sein, um Funktionen zu erstellen, die nicht als Pipeline ausgedrückt werden können. zum Beispiel
\x->x==nub x
ist länger alsnub>>=(==)
und\t->zip(tail t)t
ist länger alstail>>=zip
.quelle
Applicative
und nicht,Monad
gibt es auch die Implementierung dafür,pure
die kürzer ist alsconst
und mir vorher tatsächlich geholfen hat.Argumente können kürzer als Definitionen sein
Henkma hat mich auf eine sehr merkwürdige Art und Weise ausgelacht .
Wenn eine Hilfsfunktion
f
in Ihrer Antwort einen Operator verwendet, der an keiner anderen Stelle in Ihrer Antwort verwendet wird undf
einmal aufgerufen wird, machen Sie den Operator zu einem Argument vonf
.Diese:
Ist zwei Bytes länger als das:
quelle
Benutze den cons Operator (:)
Wenn Sie Listen verketten und die erste Länge 1 hat, verwenden Sie
:
stattdessen.quelle
1:2:3:x
anstatt[1,2,3]++x
.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:
Obwohl es das gleiche ist wie gerade
v x
.Ein anderes Beispiel ist das Schreiben
(x+1)`div`y
im Gegensatz zudiv(x+1)y
.Ich sehe es passieren , um
div
undelem
häufiger , da diese Funktionen in der Regel als Infix in regelmäßigem Code verwendet werden.quelle
Verwenden Sie Musterschutz
Sie sind kürzer als ein
let
oder ein Lambda, das die Argumente einer von Ihnen definierten Funktion dekonstruiert. Das hilft , wenn Sie so etwas wie müssenfromJust
ausData.Maybe
:ist länger als
ist länger als
ist länger als
Sie sind sogar dann kürzer, wenn Sie einen einfachen alten Wert binden, anstatt ihn zu dekonstruieren: siehe xnors Tipp .
quelle
e
es sich nicht um einen einzigen Token handelt, sondern um einen längeren Ausdruck, der$
davor benötigt wird, was normalerweise der Fall ist.Kürzere Bedingung
ist äquivalent zu
So funktioniert das:
quelle
if b then y else x
?bool
nicht kürzer, da Sie kein Listenverständnis benötigenArbeiten 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
e
, machen Sie einfach-e
. Zum Beispiel-length[1,2]
gibt-2
.e
es auch nur mäßig komplex ist, benötigen Sie Klammerne
, 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)
.e
vor=
oder nach einem Infix-Operator mit einer Fixität von weniger als 6 ein Leerzeichen steht, wirdf= -2
definiertf
undk< -2
geprüft, obk
kleiner als ist-2
. Wenn die Fixität 6 oder höher ist, benötigen Sie parens:2^^(-2)
gives0.25
. Normalerweise können Sie Dinge neu anordnen, um diese loszuwerden: zum Beispiel tun-k>2
stattk< -2
.!
es sich um einen Operator handelt,-a!b
analysiert, als(-a)!b
ob die Fixität von!
höchstens 6 (so-1<1
ergibt sichTrue
) und-(a!b)
andernfalls (so-[1,2]!!0
ergibt sich-1
) beträgt . Standardmäßig sind benutzerdefinierte Operatoren und Backticked-Funktionen auf 9 festgelegt, sodass sie der zweiten Regel folgen.map
Verwenden Sie den Abschnitt, um Negation in eine Funktion zu verwandeln (zur Verwendung mit usw.)(0-)
.Subtraktion
k
Verwenden Sie den Abschnitt(-k+)
, der addiert, um eine Funktion zu erhalten, die subtrahiert-k
.k
kann sogar ein ziemlich komplexer Ausdruck sein:(-2*length x+)
funktioniert wie erwartet.pred
stattdessen, um 1 zu subtrahieren , es sei denn, Sie benötigen auf beiden Seiten ein Leerzeichen. Dies ist selten und tritt normalerweise mituntil
oder einer benutzerdefinierten Funktion auf, damap pred x
diese nachpred<$>x
unditerate pred x
nach ersetzt werden kann[x,x-1..]
. Und wenn duf pred x
irgendwo hast, solltest du wahrscheinlichf
sowieso eine Infix-Funktion definieren . Siehe diesen Tipp .quelle
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 :
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, wobeiy
ein Byte gespeichert wird:Betrachten Sie aus der gleichen Antwort die folgende Definition:
Die leere Liste erscheint im Rückgabewert, so dass wir durch Vertauschen der Fälle zwei Bytes sparen können:
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 :
Zwischen
h
undp
im ersten Zweig befindet sich ein nerviges Leerzeichen . Wir können durch die Definition es loszuwerdenh a p q
statth p q a
:quelle
Verschwenden Sie nicht die "sonst" Wache
Ein letzter Schutz, der ein Allheilmittel ist
True
(kürzer als1>0
), kann verwendet werden, um eine Variable zu binden. Vergleichen Sie: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.
quelle
Verwenden Sie
,
statt&&
in WachenMehrere Bedingungen in einer Wache, die alle halten müssen, können mit
,
statt kombiniert werden&&
.quelle
f xs m | [x] <- xs, Just y <- m, x > 3 = y
Kürzere Syntax für lokale Deklarationen
Manchmal müssen Sie eine lokale Funktion oder Operator definieren, aber es kostet viel Bytes zu schreiben
where
oderlet…in
oder auf der obersten Ebene zu heben durch zusätzliche Argumente hinzufügen.Glücklicherweise hat Haskell eine verwirrende und selten verwendete, aber einigermaßen knappe Syntax für lokale Deklarationen :
In diesem Fall:
Sie können diese Syntax mit Mehrfachanweisungsdeklarationen oder Mehrfachdeklarationen verwenden und sie verschachtelt sogar:
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.
quelle
[f 1|let f x=x+1]
.Vermeiden
repeat 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 !
quelle
n
ist global,l=n:l;l
hat dieselbe Länge und funktioniert für (einige) längere Ausdrücke. (Kann allerdings Leerzeichen benötigen.)Kürzere Bedingungen, wenn ein Ergebnis die leere Liste ist
Wenn Sie eine Bedingung benötigen, die die Liste
A
oder die leere Liste[]
abhängig von einer Bedingung zurückgibtC
, gibt es einige kürzere Alternativen zu den üblichen bedingten Konstrukten:Beispiele: 1 , 2
quelle
A
und[]
geschaltet.*>
hat eine höhere Festigkeit als>>
(immer noch ein bisschen niedrig für den Komfort.)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
(foo$ \x -> succ x)
let a = \x -> succ x in a 4
main = getContents>>= \x -> head $ words x
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:
quelle
let
Durch Lambda ersetzenDies kann normalerweise eine einzelne Hilfsdefinition verkürzen, die aus irgendeinem Grund nicht an einen Guard gebunden oder global definiert werden kann. Zum Beispiel ersetzen
um die 3 Bytes kürzer
Bei mehreren Hilfsdefinitionen ist die Verstärkung abhängig von der Anzahl der Definitionen wahrscheinlich geringer.
Wenn sich einige der Definitionen auf die anderen beziehen, ist es noch schwieriger, Bytes auf diese Weise zu speichern:
Die wichtigste Einschränkung dabei ist,
let
dass Sie polymorphe Variablen definieren können, Lambdas jedoch nicht, wie von @ChristianSievers angegeben. Zum Beispiel,Ergebnisse in
(1,1)
, abergibt einen Tippfehler.
quelle
let
, also können wir es tunlet f=id in (f 0,f True)
. Wenn wir versuchen, dies mit Lambda umzuschreiben, wird check nicht eingegeben.Mit Wachen binden
Beim Definieren einer benannten Funktion können Sie einen Ausdruck an eine Variable in einem Guard binden. Zum Beispiel,
macht das selbe wie
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
s
nicht verwendet wird, ist sie kürzerDies gilt jedoch nicht für die Bindung komplexerer Ausdrücke.)
quelle
Just
kam mir das Beispiel vor, dass der Mustervergleich aus einem Container extrahiert und nicht in einem Ausdruck gespeichert werden soll.Verwenden Sie
(0<$)
stattlength
für VergleicheWenn man prüft, ob eine Liste
a
länger als eine Liste istb
, schreibt man normalerweiseEs
0
kann jedoch kürzer sein , jedes Element beider Listen durch denselben Wert zu ersetzen, z. B. und dann diese beiden Listen zu vergleichen: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.quelle
Kürzer
transpose
Um die
transpose
Funktion nutzen zu können,Data.List
muss sie importiert werden. Wenn dies die einzige Funktion ist, die importiert werden muss, kann ein Byte mit der folgendenfoldr
Definition von gespeichert werdentranspose
:Beachten Sie, dass das Verhalten nur für eine Liste von Listen mit derselben Länge identisch ist.
Ich habe das hier erfolgreich genutzt .
quelle
Suffixe abrufen
Verwenden Sie
scanr(:)[]
diese Option , um die Suffixe einer Liste abzurufen:Das ist viel kürzer als
tails
danachimport Data.List
. Sie können Präfixe mitscanr(\_->init)=<<id
(gefunden von Ørjan Johansen) machen.Das spart ein Byte mehr
quelle
scanl(flip(:))[] "abc"
=["","a","ba","cba"]
auch erwähnenswert - manchmal spielt es keine Rolle, ob die Präfixe rückwärts sind.scanr(\_->init)=<<id