下面的簡單函數迭代地應用給定的一元函數,直到它遇到一個Nothing,此時它返回最後一個非Nothing值。它做我需要的,並且我理解它是如何工作的。在Haskell中避免顯式遞歸
lastJustM :: (Monad m) => (a -> m (Maybe a)) -> a -> m a
lastJustM g x = g x >>= maybe (return x) (lastJustM g)
由於在Haskell我的自我教育的一部分,我試圖避免明確遞歸(或至少明白如何)時,我可以。在這種情況下,似乎應該有一個簡單的非顯式遞歸解決方案,但我無法解決這個問題。
我不想要a monadic version的takeWhile
之類的東西,因爲收集所有的Nothing之前的值可能很貴,而且我也不關心它們。
我檢查了Hoogle的簽名並沒有顯示出來。 m (Maybe a)
位讓我覺得monad變壓器在這裏可能很有用,但我並沒有真正需要的直覺來提供細節(還)。
這可能是令人尷尬的容易做到這一點或令人尷尬的容易看出爲什麼它不能或不應該做,但這不會是我第一次使用自我尷尬作爲教學策略。
更新:我當然可以提供一個謂語,而不是使用Maybe
:像(a -> Bool) -> (a -> m a) -> a
(返回的最後一個值,對於這個斷言是真的)將工作一樣好。我感興趣的是使用標準組合器來編寫沒有顯式遞歸的版本。
背景:這裏有一個背景下,簡化工作示例:假設我們有興趣在單位正方形隨機遊動,但我們只關心出口點。我們有以下階躍函數:
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')
喜歡的東西evalState (lastJustM (randomStep 0.01) (0.5, 0.5)) <$> newStdGen
會給我們一個新的數據點。
'lastJustM =修復(。liftM2 AP((>> =))(翻轉(也許返回))。)'。 (好吧,我用'pointfree'騙了。) – kennytm 2010-05-18 18:30:55
@KennyTM:謝謝!我甚至沒有想過嘗試'pointfree',因爲我不知道它能夠處理這種事情。現在我只需要弄清楚它是如何工作的。 – 2010-05-18 18:43:47
有一種算法可以減少*任何*到無點形式給出一些組合器;這是'pointfree'使用的。當然,結果可能有用或不可用:) – 2010-05-18 19:25:24