List 如何在haskell中执行有状态列表操作

List 如何在haskell中执行有状态列表操作,list,haskell,iteration,List,Haskell,Iteration,我需要一个迭代列表并生成新列表的操作,其中新列表元素依赖于以前看到的所有元素。为了做到这一点,我想在迭代之间传递累加器/状态 以元组列表为例,元组的组件可以是“未定义的”。未定义的值应采用列表中较早的相同组件的最新值(如有)。因此,在任何阶段,我都会有一个已定义组件的状态,我需要将其传递给下一个迭代 因此,对于类型为[l]的列表和类型为a的累加器/状态,将有一个类型为 f :: a -> l -> (a,l) i、 它吐出一个新的列表元素和一个新的累加器 是否有一个函数可以简单地将

我需要一个迭代列表并生成新列表的操作,其中新列表元素依赖于以前看到的所有元素。为了做到这一点,我想在迭代之间传递累加器/状态

以元组列表为例,元组的组件可以是“未定义的”。未定义的值应采用列表中较早的相同组件的最新值(如有)。因此,在任何阶段,我都会有一个已定义组件的状态,我需要将其传递给下一个迭代

因此,对于类型为
[l]
的列表和类型为
a
的累加器/状态,将有一个类型为

f :: a -> l -> (a,l)
i、 它吐出一个新的列表元素和一个新的累加器

是否有一个函数可以简单地将f应用于列表?我看了看折叠、扫描和展开,但它们似乎都不起作用


编辑:虽然state monad看起来很有希望,但我只能看到如何获得最终状态,但我看不到如何获得新的列表元素。

有一些标准函数可用于执行您的要求

这听起来很像你想要
mapAccum
,所以你只需要导入
数据。列出
并决定你的累加方式。(我怀疑您想要
mapAccumL::(acc->x->(acc,y))->acc->[x]->(acc,[y])

mapAccumL 这会给

ghci> mapAccumL tell 10 [MoveBy 5, MoveBy 3, NoChange, Reset, MoveBy 7]
(7,["Add 5 to get 15","Add 3 to get 18","","Reset to zero","Add 7 to get 7"])
斯堪尔 但是也许你不需要使用mapAccum的全部功能,因为有时候累加器就是你在新列表中想要的,所以
scanl::(a->b->a)->a->[b]->[a]
就可以了

act :: Int -> Instruction -> Int
act n NoChange = n
act n Reset = 0
act n (MoveBy i) = n+i
像这样:

ghci> scanl act 10 [MoveBy 5, MoveBy 3, NoChange, Reset, MoveBy 7]
[10,15,18,18,0,7]
f'' :: l -> State a l
f'' = state . f'
mapAccum的定义 无论如何,以下是
mapAccumL
mapAccumR
的描述:

mapAccumL
函数的行为类似于
map
foldl
的组合;它将一个函数应用于列表的每个元素,从左到右传递一个累加参数,并将此累加器的最终值与新列表一起返回

mapAccumR :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
mapAccumR _ state []        =  (state, [])
mapAccumR f state (x:xs)    =  (finalstate, y:ys)
                           where (finalstate,y ) = f nextstate x
                                 (nextstate, ys) = mapAccumR f state xs

mapAccumR
函数的行为类似于
map
foldr
的组合;它对列表中的每个元素应用一个函数,从右到左传递一个累加参数,并将此累加器的最终值与新列表一起返回

如果没有你真正想要达到的具体目标,要找到答案有点困难。但是,如果您的
f
具有以下类型:

f :: (a, [l]) -> l -> (a,l)
然后您可以定义一个函数,
f'

f' :: (a, [l]) -> l -> (a,l)
f' acc@(y, xs) x = (z, x':xs)
    where
        (z, x') = f acc
f' :: l -> a -> (l, a)
f' :: l -> (a -> (l, a))
然后可以在折叠中使用

foldr f' (e, []) xs

f
的新签名允许它访问列表中前面的所有元素,并且
f'
将调用
f
的新元素添加到列表中。

您希望
mapM
状态
单子一起使用,其中累加器
a
将是
状态
。首先,要了解为什么需要
状态
,只需获取类型签名并翻转参数和结果的顺序:

import Data.Tuple

f :: a -> l -> (a, l)

uncurry f :: (a, l) -> (a, l)

swap . uncurry f . swap :: (l, a) -> (l, a)

curry (swap . uncurry f . swap) :: l -> a -> (l, a)
或者您可以定义
f
,以使参数和结果按正确的顺序排列,以您喜欢的顺序为准。我将调用此交换函数
f'

f' :: (a, [l]) -> l -> (a,l)
f' acc@(y, xs) x = (z, x':xs)
    where
        (z, x') = f acc
f' :: l -> a -> (l, a)
f' :: l -> (a -> (l, a))
现在,让我们在
f'
的类型签名的右半部分添加一组额外的括号:

f' :: (a, [l]) -> l -> (a,l)
f' acc@(y, xs) x = (z, x':xs)
    where
        (z, x') = f acc
f' :: l -> a -> (l, a)
f' :: l -> (a -> (l, a))
括号中分组的部分是一个
状态
计算,其中状态为
a
,结果为
l
。因此,我将继续使用
Control.Monad.Trans.State
中的
State
函数将其转换为
State
类型:

state :: (a -> (l, a)) -> State a l
mapM :: (Monad m) => (a -> m b) -> ([a] -> m [b])
因此,转换后的
f'
如下所示:

ghci> scanl act 10 [MoveBy 5, MoveBy 3, NoChange, Reset, MoveBy 7]
[10,15,18,18,0,7]
f'' :: l -> State a l
f'' = state . f'
但是,您最终真正想要的功能是:

final :: [l] -> a -> ([l], a)

-- which is really just:
state . final :: [l] -> State a [l]
这意味着我需要一个函数,它接受一个
l->stateal
,并将它转换成一个
[l]->statea[l]
。这正是
mapM
所做的,除了
mapM
适用于任何
Monad
,而不仅仅是
状态

state :: (a -> (l, a)) -> State a l
mapM :: (Monad m) => (a -> m b) -> ([a] -> m [b])
请注意,如果我们将
m
替换为
状态a
,并将
a
b
设置为
l
,则它的类型完全正确:

mapM :: (l -> State a l) -> ([l] -> State a [l])

f''' :: [l] -> State a [l]
f''' = mapM f''
现在,我们可以使用
runState
打开
状态
,以获取适当类型的列表线程函数:

final :: [l] -> a -> ([l], a)
final = runState . f'''
因此,如果我们将所有这些步骤合并为一个步骤,我们将得到:

final = runState . mapM (state . f')
。。。其中
f'
是用来交换参数和结果顺序的函数。如果选择不修改原始函数,则解决方案会稍微冗长一些:

final = runState . mapM (state . uncurry (swap . curry f . swap))

请注意,如果您试图区分有值和无值,则应该使用
Maybe
而不是
undefined
。当然,“undefined”是随意编写的。真正的数据类型是三个可能值的变化指示,其中一个是“无变化”。这只是一个完全正常的
折叠
,或者我不理解什么。折叠将列表折叠为单个“值”,但我还需要新的列表元素。嗯-我可能会让状态累积新的列表元素(而不仅仅是最新的“值”)。这就是你的建议吗?