7

So suppose we want to produce the list [0, 1, -1, 2, -2, ...in Haskell.

What is the most elegant way of accomplishing this?

I came up with this solution:

solution = [0] ++ foldr (\(a,b) c->a:b:c) [] zip [1..] $ map negate [1..]

But I am sure there must be a better way.

Jsevillamol
  • 2,425
  • 2
  • 23
  • 46

8 Answers8

20

This seems like the kind of thing that comprehensions are made for:

solution = 0 : [y | x <- [1..], y <- [x, -x]]
Chris Martin
  • 30,334
  • 10
  • 78
  • 137
12

With iterate

Perhaps a more elegant way to do this, is by using iterate :: (a -> a) -> a -> [a] with a function that generates each time the next item. For instance:

solution = iterate nxt 0
    where nxt i | i > 0 = -i
                | otherwise = 1-i

Or we can inline this with an if-then-else:

solution = iterate (\i -> if i > 0 then -i else 1-i) 0

Or we can convert the boolean to an integer, like @melpomene says, with fromEnum, and then use this to add 1 or 0 to the answer, so:

solution = iterate (\i -> fromEnum (i < 1)-i) 0

Which is more pointfree:

import Control.Monad(ap)

solution = iterate (ap subtract (fromEnum . (< 1))) 0

With (<**>)

We can also use the <**> operator from applicate to produce each time the positive and negative variant of a number, like:

import Control.Applicative((<**>))

solution = 0 : ([1..] <**> [id, negate])
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
4

How about

concat (zipWith (\x y -> [x, y]) [0, -1 ..] [1 ..])

or

concat (transpose [[0, -1 ..], [1 ..]])

?

melpomene
  • 84,125
  • 8
  • 85
  • 148
4

How about:

tail $ [0..] >>= \x -> [x, -x]

On a moment's reflection, using nub instead of tail would be more elegant in my opinion.

aplainzetakind
  • 490
  • 2
  • 8
  • 5
    `nub` is going to give you quadratic runtime and leak memory. It has to hold onto all of the earlier elements to check whether they have already occurred. – Chris Martin Jan 06 '18 at 17:55
  • Right. I confused what `nub` does; I meant to remove only consecutive duplicates. And the reason I'd favor that was a semantic one. – aplainzetakind Jan 06 '18 at 20:18
4

another primitive solution

alt = 0 : go 1
  where go n = n : -n : go (n+1)
karakfa
  • 66,216
  • 7
  • 41
  • 56
2

You could also use concatMap instead of foldr here, and replace map negate [1..] with [0, -1..]:

solution = concatMap (\(a, b) -> [a, b]) $ zip [0, -1..] [1..]

If you want to use negate instead, then this is another option:

solution = concatMap (\(a, b) -> [a, b]) $ (zip . map negate) [0, 1..] [1..]
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
1

Just because no one said it:

0 : concatMap (\x -> [x,-x]) [1..]
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
0

Late to the party but this will do it as well

solution = [ (1 - 2 * (n `mod` 2)) * (n `div` 2) | n <- [1 .. ] ]
Elmex80s
  • 3,428
  • 1
  • 15
  • 23