Haskell: Change string into an all uppercase alpha string using list comprehension

haskell, list, list-comprehension

Solution

Your problem can be broken down into two subproblems:

- Select only alphabetic characters (characters between 'a' and 'z', or 'A' and 'Z')

- Convert the lowercase characters to uppercase.

The former can be done with a filter, or a (in a list comprehension) a condition on the element selected. In Unicode (and ASCII) lowercase characters come after uppercase characters, so we can trivially just check whether the character is less than 'a' to determine whether it is uppercase (once we know it's a letter), and all alphabetic characters are in English-alphabet order, so e.g. a lowercase letter is one that's between 'a' and 'z' (inclusive).

With Data.Char (chr, ord):

f xs = [ if x < 'a' then x else chr $ ord x + ord 'A' - ord 'a'
         | x <- xs, (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') ]

With only Prelude (but would be better written using Data.Map):

f xs = [ if x < 'a' then x else maybe x id $ lookup x charMap
         | x <- xs, (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') ]
  where charMap = zip ['a' .. 'z'] ['A' .. 'Z']

The right way, of course, is to use the standard library. This can be done quite trivially with some elementary functions:

-- with Data.Char (toUpper, isAlpha)
f xs = [ toUpper x | x <- xs, isAlpha x ]

This is vastly superior in many ways: it is probably faster, and it doesn't rely on ASCII input — it can handle any Unicode character (and in principle any localization: for example, Turkish ‘i’ is correctly capitalized as ‘İ’, not ‘I’ as it would be in ASCII or an English locale, as ‘I’ is the capital of ‘ı’, though I don't know if any Haskell implementations correctly implement this).

Note that list comprehensions are a subset of recursion: if you can manage to write a recursive function of the form:

f []       = []
f (x : xs) = if p x then g x : f xs else f xs 

it can be mechanically converted into a list comprehension of the form:

f xs = [ g x | x <- xs, p x ]

although you can also have multi-variable list expressions, which are a little more complicated to express recursively. Therefore, if you understand recursion, list comprehensions should really be trivial for you.

Problem

I find list comprehension to be nearly impossible compared to recursion. I'm trying to take a string such as "te1234ST" and return "TEST". Seems easy BUT there are restrictions. Not allowed to use any Haskell predefined functions such as isAlpha and it MUST be list comprehension. What I have so far, which is pretty terrible for how long I have spent on it: ``` convertAllToUpper :: String -> String convertAllToUpper xs = [n |n <- xs, check n == True] -- This may not even be allowed, and I know it's incorrect anyway check :: n -> Bool check (n:ns) | n `elem` ['a'..'z'] = True | n `elem` ['A'..'Z'] = True | otherwise = False ``` I'm just trying to get this to work and I haven't even started to worry about changing the lower case to upper case yet. Any points in the right direction would be very very appreciated. EDIT: Should mention for the conversion from lower to upper can't use: if, then, else. Simply list comprehension and list operators.

Original source