Using or in list comprehension

2019-02-23 18:01发布

问题:

I'm trying to take out every number between 1 to 10 that is divisible by either 5 or 3
Here is my code till now -

giveList = 
[ x
 | x <- [1..10] ,
 (x `mod` 5 == 0) or (x `mod` 3 == 0)
]

And then I load the function in ghci. But it's giving me a error -

> [1 of 1] Compiling Main             ( problem1.hs, interpreted )

problem1.hs:4:10:
    The function `x `mod` 5 == 0' is applied to two arguments,
    but its type `Bool' has none
    In the expression: (x `mod` 5 == 0) or (x `mod` 3 == 0)
    In a stmt of a list comprehension:
      (x `mod` 5 == 0) or (x `mod` 3 == 0)
    In the expression:
      [x | x <- [1 .. 10], (x `mod` 5 == 0) or (x `mod` 3 == 0)]
Failed, modules loaded: none.

My question is -

  • Can we use 'or' in list comprehensions?
  • If not, please tell me how it can be done in other ways

I'm new to functional programming, please help me.

回答1:

You can use or in list comprehensions, but

Prelude> :t or
or :: [Bool] -> Bool

that is not the one you need at that place, it's a function that checks whether any element in the list is True. There you need

(||) :: Bool -> Bool -> Bool

And if you want to use a function with a name consisting of letters infix, you need to enclose it in backticks, like you did with mod, but since or takes only one argument, you cannot apply that function infix.

A correct version of your list would be

giveList = [ x | x <- [1 .. 10], x `mod` 5 == 0 || x `mod` 3 == 0]


回答2:

or has a type signature of or :: [Bool] -> Bool. It takes a list of Boolean values, and returns True if any one of them is True, or False otherwise.

The function you're looking for is || :: Bool -> Bool -> Bool. It takes two Boolean values, and returns True if either of them is True, or False otherwise.

[x | x <- [1..10], x `mod` 5 == 0 || x `mod` 3 == 0]


回答3:

You can do list comprehension like that:

[x | x <- [1..10], ((x `mod` 3) == 0) || ((x `mod` 5) == 0)]

or function works on lists



回答4:

In fact, I might be not so bad to use or here:

[x | x <- [1..10], or $ map ((==0).(x `mod`)) [3,5]]

... or probably more readable ...

[x | x <- [1..10], any (==0) $ map (x `mod`) [3,5]]

The down side is that it might look a little bit scary, but on the up side you can easily test for more divisors that way.