chris Frisina
chris Frisina

Reputation: 19688

How to break|break|return in a Haskell list comprehension

I want to return all the cubes (x^3) that are less than a parameter Int using a list comprehension. I have the following:

cubesLessThanN :: Int -> [Int]  
cubesLessThanN int = [if x * x * x <= int then x else * | x <- [0..int]]

The asterisk is where I am having the problem. I kinda want to stop processing the loop once the else happens. The final [list] should only have the cubes, not the other values of x. I don't care really how it happens, but would like to know the options, and what the differences are (if any).

If I try to return null, Nothing, '', and a few other ones. I know I am supposed to return a type of int if I return anything at all.

Upvotes: 6

Views: 3137

Answers (3)

rampion
rampion

Reputation: 89093

Take a cube root

[ x ^ 3 | x <- [ 1 .. ceiling (fromIntegral int ** (1/3)) ], x ^ 3 < int ]

For example:

λ let int = 1000 in [ x ^ 3 | x <- [ 1 .. ceiling (fromIntegral int ** (1/3)) ], x ^ 3 < int ] :: [Int]
[1,8,27,64,125,216,343,512,729]

Upvotes: 1

Ry-
Ry-

Reputation: 225095

Use takeWhile:

cubesLessThanN :: Int -> [Int]
cubesLessThanN int = takeWhile ((<= int) . (^3)) [0..]

Upvotes: 10

Jon Purdy
Jon Purdy

Reputation: 55059

List comprehensions support guards.

[x | x <- [0..int], x ^ 3 <= int]

Since list comprehensions are sugar for the list monad, this is equivalent to using the guard function in a do block:

do
  x <- [0..int]
  guard (x ^ 3 <= int)
  return x

If we desugar this into >>= and inline the definitions of >>= and guard:

concatMap (\x -> if x ^ 3 <= int then [x] else []) [0..int]

This is akin to a filter.

filter (\x -> x ^ 3 <= int) [0..int]

Checking the condition will continue (lazily) even after the value of x ^ 3 exceeds the value of int. To prevent that, you can use takeWhile because you know that your function is monotonic.

takeWhile (\x -> x ^ 3 <= int) [0..int]

Upvotes: 9

Related Questions