2015-10-20 9 views
10

Erklären Sie über ein "Duplikat"

Jemand zeigen Sie auf Is this a case for foldM? als ein mögliches Duplikat. Nun bin ich der festen Überzeugung, dass zwei Fragen, die mit identischen Antworten beantwortet werden können, nicht unbedingt Duplikate sind! "Was ist 1 - 2" und "Was ist i^2" ergibt beide "-1", aber nein, sie sind keine doppelten Fragen. Meine Frage (die bereits beantwortet wurde, Art von) war etwa "ob die Funktion iterateM in Haskell Standard-Bibliothek existiert", nicht "Wie eine verkettete Monade Aktion implementieren".Gibt es in Haskell eine "Ketten" -Monadenfunktion?

Die Frage

Als ich einige Projekte schreiben, fand ich mich dieses combinator schreiben:

repeatM :: Monad m => Int -> (a -> m a) -> a -> m a 
repeatM 0 _ a = return a 
repeatM n f a = (repeatM (n-1) f) =<< f a 

Es führt nur eine einstellige Aktion n mal, das vorherige Ergebnis in die nächste Aktion Fütterung. Ich habe versucht, einige hoogle Suche und einige Google-Suche, und habe nichts gefunden, das mit dem "Standard" Haskell kommt. Gibt es eine solche formale Funktion, die vordefiniert ist?

+3

