2011-05-30 41 views
6

的总和哈斯克尔使用Haskell的映射函数来计算列表

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

我是能够实现使用sum函数来获取列表的总和,但有可能得到一个列表的总和使用map函数?还有什么使用地图功能?

+1

您可以将'addm'简化为'addm = sum'。 – Waldheinz 2011-05-30 10:59:06

+1

注意:除非你像Waldheinz建议的那样做,否则你的addm函数对于空列表是未定义的。 – 2011-05-30 17:16:59

回答

18

您不能真正使用map来总结一个列表,因为map将每个列表元素与其他列表元素分开处理。您可以在

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

使用map例如递增每个值列表中的像另一种方式来实现你的ADDM是使用foldl

addm' = foldl (+) 0 
2

地图“地图”列表中的每个元素到您输出中的元素:

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

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

综上所述列表中的所有元素,使用倍:

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

+是要应用的功能,和0是初始值(0代表总和,1代表产品等)

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,格雷厄姆赫顿,J.函数编程9(4):355-372,1999年7月

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)。

4

经过一些见解后,我不得不添加另一个答案:你不能得到与map列表的总和,但你可以得到它的一元版本mapM的总和。您需要做的就是在Sum monoid上使用Writer 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,这里它做了一些工作:当执行>>=(a.k.a.“bind”)时,它返回新结果以及结果的运行总和的值加上原始运行总和toAdder函数接受一个I​​nt并创建一个Adder,它将该参数保存为包装值和运行总和(实际上,我们对该值不感兴趣,但仅在总和部分中)。然后在sum'mapM可以做它的魔力:虽然它的工作原理类似于map嵌入在单子的值,它执行像toAdder“一元”的功能,并这些调用(它使用sequence做到这一点)。此时,我们通过我们monad的“后门”获得标准map缺失的列表元素之间的交互。

+0

['mapM'](http://hackage.haskell.org/package/base-4.8.1.0/docs/src/GHC.Base.html#mapM)虽然只是一个伪装。 – 2015-08-27 12:20:53

6

这就是,summap方面理应不可能定义:

summ xs = let ys = 0 : map (\(a,b)->a+b) (zip xs ys) in last ys 

这实际上显示了如何scanl可以在map方面来实现,在上述等效于foldl (+) 0 xs === last $ scanl (+) 0 xs

scannl f z xs = let ys = z : map (uncurry f) (zip ys xs) in ys 

我希望能用map计算很多东西,安排各种信息流。

编辑:以上只是在过程中的变相zipWith(和zipWith是怎样的一个map2的):

summ xs = let ys = 0 : zipWith (+) ys xs in last ys 

这似乎表明,scanlfoldl更基本,从这个看到至少是角度。

+2

好的,这是可能的,但我不认为这是惯用的Haskell。但是,从技术上说,你是对的。 :-) – Waldheinz 2012-08-14 12:15:42

+2

我认为你在这里作弊,威尔。基本上,我读到的问题是询问您是否可以使用'Functor []'实例计算列表的长度;你的答案是你可以使用'Applicative []'实例(基本上'ZipList')。通过阅读“Functor”,我意识到我正在“丰富”这个问题(可以这么说),但通过这个论证,你比我更丰富它,因为'Applicative'比'Functor'更强大。 – 2012-08-28 17:38:15

+0

@LuisCasillas这个答案的重点是列表不是我猜的;它们的结构(项目的位置;第一个/最后一个元素)完全可见; 'map'不只是'fmap'。 IOW'实例Functor []'将列表作为集合进行会谈,因为只有'fmap'可用,没有别的。有套只有会员资格,没有职位,所以没有订单。我们可以很好地定义“应用”列表,也就是笛卡尔产品唯一可能的实现 - 正是因为没有位置的概念。但有了它,内积成为另一种可能性(即'zip',即'ZipList')。 – 2015-08-26 22:46:05

0

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

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

我相信它返回列表中的每个项目常数1,并返回总和! 可能不是最好的编码练习,但这是我的教授给我们的学生提供的一个例子,它似乎很好地涉及了这个问题。

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,因为它实际上是工作做出。