use*_*917 0 monads haskell list
我有一个对结构列表[("oct",1),("nov",1),("dec",1)]
我想计算对内的总和:[("oct",1),("nov",2),("dec",3)]。我认为这是单子实现的一个很好的例子,但无法弄清楚如何保存容器。
我尝试在列表上创建函数(我已经知道了scanl1,只是在这里展示努力:)
csm (x:[]) = [x]
csm (x:y:xs) = x : csm ((x + y) : xs)
Run Code Online (Sandbox Code Playgroud)
然后尝试类似的事情:
sumOnPairs l = do
pair <- l
return (csm (snd pair))
Run Code Online (Sandbox Code Playgroud)
我的解决方案不起作用,请为我指出正确的方向
列表 monad 建模了非确定性:对列表中的每个元素执行相同的操作,然后将结果收集到新列表中。
对于您想要的顺序遍历类型(对元素执行某些操作,然后使用结果对下一个元素执行某些操作等),您可以使用 monadState执行类似的操作
import Control.Monad.Trans.State
import Data.Bifunctor
type Pair = (String, Int)
foo :: Pair -> State Pair Pair
foo (month, y) = do
-- bimap f g (x,y) == (f x, g y)
-- The new month replaces the old month,
-- and y is added to the sum.
modify (bimap (const month) (+y))
-- Return a snapshot of the state
get
sumOnPairs :: [Pair] -> [Pair]
sumOnPairs = flip evalState ("", 0) . traverse foo
Run Code Online (Sandbox Code Playgroud)
在每一步中,新状态是当前月份以及旧状态数字和当前数字的总和。traverse在遍历原始列表时将这些状态累积在列表中。
> sumOnPairs [("oct",1),("nov",1),("dec",1)]
[("oct",1),("nov",2),("dec",3)]
Run Code Online (Sandbox Code Playgroud)
您还可以仅保留状态中的总和,而不是刚刚更换的月份和总和。
foo' :: Pair -> State Int Pair
foo' x@(_, count) = do
modify (+ count)
fmap (<$ x) get
sumOnPairs' :: [Pair] -> [Pair]
sumOnPairs' = flip evalState 0 . traverse bar
Run Code Online (Sandbox Code Playgroud)
在这种情况下,状态中仅保留当前总和;新的对是通过使用<$运算符生成的,该Functor运算符的实例(,) String用状态中的总和替换当前对中的数字
> 6 <$ ("foo", 3)
("foo", 6)
Run Code Online (Sandbox Code Playgroud)
我认为如果您选择这条路线,使用Data.Functor.($>)( 的翻转版本<$)可能更具可读性。
foo' x@(_, count) = do
modify (+ count)
fmap (x $>) get
Run Code Online (Sandbox Code Playgroud)
从视觉上看,它更类似于您不需要映射get:时可以编写的内容x $> y == (fst x, y)。
| 归档时间: |
|
| 查看次数: |
264 次 |
| 最近记录: |