使用Haskell的map函数计算列表的总和

时间:2011-05-30 10:56:36

标签: list haskell recursion fold

的Haskell

addm::[Int]->Int
addm (x:xs) = sum(x:xs)

我能够使用sum函数获得一个列表的总和,但是可以使用map函数获取列表的总和吗?还有什么地图功能的使用?

8 个答案:

答案 0 :(得分:20)

您无法真正使用map来总结列表,因为map会独立地处理每个列表元素。例如,您可以使用map递增列表中的每个值,如

map (+1) [1,2,3,4] -- gives [2,3,4,5]

实现addm的另一种方法是使用foldl

addm' = foldl (+) 0

答案 1 :(得分:6)

sum而言map所谓的sum' xs = let { ys = 0 : map (\(a,b) -> a + b) (zip xs ys) } in last ys 所谓的不可能的定义:

scanl

这实际上显示了如何根据map ziplast实施 foldl (+) 0 xs === last $ scanl (+) 0 xs ),以上等同于scanl' f z xs = let { ys = z : map (uncurry f) (zip ys xs) } in ys

map

我希望可以用zip计算很多东西,安排通过zipWith的各种信息流。

编辑:以上只是伪装成zipWithmap2有点sum' xs = let { ys = 0 : zipWith (+) ys xs } in last ys ):

scanl

这似乎表明foldl比{{1}}更具通用性。

答案 2 :(得分:5)

无法使用map将列表缩减为其总和。递归模式是fold

sum :: [Int] -> Int
sum = foldr (+) 0

另外,请注意您也可以将map定义为折叠:

map :: (a -> b) -> ([a] -> [b])
map f = fold (\x xs -> f x : xs) []

这是因为foldr是列表上的规范递归函数。


参考文献A tutorial on the universality and expressiveness of fold,Graham Hutton,J。Functional Programming 9(4):355 - 343,1999年7月。

答案 3 :(得分:4)

在获得一些见解之后,我必须添加另一个答案:您无法获得带有map的列表总和,但您可以使用其monadic版本mapM获得总和。您需要做的就是在Writer幺半群上使用Sum monad(请参阅LYAHFGG)(参见LYAHFGG)。

我写了一个专门的版本,可能更容易理解:

data Adder a = Adder a Int

instance Monad Adder where
  return x = Adder x 0
  (Adder x s) >>= f = let Adder x' s' = f x
                      in Adder x' (s + s') 

toAdder x = Adder x x

sum' xs = let Adder _ s = mapM toAdder xs in s  

main = print $ sum' [1..100]
--5050

Adder只是某种类型的包装器,它也保留了“运行总和”。我们可以使Adder成为一个monad,在这里它做了一些工作:当执行操作>>=(又名“bind”)时,它返回新结果和该结果的运行总和的值加上原始运行总额toAdder函数接受一个I​​nt并创建一个Adder,它将该参数保存为包装值和运行总和(实际上我们对该值不感兴趣,但只在sum部分中感兴趣)。然后在sum' mapM中可以发挥其魔力:虽然它与monad中嵌入的值类似于map,但它执行“monadic”函数,如toAdder和< em> chains 这些调用(它使用sequence来执行此操作)。在这一点上,我们通过我们monad的“后门”了解了标准map缺少的列表元素之间的交互。

答案 4 :(得分:2)

将列表的每个元素“映射”到输出中的元素:

let f(x) = x*x
map f [1,2,3]

这将返回正方形列表。

要对列表中的所有元素求和,请使用fold:

foldl (+) 0 [1,2,3]

+是您要应用的函数,0是初始值(0表示总和,1表示产品等)

答案 5 :(得分:1)

正如其他答案所指出的那样,“正常”方式是使用fold函数之一。但是,可以在命令式语言中编写与while循环非常相似的东西:

sum' [] = 0
sum' xs = head $ until single loop xs where 
   single [_] = True
   single _ = False
   loop (x1 : x2 : xs) = (x1 + x2) : xs 

它将列表的前两个元素添加到一起,直到它以一个元素列表结束,并返回该值(使用head)。

答案 6 :(得分:1)

我意识到这个问题已得到解答,但我想补充一下这个想法...

listLen2 :: [a] -> Int
listLen2 = sum . map (const 1)

我相信它会为列表中的每个项目返回常量1,并返回总和! 可能不是最好的编码实践,但这是我的教授给我们学生的一个例子,这似乎与这个问题有关。

答案 7 :(得分:0)

map永远不能成为总结容器元素的主要工具,就像螺丝刀永远不能成为观看电影的主要工具一样。但您可以使用螺丝刀修理电影放映机。如果你真的想要,你可以写

import Data.Monoid
import Data.Foldable

mySum :: (Foldable f, Functor f, Num a)
      => f a -> a
mySum = getSum . fold . fmap Sum

当然,这很愚蠢。您可以获得更通用,更高效的版本:

mySum' :: (Foldable f, Num a) => f a -> a
mySum' = getSum . foldMap Sum

或者更好,只需使用sum,因为它实际上是为了工作而做的。