使用Haskell的map函数计算列表的总和

Sud*_*ha 8 recursion haskell list fold

哈斯克尔

addm::[Int]->Int
addm (x:xs) = sum(x:xs)
Run Code Online (Sandbox Code Playgroud)

我能够实现使用sum函数获得列表的总和但是可以使用函数获得列表的总和map吗?还有什么地图功能的使用?

Wal*_*inz 20

您无法真正使用map总结列表,因为map会独立于其他列表元素处理每个列表元素.map例如,您可以使用增量列表中的每个值

map (+1) [1,2,3,4] -- gives [2,3,4,5]
Run Code Online (Sandbox Code Playgroud)

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

addm' = foldl (+) 0
Run Code Online (Sandbox Code Playgroud)


Wil*_*ess 6

这是,对所谓不可能的定义sum来讲map:

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

这实际上说明了如何scanl实现map,以上等同于zip:

scanl' f z xs  =  let { ys = z : map (uncurry f) (zip ys xs) } in ys
Run Code Online (Sandbox Code Playgroud)

我希望可以计算很多东西last,安排各种信息流.

编辑:上面只是一个 foldl (+) 0 xs === last $ scanl (+) 0 xs伪装当然(并且map是一种zip):

sum' xs  =  let { ys = 0 : zipWith (+) ys xs } in last ys
Run Code Online (Sandbox Code Playgroud)

这似乎表明,至少从这个角度来看,这zipWith是更基本的zipWith.

  • 好吧,这是可能的,但我认为这不是惯用的Haskell.但+1因为技术上你是对的.:-) (2认同)
  • 威尔,我想你在这里作弊.基本上,我把问题看作是问你是否可以使用`Functor []`实例来计算列表的长度; 你的答案是你可以使用`Applicative []`实例(基本上是`ZipList`).通过阅读"Functor",我意识到我正在"丰富"这个问题(可以这么说),但是通过这个论证,你比我更丰富了它,因为`Applicative`比`Functor`更强大. (2认同)

Don*_*art 5

无法使用map将列表减少到其总和.那个递归模式是一个fold.

sum :: [Int] -> Int
sum = foldr (+) 0
Run Code Online (Sandbox Code Playgroud)

另外,请注意您也可以定义map为折叠:

map :: (a -> b) -> ([a] -> [b])
map f = fold (\x xs -> f x : xs) []
Run Code Online (Sandbox Code Playgroud)

这是因为foldr列表上的规范递归函数.


参考文献:关于折叠的普遍性和表现力的教程,Graham Hutton,J.Functional Programming 9(4):355 - 343,1999年7月.


Lan*_*dei 5

在获得一些见解之后,我必须添加另一个答案:您无法使用 获得列表的总和map,但您可以使用其 monadic 版本获得总和mapM。所有你需要做的就是用一个Writer单子(见LYAHFGG)在Sum半群(见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
Run Code Online (Sandbox Code Playgroud)

Adder只是某种类型的包装器,它也保持“运行总和”。我们可以创建Adder一个 monad,它在这里做了一些工作:当操作>>=(又名“绑定”)执行时,它返回新结果和该结果的运行总和加上原始运行总和的值。该toAdder函数接受一个 Int 并创建一个Adder将该参数作为包装值和运行总和(实际上我们对值不感兴趣,而只对总和部分感兴趣)。然后 insum' mapM可以发挥它的魔力:虽然它的工作方式类似于map嵌入在 monad 中的值,但它执行“monadic”函数,例如toAdder,并将这些调用链接起来(它使用sequence去做这个)。在这一点上,我们通过 monad 的“后门”了解了标准map缺失的列表元素之间的交互。