8

哈斯克尔

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

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

4

8 回答 8

21

你不能真正map用来总结一个列表,因为 map 将每个列表元素独立于其他元素。例如,您可以使用map来增加列表中的每个值,例如

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

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

addm' = foldl (+) 0
于 2011-05-30T11:02:04.973 回答
6

在这里,根据 的假设是不可能的sum定义map

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

这实际上显示了如何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

我期望一个人可以计算很多东西map,安排各种信息流过zip

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

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

这似乎表明它scanlfoldl.

于 2012-08-14T11:46:16.353 回答
5

It is not possible to use map to reduce a list to its sum. That recursive pattern is a fold.

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

As an aside, note that you can define map as a fold as well:

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

This is because foldr is the canonical recursive function on lists.


References: A tutorial on the universality and expressiveness of fold, Graham Hutton, J. Functional Programming 9 (4): 355–372, July 1999.

于 2011-05-30T14:23:38.853 回答
5

在一些见解之后,我必须添加另一个答案:你不能得到一个列表的总和map,但你可以得到它的单子版本的总和mapM。您需要做的就是在幺半群(请参阅LYAHFGGWriter )上使用monad(请参阅LYAHFGG)。Sum

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

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函数接受一个 Int 并创建一个Adder将该参数保存为包装值和运行总和(实际上我们对值不感兴趣,而只对总和部分感兴趣)。然后 insum' mapM可以发挥它的魔力:虽然它的工作方式类似于map嵌入 monad 中的值,但它执行“monadic”函数,如toAdder,并链接这些调用(它使用sequence去做这个)。至此,我们通过 monad 的“后门”获得了标准map缺少的列表元素之间的交互。

于 2012-07-13T21:25:21.947 回答
2

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

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

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

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

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

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

于 2011-05-30T11:04:16.153 回答
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)。

于 2011-05-30T20:29:13.713 回答
1

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

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

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

于 2014-05-02T12:26:05.840 回答
1

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

于 2016-10-08T04:38:12.550 回答