前回はXML風に木を出力するという機能をつけていましたが、本命はDOT言語に出力する事です。
そっちのほうが確認しやすいからね。というわけで付けてみました。
以下は変更した部分だけ
-- ParseTreeをDOT言語として出力 type IndentLevel = Int --- インデントをスペースでつける putIndent :: Handle -> IndentLevel -> IO () putIndent h ident = hPutStr h $ take ident (repeat ' ') --- 辺を出力(始点はかならずforkである) printEdge :: Handle -> Int -> Int -> ParseTree -> IO () printEdge h tid nid (Fork _ _) = hPrintf h "f%d -> f%d;\n" tid nid printEdge h tid nid (Leaf _) = hPrintf h "f%d -> n%d;\n" tid nid type MaxId = Int parseTree2DOT' :: Handle -> MaxId -> ParseTree -> IO Int parseTree2DOT' h i (Leaf token) = do { putIndent h 8; hPrintf h "n%d [label=\"%s (%s)\"];\n" i (token^.str) (show (token^.tp)); return (i+1); } parseTree2DOT' h i (Fork ntp ts) = do { putIndent h 8; hPrintf h "f%d [label=\"%s\"];\n" i (show ntp); foldM (\a b -> do { putIndent h 8; printEdge h i a b; parseTree2DOT' h a b; }) (i+1) ts; } parseTree2DOT :: Handle -> ParseTree -> IO () parseTree2DOT h t@(Fork _ _) = do { hPutStrLn h "digraph parsetree {"; parseTree2DOT' h 0 t; hPutStrLn h "}"; } -- Maybe ParseTreeのListを個別のファイルに出力 type FilenamePrefix = String outputTrees :: FilenamePrefix -> [Maybe ParseTree] -> IO () outputTrees prefix ts = do { foldM_ (\i t -> do { withFile (prefix ++ (show i) ++ ".dot") WriteMode (\h -> case (ts !! i) of Just t -> do {parseTree2DOT h t; return (i+1)}; Nothing -> return i) }) 0 ts; } -- 英文を構文解析して生成された構文木を全通りDOTファイルに出力 main = do { let trees = generalizedLR englishSentence englishLang in do { outputTrees "parse" trees; }; }
System.IOのHandleを利用するようにして、出力先を簡単に変えられるようにしました。
出力されるファイルはたとえば以下のような物で。
digraph parsetree {
f0 [label="S"];
f0 -> f1;
f1 [label="NP"];
f1 -> n2;
n2 [label="I (Noun)"];
f0 -> f3;
f3 [label="VP"];
f3 -> n4;
n4 [label="saw (Verb)"];
f3 -> f5;
f5 [label="NP"];
f5 -> f6;
f6 [label="NP"];
f6 -> n7;
n7 [label="a (Det)"];
f6 -> n8;
n8 [label="man (Noun)"];
f5 -> f9;
f9 [label="PP"];
f9 -> n10;
n10 [label="with (Prep)"];
f9 -> f11;
f11 [label="NP"];
f11 -> f12;
f12 [label="NP"];
f12 -> n13;
n13 [label="a (Det)"];
f12 -> n14;
n14 [label="telescope (Noun)"];
f11 -> f15;
f15 [label="PP"];
f15 -> n16;
n16 [label="in (Prep)"];
f15 -> f17;
f17 [label="NP"];
f17 -> n18;
n18 [label="the (Det)"];
f17 -> n19;
n19 [label="park (Noun)"];
}と綺麗に出力されます。mainの設定ですと、parse0.dot,parse1.dot...というファイルたちに出力されます。
こいつをgraphvizで処理すると

のように構文木が確認できます。望遠鏡が公園に居る感じになってておもしろいですね。