哈斯克尔
addm::[Int]->Int
addm (x:xs) = sum(x:xs)
我能够使用函数获得列表的总和,sum
但是是否可以使用函数获得列表的总和map
?还有map函数有什么用?
你不能真正map
用来总结一个列表,因为 map 将每个列表元素独立于其他元素。例如,您可以使用map
来增加列表中的每个值,例如
map (+1) [1,2,3,4] -- gives [2,3,4,5]
实现 addm 的另一种方法是使用foldl:
addm' = foldl (+) 0
在这里,根据 的假设是不可能的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
这似乎表明它scanl
比foldl
.
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.
在一些见解之后,我必须添加另一个答案:你不能得到一个列表的总和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
缺少的列表元素之间的交互。
将列表中的每个元素“映射”到输出中的元素:
let f(x) = x*x
map f [1,2,3]
这将返回一个正方形列表。
要对列表中的所有元素求和,请使用 fold:
foldl (+) 0 [1,2,3]
+ 是您要应用的函数,0 是初始值(0 表示总和,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
)。
我意识到这个问题已经得到解答,但我想添加这个想法......
listLen2 :: [a] -> Int
listLen2 = sum . map (const 1)
我相信它会为列表中的每个项目返回常量 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
,因为它实际上是为工作而设计的。