我有这个提示:创建一个带字符串的函数,看看是否所有其他字母都是['a'...'f']的元素
所以我有这个:
betweenAF :: String -> Bool
betweenAF (x:y:xs)
| x `elem` ['a' .. 'f'] = True
| otherwise = betweenAF xs
betweenAF _ = False
现在它可以在字符串的第一个字母上工作,就像我把它放在AF“all”之间,但是如果我放在AF“lla”之间它就不起作用。有人能告诉我我做错了什么以及如何解决它?
答案 0 :(得分:2)
该功能有效。就是这样做:它会看到 是否存在'a'..'f'
中的任何偶数索引元素。而您显然希望它能够看到所有元素是否在范围内。
应用简单的逻辑反转来解决这个问题。
答案 1 :(得分:2)
你的情况有所逆转。如果x
在范围内,那么您只能完成部分工作并且必须递归。如果范围内的x
不,您可以立即返回False,因为其他字母是否在范围内无关紧要。对于空列表,该属性是真空的,因为没有不在期望范围内的字母。
betweenAF :: String -> Bool
betweenAF [x] = betweenAF [x,x] -- hack to handle odd-length lists
betweenAF (x:y:ys) | x `elem` ['a'..'f'] = betweenAF ys
| otherwise = False
betweenAF [] = True
更好的版本,感谢Will Ness:
betweenAF (x:xs) = x `elem` ['a'..'f'] && betweenAF (drop 1 xs)
betweenAF [] = True
因为drop 1 [x]
和drop 1 []
都评估为[]
。
答案 2 :(得分:1)
仅仅因为我花了几分钟时间,我想指出另一种可能的解决方案。您可以拉出所有其他字符,而不是对整个字符串进行模式匹配,并使用all
根据谓词检查所有字符。这消除了进行任何显式递归或模式匹配的需要。
betweenAF :: String -> Bool
betweenAF = all (flip elem ['a'..'z']) . everyOther where
everyOther xs = [v | (k,v) <- zip (cycle [True, False]) xs, k]
everyOther
相当于map snd . filter (fst) . zip (cycle [True, False])
,flip elem ['a'..'z']
相当于\x -> elem x ['a'..'z']
。总而言之,这可以重写为:
betweenAF = all (flip elem ['a'..'z']) . map snd . filter fst . zip (cycle [True, False])
然而,这比罪恶更加丑陋!
答案 3 :(得分:1)
对于某些练习,我猜你也可以这样做;
everyOtherInAF :: String -> Bool
everyOtherInAF = all id . zipWith ($) (cycle [(`elem` ['A'..'F']), const True])
*Main> everyOtherInAF "AsErCgHm"
False
*Main> everyOtherInAF "AsErCgFm"
True
*Main> everyOtherInAF "AsErjgFm"
False
*Main> everyOtherInAF "AsErAgFm"
True