与字符串匹配的复杂模式

时间:2012-07-11 23:55:37

标签: string haskell

我有一个字符串列表,如下所示:

xs = ["xabbaua", "bbbaacv", "ggfeehhaa", "uyyttaccaa", "ibbatb"]

我想在列表中找到只有字符串的字符串,后面跟着两个b,后跟任意字符后面跟一个元音。如何在Haskell中完成这样的简单匹配。是否有更好的正则表达式解决方案?任何人都可以帮我一个例子吗?感谢。

4 个答案:

答案 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"],我认为这是必需的结果。