使用 foldl 和元组同时计算列表的总和和长度
Calculate sum and length of a list simultaneously with foldl and a tuple
我已经为 foldr 苦苦挣扎了一段时间。我想将一个整数列表转换为一个包含列表总和和整数数量的元组。例如 [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列表中的每个元素。有什么修改建议吗?谢谢!
那么 foldl
的类型是 foldl :: (a -> b -> a) -> a -> [b] -> a
,a
是累加器的类型(这里是一个 2 元组),b
是列表中元素的类型.
但是在你的 lambda 表达式中 你写
\a b -> ((sum1 + b),(len1 + 1))
注意这里的变量b
是列表的一个元素,a
是一个元组。但是这里省略了累加器。您总是将 sum1
添加到 b
,但是由于 sum1
是一个常量,所以这没什么用。 len1
也是如此,因此,您将始终获得一个元组,其中包含列表的 last 元素作为第一项,而 1
作为第二项.
但是根据您的尝试,我认为您的目标是在 Haskell 中编写 "imperative" 代码。现在 Haskell 中的所有变量都是 不可变的 ,因此将 len1
设置为 0
(在 where
子句中),将导致事实上 len1
始终是 0
,等等。我们不更改累加器,实际上 foldr
是一个递归函数,每次调用具有不同值的参数,并在最后returns我们称之为“累加器”的参数。
因此我们可以将尝试更改为:
toTuple = (Fractional s, Fractional n) => [s] -> (s, n)
toTuple = foldl (\(cursum, curlen) b -> (cursum + b, curlen + 1)) (0.0,0.0)
所以这里我们每次都将累加器与 (cursum, curlen)
进行模式匹配(包含“current”(到目前为止)总和和长度),并且每次我们构造包含 "new" 当前总和(旧总和与 b
的总和)和 "new" 当前长度(长度加一)的新元组。
但它仍然不是很优雅:这里初始元组的值为 (0.0, 0.0)
,但结果,你告诉 Haskell 这是一个二元组,其中两个元素都是 Fractional
秒。虽然这可能会在没有舍入错误的情况下起作用,但为什么要将其限制为 Fractional
s?例如,如果您有一个 Integer
的列表,我们可以将它们相加而不会出现任何舍入错误。通过使用 (0, 0)
作为初始元组,我们使元组成为一个二元组,其中两个元素都具有属于 Num
类型类的类型:so numbers。请注意,两者 本身 具有相同的 Num
类型,后者限制较少。
所以现在我们得到:
toTuple = (Num s, Num n) => [s] -> (s, n)
toTuple = foldl (\(cursum, curlen) b -> (cursum + b, curlen + 1)) (0, 0)
看来您还没有建立起折叠的直觉。您可以将 foldl combine start input
之类的折叠视为以某个 start
值开始,然后使用 combine
函数将该值与 input
的每个元素组合。该函数接受当前“状态”和列表的下一个元素,以及 returns 更新后的状态。所以你非常接近一个可行的解决方案:
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)
我们在任何时候都不会修改任何变量,只是通过组合 current 部分来计算总和的 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)))
我已经为 foldr 苦苦挣扎了一段时间。我想将一个整数列表转换为一个包含列表总和和整数数量的元组。例如 [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列表中的每个元素。有什么修改建议吗?谢谢!
那么 foldl
的类型是 foldl :: (a -> b -> a) -> a -> [b] -> a
,a
是累加器的类型(这里是一个 2 元组),b
是列表中元素的类型.
但是在你的 lambda 表达式中 你写
\a b -> ((sum1 + b),(len1 + 1))
注意这里的变量b
是列表的一个元素,a
是一个元组。但是这里省略了累加器。您总是将 sum1
添加到 b
,但是由于 sum1
是一个常量,所以这没什么用。 len1
也是如此,因此,您将始终获得一个元组,其中包含列表的 last 元素作为第一项,而 1
作为第二项.
但是根据您的尝试,我认为您的目标是在 Haskell 中编写 "imperative" 代码。现在 Haskell 中的所有变量都是 不可变的 ,因此将 len1
设置为 0
(在 where
子句中),将导致事实上 len1
始终是 0
,等等。我们不更改累加器,实际上 foldr
是一个递归函数,每次调用具有不同值的参数,并在最后returns我们称之为“累加器”的参数。
因此我们可以将尝试更改为:
toTuple = (Fractional s, Fractional n) => [s] -> (s, n)
toTuple = foldl (\(cursum, curlen) b -> (cursum + b, curlen + 1)) (0.0,0.0)
所以这里我们每次都将累加器与 (cursum, curlen)
进行模式匹配(包含“current”(到目前为止)总和和长度),并且每次我们构造包含 "new" 当前总和(旧总和与 b
的总和)和 "new" 当前长度(长度加一)的新元组。
但它仍然不是很优雅:这里初始元组的值为 (0.0, 0.0)
,但结果,你告诉 Haskell 这是一个二元组,其中两个元素都是 Fractional
秒。虽然这可能会在没有舍入错误的情况下起作用,但为什么要将其限制为 Fractional
s?例如,如果您有一个 Integer
的列表,我们可以将它们相加而不会出现任何舍入错误。通过使用 (0, 0)
作为初始元组,我们使元组成为一个二元组,其中两个元素都具有属于 Num
类型类的类型:so numbers。请注意,两者 本身 具有相同的 Num
类型,后者限制较少。
所以现在我们得到:
toTuple = (Num s, Num n) => [s] -> (s, n)
toTuple = foldl (\(cursum, curlen) b -> (cursum + b, curlen + 1)) (0, 0)
看来您还没有建立起折叠的直觉。您可以将 foldl combine start input
之类的折叠视为以某个 start
值开始,然后使用 combine
函数将该值与 input
的每个元素组合。该函数接受当前“状态”和列表的下一个元素,以及 returns 更新后的状态。所以你非常接近一个可行的解决方案:
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)
我们在任何时候都不会修改任何变量,只是通过组合 current 部分来计算总和的 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)))