如何在列表上进行映射时考虑以前的元素?

时间:2014-10-14 09:23:21

标签: haskell

我坚持在Haskell中创建一个函数,必须执行以下操作:

对于列表中的每个整数,检查它前面的整数是多少。

smallerOnes [1,2,3,5] will have the result [(1,0), (2,1), (3,2), (5,3)]

目前我有:

smallerOnes :: [Int] -> [(Int,Int)]
smallerOnes [] = []
smallerOnes (x:xs) =  

我对如何解决这个问题没有任何线索。递归可能是在这里思考的方式,但在那时我失去了它。

2 个答案:

答案 0 :(得分:4)

这里不是从基础案例开始,而是从主案例开始,这是有益的。

想象一下,我们已经处理了一半的清单。现在我们面对列表的其余部分,比如x:xs。我们想知道&#34;之前有多少个整数&#34; 小于x;所以我们需要知道这些元素,比如yslength [y | y<-ys, y<x]就是答案。

因此,您需要使用一个内部函数来维护前缀ys,为每个x生成结果并将其返回到列表中:

smallerOnes :: [Int] -> [(Int,Int)]
smallerOnes [] = []
smallerOnes xs = go [] xs
  where
      go ys (x:xs) = <result for this x> : <recursive call with updated args>
      go ys []     = []

这也可以使用一些内置的高阶函数进行编码,例如

scanl :: (a -> b -> a) -> a -> [b] -> [a]

需要进行一些后期处理(如map snd或其他)或更多

mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])

mapAccumL is in Data.List

答案 1 :(得分:3)

import Data.List (inits)
smallerOnes :: [Int] -> [(Int,Int)]
smallerOnes xs = zipWith (\x ys -> (x, length $ filter (< x) ys)) xs (inits xs)