user1816631
user1816631

Reputation:

First three items of a list in Haskell

I am very new to Haskell, and struggling a bit with a function here. The premise is simple enough: Run through a list, and combine each 3 items next to each other with another function and return a list with the results. The problem is to do it in a nice way.

Here is what I've got:

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]

What I don't like, is the 5th line, containing all the !!'s. It feels like I'm thinking about it the wrong way, and that there should be a better way of doing this. I'd like to do something like

foo (x:y:z:xs)
    -- ...

But that will fail when the list gets less than three items. So, then I'd have to declare other patterns for when the list has fewer items?

Also, in case there is already a function that does what foo does (there probably is, it seems there is one for everything), then I'm not really all that interested in it. I'm trying to grok the Haskell way of doing things, more than expanding my repetoire of functions.

Edit: In JS, I'd do something like n = calc.apply(null, take(3, xs)). I wonder if Haskell has something like apply that takes an array and applies it to a function as parameters.

Edit 2 -- Solution: (based on comment below)

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

Last pattern match is a catch-all, so if the first "fails" it will fall through and just return an empty list.

Upvotes: 1

Views: 3667

Answers (1)

leftaroundabout
leftaroundabout

Reputation: 120751

Well, foo (x:y:z:xs) plus a “too short clause” certainly wouldn't be a bad solution. Another would be

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

Or, perhaps nicest,

import Data.List (tails)

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

Upvotes: 2

Related Questions