HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Check whether a string contains exactly one distinct vowel

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
distinctwhetheronecontainsvowelcheckexactlystring

Problem

Over on Stack Overflow, someone asked a simple Haskell question: how to test whether there is exactly one distinct vowel in a given string. To clarify, there can be any number of non-vowel characters, and the vowel may be repeated any number of times (at least once!), but no other vowels may occur.

I proposed the below solution, and am feeling fairly proud of it: it seems clean, short, general, and readable. I have a hard time imagining an improvement to it, but I know there is still plenty of room for me to get better at Haskell, so there is probably something cool I'm missing. Would someone please look over this, and tell me what could be better, or confirm that it truly has reached Nirvana?

exactlyOne :: Eq a => (a -> Bool) -> [a] -> Bool
exactlyOne pred [] = False
exactlyOne pred (x:xs)
  |    pred x = not . any pred . filter (/= x) $ xs
  | otherwise = exactlyOne pred xs

exactlyOneVowel :: String -> Bool
exactlyOneVowel = exactlyOne (`elem` "aeiouAEIOU")

Solution

exactlyOne pred l = case filter pred l of
  [] -> False
  (x:xs) -> all (==x) xs


or equivalently

exactlyOne pred = (== (1 :: Natural)) . genericLength . nub . filter pred


since nub is lazy and takes O(optimal) time for this problem: If its input list has exactly one distinct member, the list is traversed once; if it has at least two, the list is traversed up to the second member and then (== (1 :: Natural)) knows to shortcut to False.

(I also like the operator .: and keep using it everywhere:)

infixr 8 .:
(f .: g) x = f . g x
exactlyOne = (== (1 :: Natural)) . genericLength . nub .: filter

Code Snippets

exactlyOne pred l = case filter pred l of
  [] -> False
  (x:xs) -> all (==x) xs
exactlyOne pred = (== (1 :: Natural)) . genericLength . nub . filter pred
infixr 8 .:
(f .: g) x = f . g x
exactlyOne = (== (1 :: Natural)) . genericLength . nub .: filter

Context

StackExchange Code Review Q#123811, answer score: 4

Revisions (0)

No revisions yet.