繁体   English   中英

使用 Haskell 的 map function 计算一个列表的总和

[英]Using Haskell's map function to calculate the sum of a list

Haskell

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

我能够使用sum function 获得列表的总和,但是是否可以使用map function 获得列表的总和? 还有map function有什么用?

您不能真正使用map来总结列表,因为 map 将每个列表元素与其他元素分开处理。 例如,您可以使用map来增加列表中的每个值,例如

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

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

addm' = foldl (+) 0

在这里,用map来定义sum是不可能的:

sum' xs  =  let { ys = 0 : map (\(a,b) -> a + b) (zip xs ys) } in last ys

这实际上显示了如何根据scanlziplast )来实现map ,上面等同于foldl (+) 0 xs === last $ scanl (+) 0 xs

scanl' f z xs  =  let { ys = z : map (uncurry f) (zip ys xs) } in ys

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

编辑:上面当然只是一个变相的zipWith (并且zipWith是一种map2 ):

sum' xs  =  let { ys = 0 : zipWith (+) ys xs } in last ys

这似乎表明scanlfoldl更通用。

无法使用map将列表减少到其总和。 该递归模式是fold

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

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

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

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


参考文献关于 fold 的普遍性和表现力的教程,Graham Hutton, J. Functional Programming 9 (4): 355–372,1999 年 7 月。

经过一些见解后,我必须添加另一个答案:您无法使用map获得列表的总和,但您可以使用其一元版本mapM获得总和。 您需要做的就是在Sum幺半群(参见LYAHFGG )上使用Writer monad(参见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 function 接受一个 Int 并创建一个Adder ,该 Adder 将该参数保存为包装值和运行总和(实际上我们对值不感兴趣,而只对总和部分感兴趣)。 然后sum' mapM可以发挥它的魔力:虽然它对于嵌入在 monad 中的值类似于map ,但它执行像toAdder这样的“monadic”函数,并链接这些调用(它使用sequence来执行此操作)。 至此,我们通过 monad 的“后门”获得了标准map缺少的列表元素之间的交互。

Map 将列表中的每个元素“映射”到 output 中的元素:

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

这将返回一个正方形列表。

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

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

+ 是您要申请的 function,0 是初始值(0 表示总和,1 表示产品等)

我意识到这个问题已经得到解答,但我想添加这个想法......

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

我相信它为列表中的每个项目返回常量 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 )。

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 ,因为它实际上是为这项工作而设计的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM