Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: Want a better way of doing: value == x || value == y ||

Tags:

idioms

haskell

I'm new to Haskell, so am sorry if this is incredibly obvious...


I have made the following function (used here as an example to ask about multiple value==something || value==somethingElse checks) to check if a character is a number:


isDigit :: Char -> Bool
isDigit x = 
    if 
    x == '0' 
    || x == '1'  
    || x == '2'  
    || x == '3'  
    || x == '4'  
    || x == '5'  
    || x == '6'  
    || x == '7'  
    || x == '8'  
    || x == '9'  
    then True  
    else False


Surely though there must be a neat way to write functions like the one above, so you don't have to repeat the || x == quite so much?



Thank you in advance for your help :)

(If it's relevant: I'm using Hugs as the interpreter.)

like image 981
Jon Cox Avatar asked Nov 28 '10 00:11

Jon Cox


People also ask

What does \x mean in Haskell?

It is a lambda function, that is, a function that you define in the spot mostly for convenience. You read it as "take x as your input, multiply it by 6 and see if it is less than 100".

What is -> in Haskell?

(->) is often called the "function arrow" or "function type constructor", and while it does have some special syntax, there's not that much special about it. It's essentially an infix type operator. Give it two types, and it gives you the type of functions between those types.

What does A -> B mean in Haskell?

a -> b Bool means... forall (a :: *) (b :: * -> *). a -> b Bool. b is therefore a type constructor taking a single type argument. Examples of single-argument type constructors abound: Maybe , [] , IO are all examples of things which you could use to instantiate b .

What is pattern match in Haskell?

Pattern matching consists of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns. When defining functions, you can define separate function bodies for different patterns.


1 Answers

In this case you can use elem from the Prelude:

isDigit x = elem x "0123456789"

(Remember that strings are lists of Char)

Or you can use isDigit from Data.Char :-)

Yes, there is a neat way to write almost every repetitive pattern. Here's how to derive it for this one. Start with the list of chars (I'll just do 0-4 for brevity)

"01234"

Map the comparisons:

map (x ==) "01234"
  = [x == '0', x == '1', x == '2', x == '3', x == '4']
  = (x == '0') : (x == '1') : (x == '2') : (x == '3') : (x == '4') : []

Then use foldr. foldr f z is best described as a function that takes a list and replaces : with f and [] with z.

foldr (||) False (map (x ==) "01234")
  = x == '0' || x == '1' || x == '2' || x == '3' || x == '4' || False

And there you have it. foldr is kind of the granddaddy of list functions, so this is the "lowest level" way to do it without explicit recursion. Here are two more spellings for your vocabulary:

isDigit x = any (x ==) "0123456789"
isDigit x = or [ x == d | d <- "0123456789" ]

If I had to guess at the most common "idiomatic" spelling, it would probably be this variant of the first one:

isDigit = (`elem` "0123456789")

Once you get familiar with all the handy functions in the Prelude, writing code like this is a joyous breeze :-)

like image 70
luqui Avatar answered Nov 04 '22 14:11

luqui