newLISP で On Lisp する...第22章(その1)
(この blog は、“short short story または 晴耕雨読な日々”からの引越してきたもの。スクリプトは、newLISP V10.2.1 以降で動作するように書き直しています。
defun 等の newLISP組込関数に無い関数は、特に断らない限り、newlisp-utility.lsp と onnewlisp.lsp に定義してあります。)
番外編から始まった 第22章 非決定性 の本体の始まりです(笑)。
最初の 概念 は飛ばして、探索 から。
まずは手慣らしに、決定的なツリーによる探索 の Schemeスクリプトを newLISP で記述すると、
(define (descent n1 n2)
(if (= n1 n2)
(list n2)
(let (p (try-paths (kids n1) n2))
(if p (cons n1 p) nil))))
(define (try-paths ns n2)
(if (null? ns)
nil
(or (descent (first ns) n2)
(try-paths (rest ns) n2))))
(define (kids n)
(case n
(a '(b c))
(b '(d e))
(c '(d f))
(f '(g))))
こんな感じです。ついでに、後で使う関数kids も定義しておきます。
当然動きます。
> (descent 'a 'g) (a c f g) >
もちろん本題は、非決定的なツリーによる検索 です。
newLISP には、Scheme のように call-with-current-continuation が無いので、次の Scheme での実装 を飛ばして、Common Lisp での実装 に入ります。
そして、Common Lisp による非決定的オペレータ を newLISP で実装します。
(defun は newlisp-utility.lsp に定義してあります)
(define *paths* '())
(define failsym '@)
(define-macro (choose)
; (choose &rest choices)
(letex (_choice (args 0)
_choices (args))
(if '_choices
(begin (map (fn (c) (letex (_c c) (push (fn () _c) *paths*)))
(reverse (rest '_choices)))
_choice)
(fail))))
(define-macro (choose-bind)
; (choose-bind var choices &body body)
(letex (_var (args 0)
_choices (args 1)
_body (cons 'begin (2 (args))))
(cb (fn (_var) _body) _choices)))
(defun cb (f choices)
(if choices
(progn
(if (cdr choices)
(letex (_f f _ch choices)
(push (fn () (cb _f (cdr '_ch)))
*paths*)))
(f (car choices)))
(fail)))
(defun fail ()
(if *paths*
((pop *paths*))
failsym))
さて、動作例を、まずは、choose から、
> (defun do2 (x) (choose (+ x 2) (* x 2) (pow x 2))) (lambda (x) (choose (+ x 2) (* x 2) (pow x 2))) > (do2 3) 5 > (fail) ERR: value expected in function * : x called from user defined function fail > (define *paths* '()) () > (defun do2 (x) (letex (_x x) (choose (+ _x 2) (* _x 2) (pow _x 2)))) (lambda (x) (letex (_x x) (choose (+ _x 2) (* _x 2) (pow _x 2)))) > (do2 3) 5 > (fail) 6 > (fail) 9 > (fail) @ >
番外編でも書きましたが、newLISP のダイナミック・スコープで、継続を行う時は、newLISP組込letex を使って変数をクロージャに組み込んでおく必要があります。
そうしないと、最初の関数do2 のように、fail を実行した時、エラーとなります。
そして、choose-bind の例。
> (choose-bind x '(marrakesh strasbourg vegas) (format "Let's go to %s." (string x))) "Let's go to marrakesh." > (fail) "Let's go to strasbourg." > (fail) "Let's go to vegas." > (fail) @ >
choose のマクロ展開例のスクリプトには、やはり、letex を使います。
(let (x 2)
(letex (_x x)
(choose
(+ _x 1)
(+ _x 100))))
例によって、展開式すると、
(if '((+ 2 1) (+ 2 100)) (begin (map (lambda (c) (letex (_c c) (push (lambda () _c) *paths*))) (reverse (rest '((+ 2 1) (+ 2 100))))) (+ 2 1)) (fail))
変数x をすべて展開して組み込みますから、“On Lisp” 本書例より複雑になります(汗)。
動作は、
> [cmd] (let (x 2) (letex (_x x) (choose (+ _x 1) (+ _x 100)))) [/cmd] 3 > (fail) 102 > (fail) @ >
同じですが。
長くなってきたので、継続渡しマクロ と共に使った例は、次回に。
以上、如何でしょうか?
コメントを残す