list 使用 Haskell 的 map 函数计算列表的总和
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6175375/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Haskell's map function to calculate the sum of a list
提问by Sudantha
Haskell
哈斯克尔
addm::[Int]->Int
addm (x:xs) = sum(x:xs)
I was able to achieve to get a sum of a list using sum
function but is it possible to get the sum of a list using map
function? Also what the use of map function?
我能够使用sum
函数获取列表的总和,但是是否可以使用函数获取列表的总和map
?还有map函数有什么用?
回答by Waldheinz
You can't really use map
to sum up a list, because map treats each list element independently from the others. You can use map
for example to increment each value in a list like in
您不能真正使用map
总结列表,因为 map 将每个列表元素独立于其他元素。您可以使用map
例如增加列表中的每个值,如
map (+1) [1,2,3,4] -- gives [2,3,4,5]
Another way to implement your addm would be to use foldl:
实现 addm 的另一种方法是使用foldl:
addm' = foldl (+) 0
回答by Will Ness
Here it is, the supposedly impossible definition of sum
in terms of map
:
这是,对所谓不可能的定义sum
来讲map
:
sum' xs = let { ys = 0 : map (\(a,b) -> a + b) (zip xs ys) } in last ys
this actually shows how scanl
can be implemented in terms of map
(andzip
and last
), the above being equivalent to foldl (+) 0 xs === last $ scanl (+) 0 xs
:
这实际上示出了如何scanl
可以在以下方面实现的map
(和zip
和last
),上述等效于foldl (+) 0 xs === last $ scanl (+) 0 xs
:
scanl' f z xs = let { ys = z : map (uncurry f) (zip ys xs) } in ys
I expect one can calculate many things with map
, arranging for all kinds of information flow through zip
.
我期望一个人可以计算很多东西map
,安排各种信息流过zip
。
edit:the above is just a zipWith
in disguise of course (and zipWith
is kind of a map2
):
编辑:以上zipWith
当然只是 伪装(并且zipWith
是一种map2
):
sum' xs = let { ys = 0 : zipWith (+) ys xs } in last ys
This seems to suggest that scanl
is more versatile than foldl
.
这似乎表明它scanl
比foldl
.
回答by Don Stewart
It is not possible to use map
to reduce a list to its sum. That recursive pattern is a fold
.
不能使用map
将列表减少到它的总和。该递归模式是一个fold
.
sum :: [Int] -> Int
sum = foldr (+) 0
As an aside, note that you can define map
as a fold as well:
顺便说map
一句,请注意,您也可以定义为折叠:
map :: (a -> b) -> ([a] -> [b])
map f = fold (\x xs -> f x : xs) []
This is because foldr
is the canonical recursive function on lists.
这是因为foldr
是列表上的规范递归函数。
References: A tutorial on the universality and expressiveness of fold, Graham Hutton, J. Functional Programming 9 (4): 355–372, July 1999.
参考资料:关于 fold 的普遍性和表现力的教程,Graham Hutton, J. Functional Programming 9 (4): 355–372,1999 年 7 月。
回答by Landei
After some insights I have to add another answer: You can't get the sum of a list with map
, but you can get the sum with its monadic version mapM
. All you need to do is to use a Writer
monad (see LYAHFGG) over the Sum
monoid (see LYAHFGG).
在获得一些见解之后,我必须添加另一个答案:您无法使用 获得列表的总和map
,但您可以使用其 monadic 版本获得总和mapM
。所有你需要做的就是用一个Writer
单子(见LYAHFGG)在Sum
半群(见LYAHFGG)。
I wrote a specialized version, which is probably easier to understand:
我写了一个专门的版本,可能更容易理解:
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
is just a wrapper around some type which also keeps a "running sum." We can make Adder
a monad, and here it does some work: When the operation >>=
(a.k.a. "bind") is executed, it returns the new result and the value of the running sum of that result plus the original running sum. The toAdder
function takes an Int and creates an Adder
that holds that argument both as wrapped value and as running sum (actually we're not interested in the value, but only in the sum part). Then in sum'
mapM
can do its magic: While it works similar to map
for the values embedded in the monad, it executes "monadic" functions like toAdder
, and chainsthese calls (it uses sequence
to do this). At this point, we get through the "backdoor" of our monad the interaction between list elements that the standard map
is missing.
Adder
只是某种类型的包装器,它也保持“运行总和”。我们可以创建Adder
一个 monad,它在这里做了一些工作:当操作>>=
(又名“绑定”)执行时,它返回新结果和该结果的运行总和加上原始运行总和的值。该toAdder
函数接受一个 Int 并创建一个Adder
将该参数作为包装值和运行总和(实际上我们对值不感兴趣,而只对总和部分感兴趣)。然后 insum'
mapM
可以发挥它的魔力:虽然它的工作方式类似于map
嵌入在 monad 中的值,但它执行“monadic”函数,例如toAdder
,并将这些调用链接起来(它使用sequence
去做这个)。在这一点上,我们通过 monad 的“后门”了解了标准map
缺失的列表元素之间的交互。
回答by Frank Schmitt
Map "maps" each element of your list to an element in your output:
将列表中的每个元素“映射”到输出中的一个元素:
let f(x) = x*x
map f [1,2,3]
This will return a list of the squares.
这将返回一个方块列表。
To sum all elements in a list, use fold:
要对列表中的所有元素求和,请使用 fold:
foldl (+) 0 [1,2,3]
+ is the function you want to apply, and 0 is the initial value (0 for sum, 1 for product etc)
+ 是您要应用的函数,0 是初始值(总和为 0,乘积为 1 等)
回答by Landei
As the other answers point out, the "normal" way is to use one of the fold
functions. However it is possible to write something pretty similar to a while
loop in imperative languages:
正如其他答案所指出的,“正常”方法是使用其中一个fold
功能。但是,可以while
在命令式语言中编写与循环非常相似的东西:
sum' [] = 0
sum' xs = head $ until single loop xs where
single [_] = True
single _ = False
loop (x1 : x2 : xs) = (x1 + x2) : xs
It adds the first two elements of the list together until it ends up with a one-element list, and returns that value (using head
).
它将列表的前两个元素相加,直到得到一个单元素列表,并返回该值(使用head
)。
回答by user3481138
I realize this question has been answered, but I wanted to add this thought...
我意识到这个问题已经得到回答,但我想补充这个想法......
listLen2 :: [a] -> Int
listLen2 = sum . map (const 1)
I believe it returns the constant 1 for each item in the list, and returns the sum! Might not be the best coding practice, but it was an example my professor gave to us students that seems to relate to this question well.
我相信它会为列表中的每个项目返回常量 1,并返回总和!可能不是最好的编码实践,但这是我的教授给我们学生的一个例子,似乎与这个问题很好地相关。
回答by dfeuer
map
can never be the primary tool for summing the elements of a container, in much the same way that a screwdriver can never be the primary tool for watching a movie. But you can use a screwdriver to fix a movie projector. If you really want, you can write
map
永远不能成为总结容器元素的主要工具,就像螺丝刀永远不能成为看电影的主要工具一样。但是您可以使用螺丝刀来固定电影放映机。如果你真的想要,你可以写
import Data.Monoid
import Data.Foldable
mySum :: (Foldable f, Functor f, Num a)
=> f a -> a
mySum = getSum . fold . fmap Sum
Of course, this is silly. You can get a more general, and possibly more efficient, version:
当然,这是愚蠢的。您可以获得更通用且可能更高效的版本:
mySum' :: (Foldable f, Num a) => f a -> a
mySum' = getSum . foldMap Sum
Or better, just use sum
, because its actually made for the job.
或者更好,只需使用sum
,因为它实际上是为工作而设计的。