我正在查看foldM
以获得关于如何使用它的直觉。
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
在这个简单的例子中,我只返回[Just 100]
。但是,如果我想使用b
?
ghci> foldM (\a _ -> return a) (Just 100) [1,2,3] :: [Maybe Int]
[Just 100]
我很担心如何使用b
中的(a -> b -> m a)
。
请帮助我获得此功能的直觉以及如何使用b
。
答案 0 :(得分:12)
这是一个愚蠢的例子。假设我们想要对列表求和,并确保部分总和从未超过(例如)10;因此:
> let ensure p x = x <$ guard (p x)
> foldM (\a b -> ensure (<10) (a+b)) 0 [1,2,3] :: Maybe Integer
Just 6
> foldM (\a b -> ensure (<10) (a+b)) 0 [5,5,5,-10] :: Maybe Integer
Nothing
答案 1 :(得分:5)
我认为您在foldl
而不是foldM
时遇到问题。 foldM
基本上只是foldl
的一个非常小的变体。
所以考虑一下:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f x xs
“发生”的是:
- 如果xs == []
,则返回x
。
- 否则f
被称为:f x (xs !! 0)
获取结果值y :: a
。然后将f
称为:f y (xs !! 1)
,获取z
。然后我们去f z (xs !! 2)
等。
正如您所看到的,对f
的每次调用都会将当前结果传递到该点以及输入中列表中的下一个元素。因此,只要您想将列表缩减为单个值并且您想要执行的计算是“顺序”,即从第一个元素计算值,然后从此值计算值,并使用以下元素计算新值,从这个和第二个元素你计算一个新的等等。
例如:
f ys x = map (+x) ys ++ [x]
foldl f [] [1,2,3]
产生
[6, 5, 3]
由于:
f [] 1 = [1]
f [1] 2 = [1+2, 2] = [3, 2]
f [3, 2] 3 = [3+3, 2+3, 3] = [6, 5, 3]
要更好地了解foldl
和foldr
的工作原理,请参阅:
现在,foldM
与foldl
相同,但现在f :: a -> b -> a
的类型为f :: a -> b -> m a
。这意味着将当前结果与下一个元素组合在一起的函数是一个monadic动作,即它可以产生效果。
例如,考虑:
f ys x = do
let a = map (+x) ys ++ [x]
print a
return a
foldM f [] [1,2,3]
文字是:
[1]
[3,2]
[6,5,3]
被打印,值[6, 5, 3]
是monadic动作的返回值。
答案 2 :(得分:2)
就像所有带初始化的折叠一样,b
类型变量表示您可能对累加器和其他类型具有不同类型的东西。例如,假设我正在重复一个字符串。我能做到
Prelude> import Control.Monad
Prelude Control.Monad> foldM (\str n -> (putStrLn $ "Repeating string " ++ (show str) ++ " " ++ (show n) ++ " times") >> (return $ concat $ replicate n str)) "Yolo" [4,5,6]
Repeating string "Yolo" 4 times
Repeating string "YoloYoloYoloYolo" 5 times
Repeating string "YoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYolo" 6 times
"YoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYoloYolo"
正如您所看到的,"Yolo"
的类型与4
,5
和6
的类型不同。这与所有折叠相同,foldM
只是增加了一个monadic方面。
答案 3 :(得分:1)
请注意,a
和b
可以是不同的类型。因此,只要函数可以返回m a
类型的值,该函数就可以对这两个值执行任何操作。
请参阅下面的示例。该函数试图总结所有字符串的长度,只要它们不为空。如果找到任何空字符串,则返回Nothing
。
count :: Int -> String -> Maybe Int
count c s = if null s
then Nothing
else Just $ c + length s
现在你可以尝试一下:
*Main> foldM count 0 ["Hello", "World"]
Just 10
*Main> foldM count 0 ["Hello", "", "World"]
Nothing