【发布时间】:2010-05-18 16:47:35
【问题描述】:
下面的简单函数迭代地应用给定的一元函数,直到它遇到一个 Nothing,此时它返回最后一个非 Nothing 值。它可以满足我的需要,而且我了解它的工作原理。
lastJustM :: (Monad m) => (a -> m (Maybe a)) -> a -> m a
lastJustM g x = g x >>= maybe (return x) (lastJustM g)
作为我在 Haskell 中自我教育的一部分,我试图尽可能避免显式递归(或者至少了解如何避免)。在这种情况下,似乎应该有一个简单的非显式递归解决方案,但我无法弄清楚。
我不想要 takeWhile 中的 a monadic version 之类的东西,因为收集所有 pre-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 = fix (liftM2 ap ((>>=) .) . (flip (maybe . return) .))。 (好吧,我欺骗了pointfree。) -
@KennyTM:谢谢!我什至没有想过要尝试
pointfree,因为我不知道它能够处理这种事情。现在我只需要弄清楚它是如何工作的。 -
有一种算法可以将 anything 简化为给定少量组合子的无点形式;这就是
pointfree使用的。当然,结果可能有用也可能没用:) -
不要被名字所迷惑,
pointfree的输出的正确术语是“无意义的风格”。那是liftM2在((->) r)monad 中工作吗?这总能提高代码的清晰度... -
fix的真正问题在于它除了“如果我搞砸了程序就会崩溃”之外没有任何语义价值。map和fold之类的东西的价值在于,只要你不留下折叠无限列表,它们就完美地定义了它们执行的操作,不会让你误入歧途。
标签: haskell functional-programming monads