7

我有一个基于字母的整数列表。例如:

let charlist = map (ord) "ABCDEF"

charlist然后将如下所示:

[65,66,67,68,69,70]

我还有一个包含三个函数的列表(+)(-)(*)。此示例中的列表如下所示

let funclist = [(+), (-), (*)]

我想在元素之间按顺序charlist应用函数(如果其中的“空格”charlist多于 中的元素funclist,则从头开始)并从左到右funclist计算最终值,如下所示:

s = ((((((65) + 66) - 67) * 68) + 69) - 70)

我正在考虑使用foldl,但foldl似乎只能使用一个功能。有没有其他方法可以做到这一点?如果可能的话,我想在一个函数中总结整个过程,尽管这不是必需的。

4

4 回答 4

7

虽然foldl可能只应用一种功能,但您的数据中可以有不同的功能。+将适当的,-*函数附加到您的数据的技巧。你的开始是正确的:

funclist = [(+), (-), (*)]

但是现在让我们制作上述列表的无限版本,比如[(+), (-), (*), (+), (-), (*)...]

infinite_funclist = cycle funclist

让我们分配我们要在这里折叠的数字。first在这种情况下65rest[66..70]

(first:rest) = [65..70]

现在我们一起拉上拉链restinfinite_funclist得到[(66,(+)), (67,(-)), (68,(*)), (69,(+)), (70,(-))]. 我们从 开始first,对于每个新元素,我们将当前元组第二部分中的操作应用于当前值和第一部分,如下所示:

result = foldl' (\acc (v, f) -> acc `f` v) first (zip rest infinite_funclist)

如果我们想打印结果,我们可以这样做:

main = print result

(链接到这里的代码)

于 2014-01-27T14:24:45.690 回答
4

从本质上讲,这是一个拉链而不是折叠。最好的将是

> zipWith ($) (循环 funclist) charlist
[(65+), (66-), (67*), (68+), (69-), (70+)]

但由于左关联性和初始元素,我们需要做的更罗嗦

> 让 ptApplied = 尾。zipWith (($) .flip) (cycle funclist) $ charlist
ptApplied = [(+66), (subtract 67), (*68), (+69), (subtract 70)]

只有这样才能折叠

> foldl' (flip ($)) (head charlist) ptApplied

于 2014-01-27T13:24:35.750 回答
1

您始终可以通过折叠的累加器传递更多信息,您的单个函数可以使用它来表现得好像每次都是不同的函数。可能最简单的方法是将当前要应用的函数列表传递给累加器中列表的其余部分。

Prelude Data.List> snd $ foldl' (\(f:fs, acc) x -> (fs, f acc x))
                                ((+):cycle funclist, 0) [65,66,67,68,69,70]
4351

(我在 funclist 的前面插入了一个额外(+)的,以便“真正的”加号介于 65 和 66 之间,而不是在初始累加器 0 和 65 之间)

于 2014-01-28T03:41:53.047 回答
0

谁说你不能“传递变量”给 foldl?

Prelude> let f = [(+), (-), (*)]

Prelude> let c = [65,66,67,68,69,70]

Prelude> foldl (\(a,i) val -> ((f!!(mod i 3)) a val,i + 1)) (head c,0) (tail c)
(4351,5)
于 2014-01-28T02:47:54.943 回答