たまにはプログラミングの話題でも書きますか。
L-99 の p70 です。p70 は既出なので、「d」を付けました。
(あちこちの問題で間違いがあるような気がします。元は Prolog の問題集なので仕方ないかな。意味がよくわからないものもあります。)
a f g ^ ^ c ^ b d ^ e ^ ^ ^
こんなノード文字列が与えられた時にマルチウェイツリーを作りなさい、という問題。
ハットはバックトラックを表すマークです。
(define ht (make-hash-table 'equal?)) (define p-node nil) (define (entry node) (car node)) (define (parent node) (cadr node)) (define (child node) (caddr node)) (define (branch node) (cadddr node)) (define (set-p-node! entry) (set! p-node entry)) (define (set-parent! node entry) (set-car! (cdr node) entry)) (define (set-child! node entry) (set-car! (cddr node) entry)) (define (set-branch! node entry) (set-car! (cdddr node) entry)) (define (make-node entry parent child branch) (hash-table-put! ht entry (list entry parent child branch)) (set-p-node! entry)) (define (backtrack) (let ((node (hash-table-get ht p-node nil))) (if (null? node) (error "BACKTRACK ERROR") (set-p-node! (parent node))))) (define (add-branch entry) (define (last-branch entry) (let ((node (hash-table-get ht entry nil))) (if (null? node) p-node (begin (set-p-node! entry) (last-branch (branch node)))))) (let ((node (hash-table-get ht p-node nil))) (if (null? node) (error "ADD BRANCH ERROR") (begin (last-branch (child node)) (let ((child (hash-table-get ht p-node nil))) (make-node entry (parent child) nil nil) (set-branch! child entry)))))) (define (add-child entry parent) (let ((node (hash-table-get ht p-node nil))) (if (null? node) (error "ADD CHILD ERROR") (begin (make-node entry parent nil nil) (set-child! node entry))))) (define (construction-mtree root) (define (iter e) (let ((node (hash-table-get ht e nil))) (if (null? node) nil (append (list (list (entry node) (iter (child node)))) (iter (branch node)))))) (let ((node (hash-table-get ht root nil))) (list root (iter (child node))))) (define (parse-node-string lst) (define (iter l p) (if (null? l) ht (begin (cond ((null? p) (make-node (car l) nil nil nil)) ((eq? (car l) '^) (backtrack)) ((eq? p '^) (add-branch (car l))) (else (add-child (car l) p))) (iter (cdr l) (car l))))) (hash-table-clear! ht) (iter lst nil)) (define (list->mtree lst) (begin (parse-node-string lst) (construction-mtree (car lst))))
バックトラックは Lisp らしいコードを書いていたのでは出来ない気がしたので、
外部変数使っています。きれいなコードとは言えません。
あと外部変数はアスタリスクで括るのが一般的です。
(define *ht* (make-hash-table 'equal?)) (define *p-node* nil)
みたいに。
バックトラックしないといけないので、
ノード文字列をパースしてノードの情報(リスト)を作ります。
親の情報を持たせるのがポイントです。
(ノードエントリ 親ノード 子ノード ブランチノード)
各ノードを見てみると、
gosh> (define zzz (parse-node-string '(a f g ^ ^ c ^ b d ^ e ^ ^ ^))) zzz gosh> (hash-table-get zzz 'a) (a () f ()) gosh> (hash-table-get zzz 'f) (f a g c) gosh> (hash-table-get zzz 'g) (g f () ()) gosh> (hash-table-get zzz 'c) (c a () b) gosh> (hash-table-get zzz 'b) (b a d ()) gosh> (hash-table-get zzz 'd) (d b () e) gosh> (hash-table-get zzz 'e) (e b () ())
という感じのものをハッシュテーブルに持ちます。
なお、p-node は現在処理中のノードを指し示すポインターもどきです。
これが出来てしまえば後は簡単です。
gosh> (list->mtree '(a f g ^ ^ c ^ b d ^ e ^ ^ ^)) (a ((f ((g ()))) (c ()) (b ((d ()) (e ())))))
という感じでマルチウェイツリーが出来上がりました。
図を書かないとなかなかわかりにくいですが、
p70, p70b それから p70c の問題を解くと、
見ただけでもなんとなくわかるようになるかも?です。
マルチウェイツリー図は紙に書いたのですが、
それをテキストに起こすの面倒くさいので、
誠に勝手ながら省略させていただきました。