的总和哈斯克尔使用Haskell的映射函数来计算列表
addm::[Int]->Int
addm (x:xs) = sum(x:xs)
我是能够实现使用sum
函数来获取列表的总和,但有可能得到一个列表的总和使用map
函数?还有什么使用地图功能?
的总和哈斯克尔使用Haskell的映射函数来计算列表
addm::[Int]->Int
addm (x:xs) = sum(x:xs)
我是能够实现使用sum
函数来获取列表的总和,但有可能得到一个列表的总和使用map
函数?还有什么使用地图功能?
您不能真正使用map
来总结一个列表,因为map将每个列表元素与其他列表元素分开处理。您可以在
map (+1) [1,2,3,4] -- gives [2,3,4,5]
使用map
例如递增每个值列表中的像另一种方式来实现你的ADDM是使用foldl:
addm' = foldl (+) 0
地图“地图”列表中的每个元素到您输出中的元素:
let f(x) = x*x
map f [1,2,3]
这将返回一个正方形列表。
综上所述列表中的所有元素,使用倍:
foldl (+) 0 [1,2,3]
+是要应用的功能,和0是初始值(0代表总和,1代表产品等)
无法使用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月
作为其他的答案指出的, “正常” 的方式是使用fold
函数之一。然而,它是可以写非常类似while
循环中的命令式语言的东西:
sum' [] = 0
sum' xs = head $ until single loop xs where
single [_] = True
single _ = False
loop (x1 : x2 : xs) = (x1 + x2) : xs
它添加列表在一起的前两个元素,直到它有一个元素的列表结束,并返回该值(使用head
)。
经过一些见解后,我不得不添加另一个答案:你不能得到与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
函数接受一个Int并创建一个Adder
,它将该参数保存为包装值和运行总和(实际上,我们对该值不感兴趣,但仅在总和部分中)。然后在sum'
mapM
可以做它的魔力:虽然它的工作原理类似于map
嵌入在单子的值,它执行像toAdder
“一元”的功能,并链这些调用(它使用sequence
做到这一点)。此时,我们通过我们monad的“后门”获得标准map
缺失的列表元素之间的交互。
['mapM'](http://hackage.haskell.org/package/base-4.8.1.0/docs/src/GHC.Base.html#mapM)虽然只是一个伪装。 – 2015-08-27 12:20:53
这就是,sum
在map
方面理应不可能定义:
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
这似乎表明,scanl
比foldl
更基本,从这个看到至少是角度。
好的,这是可能的,但我不认为这是惯用的Haskell。但是,从技术上说,你是对的。 :-) – Waldheinz 2012-08-14 12:15:42
我认为你在这里作弊,威尔。基本上,我读到的问题是询问您是否可以使用'Functor []'实例计算列表的长度;你的答案是你可以使用'Applicative []'实例(基本上'ZipList')。通过阅读“Functor”,我意识到我正在“丰富”这个问题(可以这么说),但通过这个论证,你比我更丰富它,因为'Applicative'比'Functor'更强大。 – 2012-08-28 17:38:15
@LuisCasillas这个答案的重点是列表不是我猜的;它们的结构(项目的位置;第一个/最后一个元素)完全可见; 'map'不只是'fmap'。 IOW'实例Functor []'将列表作为集合进行会谈,因为只有'fmap'可用,没有别的。有套只有会员资格,没有职位,所以没有订单。我们可以很好地定义“应用”列表,也就是笛卡尔产品唯一可能的实现 - 正是因为没有位置的概念。但有了它,内积成为另一种可能性(即'zip',即'ZipList')。 – 2015-08-26 22:46:05
我意识到这个问题已经回答了,但我想添加这个想法...
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
,因为它实际上是工作做出。
您可以将'addm'简化为'addm = sum'。 – Waldheinz 2011-05-30 10:59:06
注意:除非你像Waldheinz建议的那样做,否则你的addm函数对于空列表是未定义的。 – 2011-05-30 17:16:59