Ich habe es auch in * normalen * Paketen nicht gefunden - aber [Hayoo] (http://hayoo.fh-wedel.de/?query=Monad+m+%3D%3E+Int+%%%%%+E3++) % 28a + -% 3E + m + a% 29 + -% 3E + a + -% 3E + m + a) kennt mindestens 2 Orte - in diesem Fall würde ich * replizieren * ich denke (vielleicht ist 'chainM' ein besserer name though - 'replicate' ist * gegeben *) – Carsten

+2

Liefert' replicateM' das vorherige Ergebnis? –

+0

Nein, tut es nicht - aber wenn ich wiederhole oder repliziere denke ich über verschiedene Dinge (gerade aus diesem Grund) - es ist wirklich nur ein Kommentar auf die Namensgebung - Entschuldigung, wenn es ein Missverständnis verursacht – Carsten

Antwort

10

Sie können foldM, z.B .:

import Control.Monad 

f a = do print a; return (a+2) 

repeatM n f a0 = foldM (\a _ -> f a) a0 [1..n] 

test = repeatM 5 f 3 
    -- output: 3 5 7 9 11 
+0

Das ist klar genug! Besser als drei Zeilen schreiben –

9

Carsten mentionedreplicate, und das ist kein schlechter Gedanke verwenden.

import Control.Monad 
repeatM n f = foldr (>=>) pure (replicate n f) 

Die Idee dahinter ist, dass für jede Monade m,

die Funktionen des Typs a -> m b die Kleisli Kategorie von m bilden, mit Identitäts Pfeile
pure :: a -> m a 

(auch return genannt)

und Zusammensetzungsoperator

(<=<) :: (b -> m c) -> (a -> m b) -> a -> m c 
f <=< g = \a -> f =<< g a 

Da es sich eigentlich um eine Funktion vom Typ a -> m a handelt, schauen wir uns eigentlich einen Monoid der Kleisli Kategorie an, so dass wir über das Falten dieser Pfeile Listen nachdenken können.

Der obige Code faltet den Zusammensetzungsoperator umgedreht in eine Liste von n Kopien von f und endet mit einer Identität wie üblich. Das Umdrehen des Kompositionsoperators versetzt uns in die duale Kategorie; für viele übliche Monaden ist x >=> y >=> z >=> w effizienter als w <=< z <=< y <=< x; Da alle Pfeile die gleichen in diesem Fall sind, scheint es uns genauso gut. Beachten Sie, dass es für die Lazy-State-Monade und wahrscheinlich auch für die Reader-Monade besser sein kann, den unliped-Operator <=< zu verwenden; >=> wird im Allgemeinen für IO, ST s und den üblichen strengen Zustand besser sein.

Hinweis: Ich bin kein Kategorientheoretiker, daher kann es Fehler in der obigen Erklärung geben.

2

Ich finde mich diese Funktion oft wünschen, ich wünschte, es hätte einen Standardnamen. Dieser Name wäre jedoch nicht repeatM - das wäre für eine unendliche Wiederholung, wie forever, wenn es existiert, nur für die Konsistenz mit anderen Bibliotheken (und repeatM ist in einigen Bibliotheken auf diese Weise definiert).

Genau wie eine andere Perspektive aus den bereits gegebenen Antworten, zeige ich, dass (s -> m s) ein bisschen wie eine Aktion in einer State Monad mit State Type s aussieht.

In der Tat ist es isomorph zu StateT s m() - eine Aktion, die keinen Wert zurückgibt, weil die ganze Arbeit, die es tut, in der Art verkapselt wird, wie es den Zustand ändert. In dieser Monade ist die gewünschte Funktion wirklich replicateM. Sie können es in haskell so schreiben, obwohl es wahrscheinlich hässlicher aussieht, als es direkt zu schreiben.

Zuerst konvertiert s -> m s der äquivalenten Form, die StateT Verwendungen, wobei sich die informationsfreien (), liftM unter Verwendung einer Funktion über den Rückgabetyp abzubilden.

> :t \f -> liftM (\x -> ((),x)) . f 
\f -> liftM (\x -> ((),x)) . f :: Monad m => (a -> m t) -> a -> m ((), t) 

(könnte fmap verwendet haben, aber die Monade Einschränkung scheint hier klarer, verwendet TupleSections haben könnte, wenn Sie mögen, wenn Sie leichter tun Notation finden es einfach \f s -> do x <- f s; return ((),s) zu lesen).

Jetzt hat dies die richtige Art mit StateT einpacken:

> :t StateT . \f -> liftM (\x -> ((),x)) . f 
StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => (s -> m s) -> StateT s m() 

und dann können Sie es n-mal wiederholen, mit der replicateM_ Version, weil die zurückgegebene Liste [()] von replicateM nicht interessant wäre:

> :t \n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f 
\n -> replicateM_ n . StateT . \f -> liftM (\x -> ((),x)) . f :: Monad m => Int -> (s -> m s) -> StateT s m() 

und schließlich kann man execStateT verwenden, um die Monade zu gehen zurück arbeiteten ursprünglich in:

runNTimes :: Monad m => Int -> (s -> m s) -> s -> m s 
runNTimes n act = 
    execStateT . replicateM_ n . StateT . (\f -> liftM (\x -> ((),x)) . f) $ act 
+0

Das ist meiner Antwort sehr ähnlich. Die monadische Schnittstelle von 'StateT' ist Overkill, da man nie die Ergebnisse betrachtet. Angenommen, Sie nehmen 'newtype StateM s m = ZustandM {runStateM :: s -> m s}'. Dann: Instanz Monade m => Monoid (Zustand M s) wobei {mempty = rein; mappend (StateM f) (ZustandM g) = StateM (f> => g)} '. Jetzt können Sie 'foldMap StateM' verwenden, um eine Reihe von' s -> m s'-Funktionen aneinander zu reihen! – dfeuer

+2

Absolut. Es gibt überall Monoide. 'f()' ist ein Monoid unter '*>', 's -> m s' ist ein Monoid unter'> => ', und so weiter. Der Punkt meiner Antwort war einfach, die Frage mit der Standard-Bibliotheksfunktion "replicateM" zu verbinden, weil sie auf den ersten Blick anwendbar erscheint, und dann nicht. Die Beobachtung ist "Nun, es ist' replicateM' - nur für eine etwas andere Monade ". – drquicksilver

+0

Ja, das macht Sinn. – dfeuer

Verwandte Themen