Haskell中列表的前三项

时间:2015-06-14 21:10:26

标签: haskell

我对Haskell很新,并且在这里使用函数进行了一些努力。前提很简单:运行一个列表,并将每个彼此相邻的3个项目与另一个函数相结合,并返回一个包含结果的列表。问题是以一种很好的方式做到这一点。

以下是我所得到的:

foo :: [Int] -> [Int]
foo xs
    | length xs < 3 = []
    | otherwise     = n : foo (tail xs)
    where n = calc (xs!!0) (xs!!1) (xs!!2)

-- This function is actually significantly more complicated.
calc :: Int -> Int -> Int -> Int
calc x y z = x + y - (z * 2)

-- And we can use it like this:
foo [1,2,3]     -- [-3]
foo [1,2,3,4]   -- [-3,-3]
foo [1,1,5,3,3] -- [-8,0,2]

我不喜欢的是第5行,其中包含所有!!个。感觉就像我错误地思考它,并且应该有更好的方法来做到这一点。我想做像

这样的事情
foo (x:y:z:xs)
    -- ...

但是当列表少于三个项目时,这将失败。那么,当列表中的项目较少时,我必须声明其他模式吗?

此外,如果已经有一个函数执行了foo所做的事情(可能有,似乎所有东西都有),那么我并不是真的对此感兴趣。我试图了解Haskell的做事方式,而不仅仅是扩展我的功能重复。

编辑:在JS中,我会做n = calc.apply(null, take(3, xs))之类的事情。我想知道Haskell是否有类似apply的东西,它接受一个数组并将它作为参数应用于函数。

编辑2 - 解决方案:(根据以下评论)

foo (x:y:z:xs) = calc x y z : foo (y:z:xs)
foo _          = []

最后的模式匹配是一个全能的,所以如果第一个&#34;失败&#34;它将落空并返回一个空列表。

1 个答案:

答案 0 :(得分:3)

好吧,foo (x:y:z:xs)加上“太短的条款”肯定不是一个糟糕的解决方案。另一个是

foo xs = case splitAt 3 xs of
           ([x,y,z],xs') -> calc x y z : foo (y:z:xs')
           _ -> []

或者,也许是最好的,

import Data.List (tails)

foo xs = [ calc x y z | (x:y:z:_) <- tails xs ]