代码之家  ›  专栏  ›  技术社区  ›  Piskator

在Haskell中提取、操作和累积monad列表中的值

  •  0
  • Piskator  · 技术社区  · 2 年前

    我正在尝试创建一个函数,该函数“提取”并计算一个单子列表中每个单子的值,这样就可以返回最终值(累计值左右)。这样一个函数的签名应该是这样的 accCurryingMonad :: [SomeCurryingMonad a d] -> SomeCurryingMonad a d 更难的是,每个monad的值都是元组。此外,如果其中一个评估值“失败”或“什么都不返回”,则函数仍将继续使用迄今为止累积的值,并将其用于列表中的一元运算链的其余部分。

    为了解决我的问题,我重读了 State s a monad,如中所述 LYAH Stacking Manip ,并试图重写“堆栈和石头”问题的代码以符合我的目的,因为我怀疑这是一个可以用 foldM .

    以下是重写的MRE代码:

    import Control.Monad.State
    
    type Stack = [Int]
    
    pop :: State Stack Int
    pop = do
        stack <- get
        case stack of
            (x:xs) -> do
                put xs
                return x
            _ -> error "Empty stack"
    
    push :: Int -> State Stack ()
    push a = do
        stack <- get
        put (a:stack)
    
    -- Perform a single step of the computation using State monad
    singleStep :: Int -> State Stack Int
    singleStep value = do
        push value    
        pop
    
    -- Perform the computation 10 times using foldM with State monad
    stackManip10Times :: State Stack Int
    stackManip10Times = foldM (\_x v -> v) 0 [singleStep 10, singleStep 20, singleStep 30]
    
    main :: IO ()
    main = do
        let initialStack = [200, 300, 400, 500] :: Stack
            (result, finalStack) = runState stackManip10Times initialStack
        putStrLn $ "Result: " ++ show result
        putStrLn $ "Final Stack: " ++ show finalStack
    

    我如何使用 foldM (如果这是正确的方法,那就是)转向:

    stackManip10Times = foldM (\_x v -> v) 0 [singleStep 10, singleStep 20, singleStep 30]
    
    

    转换成类似这样的函数,它不采用泛型函数(例如 singleStep v ),但在单个monad中评估和操作值,并返回所有操作的结果,无论是哪种monad操作:

    stackManip10Times :: State Stack Int
    stackManip10Times = foldM (\_x v -> v) () [push 10, pop, push 20]
    
    0 回复  |  直到 2 年前
        1
  •  3
  •   n. m. could be an AI    2 年前

    这就是我对这个问题的解释,我很可能大错特错。

    让我们看看的实现 sequence (用于列表,不用于通用遍历)。

    sequence = mapM id
    
    mapM :: Monad m => (a -> m b) -> [a] -> m [b]
    mapM f as = foldr k (return []) as
                where
                  k a r = do { x <- f a; xs <- r; return (x:xs) }
    

    这没有捕获和丢弃错误的规定,所以让我们添加它。

    sequenceNF :: MonadError e m => [m a] -> m [a]
    sequenceNF = mapMNF id
    
    mapMNF :: MonadError e m => (a -> m b) -> [a] -> m [b]
    mapMNF f as = foldr k (return []) as
                where k a r = do { 
                   x <- f a; xs <- r; return (x:xs) 
                } `catchError` (\_ -> r)
    

    现在我们可以:

    sequenceNF [Just 42, Nothing, Just 13] 
    -- => Just [42, 13]
    
    sequenceNF [print 42, ioError (userError "Oops"), print "Hi"] 
    -- => IO [(), ()]
    -- will print 42 and "Hi" when executed 
    
    sequenceNF [
       print "abc",
       do { print 42; print "def" },
       do { print "Hi"; ioError (userError "Oops"); print "Bye"; },
       print "Ok" ]
    -- => IO [(), (), ()]
    -- will print: "abc" 42 "def" "Hi" "Ok"
    -- note there are 4 elements in the input list
    -- but only 3 elements in the output list
    -- execution of the third element failed, so no value collected
    -- although side effects of it are still executed
    
    推荐文章