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)