Archive for the ‘cdr’ Tag

first, second, third 、、、

 newLISP には、first は有りますが、second, third 等はありません。
 別に無くても、インデックス機能があるので、要らないのですが、困ったこともあります。

> ('(0 1 2) 2)
2
> ('(0 1) 2)

ERR: list index out of bounds
> 

 こんな風に、範囲外を指定するとエラーになります。最初に length でリストの大きさを調べるか、catchthrow の組み合わせれば良いのですが、結構手間なので、私は“newlisp-utility.lsp”で次のように登録して、使っています。

(define cdr    (fn (lst) (or (rest lst) nil)))
(define car    (fn (lst) (if (empty? lst) nil (first lst))))
(define second (fn (lst) (first (or (rest lst) '(nil)))))
(define third  (fn (lst) (first (or (rest (rest lst)) '(nil)))))
(define fourth (fn (lst) (first (or (rest (rest (rest lst))) '(nil)))))

 これなら、

> (third '(1 2 3))
3
> (third '(1 2))
nil
> 

 こんな感じで、範囲外は nil になってくれます。
 また、carcdr は、引数が空リスト () の時、nil が返ります。私のスクリプトで、時々、carcdr を使っているのは、そういう効果を期待していたりします。
 さて今回、carcdr 共に、second 以下を一新しました。

(define cdr    (fn (lst) (if-not (nil? lst) (or (rest lst) nil))))
(define car    (fn (lst) (first (or lst '(nil)))))
(define second (fn (lst) (car (cdr lst))))
(define third  (fn (lst) (car (cdr (cdr lst)))))
(define fourth (fn (lst) (car (cdr (cdr (cdr lst))))))

 変更点は、上記関数の引数が nil の時でも、nil を返すようにしたこと。
 と言うことで、またまた、ですが、“newlisp-utility.lsp”のアップデートです(笑)。

 以上、如何でしょうか?

newLISP のnil と 空リスト()

 “newLISP で On Lisp する...第20章(その1)...または、newLISP と scheme の違い” でも書いていますが、newLISP では nil と 空リスト () は別のものです。
 実際、マニュアルには、

newLISP では、nil と空リスト () は、他の Lisp のように同じではありません。条件式、and, or, if, while, unless, until, cond、においてのみ、それらはブールの偽として扱われます。

In newLISP, nil and the empty list () are not the same as in some other Lisps. Only in conditional expressions are they treated as a Boolean false, as in and, or, if, while, unless, until, and cond.

 とあります。
 つまり、空リストは、フロー制御式の条件式においては nil ですが、それ以外の組込関数では、true 相当です。
 ここで、フロー制御式には、do-whiledo-until も含まれます。
 それ以外では、true 相当ですから、例えば、組込filterclean で、

> (map rest '((a b) (a) ()))
((b) () ())
> (filter rest '((a b) (a) ()))
((a b) (a) ())
> (clean rest '((a b) (a) ()))
()
>

 となります。これは、以前、私が使い方を間違えた例です(汗)。
 組込filter は、第一引数の関数を第二引数のリストの項目に適用して、true に評価されるものを取り除きます。組込clean は、逆に残します。これらの関数では、空リストは true 相当ですから、上記のような結果になったわけです。
 ここでは、次のようにして、空リストを nil にすべきだったのです。

> (map rest '((a b) (a) ()))
((b) () ())
> (filter (fn (x) (or (rest x) nil)) '((a b) (a) ()))
((a b))
> (clean (fn (x) (or (rest x) nil)) '((a b) (a) ()))
((a) ())
> 

 ここで、使っているラムダ式は、newlisp-utility.lsp で定義している cdr と同じです。

> cdr
(lambda (lst) (or (rest lst) nil))
>

 引数が空リストなら、マニュアルにあるように組込or では、空リストは、nil 相当ですから、次の項目に進み、nil が返ります。
 ちなみに、newlisp-utility.lsp で定義している car は、

> car
(lambda (lst) 
 (if (empty? lst) 
  nil 
  (first lst)))
> (car '())
nil
> 

 としています。空リストに組込first を適用するとエラーですから。
 “newLISP で On Lisp する”で、car と first、cdr と rest の両方を使い分けているのは、こういう理由だったりします(笑)。

 以上、如何でしょうか?

newLISP で On Lisp する...第15章(その1)

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

 第15章 関数を返すマクロ は、関数の構築 から、汎用の関数生成マクロ を実装します。(gensym と labels は newlisp-utility.lsp に定義してあります。)

(define-macro (fn+ expr)
  (rbuild expr)) 

(defun rbuild (expr)
  (if (or (atom? expr) (lambda? expr))
      expr
    (if (= (first expr) 'compose)
        (build-compose (rest expr))
      (build-call (first expr) (rest expr))))) 

(defun build-call (op fns)
  (let (g (gensym))
  (letex (_g g
          _op op
          _fbody (cons op (map (fn (f) (list (rbuild f) g)) fns)))
    (fn (_g) _fbody)))) 

(defun build-compose (fns)
  (let (g (gensym))
    (letex (_g g
            _lbody (labels ((rec (_f)
                              (if _f 
                                  (list (rbuild (first _f))
                                        (rec (rest _f)))
                                g)))
                           (rec fns)))
      (fn (_g) _lbody))))

 newLISP では、fn は、lambda と同じなので、fn+ に改名してあります。
 動作は(oddp は newlisp-utility.lsp に定義してあります)、

> (define int-odd? (fn+ (and integer? oddp)))
(lambda (gensym7) (and (integer? gensym7) (oddp gensym7)))
> (map int-odd? '(2 3 'a))
(nil true nil)
> (define s2i+ (fn+ (compose list ++ int)))
(lambda (gensym9) (list (++ (int gensym9))))
> (map s2i+ '("2" "3" "14"))
((3) (4) (15))
> (define s2i+3 (fn+ (compose (fn (x) (+ 3 x)) int)))
(lambda (gensym10) ((lambda (x) (+ 3 x)) (int gensym10)))
> (map s2i+3 '("2" "3" "14"))
(5 6 17)
> 

 という風に関数が返り、実際に組み合わせの動作をします。newLISP組込int は、Common Lisp の truncate と同じように使えます。
 この関数を使えば、(identity と map1-n は onnewlisp.lsp に定義してあります。)

> (map (fn+ (and integer? oddp)) '(c 3 p 0))
(nil true nil nil)
> (map (fn+ (or integer? symbol?)) '(c 3 p 0.2))
(true true true nil)
> (map1-n (fn+ (if oddp ++ identity)) 6)
(2 2 4 4 6 6)
> (map (fn+ (list -- ++ ++)) '(1 2 3))
((0 1 2) (1 2 3) (2 3 4))
> 

 と、なります。
 newLISP 組込 ++-- は破壊的関数なので、最後の例のような動作をします。
 さて、今までの newlisp-utility.lsp では cdr を rest で置き換えていますが、今回から、次の定義を使います。

(define cdr (fn (lst) (or (rest lst) nil)))

 そして(consp と remove-if は newlisp-utility.lsp に定義してあります)、

> (remove-if (fn+ (or (and integer? oddp) (and consp cdr))) '(1 (a b) c (d) 2 3.4 (e f g)))
(c (d) 2 3.4)
> 

 ちなみに、cdr と newLISP組込rest の違いは、

> (map cdr '((a b) (a) ()))
((b) nil nil)
> (map rest '((a b) (a) ()))
((b) () ())
> 

 そして、newLISP では、nil と 空リスト () は別物なので、上記remove-if の使い方では、cdr が必要です。

 さて、関数を入れ子するよりも

> (fn+ (list (++ int)))
(lambda (gensym14) (list ((lambda (gensym15) (++ (int gensym15))) gensym14)))
> (fn+ (compose list ++ int))
(lambda (gensym16) (list (++ (int gensym16))))
> 

 compose を使った方が簡潔になるというのが、マクロfn+ のポイントでしょうか(笑)。

 ということで、切りが良いので、Cdr 部での再帰 からは、次回に。
 関数版に相当する 第5章 返り値としての関数 と同様、長丁場になります。

 以上、如何でしょうか?