Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
与Haskell的州monad合作_Haskell_Monads - Fatal编程技术网

与Haskell的州monad合作

与Haskell的州monad合作,haskell,monads,Haskell,Monads,我一直在一点一点地学习Haskell,并且(慢慢地)努力理解状态单子,试图编写一个函数,重复状态计算,直到状态满足某种布尔测试,并将返回值收集到一个列表中以获得总体结果。我终于成功了: collectUntil :: (s -> Bool) -> State s a -> State s [a] collectUntil f s = do s0 <- get let (a,s') = runState s s0

我一直在一点一点地学习Haskell,并且(慢慢地)努力理解状态单子,试图编写一个函数,重复状态计算,直到状态满足某种布尔测试,并将返回值收集到一个列表中以获得总体结果。我终于成功了:

collectUntil :: (s -> Bool) -> State s a -> State s [a]
collectUntil f s = do s0 <- get
                      let (a,s') = runState s s0
                      put s'
                      if (f s') then return [a] else liftM (a:) $ collectUntil f s

对于这个任务来说,这是一个合理的函数,还是有一种更惯用的方法?

大多数monad都带有一些基本的“运行”操作,例如
运行状态
执行状态
,等等。如果您经常在state monad中调用
runState
,这意味着您没有真正使用monad提供的功能。你已经写信了

s0 <- get                    -- Read state
let (a,s') = runState s s0   -- Pass state to 's', get new state
put s'                       -- Save new state

你犯的错误与我第一次开始编写一元代码时所犯的错误完全相同——使它变得太复杂,过度使用
liftM
而未充分使用
>=
(相当地,对于这样一个简单的任务,在使用
时,我不会使用
状态
单子。其他人已经澄清了您实际上应该如何编写单子版本,但我想添加我的个人(更简单)解决方案,因为您要求的是最惯用的编写方式

collectWhile, collectUntil :: (a -> a) -> (a -> Bool) -> a -> [a]
collectWhile f cond z = takeWhile cond $ iterate f z
collectUntil f cond z = collectWhile f (not . cond) z
或者,如果您只想
collectil

collectUntil f cond z = takeWhile (not.cond) $ iterate f z
这里和来自。为了完整性,因为它是实现的核心,下面是迭代的(非常简单)代码:

iterate f x =  x : iterate f (f x)

警告:从我的回答来看,这可能不够清楚,但这个解决方案实际上并不相同,因为我通过在
状态
之外工作,将状态和结果融合在一起。当然,使用
f::(s,a)->(s,a)可以做一些非常类似的事情
然后使用
map fst
map snd
进行投影,分别得到中间状态或结果的列表。不过,为了便于记谱,使用
State
的解决方案可能更简单。

惯用的方法很可能涉及到更一般的函数,例如
Monad m=>m Bool->m a->m[a]
。使用
monad循环中的
untilM
,可以得到:
untilM simpleState(liftM(>10)get)
。谢谢,这非常有帮助。我想我的困惑部分在于将状态参数
s
视为一个完全独立的计算,我必须使用
runState
显式计算,并使用
put
将其结果状态注入到当前计算中。感谢@Heatsink提供了相同的洞察力。这并不是做同样的事情。它返回的是状态序列,而不是结果序列。我知道,为了简单起见,我把状态和结果放在一边。很抱歉,我的回答不够清楚。正如我在第一行中所说,我的解决方案是针对Dan示例中的简单任务,其中状态和结果是re实际上是相同的东西(例如,
collectUntil(+1)(>10)0==[0,1,2,3,4,5,6,7,8,9,10]
)。
collectUntil f comp = do
    s <- get                              -- Get the current state
    if f s then return []                 -- If it satisfies predicate, return
           else do                        -- Otherwise...
               x  <- comp                 -- Perform the computation s
               xs <- collectUntil f comp  -- Perform the rest of the computation
               return (x:xs)              -- Collect the results and return them
collectUntil :: MonadState t m => (t -> Bool) -> m a -> m [a]
collectUntil :: (s -> Bool) -> State s a -> State s [a]
x <- s
s' <- get
collectWhile, collectUntil :: (a -> a) -> (a -> Bool) -> a -> [a]
collectWhile f cond z = takeWhile cond $ iterate f z
collectUntil f cond z = collectWhile f (not . cond) z
collectUntil f cond z = takeWhile (not.cond) $ iterate f z
iterate f x =  x : iterate f (f x)