的Haskell
addm::[Int]->Int
addm (x:xs) = sum(x:xs)
我能够使用sum
函数获得一个列表的总和,但是可以使用map
函数获取列表的总和吗?还有什么地图功能的使用?
答案 0 :(得分:20)
您无法真正使用map
来总结列表,因为map会独立地处理每个列表元素。例如,您可以使用map
递增列表中的每个值,如
map (+1) [1,2,3,4] -- gives [2,3,4,5]
实现addm的另一种方法是使用foldl:
addm' = foldl (+) 0
答案 1 :(得分: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
):
scanl
这似乎表明foldl
比{{1}}更具通用性。
答案 2 :(得分:5)
无法使用map
将列表缩减为其总和。递归模式是fold
。
sum :: [Int] -> Int
sum = foldr (+) 0
另外,请注意您也可以将map
定义为折叠:
map :: (a -> b) -> ([a] -> [b])
map f = fold (\x xs -> f x : xs) []
这是因为foldr
是列表上的规范递归函数。
参考文献:A tutorial on the universality and expressiveness of fold,Graham Hutton,J。Functional Programming 9(4):355 - 343,1999年7月。
答案 3 :(得分:4)
在获得一些见解之后,我必须添加另一个答案:您无法获得带有map
的列表总和,但您可以使用其monadic版本mapM
获得总和。您需要做的就是在Writer
幺半群上使用Sum
monad(请参阅LYAHFGG)(参见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
函数接受一个Int并创建一个Adder
,它将该参数保存为包装值和运行总和(实际上我们对该值不感兴趣,但只在sum部分中感兴趣)。然后在sum'
mapM
中可以发挥其魔力:虽然它与monad中嵌入的值类似于map
,但它执行“monadic”函数,如toAdder
和< em> chains 这些调用(它使用sequence
来执行此操作)。在这一点上,我们通过我们monad的“后门”了解了标准map
缺少的列表元素之间的交互。
答案 4 :(得分:2)
将列表的每个元素“映射”到输出中的元素:
let f(x) = x*x
map f [1,2,3]
这将返回正方形列表。
要对列表中的所有元素求和,请使用fold:
foldl (+) 0 [1,2,3]
+是您要应用的函数,0是初始值(0表示总和,1表示产品等)
答案 5 :(得分: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
)。
答案 6 :(得分:1)
我意识到这个问题已得到解答,但我想补充一下这个想法...
listLen2 :: [a] -> Int
listLen2 = sum . map (const 1)
我相信它会为列表中的每个项目返回常量1,并返回总和! 可能不是最好的编码实践,但这是我的教授给我们学生的一个例子,这似乎与这个问题有关。
答案 7 :(得分:0)
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
,因为它实际上是为了工作而做的。