在列表

时间:2017-01-23 16:25:15

标签: list function haskell

我想知道在函数式编程语言中实现以下问题的最佳方法是什么(在本例中为Haskell):

你有一个函数(或'方法')在同一类型的2个输出中转换2个输入,类型为ab(例如:半加法器)。我们称之为f 在Haskell中它会有这种类型的签名 a -> b -> (a, b)

您有一个包含a类型元素的列表。 (或其他类型的数据结构)。

现在,如果提供了一个初始b,我希望发生以下事情(概念用递归实现解释):

使用初始f和第一个元素执行b,修改b和带有函数输出的元素,然后重复下一个元素。

在Haskell:

exec _ [] _     = []
exec f (x:xs) b = let (x',b') = f x b in x':(exec f xs b')

对这种行为进行建模的最佳/最有效方法是什么。

1 个答案:

答案 0 :(得分:0)

mapM monad为State

好的,扩大一点。

让我们首先将其输入ghci并查看exec函数的类型:

Prelude> let {exec _ [] _ = []; exec f (x:xs) b = let (x',b') = f x b in x':(exec f xs b')}
Prelude> :t exec
exec :: (t2 -> t1 -> (t, t1)) -> [t2] -> t1 -> [t]

这几乎和你描述的一样,只是tt2不一定是同一类型。那很好。

现在,另一个观察结果是:当我们按照你描述的方式行事时,我们实际上会丢失信息具体来说,我们丢弃b(或t的最后一个值,如ghci所称的那样)。让我们保留片刻;我们总是可以把它扔掉:

exec' _ [] b = ([], b)
exec' f (x:xs) b =
  let (x',b') = f x b
      (xs', b'') = exec' f xs b'
  in (x':xs', b'')

而且ghci说

Prelude> :t exec'
exec' :: (t2 -> t1 -> (t, t1)) -> [t2] -> t1 -> ([t], t1)

我们可以定义

exec f xs b = fst $ exec' f xs b

但现在exec'的类型包含一个清晰的模式。我们可以更明确地说明:

type S b c = b -> (c, b)
exec' :: (a -> S b c) -> [a] -> S b [c]

现在很明显S几乎就是State monad(嗯,它在现代环境中的实际定义有点复杂,但并不多:https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-State-Lazy.html#t:StateT);真的,这只是一个新类型:

newtype State b c = State {runState :: b -> (c, b)}

如果我们将exec'的类型概括为使用任意monad而不是State,我们得到

Monad m => (a -> m c) -> [a] -> m [c]

当然,我们无法确定这样的事情确实存在,因为我们只有State monad的实现,但是......确实如此。它被称为mapM(同样,它在现代环境中的实际定义更复杂:https://hackage.haskell.org/package/base-4.9.1.0/docs/Prelude.html#v:mapM) - 这是有道理的,因为没有monad它就是

(a -> c) -> [a] -> [c]

这正是map的类型。

当然,如果没有检查后者的实现,您无法确定exec'是否为mapM。但是在Haskell中,经常发生的是,具有相同类型的东西(如果它是合理的通用的)是同一个。

State monad会以某种方式参与其中也是有道理的 - 毕竟,你使用b作为状态,在列表中更改它。

所以,如果exec'mapM,我们如何回到exec?好吧,我们需要从monadic值State b [c]变为[c],只需要b。我们可以 - 再次 - 概括;比方说,我们从State b d转到d,但未提及列表。再一次 - 有一个类似的功能,它被称为evalStatehttps://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-State-Lazy.html#v:evalState

所以,最后,我们能够产生最终结果:

eval f xs b = evalState (mapM (\x -> state (\b -> f x b)) xs) b

我们可以缩短到

eval f xs b = evalState (mapM (\x -> state (f x)) xs) b

或只是

eval f xs = evalState (mapM (state . f) xs)

甚至

eval f = evalState . mapM (state . f)

我们可以让它完全无点,但这将毫无意义(并且包含太多点):

eval = (evalState .) . mapM . (state .)