Kevin
Kevin

Reputation: 391

Haskell - causing immediate termination of list comprehension

I am trying to write a function that efficiently tells me when items in a list are divisible or not by all items in a range. The problem is, I don't know to stop the list comprehension immediately when a false is encountered.

allDivisByRng n start stop =
  [n `mod` x == 0 | x <- [start, start+1 .. stop]]

I have tried takeWhile but can't get that to do what I want. Please advise.

allDivisByRng n start stop =
  takeWhile( == True )     [ n  `mod`  x == 0 | x <- [start, start+1.. stop] ]

When I run allDivisByRng 10 2 4 I get a list of trues

Some other sample runs

*Main> allDivisByRng 12 2 10
[True,True,True]
*Main> allDivisByRng 12 2 150
[True,True,True]
-- I want this to return false
*Main> allDivisByRng 2520 2 10
[True,True,True,True,True,True,True,True,True]
-- I want this to return True

I have also tried searching for False

allDivisByRng n start stop =
   takeWhile( == False )     [ n  `mod`  x == 0 | x <- [start, start+1.. stop] ]

*Main> allDivisByRng 10 2 3
[]
-- want false to be returned
*Main> allDivisByRng 10 2 10
[]
-- want false to be returned
*Main> allDivisByRng 2520 2 10
[]
-- want true to be returned
*Main> allDivisByRng 12 2 10
[]
-- want false to be returned 

Upvotes: 0

Views: 508

Answers (2)

MBlanc
MBlanc

Reputation: 1783

There is no need of "stopping a list comprehension" in Haskell. By default, evaluation is lazy. This means values aren't really computed until they are actually needed.

This allows us to work with infinite data structures:

take 10 [1..]
--> [1,2,3,4,5,6,7,8,9,10]

Or with structures that have invalid values in them:

take 1 ["It works!", error "Exception thrown"]
--> "It works!"

So we just need to process the list to a function that reduces the list of booleans to a single bool.

Hoogle is a nifty little tool to search for functions that match a given type signature. I'd recommend you get acquainted with it, because more often that not you'll find exactly what you're looking for.

In this case, searching for [Bool] -> Bool (a function that reduces a list of booleans to a single bool) gives us the following function defined in the Prelude (no imports needed):

and :: Foldable t => t Bool -> Bool

See if you can apply it to get what you want!

There happens to be another function that is a bit closed to what we want: test if all elements in a list satisfy a predicate. With this "all" fuction, we can rewrite your function in a very legible way (imo):

allDivisByRng n start stop =
    all (n `isDividedBy`) [start .. stop]
    where
        n `isDividedBy` x  =  n `mod` x == 0

Upvotes: 2

Alicia Garcia-Raboso
Alicia Garcia-Raboso

Reputation: 13913

The function all,

all :: Foldable t => (a -> Bool) -> t a -> Bool

allows you to check whether all elements of a foldable structure satisfy a certain condition. In your case,

allDivisByRng n start stop = all ((== 0) . (mod n)) [start..stop]

Upvotes: 5

Related Questions