我需要创建一个正则表达式(对于haskell中的程序),它将捕获包含“X”和“。”的字符串,假设有4个“X”且只有一个“。”。它无法捕获任何其他X-to-dot关系的字符串。 我想过像
这样的东西[X\.]{5}
但它也会捕获“XXXXX”或“.....”,所以它不是我需要的。
答案 0 :(得分:5)
这称为排列解析,而且"纯粹"如果你的正则表达式引擎支持前瞻,正则表达式无法解析它可能的排列。 (有关示例,请参阅this answer。)
但是我发现链接答案中的正则表达式很难理解。在我看来,使用专为排列解析而设计的库比较清晰,例如megaparsec
。
使用Text.Megaparsec.Perm
模块,使用PermParser
运算符以准Applicative
样式构建<||>
,然后将其转换为常规MonadParsec
使用makePermParser
进行操作。
因此,这是一个解析器,可识别四个X
和一个.
的任意组合:
import Control.Applicative
import Data.Ord
import Data.List
import Text.Megaparsec
import Text.Megaparsec.Perm
fourXoneDot :: Parsec Dec String String
fourXoneDot = makePermParser $ mkFive <$$> x <||> x <||> x <||> x <||> dot
where mkFive a b c d e = [a, b, c, d, e]
x = char 'X'
dot = char '.'
我将mkFive
函数应用于x
解析器的四个实例和dot
的四个实例,它将其参数填充到五元素列表中,并将其与<||>
。
ghci> parse fourXoneDot "" "XXXX."
Right "XXXX."
ghci> parse fourXoneDot "" "XX.XX"
Right "XXXX."
ghci> parse fourXoneDot "" "XX.X"
Left {- ... -}
此解析器始终返回"XXXX."
,因为这是我将解析器组合在一起的顺序:我在{5}解析器上映射mkFive
并且它没有重新排序参数。如果您希望排列解析器准确地返回其输入字符串,那么诀窍是在组件解析器中track the current position,然后对输出进行排序。
fourXoneDotSorted :: Parsec Dec String String
fourXoneDotSorted = makePermParser $ mkFive <$$> x <||> x <||> x <||> x <||> dot
where mkFive a b c d e = map snd $ sortBy (comparing fst) [a, b, c, d, e]
x = withPos (char 'X')
dot = withPos (char '.')
withPos = liftA2 (,) getPosition
ghci> parse fourXoneDotSorted "" "XX.XX"
Right "XX.XX"
正如the megaparsec
docs所述,Text.Megaparsec.Perm
模块的实施基于Parsing Permutation Phrases;这个想法在论文和the accompanying slides中有详细描述。
答案 1 :(得分:5)
其他答案看起来很复杂,因为这种语言中只有五个字符串。这是一个完美的,非常易读的正则表达式:
\.XXXX|X\.XXX|XX\.XX|XXX\.X|XXXX\.
答案 2 :(得分:2)
您是否依赖于正则表达式,或者您最终是否正在使用正则表达式,因为这是一个您不想尝试使用应用解析器进行回答的问题?
这是我能想到的最简单的attoparsec实现:
parseDotXs :: Parser ()
parseDotXs = do
dotXs <- count 5 (satisfy (inClass ".X"))
let (dots,xS) = span (=='.') . sort $ dotXs
if (length dots == 1) && (length xS == 4) then do
return ()
else do
fail "Mismatch between dots and Xs"
您可能需要根据输入类型稍微调整一下。
在应用解析的土地上有许多奇特的方法可以做些什么,但是没有规则说你不能只用愚蠢的简单方法做事。
答案 3 :(得分:0)