我现在已经和foldr挣扎了一段时间。我想将一个int列表转换为一个元组,其中包含列表和int数的总和。例如[1,2,3,4] - > (10,4) 我在下面的函数迭代通过列表罚款,但只输出最后一个元素作为x val和1作为y val。
toTuple xs = let tuple = foldl (\a b -> ((sum1 + b),(len1 + 1))) (0.0,0.0) xs
in tuple
where sum1 = 0
len1 = 0
我最初将sum1和len1作为一个函数,它将一个int作为一个输入但是没有'工作要么所以我将它们更改为初始化为0的变量。但是它似乎在列表中的每个元素上设置sum和len为0。有什么建议可以修改吗?谢谢!
答案 0 :(得分:6)
好foldl
的类型为foldl :: (a -> b -> a) -> a -> [b] -> a
,其中a
为累加器的类型(此处为2元组),b
列表中的元素类型。< / p>
但是在 lambda表达式中你写了
\a b -> ((sum1 + b),(len1 + 1))
请注意,此处变量b
是列表的元素,a
是元组。但是这里你省略了累加器。您始终将sum1
添加到b
,但由于sum1
是常量,因此无用。 len1
的结果相同,因此,您将始终获得一个元组,其中包含列表的 last 元素作为第一项,1
作为第二项。
但是根据你的尝试,我认为你以某种方式打算写出'#34;命令式的&#34; Haskell中的代码。现在在Haskell中,所有变量都是不可变的,因此将len1
设置为0
(在where
子句中)将导致len1
这一事实总是0
等等。我们不更改累加器,实际上foldr
是一个递归函数,每次用不同值的参数调用自身,最后返回我们称为&的参数#34; 累加器&#34;
我们可以改变尝试:
toTuple = (Fractional s, Fractional n) => [s] -> (s, n)
toTuple = foldl (\(cursum, curlen) b -> (cursum + b, curlen + 1)) (0.0,0.0)
所以这里我们每次模式都将累加器与(cursum, curlen)
匹配(包含&#34; 当前&#34;(到目前为止)总和和长度),每次我们构造一个包含&#34; new&#34;的新元组。当前总和(旧金额和b
的总和)和&#34; new&#34;当前长度(长度增加1)。
但它仍然不是很优雅:这里初始元组的值为(0.0, 0.0)
,但结果是,你告诉Haskell这是一个2元组,其中两个元素都是Fractional
s。虽然没有舍入错误,但这可能会有效,为什么要将此限制为Fractional
s?例如,如果您有一个Integer
列表,我们可以将它们相加而不会出现任何舍入错误。通过使用(0, 0)
作为初始元组,我们使元组成为一个2元组,其中两个元素都具有属于Num
类型类的类型:所以数字。请注意,这两个本身具有相同的Num
类型,这种限制较少。
所以现在我们得到了:
toTuple = (Num s, Num n) => [s] -> (s, n)
toTuple = foldl (\(cursum, curlen) b -> (cursum + b, curlen + 1)) (0, 0)
答案 1 :(得分:6)
看起来你还没有建立褶皱的直觉。您可以将foldl combine start input
之类的折叠视为从某个start
值开始,然后使用input
函数将该值与combine
的每个元素相结合。该函数接受当前“状态”和列表的下一个元素,并返回更新的状态。所以你非常接近一个有效的解决方案:
toTuple xs = foldl (\ (sum, len) x -> (sum + x, len + 1)) (0, 0) xs
逐步执行像[1, 2, 3, 4]
这样的示例输入,状态(sum, len)
,通常称为“累加器”,每次调用折叠函数时都会采用以下值:
(0, 0)
(0+1, 0+1) = (1, 1)
(0+1+2, 0+1+1) = (3, 2)
(0+1+2+3, 0+1+1+1) = (6, 3)
(0+1+2+3+4, 0+1+1+1+1) = (10, 4)
我们不会修改任何变量,只需通过组合当前部分和&amp;来计算总和的 next 值和长度。列表中每个元素的长度。对于从左到右完成的左折(foldl
);对于右折(foldr
)它是从右到左,所以参数的顺序((sum, len)
和x
)是相反的:
toTuple xs = foldr (\ x (sum, len) -> (sum + x, len + 1)) (0, 0) xs
但是,对于右侧折叠,我发现更容易将它们视为使用函数替换列表中的所有:
并将[]
替换为值:
foldr f z [1, 2, 3, 4]
foldr f z (1 : (2 : (3 : (4 : []))))
1 `f` (2 `f` (3 `f` (4 `f` z)))