使用正则表达式查找排列

时间:2017-06-09 15:27:53

标签: regex haskell permutation

我需要创建一个正则表达式(对于haskell中的程序),它将捕获包含“X”和“。”的字符串,假设有4个“X”且只有一个“。”。它无法捕获任何其他X-to-dot关系的字符串。 我想过像

这样的东西
[X\.]{5}

但它也会捕获“XXXXX”或“.....”,所以它不是我需要的。

4 个答案:

答案 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)

尝试以下正则表达式:
(?<=^| )(?=[^. ]*\.)(?=(?:[^X ]*X){4}).{5}(?=$| )

演示here

如果每个字符串有一个单词,则可以通过以下方式简化正则表达式:
^(?=[^. \n]*\.)(?=(?:[^X \n]*X){4}).{5}$

演示here