Archive for the ‘multiple-value-list’ Tag

何故、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”以外で使うことは、まず無いでしょうね(笑)。

以上、如何でしょうか?