Home > database >  How to perform a check on vowels?
How to perform a check on vowels?

Time:10-04

I am trying to write a function score :: Char -> Int that converts a character to its score. Each letter starts with a score of 1; 1 is added to the score of a character if it is a vowel (a, e, i, o, u) and 1 is added to the score of a character if it is upper case; a character that is not a letter scores 0. For example,

score 'A' == 3
score 'a' == 2
score 'B' == 2
score 'b' == 1
score '.' == 0

This is my code so far:

n :: Int -> Int
n = 0

isVowel :: Char -> Char
isVowel x = [if x == 'a' || x == 'e' || x == 'i' ||
                x == 'o' || x == 'u' then x else x]

score :: Char -> Int
score x  =
    [if isAlpha x then n 1 else n ]
    [if isUpper x then n 1 else n ]
    [if isVowel x then n 1 else n ]

something is definitely wrong with this code, but I cannot find what. Specifically, is there any way to write a function for detecting vowels? I'm not even sure that the one I wrote is correct. Or do I change the type of isVowel to Char -> Bool then if isVowel = True will that work? I will be thankful for any help provided

CodePudding user response:

You are writing expression in a (singleton) list. Furthermore your isVowel should return a Bool. We thus can implement this as:

isVowel :: Char -> Bool
isVowel x = x == 'a' || x == 'e' || x == 'i' || x == 'o' || x == 'u'

You however forgot the uppercase vowels (AEIOU) We can write this more compact with an elem check:

isVowel :: Char -> Bool
isVowel x = x `elem` "aeiouAEIOU"

or even with a section of an infix operator:

isVowel :: Char -> Bool
isVowel = (`elem` "aeiouAEIOU")

In your score function you seem to use a variable n but that variable is not defined somewhere, and furthermore in Haskell all variables are immutable: once assigned a value, you can no longer change its value.

We can calculate the score by adding three items together:

score :: Char -> Int
score x  = (if isAlpha x then 1 else 0)   (if isUpper x then 1 else 0)   (if isVowel x then 1 else 0)

Since Bool is a member of the Enum typeclass where False maps to 0 and True maps to 1, we can make use of fromEnum :: Enum a => a -> Int to automatically do the conversion:

score :: Char -> Int
score x  = fromEnum (isAlpha x)   fromEnum (isUpper x)   fromEnum (isVowel x)

or, as @leftroundabout says: we can also make use of list comprehension and work with a list of checks, so:

score :: Char -> Int
score x  = length [ 1 | p <- [isAlpha, isUpper, isVowel], p x ]

CodePudding user response:

You write:

n :: Int -> Int
n = 0

n is defined to be 0. It will always be 0. We can't change that, we've defined it that way.

It's also not a function, it's just Int.

n :: Int
n = 0

isVowel :: Char -> Char
isVowel x = [if x == 'a' || x == 'e' || x == 'i' ||
                x == 'o' || x == 'u' then x else x]

Some parts are good here obviously, but first, why the brackets? We don't need them here at all. Second, the return type should be Bool:

isVowel :: Char -> Bool
isVowel x =  if x == 'a' || x == 'e' || x == 'i' ||
                x == 'o' || x == 'u' then True else False

but then the if just returns its test as is, so there's no need for it either:

isVowel :: Char -> Bool
isVowel x =     x == 'a' || x == 'e' || x == 'i' ||
                x == 'o' || x == 'u' 

Now, finally, the score:

score :: Char -> Int
score x  =
    [if isAlpha x then n 1 else n ]
    [if isUpper x then n 1 else n ]
    [if isVowel x then n 1 else n ]

Here n 1 == 0 1 == 1, so no need for that n. And in fact,

score :: Char -> Int
score x  =  sum (
    [1 | isAlpha x ]    
    [1 | isUpper x ]    
    [1 | isVowel x ]  )

is all we need.

CodePudding user response:

When you wrote

score x  =
    [if isAlpha x then n 1 else n]
    [if isUpper x then n 1 else n]
    [if isVowel x then n 1 else n]

you probably had something in mind like in Python

def score(x):
    n = 0
    if isAlpha(x): n =1
    if isUpper(x): n =1
    if isVowel(x): n =1
    return n

As the other answers noted, this can – and should – just be expressed with sum or length, but in more general case of something similar (sequential similar updates to a variable) you would use a fold:

import Data.List

score x = foldl' (\n p -> if p x then n 1 else n) 0
           [isAlpha, isUpper, isVowel]

or even

score x = foldl' (flip id) 0
   [ \n -> if isAlpha x then n 1 else n
   , \n -> if isUpper x then n 1 else n
   , \n -> if isVowel x then n 1 else n
   ]

It is even possible (though not recommended in this case) to make it look completely imperative, by working in the state monad:

import Control.Monad
import Control.Monad.Trans.State

score = (`execState`0) $ do
   when (isAlpha x) $ modify ( 1)
   when (isUpper x) $ modify ( 1)
   when (isVowel x) $ modify ( 1)
  • Related