(Diese die Frage nicht direkt beantworten, aber es wird Ihr Code mehr idiomatische und damit leichter lesbar machen.)
Sie verwenden das Muster \x -> f x >>= ...
viel, kann diese (und sollte) beseitigt werden : Es ist (meistens) unnötiges Rauschen, das die Bedeutung des Codes verdunkelt. Ich werde nicht den Code verwenden, da es Hausaufgaben, aber dieses Beispiel betrachten (man beachte, dass ich mit return
wie der anderen Antwort vorgeschlagen):
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> return (lines str) >>=
\lns -> return (length lns) >>=
\num -> print num
(Er liest einen Dateinamen von dem Benutzer, und dann druckt die Anzahl der Zeilen in dieser Datei.)
Die einfachste Optimierung ist der Abschnitt, wo wir die Anzahl der Zeilen zählen (das entspricht dem Teil, wo Sie die Wörter trennen und die zweite bekommen): die Anzahl von Zeilen in einer Zeichenfolge str
ist nur length (lines str)
(das ist das gleiche wie length . lines $ str
), so gibt es keinen Grund für uns, den Anruf an length
und den Anruf anzu habengetrennt. Unser Code ist jetzt:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> return (length . lines $ str) >>=
\num -> print num
nun die nächste Optimierung auf \num -> print num
ist. Dies kann als nur print
geschrieben werden. (Dies wird eta conversion genannt). (Sie können darüber nachdenken als "eine Funktion, die ein Argument annimmt und print
darauf aufruft, ist dasselbe wie print
selbst"). Jetzt haben wir:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> return (length . lines $ str) >>= print
Die nächste Optimierung wir tun können, auf der monad laws basiert. Unter Verwendung der ersten können wir return (length . lines $ str) >>= print
in print (length . lines $ str)
(d. H. "Erstellen eines Containers, der einen Wert enthält (dies erfolgt durch return
) und dann übergibt diesen Wert an print
ist das gleiche wie nur übergibt den Wert an print
"). Auch hier können wir die Klammer entfernen, also haben wir:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>=
\str -> print . length . lines $ str
Und schau! Wir haben eine Eta-Konvertierung, die wir tun können: \str -> print . length . lines $ str
wird nur print . length . lines
. Dies läßt:
main = getLine >>=
\fname -> openFile fname ReadMode >>=
\handle -> hGetContents handle >>= print . length . lines
An diesem Punkt können wir wahrscheinlich stoppen, da dieser Ausdruck viel einfacher als unsere ursprünglichen ist (wir durch die Verwendung >=>
gehen könnten halten, wenn wir wollten).Da es so viel einfacher ist, ist es auch einfacher zu debuggen (sich vorstellen, wenn wir lines
zu verwenden vergessen hatte. In der ursprünglichen main
wäre es nicht sehr klar sein, in der letzten offensichtlich es ist)
In Ihrem Code Sie können und sollten das Gleiche tun: Sie können Dinge wie sections (was \x -> x !! 1
ist das gleiche wie (!! 1)
) und die Eta-Konvertierung und Monad-Gesetze, die ich oben verwendet.
Vielen Dank, Sie haben mein Problem gelöst: D –