确定Haskell中的匹配括号

Iul*_*anu 2 haskell parentheses

假设我有一个字符串,如:

abc(def(gh)il(mn(01))afg)lmno(sdfg*)
Run Code Online (Sandbox Code Playgroud)

如何确定第一个的匹配括号?(意思(def(gh)il(mn(01))afg))

我试图通过计算开放括号的数量直到第一个')'来创建一个between函数,但它不适用于像这样的字符串.

Ste*_*ans 7

您可以使用一个简单地遍历字符串的函数,同时跟踪一组开括号的索引.每当遇到右括号时,您就会知道它与堆栈顶部的索引匹配.

例如:

parenPairs :: String -> [(Int, Int)]
parenPairs = go 0 []
  where
    go _ _        []         = []
    go j acc      ('(' : cs) =          go (j + 1) (j : acc) cs
    go j []       (')' : cs) =          go (j + 1) []        cs -- unbalanced parentheses!
    go j (i : is) (')' : cs) = (i, j) : go (j + 1) is        cs
    go j acc      (c   : cs) =          go (j + 1) acc       cs
Run Code Online (Sandbox Code Playgroud)

此函数返回属于匹配括号对的所有索引对的列表.

将该函数应用于示例字符串会给出:

> parenPairs "abc(def(gh)il(mn(01))afg)lmno(sdfg*)"
[(7,10),(16,19),(13,20),(3,24),(29,35)]
Run Code Online (Sandbox Code Playgroud)

您感兴趣的左括号出现在索引3处.返回的列表显示匹配的右括号将在索引24处找到.

以下函数为您提供字符串的所有正确括号段:

parenSegs :: String -> [String]
parenSegs s = map (f s) (parenPairs s)
  where
    f s (i, j) = take (j - i + 1) (drop i s)
Run Code Online (Sandbox Code Playgroud)

例如:

> parenSegs "abc(def(gh)il(mn(01))afg)lmno(sdfg*)"
["(gh)","(01)","(mn(01))","(def(gh)il(mn(01))afg)","(sdfg*)"]
Run Code Online (Sandbox Code Playgroud)

根据Frerich Raabe的建议,我们现在也可以编写一个只返回最左边段的函数:

firstParenSeg :: String -> String
firstParenSeg s = f s (minimum (parenPairs s))
  where
    f s (i, j) = take (j - i + 1) (drop i s)
Run Code Online (Sandbox Code Playgroud)

例如:

> firstParenSeg "abc(def(gh)il(mn(01))afg)lmno(sdfg*)"
"(def(gh)il(mn(01))afg)"
Run Code Online (Sandbox Code Playgroud)

请注意,firstParenSeg如果输入字符串不包含至少一对匹配的括号,则会失败.

最后,对parenPairs函数的一个小修改使它在不平衡的括号上失败:

parenPairs' :: String -> [(Int, Int)]
parenPairs' = go 0 []
  where
    go _ []        []         = []
    go _ (_ : _ )  []         = error "unbalanced parentheses!"
    go j acc       ('(' : cs) =          go (j + 1) (j : acc) cs
    go j []        (')' : cs) = error "unbalanced parentheses!"
    go j (i : is)  (')' : cs) = (i, j) : go (j + 1) is        cs
    go j acc       (c   : cs) =          go (j + 1) acc       cs
Run Code Online (Sandbox Code Playgroud)