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.lsp と onnewlisp.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章 返り値としての関数 と同様、長丁場になります。
以上、如何でしょうか?