Archive for the ‘fn+’ Tag

projecteuler6...または、newLISP の無名関数

 projecteuler問題 6 は、1 から 100 までの自然数の和を二乗したものと、1 から 100 までを各々二乗してから和を求めたものとの差。
 と、projecteuler にしては至極単純な計算。これを newLISP 風に料理する(笑)と、

(let (numbers (sequence 1 100)
      square (fn (x) (* x x)))
  (- (square (apply + numbers))
     (apply + (map square numbers))))

 こんな感じ。newLISP にはmのn乗を計算してくれる組込関数pow もありますが、ここでは二乗する関数 square をラムダ式で定義しています。LISP で一般的な無名関数lambda は当然 newLISP でも使えますが、newLISP では組込関数fn を使うのが普通。fn と言えば、マニュアルにも記載がある通り、

Paul Graham氏 が Arc 言語プロジェクトで提案している代替構文

 です。新世代 LISP にふさわしい構文と言えましょう。まっ、欠点といえば、fn というシンボル名が使えないことくらい(笑)。
 さて、気になる答えは、

> [cmd]
(let (numbers (sequence 1 100)
      square (fn (x) (* x x)))
  (- (square (apply + numbers))
     (apply + (map square numbers))))
[/cmd]
25164150
> 

 以上、如何でしょうか?

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章 返り値としての関数 と同様、長丁場になります。

 以上、如何でしょうか?