Die folgende einfache Funktion wendet eine gegebene monadische Funktion iterativ an, bis sie auf Nothing trifft. An diesem Punkt wird der letzte Nicht-Nothing-Wert zurückgegeben. Es tut, was ich brauche, und ich verstehe, wie es funktioniert.Vermeidung expliziter Rekursion in Haskell
lastJustM :: (Monad m) => (a -> m (Maybe a)) -> a -> m a
lastJustM g x = g x >>= maybe (return x) (lastJustM g)
Im Rahmen meiner Selbsterziehung in Haskell Ich versuche explizite Rekursion zu vermeiden (oder zumindest verstehen, wie man), wann immer ich kann. Es scheint, dass es in diesem Fall eine einfache, nicht explizit rekursive Lösung geben sollte, aber ich habe Probleme, es herauszufinden.
Ich möchte nicht etwas wie a monadic version von , da es teuer sein könnte, alle Pre-Nothing-Werte zu sammeln, und ich kümmere mich nicht um sie sowieso.
Ich überprüfte Hoogle nach der Unterschrift und nichts taucht auf. Das m (Maybe a)
Bit lässt mich denken, dass ein Monade-Transformator hier nützlich sein könnte, aber ich habe nicht wirklich die Intuitionen, die ich (noch) mit den Details bekommen müsste.
Es ist wahrscheinlich entweder peinlich leicht, dies zu tun oder peinlich leicht zu sehen, warum es nicht getan werden kann oder sollte, aber dies wäre nicht das erste Mal, dass ich Selbstpeinlichkeit als pädagogische Strategie benutzt hätte.
Update: Ich könnte natürlich ein Prädikat bietet stattdessen Maybe
verwenden: so etwas wie (a -> Bool) -> (a -> m a) -> a
(den letzten Wert zurückgegeben wird, für die das Prädikat wahr ist), als auch nur funktionieren würde. Was mich interessiert, ist eine Möglichkeit, eine der beiden Versionen ohne explizite Rekursion zu schreiben, mit Standard-Kombinatoren.
Hintergrund: Hier ist ein vereinfachtes Ausführungsbeispiel für Kontext: Angenommen, wir in Irrfahrten in der Einheit Platz interessiert sind, aber wir kümmern sich nur um Punkte der Ausfahrt. Wir haben die folgenden Schritt Funktion:
randomStep :: (Floating a, Ord a, Random a) =>
a -> (a, a) -> State StdGen (Maybe (a, a))
randomStep s (x, y) = do
(a, gen') <- randomR (0, 2 * pi) <$> get
put gen'
let (x', y') = (x + s * cos a, y + s * sin a)
if x' < 0 || x' > 1 || y' < 0 || y' > 1
then return Nothing
else return $ Just (x', y')
So etwas wie evalState (lastJustM (randomStep 0.01) (0.5, 0.5)) <$> newStdGen
wird uns einen neuen Datenpunkt.
'lastJustM = fix (.. LiftM2 ap ((>> =)) (Flip (vielleicht zurück))..)'. (OK, ich habe mit "pointfree" betrogen.) – kennytm
@KennyTM: Danke! Ich habe nicht einmal daran gedacht, "punktfrei" zu versuchen, weil ich keine Ahnung hatte, dass es in der Lage wäre, mit so etwas umzugehen. Jetzt muss ich nur herausfinden, wie es funktioniert. –
Es gibt einen Algorithmus, um * alles * auf eine punktfreie Form zu reduzieren, wenn man eine Handvoll Kombinatoren benutzt; Dies ist, was 'pointfree' verwendet. Natürlich kann das Ergebnis nützlich sein oder auch nicht :) –