Haskell:使用列表推导将字符串更改为全大写字母字符串

joh*_*mos 3 haskell list-comprehension list

与递归相比,我发现列表理解几乎是不可能的.我正在尝试使用诸如"te1234ST"之类的字符串并返回"TEST".似乎很容易,但有限制.不允许使用任何Haskell预定义函数,如isAlpha,它必须是列表理解.

到目前为止,我花了多长时间这是非常可怕的:

    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
Run Code Online (Sandbox Code Playgroud)

我只是想让它工作,我甚至还没有开始担心将小写改为大写.

任何正确方向的点都将非常受欢迎.

编辑:应该提到从低到高的转换不能使用:if,then,else.只需列出理解和列表运算符.

Mat*_*zyk 9

您的问题可以分解为两个子问题:

  1. 仅选择字母字符('a'和'z'之间的字符,或'A'和'Z'之间的字符)
  2. 将小写字符转换为大写.

前者可以使用过滤器,或者(在列表解析中)对所选元素的条件.在Unicode(和ASCII)中,小写字符位于大写字符之后,因此我们可以简单地检查字符是否小于'a'以确定它是否为大写(一旦我们知道它是一个字母),并且所有字母字符都是英文的-alphabet order,所以例如小写字母是'a'和'z'(包括)之间的字母.

使用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') ]
Run Code Online (Sandbox Code Playgroud)

只使用Prelude(但使用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']
Run Code Online (Sandbox Code Playgroud)

当然,正确的方法是使用标准库.这可以通过一些基本功能来完成:

-- with Data.Char (toUpper, isAlpha)
f xs = [ toUpper x | x <- xs, isAlpha x ]
Run Code Online (Sandbox Code Playgroud)

这在很多方面都非常优越:它可能更快,并且它不依赖于ASCII输入 - 它可以处理任何Unicode字符(原则上任何本地化:例如,土耳其语'i'被正确地大写为'İ' ,而不是'我',因为它将在ASCII或英语语言环境中,因为'我'是'ı'的首都,但我不知道是否有任何Haskell实现正确实现了这一点).

请注意,列表推导是递归的子集:如果您可以设法编写表单的递归函数:

f []       = []
f (x : xs) = if p x then g x : f xs else f xs 
Run Code Online (Sandbox Code Playgroud)

它可以机械地转换成表格的列表理解:

f xs = [ g x | x <- xs, p x ]
Run Code Online (Sandbox Code Playgroud)

虽然你也可以有多变量列表表达式,递归表达要复杂一些.因此,如果您理解递归,列表推导对您来说应该是微不足道的.