我有一个字符串列表,如下所示:
xs = ["xabbaua", "bbbaacv", "ggfeehhaa", "uyyttaccaa", "ibbatb"]
我想在列表中找到只有字符串的字符串,后面跟着两个b,后跟任意字符后面跟一个元音。如何在Haskell中完成这样的简单匹配。是否有更好的正则表达式解决方案?任何人都可以帮我一个例子吗?感谢。
答案 0 :(得分:3)
一种方法是构建一个小型模式匹配语言并将其嵌入Haskell。
在您的示例中,模式基本上是字符规范列表。让我们定义一种抽象字符,其值将作为此类规范,
data AbsChar = Exactly Char | Vowel | Any
与“解释器”一起告诉我们角色是否符合规范:
(=?) :: AbsChar -> Char -> Bool
Exactly c' =? c = c == c'
Vowel =? c = c `elem` "aeiou"
Any =? c = True
例如,Vowel =? 'x'
将生成False
,而Vowel =? 'a'
将生成True
。
然后,确实,模式只是一个抽象字符列表:
type Pattern = [AbsChar]
接下来,我们编写一个函数来测试字符串的前缀是否与给定模式匹配:
matchesPrefix :: Pattern -> String -> Bool
matchesPrefix [] _ = True
matchesPrefix (a : as) (c : cs) = a =? c && matchesPrefix as cs
matchesPrefix _ _ = False
例如:
> matchesPrefix [Vowel, Exactly 'v'] "eva"
True
> matchesPrefix [Vowel, Exactly 'v'] "era"
False
由于我们不想将自己限制为匹配前缀,而是匹配单词中的任何位置,我们的下一个函数会匹配字符串的每个结束段的前缀:
containsMatch :: Pattern -> String -> Bool
containsMatch pat = any (matchesPrefix pat) . tails
它使用tails
函数,该函数可以在模块Data.List
中找到,但我们可以使这个解释自成一体,也很容易定义自己:
tails :: [a] -> [[a]]
tails [] = [[]]
tails l@(_ : xs) = l : tails xs
例如:
> tails "xabbaua"
["xabbaua","abbaua","bbaua","baua","aua","ua","a",""]
现在,最后,您正在寻找的函数,从列表中选择包含匹配段的所有字符串,简单地写为:
select :: Pattern -> [String] -> [String]
select = filter . containsMatch
让我们在你的例子上测试它:
> let pat = [Vowel, Exactly 'b', Exactly 'b', Any, Vowel]
> select pat ["xabbaua", "bbbaacv", "ggfeehhaa", "uyyttaccaa", "ibbatb"]
["xabbaua"]
答案 1 :(得分:3)
您可以将经典过滤器功能与任何regexp库结合使用。你的模式很简单,这适用于任何regexp库:
filter (=~ "bb.[aeiuy]") xs
Haskell中regexps的一个令人困惑的部分是,有一个非常强大的通用API(在regex-base中)以相同的方式为所有特定的库和你想要的多种结果类型使用它们(Bool,String ,国际...)。对于基本用法,它应该按照你的意思(tm)工作。根据您的具体需要,regex-posix应该足够了(并且带有haskell平台,因此无需正常安装)。所以不要忘记导入它:
import Text.Regex.Posix
这个tutorial应该向您展示正则表达式API的基础知识,如果您有其他需求,现在有点过时,但基本原理保持不变,只有正则表达式的细节已经改变。
答案 2 :(得分:1)
好吧,你可以试试这个功能,虽然这可能不是最好的方法:
elem' :: String -> String -> Bool
elem' p xs = any (p==) $ map (take $ length p) $ tails xs
用法:
filter (elem' "bb") ["xxbbaua", "bbbaacv", "ggfeehhaa", "uyyttaccaa", "bbbaab"]
或
bbFilter = filter (elem' "bb")
答案 3 :(得分:1)
如果你绝对反对用Regexs做这件事你可以用模式匹配和递归来做,虽然它很难看。
xs = ["xabbaua", "bbbaacv", "ggfeehhaa", "uyyttaccaa", "ibbatb"]
vowel = "aeiou"
filter' strs = filter matches strs
matches [] = False
matches str@(x:'b':'b':_:y:xs)
| x `elem` vowel && y `elem` vowel = True
| otherwise = matches $ tail str
matches (x:xs) = matches xs
致电filter' xs
将返回["xabbaua"]
,我认为这是必需的结果。