たとえば、複数の括弧で囲まれたグループを持つ文字列を解析して、各グループを保持する文字列のリストにしたい場合
"((a b c) a b c)"
の中へ
["((a b c) a b c)","( a b c)"]
parsecを使用してそれを行うにはどうすればよいですか? の使用は良さそうにbetween
見えますが、開始値と終了値で分離することはできないようです。
私は再帰パーサーを使用します:
data Expr = List [Expr] | Term String
expr :: Parsec String () Expr
expr = recurse <|> terminal
terminal
この場合、これらは文字列のように見えるので、プリミティブはどこにありますか
where terminal = Term <$> many1 letter
そしてrecurse
_
recurse = List <$>
(between `on` char) '(' ')' (expr `sepBy1` char ' ')
Expr
これで、収集できる s の素敵なツリーができました
collect r@(List ts) = r : concatMap collect ts
collect _ = []
jozefg の解決策は、私が思いついたものとほとんど同じですが (そして、彼のすべての提案に完全に同意します)、いくつかの小さな違いがあり、2 番目の回答を投稿する必要があると思いました。
だからここに私のバージョンがあります。jozefg によって既に提案されているように、タスクをいくつかのサブタスクに分割します。それらは:
1に関しては、まずツリーデータ型が必要です
import Text.Parsec
import Text.Parsec.String
import Control.Applicative ((<$>))
data Tree = Leaf String | Node [Tree]
次に、文字列をこの型の値に解析できる関数。
parseTree :: Parser Tree
parseTree = node <|> leaf
where
node = Node <$> between (char '(') (char ')') (many parseTree)
leaf = Leaf <$> many1 (noneOf "()")
私のバージョンでは、括弧の間の穴の文字列をLeaf
ノードと見なします (つまり、空白で分割しません)。
次に、関心のあるツリーのサブツリーを収集する必要があります。
nodes :: Tree -> [Tree]
nodes (Leaf _) = []
nodes t@(Node ts) = t : concatMap nodes ts
最後に、 s のShow
-instance をTree
使用すると、それらを文字列に変換できます。
instance Show Tree where
showsPrec d (Leaf x) = showString x
showsPrec d (Node xs) = showString "(" . showList xs . showString ")"
where
showList [] = id
showList (x:xs) = shows x . showList xs
次に、元のタスクは、たとえば次のように解決できます。
parseGroups :: Parser [String]
parseGroups = map show . nodes <$> parseTree
> parseTest parseGroups "((a b c) a b c)"
["((a b c) a b c)","(a b c)"]