Anstatt die State
-Monade zu verwenden, können Sie auch die Writer
-Monade verwenden und von String
's Monoid
-Instanz (wirklich [a]
' s Monoid
-Instanz) profitieren:
Was ich denke, ist ziemlich prägnant, sauber und einfach.
Ein Vorteil gegenüber der State
-Monade besteht darin, dass Sie die Reihenfolge, in der Verkettungen stattfinden, neu anordnen können, indem Sie einfach die Zeilen neu anordnen. Wenn Sie also beispielsweise f 30 "test"
ausführen und "atestbc"
herausholen möchten, müssen Sie nur die ersten beiden Zeilen von do
:
Während Sie in der State
monad den Vorgang ändern müssten:
Statt eine Beziehung zwischen der Ausführungsreihenfolge und der Reihenfolge in der Ausgabezeichenfolge zu haben, müssen Sie die tatsächlichen Operationen genau untersuchen (es gibt einen feinen Unterschied zwischen (++ "a")
und ("a" ++)
), während der Writer
Code sehr groß ist auf den ersten Blick meiner Meinung nach klar.
Wie @JohnL darauf hingewiesen hat, ist dies nicht gerade eine effiziente Lösung, da die Verkettung auf Haskell Strings
nicht sehr schnell ist, aber Sie könnten Text
und Builder
ganz einfach verwenden, um dies zu umgehen:
Es gibt also keine wirkliche Änderung am Algorithmus außer der Umwandlung in effizientere Typen.
Die Funktion kann sehr knapp geschrieben werden, vorausgesetzt, wir sind bereit, die Logik der ursprünglichen imperativen Version etwas zu verschleiern:
%Vor%Monad-Comprehensions können die ursprüngliche Logik schön reproduzieren:
%Vor%Es handelt sich jedoch nicht um eine häufig verwendete Spracherweiterung. Zum Glück für uns (Hut Tipp zu Ørjan Johansen in den Kommentaren), gibt es bereits eingebauten Verständnis Zucker für die Liste Monade, die wir auch hier verwenden können:
%Vor% Ich denke, die kurze Antwort ist, dass der Ansatz für den Absturz in Haskell Monoids
ist. Wann immer du viele Dinge zu einer Sache kombinieren willst, denke an Monoids
. Zusatz ist ein großartiges Beispiel:
1 + 2 + 4 + 0 + 3 = 10.
Beim Hinzufügen von Zahlen gibt es eine Art von no-op Wert 0
. Sie können es immer hinzufügen und es wird das Ergebnis nicht ändern. Monoids verallgemeinern dieses Konzept, und Haskell nennt den no-op-Wert mempty
. Auf diese Weise werden Elemente aus Ihrer Kombination entfernt (in Ihrem Beispiel fallen die Werte, die nicht gleichmäßig verteilt werden). +
ist der Kombinierer. Haskell nennt es mappend
. Dafür gibt es ein Kürzel: <>
.
Multiplikation ist ein Monoid und der Wert mempty
ist 1
, der Kombinator ist *
.
Strings sind auch ein Monoid. Der mempty
-Wert ist ""
, der Combiner ist ++
;
Hier ist eine sehr einfache Implementierung Ihrer Funktion mit Monoids:
%Vor%Das Schöne daran ist, dass Monoids das Konzept verallgemeinern können, weil es ein Monoid und nicht nur eine Zeichenkette aufbaut. Sie können zum Beispiel eine Liste von Divisoren, Monoidpaaren und einem anfänglichen Monoid übergeben, und wenn der Divisor gleichmäßig verteilt wird, fügen Sie das Monoid hinzu:
%Vor% mconcat
kombiniert nur eine Liste von Monoiden.
Ihr erstes Beispiel könnte jetzt wie folgt ausgeführt werden:
%Vor%Aber Sie könnten genauso einfach eine Nummer aufbauen:
%Vor% Eines der großartigen Dinge an Haskell ist, dass es viele Konzepte erfasst und verallgemeinert, von denen ich nicht einmal wusste, dass sie da waren. Monoids
sind wirklich praktisch, und ganze App-Architekturen können darauf gebaut werden.
IMO solltest du das komplett anders angehen. Sie testen eine Reihe sehr ähnlicher Bedingungen. diese sollten zusammen gehalten werden, nicht über eine Reihe von Bedingungen ohne offensichtliche Beziehung verteilt werden. Warum nicht in eine Liste eintragen! Da jede Mod-Option ein anderes Signalzeichen auslöst, möchten Sie eine Assoziationsliste . Du beginnst also mit [(2,'a'),(3,'b'),(5,'c')]
. (Wenn es mehr davon ist, beispielsweise 10, verwende take 10 $ zip primes ['a'..]
mit einer Liste aller Primzahlen!)
Nun müssen wir für jeden der Einträge entscheiden: Wenn die gegebene Zahl durch die Primzahl teilbar ist, geben Sie das Zeichen zurück, fügen Sie sonst nichts hinzu. Das ist ein sehr kurzes Listenverständnis:
%Vor%Tags und Links haskell