Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 598
chris Frisina Avatar asked Sep 11 '14 22:09

chris Frisina


2 Answers

Use takeWhile:

cubesLessThanN :: Int -> [Int]
cubesLessThanN int = takeWhile ((<= int) . (^3)) [0..]
like image 178
Ry- Avatar answered Nov 15 '22 11:11

Ry-


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]
like image 22
Jon Purdy Avatar answered Nov 15 '22 10:11

Jon Purdy