diff options
Diffstat (limited to 'edit-lens/src/Control/FST.lhs')
-rw-r--r-- | edit-lens/src/Control/FST.lhs | 161 |
1 files changed, 149 insertions, 12 deletions
diff --git a/edit-lens/src/Control/FST.lhs b/edit-lens/src/Control/FST.lhs index 9aa5341..9357da7 100644 --- a/edit-lens/src/Control/FST.lhs +++ b/edit-lens/src/Control/FST.lhs | |||
@@ -15,7 +15,7 @@ module Control.FST | |||
15 | -- * Operations on FSTs | 15 | -- * Operations on FSTs |
16 | , productFST, restrictOutput, restrictFST | 16 | , productFST, restrictOutput, restrictFST |
17 | -- * Debugging Utilities | 17 | -- * Debugging Utilities |
18 | , liveFST | 18 | , liveFST, dotFST |
19 | ) where | 19 | ) where |
20 | 20 | ||
21 | import Data.Map.Lazy (Map, (!?), (!)) | 21 | import Data.Map.Lazy (Map, (!?), (!)) |
@@ -38,6 +38,11 @@ import Control.Monad.State.Strict | |||
38 | import Text.PrettyPrint.Leijen (Pretty(..)) | 38 | import Text.PrettyPrint.Leijen (Pretty(..)) |
39 | import qualified Text.PrettyPrint.Leijen as PP | 39 | import qualified Text.PrettyPrint.Leijen as PP |
40 | 40 | ||
41 | import Data.Bool (bool) | ||
42 | import Data.Monoid ((<>)) | ||
43 | |||
44 | import Text.Dot | ||
45 | |||
41 | \end{code} | 46 | \end{code} |
42 | \end{comment} | 47 | \end{comment} |
43 | 48 | ||
@@ -50,7 +55,7 @@ In Haskell lockern wir die Anforderung, dass die Ein- und Ausgabe-Alphabete endl | |||
50 | Zudem speichern wir die Transitionsrelation als multimap um effiziente lookups von Zustand-Eingabe-Paaren zu ermöglichen. | 55 | Zudem speichern wir die Transitionsrelation als multimap um effiziente lookups von Zustand-Eingabe-Paaren zu ermöglichen. |
51 | 56 | ||
52 | \begin{code} | 57 | \begin{code} |
53 | dFSeata FST state input output = FST | 58 | data FST state input output = FST |
54 | { stInitial :: Set state | 59 | { stInitial :: Set state |
55 | , stTransition :: Map (state, Maybe input) (Set (state, Maybe output)) | 60 | , stTransition :: Map (state, Maybe input) (Set (state, Maybe output)) |
56 | , stAccept :: Set state | 61 | , stAccept :: Set state |
@@ -58,13 +63,56 @@ dFSeata FST state input output = FST | |||
58 | \end{code} | 63 | \end{code} |
59 | \end{defn} | 64 | \end{defn} |
60 | 65 | ||
66 | \begin{eg} \label{eg:linebreak} | ||
67 | Als wiederkehrendes Beispiel betrachten wir einen Transducer $L_{80} = (\Sigma, \Delta, Q, I, F, E)$, der für ein beliebiges Alphabet $\Sigma \supseteq \{ \text{\tt ' '}, \text{\tt \textbackslash n} \}$ durch Umwandlung zwischen Leerzeichen und Zeilenumbrüchen sicherstellt, dass jede Zeile des Ausgabetextes mindestens 80 Zeichen enthält, jedoch nur an Wortgrenzen umbricht: | ||
68 | |||
69 | \begin{align*} | ||
70 | \Delta & = \Sigma \\ | ||
71 | Q & = \{ 0, 1, \ldots, 80 \} \\ | ||
72 | I & = \{ 0 \} \\ | ||
73 | F & = Q \\ | ||
74 | E & = \{ (q, \sigma, \sigma, q + 1) \mid q \in Q \mysetminus \{ 80 \}, \sigma \in \Sigma \mysetminus \{ \text{\tt \textbackslash n} \} \} \\ | ||
75 | & \cup \{ (q, \text{\tt \textbackslash n}, \text{\tt ' '}, q + 1) \mid q \in Q \mysetminus \{ 80 \}, \sigma \in \Sigma \} \\ | ||
76 | & \cup \{ (80, \text{\tt ' '}, \text{\tt \textbackslash n}, 0), (80, \text{\tt \textbackslash n}, \text{\tt \textbackslash n}, 0) \} \\ | ||
77 | & \cup \{ (80, \sigma, \sigma, 80) \mid \sigma \in \Sigma \mysetminus \{ \text{\tt ' '}, \text{\tt \textbackslash n} \} \} | ||
78 | \end{align*} | ||
79 | |||
80 | \begin{figure}[p] | ||
81 | \centering | ||
82 | \begin{tikzpicture}[->,auto,node distance=5cm] | ||
83 | \node[initial,state,accepting] (0) {$0$}; | ||
84 | \node[state,accepting] (1) [right of=0] {$1$}; | ||
85 | \node[] (rest) [below of=1] {$\ldots$}; | ||
86 | \node[state,accepting] (i) [right of=rest,xshift=-2cm] {$i$}; | ||
87 | \node[state,accepting] (si) [below of=rest,yshift=2cm] {$i + 1$}; | ||
88 | \node[state,accepting] (80) [left of=rest] {$80$}; | ||
89 | |||
90 | \path (0) edge node {$\{ (\sigma, \sigma) \mid \sigma \in \Sigma, \sigma \neq \text{\tt \textbackslash n} \}$} (1) | ||
91 | (1) edge [bend left=20] node {$\{ (\sigma, \sigma) \mid \sigma \in \Sigma, \sigma \neq \text{\tt \textbackslash n} \}$} (rest) | ||
92 | edge [bend right=20] node [left] {$(\text{\tt \textbackslash n}, \text{\tt ' '})$} (rest) | ||
93 | (rest) edge node [below] {$\{ (\sigma, \sigma) \mid \sigma \in \Sigma, \sigma \neq \text{\tt \textbackslash n} \}$} (80) | ||
94 | edge [bend right=20] node [above] {$(\text{\tt \textbackslash n}, \text{\tt ' '})$} (80) | ||
95 | (i) edge [bend left=45] node {$\{ (\sigma, \sigma) \mid \sigma \in \Sigma, \sigma \neq \text{\tt \textbackslash n} \}$} (si) | ||
96 | edge [bend left=10] node [left] {$(\text{\tt \textbackslash n}, \text{\tt ' '})$} (si) | ||
97 | (80) edge [loop below] node [below] {$\{ (\sigma, \sigma) \mid \sigma \in \Sigma, \sigma \neq \text{\tt \textbackslash n}, \sigma \neq \text{\tt ' '} \}$} (80) | ||
98 | edge [bend left=20] node {$(\text{\tt \textbackslash n}, \text{\tt \textbackslash n})$} (0) | ||
99 | edge [bend right=20] node [right] {$(\text{\tt ' '}, \text{\tt \textbackslash n})$} (0); | ||
100 | |||
101 | \draw[-] (rest)--(i.north); | ||
102 | \draw[-] (rest)--(si.west); | ||
103 | \end{tikzpicture} | ||
104 | \caption{Beispiel \ref{eg:linebreak} dargestellt als Graph} | ||
105 | \end{figure} | ||
106 | \end{eg} | ||
107 | |||
61 | \begin{comment} | 108 | \begin{comment} |
62 | \begin{code} | 109 | \begin{code} |
63 | instance (Show state, Show input, Show output) => Pretty (FST state input output) where | 110 | instance (Show state, Show input, Show output, Ord state, Ord input, Ord output) => Pretty (FST state input output) where |
64 | pretty FST{..} = PP.vsep | 111 | pretty fst@FST{..} = PP.vsep |
65 | [ PP.text "Initial states:" PP.</> PP.hang 2 (list . map (PP.text . show) $ Set.toAscList stInitial) | 112 | [ PP.text "Initial states:" PP.</> PP.hang 2 (list . map (PP.text . show) $ Set.toAscList stInitial) |
66 | , PP.text "State transitions:" PP.<$> PP.indent 2 (PP.vsep | 113 | , PP.text "State transitions:" PP.<$> PP.indent 2 (PP.vsep |
67 | [ PP.text (show st) | 114 | [ PP.text (bool " " "#" $ Set.member st live) |
115 | PP.<+> PP.text (show st) | ||
68 | PP.<+> (PP.text "-" PP.<> PP.tupled [label inS, label outS] PP.<> PP.text "→") | 116 | PP.<+> (PP.text "-" PP.<> PP.tupled [label inS, label outS] PP.<> PP.text "→") |
69 | PP.<+> PP.text (show st') | 117 | PP.<+> PP.text (show st') |
70 | | ((st, inS), to) <- Map.toList stTransition | 118 | | ((st, inS), to) <- Map.toList stTransition |
@@ -77,9 +125,11 @@ instance (Show state, Show input, Show output) => Pretty (FST state input output | |||
77 | label = PP.text . maybe "ɛ" show | 125 | label = PP.text . maybe "ɛ" show |
78 | list :: [PP.Doc] -> PP.Doc | 126 | list :: [PP.Doc] -> PP.Doc |
79 | list = PP.encloseSep (PP.lbracket PP.<> PP.space) (PP.space PP.<> PP.rbracket) (PP.comma PP.<> PP.space) | 127 | list = PP.encloseSep (PP.lbracket PP.<> PP.space) (PP.space PP.<> PP.rbracket) (PP.comma PP.<> PP.space) |
128 | live = liveFST fst | ||
80 | \end{code} | 129 | \end{code} |
81 | \end{comment} | 130 | \end{comment} |
82 | 131 | ||
132 | \begin{defn}[Auswertung von FSTs] | ||
83 | Wir definieren die Auswertung von finite state transducers induktiv indem wir zunächst angeben wie ein einzelner Auswertungs-Schritt erfolgt. | 133 | Wir definieren die Auswertung von finite state transducers induktiv indem wir zunächst angeben wie ein einzelner Auswertungs-Schritt erfolgt. |
84 | 134 | ||
85 | Hierzu kommentieren wir die Haskell-Implementierung eines Auswertungs-Schritts. | 135 | Hierzu kommentieren wir die Haskell-Implementierung eines Auswertungs-Schritts. |
@@ -145,6 +195,7 @@ akzeptierenden Endzustände liegt. | |||
145 | go (initial, nPath) ncs | 195 | go (initial, nPath) ncs |
146 | \end{code} | 196 | \end{code} |
147 | \end{comment} | 197 | \end{comment} |
198 | \end{defn} | ||
148 | 199 | ||
149 | Es ist gelegentlich nützlich nur die möglichen Ausgaben eines FST auf gegebener | 200 | Es ist gelegentlich nützlich nur die möglichen Ausgaben eines FST auf gegebener |
150 | Eingabe zu bestimmen, wir führen eine Hilfsfunktion auf Basis von | 201 | Eingabe zu bestimmen, wir führen eine Hilfsfunktion auf Basis von |
@@ -163,7 +214,7 @@ runFST = fmap (map $ catMaybes . fmap (view _2) . view _2) . runFST' | |||
163 | \end{comment} | 214 | \end{comment} |
164 | 215 | ||
165 | Wir können das Produkt zweier FSTs definieren. | 216 | Wir können das Produkt zweier FSTs definieren. |
166 | Intuitiv wollen wir beide FSTs gleichzeitig ausführen und dabei sicherstellen, dass Ein- und Ausgabe der FSTs übereinstimmen\footnote{Da wir $\epsilon$-Transitionen in FSTs erlauben müssen wir uns festlegen wann eine $\epsilon$-Transition übereinstimmt mit einer anderen Transition. Wir definieren, dass $\epsilon$ als Eingabe mit jeder anderen Eingabe (inkl. einem weiteren $\epsilon$) übereinstimmt.}. | 217 | Intuitiv wollen wir beide FSTs gleichzeitig ausführen und dabei sicherstellen, dass Ein- und Ausgabe der FSTs übereinstimmen\footnote{Da wir $\epsilon$-Transitionen in FSTs erlauben müssen wir uns festlegen wann eine $\epsilon$-Transition übereinstimmt mit einer anderen Transition. Wir definieren, dass $\epsilon$ als Eingabe ausschließlich mit $\epsilon$ übereinstimmt.}. |
167 | 218 | ||
168 | Hierfür berechnen wir das Graphen-Produkt der FSTs: | 219 | Hierfür berechnen wir das Graphen-Produkt der FSTs: |
169 | 220 | ||
@@ -221,7 +272,7 @@ Hierzu nehmen wir das FST-Produkt mit einem FST, der, ungeachtet der Eingabe, im | |||
221 | Da die Ausgaben der beiden FSTs übereinstimmen müssen produziert das Produkt mit einem derartigen FST (solange dessen Ausgabe in keinem Sinne von der Eingabe abhängt) die gewünschte Ausgabe. | 272 | Da die Ausgaben der beiden FSTs übereinstimmen müssen produziert das Produkt mit einem derartigen FST (solange dessen Ausgabe in keinem Sinne von der Eingabe abhängt) die gewünschte Ausgabe. |
222 | 273 | ||
223 | Zur Konstruktion eines derartigen \emph{Wort-FST}s nehmen wir Indizes im Ausgabe-Wort (natürliche Zahlen) als Zustände. | 274 | Zur Konstruktion eines derartigen \emph{Wort-FST}s nehmen wir Indizes im Ausgabe-Wort (natürliche Zahlen) als Zustände. |
224 | Übergänge sind immer entweder der Form $n \rightarrow \text{succ}(n)$, konsumieren keine Eingabe ($\epsilon$) und produzieren als Ausgabe das Zeichen am Index $n$ im Ausgabe-Wort, oder der Form $n \overset{(i, \epsilon)}{\rightarrow} n$, für jedes Eingabesymbol $i$ (um die Unabhängigkeit von der Eingabe sicherzustellen). | 275 | Übergänge sind immer entweder der Form $n \rightarrow \text{succ}(n)$, konsumieren keine Eingabe ($\epsilon$) und produzieren als Ausgabe das Zeichen am Index $n$ im Ausgabe-Wort, oder der Form $n \overset{(\sigma, \epsilon)}{\rightarrow} n$, für jedes Eingabesymbol $\sigma$ (um die Unabhängigkeit von der Eingabe sicherzustellen). |
225 | Weiter ist $0$ initial und $\text{length}(\text{Ausgabe})$ der einzige akzeptierende Endzustand. | 276 | Weiter ist $0$ initial und $\text{length}(\text{Ausgabe})$ der einzige akzeptierende Endzustand. |
226 | 277 | ||
227 | \begin{code} | 278 | \begin{code} |
@@ -251,6 +302,35 @@ wordFST inps outs = FST | |||
251 | \end{code} | 302 | \end{code} |
252 | \end{comment} | 303 | \end{comment} |
253 | 304 | ||
305 | \begin{eg} \label{eg:w100} | ||
306 | Der zum Wort $w = 1\, 2\, \ldots\, 80\, \text{\tt \textbackslash n}\, 81\, \ldots\, 98$ gehörige Wort-FST $W_w = ( \Sigma, \Delta, Q, I, F, E)$ für ein beliebiges Eingabe-Alphabet $\Sigma$ ist: | ||
307 | |||
308 | \begin{align*} | ||
309 | \Delta & = \{ 1, 2, \ldots, 98, \text{\tt \textbackslash n} \} \\ | ||
310 | Q & = \{ 0, 1, \ldots, 99 \} \\ | ||
311 | I & = \{ 0 \} \\ | ||
312 | F & = \{ 99 \} \\ | ||
313 | E & = \{ (i, \sigma, w_i, i + 1) \mid \sigma \in \Sigma \cup \{ \epsilon \}, i \in Q \mysetminus \{ 99 \} \} \\ | ||
314 | & \cup \{ (i, \sigma, \epsilon, i) \mid \sigma \in \Sigma \cup \{ \epsilon \}, i \in Q \mysetminus \{ 99 \} \} | ||
315 | \end{align*} | ||
316 | |||
317 | \begin{figure}[p] | ||
318 | \centering | ||
319 | \begin{tikzpicture}[->,auto,node distance=5cm] | ||
320 | \node[initial,state] (0) {$0$}; | ||
321 | \node[] (rest) [right of=0] {$\ldots$}; | ||
322 | \node[state,accepting] (99) [right of=rest] {$99$}; | ||
323 | |||
324 | \path (0) edge [loop above] node {$\{(\sigma, \epsilon) \mid \sigma \in \Sigma \cup \{ \epsilon \} \}$} (0) | ||
325 | edge node {$\{ (\sigma, 1) \mid \sigma \in \Sigma \cup \{ \epsilon \} \}$} (rest) | ||
326 | (rest) edge node {$\{ \sigma, 100 \} \mid \sigma \in \Sigma \cup \{ \epsilon \}$} (99) | ||
327 | (99) edge [loop above] node {$\{(\sigma, \epsilon) \mid \sigma \in \Sigma \}$} (99); | ||
328 | \end{tikzpicture} | ||
329 | |||
330 | \caption{Beispiel \ref{eg:w100} dargestellt als Graph} | ||
331 | \end{figure} | ||
332 | \end{eg} | ||
333 | |||
254 | Da \texttt{wordFST} zur Konstruktion eine komprehensive Menge aller Eingabesymbole benötigt verwenden wir im folgenden eine optimierte Variante des Produkts mit einem Wort-FST. | 334 | Da \texttt{wordFST} zur Konstruktion eine komprehensive Menge aller Eingabesymbole benötigt verwenden wir im folgenden eine optimierte Variante des Produkts mit einem Wort-FST. |
255 | 335 | ||
256 | \begin{code} | 336 | \begin{code} |
@@ -258,6 +338,46 @@ restrictOutput :: forall state input output. (Ord state, Ord input, Ord output) | |||
258 | -- ^ @restrictOutput out@ is equivalent to @productFST (wordFST inps out)@ where @inps@ is a comprehensive set of all input symbols @inp :: input@ | 338 | -- ^ @restrictOutput out@ is equivalent to @productFST (wordFST inps out)@ where @inps@ is a comprehensive set of all input symbols @inp :: input@ |
259 | \end{code} | 339 | \end{code} |
260 | 340 | ||
341 | \begin{eg} \label{eg:l80timesw100} | ||
342 | Der FST $L_{80}$ aus Beispiel \autoref{eg:linebreak} eingeschränkt auf das Wort $w$ aus Beispiel \autoref{eg:w100} (also $W_w \times L_{80}$) ist: | ||
343 | |||
344 | \begin{align*} | ||
345 | \Sigma^\times & = \{1, 2, \ldots, 99, \text{\tt \textbackslash n}, \text{\tt ' '} \} \\ | ||
346 | \Delta^\times & = \{1, 2, \ldots, 99, \text{\tt \textbackslash n} \} \\ | ||
347 | Q^\times & = \{ (\sigma, p) \mid \sigma \in \{ 0_{W_w}, \ldots, 99_{W_w} \}, p \in \{ 0_{L_{80}}, \ldots, 80_{L_{80}} \} \} \\ | ||
348 | I^\times & = \{ (0_{W_w}, 0_{L_{80}}) \} \\ | ||
349 | F^\times & = \{ (99_{W_w}, p) \mid p \in \{ 0_{L_{80}}, \ldots, 80_{L_{80}} \} \} \\ | ||
350 | E^\times & = \{ ((i, q), w_i, w_i, (i + 1, q + 1)) \mid q \in Q \mysetminus \{ 80 \}, i \in Q \mysetminus \{ 99 \} \} \\ | ||
351 | & \cup \{ ((80, 80), \text{\tt \textbackslash n}, \text{\tt \textbackslash n}, (81, 0)) \} | ||
352 | \end{align*} | ||
353 | |||
354 | \begin{figure}[p] | ||
355 | \centering | ||
356 | \begin{tikzpicture}[->,auto,node distance=5cm] | ||
357 | \node[initial,state] (0) {$0_{W_w}\, 0_{L_{80}}$}; | ||
358 | \node[] (rest1) [right of=0] {$\ldots$}; | ||
359 | \node[state] (80) [right of=rest1] {$80_{W_w}\, 80_{L_{80}}$}; | ||
360 | \node[state] (81) [below of=0] {$81_{W_w}\, 0_{L_{80}}$}; | ||
361 | \node[] (rest2) [right of=81] {$\ldots$}; | ||
362 | \node[state,accepting] (99) [right of=rest2] {$99_{W_w}\, 19_{L_{80}}$}; | ||
363 | |||
364 | \path (0) edge node {$(1, 1)$} (rest1) | ||
365 | (rest1) edge node {$(80, 80)$} (80) | ||
366 | (80) edge node {$(\text{\tt \textbackslash n}, \text{\tt \textbackslash n})$} (81) | ||
367 | (81) edge node {$(81, 81)$} (rest2) | ||
368 | (rest2) edge node {$(98, 98)$} (99); | ||
369 | \end{tikzpicture} | ||
370 | |||
371 | \caption{Beispiel \ref{eg:l80timesw100} dargestellt als Graph} | ||
372 | \end{figure} | ||
373 | \end{eg} | ||
374 | |||
375 | \begin{rem} | ||
376 | Es ist bemerkenswert, dass in Beispiel \ref{eg:l80timesw100} die Zirkuläre Struktur von $L_80$ durch Produkt mit einem Wort verloren geht. | ||
377 | |||
378 | I.\@A.\@ ist das Produkt eines beliebigen FST mit einem Wort-FST zwar nicht azyklisch, erbt jedoch die lineare Struktur des Wort-FST in dem Sinne, dass Fortschritt in Richtung der akzeptierenden Zustände nur möglich ist indem der $(i, \sigma, w_i, i + 1)$-Klasse von Transitionen des Wort-FSTs gefolgt wird. | ||
379 | \end{rem} | ||
380 | |||
261 | \begin{comment} | 381 | \begin{comment} |
262 | \begin{code} | 382 | \begin{code} |
263 | restrictOutput out FST{..} = FST | 383 | restrictOutput out FST{..} = FST |
@@ -302,18 +422,35 @@ liveFST :: forall state input output. (Ord state, Ord input, Ord output, Show st | |||
302 | -- ^ Compute the set of "live" states (with no particular complexity) | 422 | -- ^ Compute the set of "live" states (with no particular complexity) |
303 | -- | 423 | -- |
304 | -- A state is "live" iff there is a path from it to an accepting state and a path from an initial state to it | 424 | -- A state is "live" iff there is a path from it to an accepting state and a path from an initial state to it |
305 | liveFST fst@FST{..} = flip execState Set.empty $ mapM_ (depthSearch Set.empty) stInitial | 425 | liveFST fst@FST{..} = flip execState (Set.empty) . depthSearch Set.empty $ Set.toList stInitial |
306 | where | 426 | where |
307 | stTransition' :: Map state (Set state) | 427 | stTransition' :: Map state (Set state) |
308 | stTransition' = Map.map (Set.map (\(st, _) -> st)) $ Map.mapKeysWith Set.union (\(st, _) -> st) stTransition | 428 | stTransition' = Map.map (Set.map (\(st, _) -> st)) $ Map.mapKeysWith Set.union (\(st, _) -> st) stTransition |
309 | depthSearch :: Set state -> state -> State (Set state) () | 429 | depthSearch :: Set state -> [state] -> State (Set state) () |
310 | depthSearch acc curr = do | 430 | depthSearch acc [] = return () |
431 | depthSearch acc (curr : queue) = do | ||
311 | let acc' = Set.insert curr acc | 432 | let acc' = Set.insert curr acc |
312 | next = fromMaybe Set.empty $ stTransition' !? curr | 433 | next = fromMaybe Set.empty $ stTransition' !? curr |
313 | alreadyLive <- get | 434 | alreadyLive <- get |
314 | when (curr `Set.member` Set.union stAccept alreadyLive) $ | 435 | when (curr `Set.member` Set.union stAccept alreadyLive) $ |
315 | modify $ Set.union acc' | 436 | modify $ Set.union acc' |
316 | alreadyLive <- get | 437 | depthSearch acc' $ filter (not . flip Set.member next) queue ++ Set.toList (next `Set.difference` acc') |
317 | mapM_ (depthSearch acc') $ next `Set.difference` alreadyLive | 438 | |
439 | dotFST :: forall state input output. (Ord state, Ord input, Ord output, Show state, Show input, Show output) => FST state input output -> Dot () | ||
440 | dotFST FST{..} = do | ||
441 | let | ||
442 | stTransition' = concatMap (\(f, ts) -> (f,) <$> Set.toList ts) $ Map.toList stTransition | ||
443 | states = stInitial <> stAccept <> foldMap (Set.singleton . fst . fst) stTransition' <> foldMap (Set.singleton . fst . snd) stTransition' | ||
444 | stateIds <- sequence . (flip Map.fromSet) states $ \st -> node | ||
445 | [ ("label", show st) | ||
446 | , ("peripheries", bool "1" "2" $ st `Set.member` stAccept) | ||
447 | ] | ||
448 | forM_ stInitial $ \st -> do | ||
449 | init <- node [ ("label", ""), ("shape", "none") ] | ||
450 | init .->. (stateIds ! st) | ||
451 | forM_ stTransition' $ \((f, inS), (t, outS)) -> do | ||
452 | edge (stateIds ! f) (stateIds ! t) | ||
453 | [ ("label", show (inS, outS)) | ||
454 | ] | ||
318 | \end{code} | 455 | \end{code} |
319 | \end{comment} | 456 | \end{comment} |