Archive for the ‘values’ Tag

newLISP で On Lisp する...第20章(その2)

(この blog は、“short short story または 晴耕雨読な日々”からの引越してきたもの。スクリプトは、newLISP V10.2.1 以降で動作するように書き直しています。
defun 等の newLISP組込関数に無い関数は、特に断らない限り、newlisp-utility.lsponnewlisp.lsp に定義してあります。)

 第20章 継続 の2回目。 いよいよ、今回から、継続渡しマクロ です。
(values は、 onnewlisp.lsp に定義してあります)

(setq *cont* values)

(define-macro (=lambda)
; (=lambda parms &body body)
  (letex (_parms (cons '*cont* (args 0))
          _body (cons 'begin (1 (args))))
    (fn _parms _body)))

(define-macro (=defun)
; (=defun name parms &body body)
  (let (_f (sym (string "=" (args 0))))
    (letex (_mname (cons (args 0)
                   (map sym (map (curry string "_") (args 1))))
            _fname (append (list _f '*cont*)
                           (map sym (map (curry string "_") (args 1))))
            _vars (if (args 1)
                      (transpose (list (args 1)
                                       (map sym
                                            (map (curry string "_")
                                                 (args 1)))))
                    '())
            _body (cons 'begin (2 (args))))
      (begin
        (define-macro _mname
           _fname)
        (define _fname
          (letex _vars _body))))))

(define-macro (=bind)
; (=bind parms expr &body body)
  (letex (_parms (args 0)
          _expr (args 1)
          _body (cons 'begin
                      (or (set-ref '=values (2 (args)) (eval *cont*))
                          (2 (args)))))
    (let (*cont* (fn _parms _body)) _expr)))

(define-macro (=values)
; (=value &rest retvals)
  (letex (_body (cons '*cont* (args)))
    _body))

(define-macro (=funcall)
; (=fancall fn &rest args)
  (letex (_body (append (list (args 0) '*cont*) (1 (args))))
    _body))

(define-macro (=apply)
; (=apply fn &rest args)
  (letex (_body (append (list 'apply (args 0) '*cont*) (1 (args))))
    _body))

 先ずは、マクロ=defun の展開式を、例によって、見てみましょう。

> (=defun add1 (x) (=values (+ 1 x)))
(begin 
 (define-macro (add1 _x) 
  (=add1 *cont* _x)) 
 (define (=add1 *cont* _x) 
  (letex ((x _x)) 
   (begin 
    (=values (+ 1 x))))))
> 

 展開式中の関数定義で letex を使って引数を渡しているのがポイントです。そのために、“On Lisp” 本書のコードより、複雑なマクロになっています(汗)。
 では、実際の動作を、

> (=defun add1 (x) (=values (+ 1 x)))
(lambda (*cont* _x) 
 (letex ((x _x)) 
  (begin 
   (=values (+ 1 x)))))
> (=defun bar (x) (=values (list 'a (add1 x))))
(lambda (*cont* _x) 
 (letex ((x _x)) 
  (begin 
   (=values (list 'a (add1 x))))))
> (bar 5)
(a 6)
> 

 “On Lisp”本書の実装例では、グローバル変数 *cont* の初期値に values が使われています。 newLISPには多値がないので、以前は list で代用していましたが、今回は、マクロvalues を使っています。そのため、マクロbar の戻り値は、“On Lisp”本書の例と同じです。

> (=defun message () (=values 'hello 'there))
(lambda (*cont*) 
 (letex () 
  (begin 
   (=values 'hello 'there))))
> (=defun baz () (=bind (m n) (message) (=values (list m n))))
(lambda (*cont*) 
 (letex () 
  (begin 
   (=bind (m n) (message) (=values (list m n))))))
> (baz)
(hello there)
> 

 マクロ=bind の動きも、予定通りです。というより、そうするために余計なコードをマクロ=bind に追加しています(汗)。
 “On Lisp”本書の解説にあるクロージャの連鎖の簡単な例は、次回に実装と解説をします。
 次に、マクロ =lambda と=funcall の使用例を、

> [cmd]
(let (f (=lambda (n) (add1 n)))
   (=bind (y) (=funcall f 9)
     (println "9 + 1 = " y) nil))
[/cmd]
9 + 1 = 10
nil
> 

 この辺りのマクロは、“On Lisp”本書のコード通りといったところです。
 “On Lisp”本書の 継続渡しマクロに付随する制限 は、同じように適用されます。

> [cmd]
(=defun foo (x)
  (=bind (y) (bar x)
    (println "Ho ")
    (=bind (z) (baz)
      (println "Hum.")
      (list x y z))))
[/cmd]
(lambda (*cont* _x) 
 (letex ((x _x)) 
  (begin 
   (=bind (y) (bar x) (println "Ho ") (=bind (z) (baz) (println "Hum.") (list x y 
      z))))))
> (foo 1)
Ho 
Hum.
Ho 
Hum.
(1 (a (1 2 (hello there))) (hello there))
> 

 ここまでは、うまく継続渡しがされています。
 しかし、今回紹介した newLISP の継続渡しマクロでは、更に制限があります。それは、継続渡しマクロを使ったツリーの探索 と併せて、次回に。

 以上、如何でしょうか?

何故、newLISP なのか?。。。または、多値を実装してみる

前にも書いていますが、改めて書いておきたいと思います。
折しも、newLISP の生みの親、Lutz Mueller 氏のインタビュー記事で次の言葉を読んで得心しました。

Less code, less errors, less memory, less CPU cycles, less energy, less resources.

newLISP のコンセプトは、この言葉につきますね。
さて、Lisp の優位性は、Paul Graham 氏が“On Lisp”や。“ハッカーと画家”で書いている通りだと思います。
そして、Lisp の代表格 Common Lisp でできることは、大抵 newLISP でもでき、優位性もそのまま当てはまります。
その確証として、“On newLISP” を書いてきました。いや、書いていく予定です。
かたや、300Kバイトに満たない実行ファイルに400弱の組込関数を持つ newLISP。
そして、Mバイトサイズの実行ファイルに1000個以上もある関数を持つ Common Lisp。
どちらを選びます?
私は、newLISP を選びました。私のやりたい事に、前述の newLISP のコンセプトが合っているから。
Common Lisp で、できないという訳ではないです。

要するに、私にとって重要なのは、Lisp なのか、Lisp でないのかではなく、
私がやりたい事に使えるか使えないのかなのです。

さて、Common Lisp と newLISP には、決定的な違いが三つあります。
細かいことを言えば、ぜんぜん違うものかもしれません。
“newLISP は Lisp ですらない”、という人もいるでしょう。
構いません(笑)、あくまでも私の見方です。

一つ目は、ドット対の有無。
二つ目は、レキシカル・スコープとダイナミック・スコープ。
三つ目は、戻り値に多値が使えるかどうか。

ドット対がどうしても必要で、どんな場合でもレキシカル・スコープ状態でなくてはならず、多値が使えなんて考えられない、という人は Common Lisp か Scheme ということに。もっとも、Scheme ですら Lisp でないという人もいるでしょうけど。

さて、ドット対がどうしても必要というのは、どういう状況でしょうか?
私の浅はかな考えでは、ドット対を前提に書かれたスクリプトがあるからしか思いつきません(汗)。
長年 Lisp や Scheme を使ってきた人にとってドット対のないことは、とんでもなことかもしれませんが、Lisp や Scheme を知らない人にとっては、どうでもよいことかもしれません。
ちなみに私は、Common Lisp から newLISP に乗り換えましたが、 newLISP の方が迷わずに済みました(笑)。
逆を言えば、Common Lisp を使っていた時は、ドット対には悩まされました(汗)。

二つ目、newLISP はダイナミック・スコープですが、context を使って部分的にレキシカル・スコープを実現できます。また、ダイナミック・スコープといっても、C 言語と同様に大域変数と局所変数は区別されます。
つまり、必要に応じてレキシカル・スコープを使うことができるということ。

三つ目、戻り値の多値。Lisp は、リストを戻り値にできるのに、何故多値が必要なのか?私には、判りません(汗)。ただ、関数truncate のように、ふつう必要なのは整数部分のみで、時々、切り捨てた小数部分を取り出すのに、別の関数を使わずに済むという意味では、使い易いのかもしれません。
リストの中身をインデックス機能で簡単参照できる newLISP では、ただリストを返せばよいだけのような気がしています。それで、前の“On newLISP”では、単にリストを返す仕様にしていました。
とはいえ、newLISP で多値が実装できないわけではありません。

(context 'MAIN:multiple-value-bind)
(define-macro (multiple-value-bind:multiple-value-bind)
  (letex (_var (args 0)
          _val (args 1)
          _body (cons 'begin (2 (args))))
	  (local _var
      (setq values:mv-set true)
      (map set '_var _val)
      (setq values:mv-set nil)
      _body)))

(context 'MAIN:values)
(define-macro (values:values)
  (letex (_item (args 0)
          _lst  (cons 'list (args)))
    (if mv-set _lst _item)))
(context MAIN)

前は実装しない、いや、できませんでしたが(汗)、今ならできます。
試しに、多値を返す関数truncate を用意して、

(define (truncate number)
  (let (num (if (< number 0) (ceil number) (floor number)))
    (values num (sub number num))))

multiple-value-bindを試すと、

> (multiple-value-bind (i f) (truncate 26.8175) (println i " "  f))
26 0.8175
0.8175
> (multiple-value-bind (i f) (values 1 2) (println i " "  f))
1 2
2

このように、values 単独でも使えます。
ちなみに、関数truncate は、

> (truncate 26.8175)
26

リストの第一項目のみを返しています。トップレベルで多値を返す、とまではいきませんが(汗)。
前述の関数truncate の使い方であれば、これで十分のような気がします。
ちなみに、多値の第二項目以降を見るには、multiple-value-list を使います。

> (multiple-value-list (truncate 26.8175))
(26 0.8175)

multiple-value-list の実装は、以下の通り

(context 'MAIN:multiple-value-list)
(define-macro (multiple-value-list:multiple-value-list)
  (letex (_val (args 0))
    (let (_lst)
      (setq values:mv-set true)
      (setq _lst _val)
      (setq values:mv-set nil)
      _lst)))
(context MAIN)

これで、newLISP でも、どうしても必要なら、多値が使えます。
私は、“On newLISP”以外で使うことは、まず無いでしょうね(笑)。

以上、如何でしょうか?