[英]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
这实际上显示了如何根据scanl
(和zip
和last
)来实现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
这似乎表明scanl
比foldl
更通用。
无法使用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.