与递归相比,我发现列表理解几乎是不可能的。我正在尝试使用诸如“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
我只是想让它发挥作用,我甚至还没有开始担心将小写改为大写。
非常感谢正确方向的任何一点。
编辑:应该提到从低到高的转换不能使用:if,then,else。只需列出理解和列表运算符。
答案 0 :(得分:9)
您的问题可分为两个子问题:
前者可以通过过滤器或(在列表解析中)选择的元素上的条件来完成。在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') ]
仅使用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']
当然,正确的方法是使用标准库。这可以通过一些基本功能来完成:
-- with Data.Char (toUpper, isAlpha)
f xs = [ toUpper x | x <- xs, isAlpha x ]
这在很多方面都非常优越:它可能更快,并且它不依赖于ASCII输入 - 它可以处理任何Unicode字符(原则上任何本地化:例如,土耳其语'i'被正确地大写为'İ',而不是'我',因为它将是ASCII或英语语言环境,因为'我'是'ı'的首都,但我不知道是否有任何Haskell实现正确实现了这一点。
请注意,列表推导是递归的一个子集:如果您可以设法编写表单的递归函数:
f [] = []
f (x : xs) = if p x then g x : f xs else f xs
它可以机械地转换成表格的列表理解:
f xs = [ g x | x <- xs, p x ]
虽然你也可以有多变量列表表达式,递归表达要复杂一些。因此,如果您理解递归,列表推导对您来说应该是微不足道的。
答案 1 :(得分:1)
一直很难避免使用预定义的函数 - 我认为使用前奏中定义的列表函数是可以的吗?这是列表理解中的所有内容。
upcaseLetters :: String -> String
upcaseLetters cs =
[d | c <- cs
, c `elem` (['a'..'z'] ++ ['A'..'Z']) -- test if `c` is a latin letter.
, let d = if c `elem` ['A'..'Z'] -- test if `c` is uppercase
then c
-- Index into [A..Z] using the index of `c` in [a..z]
else ['A'..'Z'] !! head [i | (i, x) <- zip [0..] ['a'..'z']
, x == c]]
但是,您可能会觉得使用这些列表功能是作弊。 real programmers避免任何外部依赖。遵循这一理念,我们可以在列表理解中引导大部分前奏:
upcaseLetters :: String -> String
upcaseLetters cs =
[toUpper' c | c <- cs
, let foldr' _ z [] = z
foldr' f z (x:xs) = f x (foldr' f z xs)
True ||| _ = True
_ ||| True = True
_ ||| _ = False
elem' e xs = foldr' (|||) False [e==x | x <- xs]
head' (x:_) = x
zip' (a:as) (b:bs) = (a, b) : zip' as bs
zip' _ _ = []
isAlpha' x = x `elem'` (['a'..'z'] ++ ['A'..'Z'])
isUpper' x = x `elem'` ['A'..'Z']
toUpper' e
| isUpper' e = e
| otherwise = ['A'..'Z'] !! head' [i | (i, x) <- zip' [0..] ['a'..'z']
, x == e]
, isAlpha' c
]
这种方法将折叠的清晰度与列表理解的可读性结合起来。
不幸的是,由于语言设计的疏忽,Haskell无法在列表理解的主体内声明新的数据类型。这意味着我们无法清除我们对prelude的Char,String和Bool类型的依赖。
否则,[toUpper x | x <- xs , isAlpha x]
就是您通常想要的。
答案 2 :(得分:1)
看看这个
-- The char you want to take into account
valid_char = ['a'..'z'] ++ ['A'..'Z']
-- To filter the other char
valid xs = [ x | x<- xs, v <- valid_char, x==v]
-- Transform the list of valid char in a list of valid upper char
to_upper xs = [if (x==fst n) then snd n else x | x <- xs, n <- (zip ['a'..'z'] ['A'..'Z']), (x==fst n) || (x==snd n)]
-- composition of the two preceding function
convert = to_upper . valid
和测试
$ convert "test1234ST" => "TEST"
答案 3 :(得分:0)
convertAllToUpper :: String -> String
-- This is equivalent to `check n`
convertAllToUpper xs = [n |n <- xs, check n == True]
-- This is a type declaration. Types always begin with a uppercase letter.
-- Therefore "n" can't be valid in this position.
-- You want "check :: Char -> Bool"
check :: n -> Bool
-- This is a pattern match for a list, but you call it with single
-- characters. That can't work.
check (n:ns)
-- Again, this is the "if foo then true else false" pattern.
-- This is redundant.
| n `elem` ['a'..'z'] = True
| n `elem` ['A'..'Z'] = True
| otherwise = False
-- A shorter version of check
check' :: Char -> Bool
check' x = x `elem` ['a'..'z'] ++ ['A'..'Z']
-- when you have written the function `toUpper`
-- (or taken it from Data.Char`) you can simply apply it to
-- every element in the comprehension.
convertAllToUpper' xs = [toUpper n | n <- xs, check n]