技術野郎の復讐---Revenge of the Nerds--- [practical-scheme.net]とかそこに引用されている comp.lang.lisp > How I lost my faith (very long) [google.com]を読むとLispってのは Nerds御用達言語らしいというのが想像できて、あこがれます。2つの記事を(超)要約すると 「Lispを使えば短期間で開発ができて、コードの量も非常に小さくできる。PythonはLispの再発明だ」 ということです。しかし、ヘタレなぼくにはそのすごさがいまいち理解できません。そこに書かれている (defun foo (n)
#'(lambda (i) (incf n i))) はいったい何をしたいのでしょうか。Lisperの方、教えてください。
Nerds御用達言語 (スコア:1)
comp.lang.lisp > How I lost my faith (very long) [google.com]を読むとLispってのは
Nerds御用達言語らしいというのが想像できて、あこがれます。2つの記事を(超)要約すると
「Lispを使えば短期間で開発ができて、コードの量も非常に小さくできる。PythonはLispの再発明だ」
ということです。しかし、ヘタレなぼくにはそのすごさがいまいち理解できません。そこに書かれている
(defun foo (n)
#'(lambda (i) (incf n i)))
はいったい何をしたいのでしょうか。Lisperの方、教えてください。
Rubyとの関連については
love && peace && free_software
t-nissie
Re:Nerds御用達言語 (スコア:1)
Lispに方言が多かったのは、過去の話じゃないですか。いまはANSI Common Lisp標準がありますから。もちろん処理系によって細かい差異はあるんですけど、それはC言語なんかでも同じですよね?
標準 (スコア:1)
WHY Common Lisp? [msi.co.jp]を読むとそうみたいですね。
で、SchemeにはR5RSがあると。
> In 技術野郎の復讐---Revenge of the Nerds---
> * これが、他のLisp方言におけるアキュムレータジェネレータのコードだ。
> Scheme: (define (foo n) (lambda (i) (set! n (+ n i)) n))
> Goo: (df foo (n) (op incf n _)))
> Arc: (def foo (n) [++ n _])
Emacs は (defun foo (n) '(lambda (i) (incf n i))) でしょうか。
各Lispの方言の中では標準が固まっているといってよいのでしょうか。
しかし、各方言の特徴をWikipediaなんかで読んでも話が高度すぎて
畏れ多いというか、わけがわからないというか…
love && peace && free_software
t-nissie
Re:標準 (スコア:3, 興味深い)
念のため突っ込みを入れておくと、Emacs Lispにはclosureが無いので、これは意図通りに動きません。 例えば、元コメントの関数fooを定義した後で、以下を実行してみるとCommon Lispとの違いが分かります。
Common Lispでは2, 3, 42が順に表示されますが(1.0じゃないけどSBCL 0.9.16で確認)、 Emacs Lispでは43, 44, 44が順に表示されます(Emacs 21.3で確認)。fooの返す関数に使われている変数nが、Emacs Lispでは関数を呼び出した箇所の変数nを指している一方、 Common Lispでは元のfooの定義に現われる変数nを指している(そしてfooの呼出時に新たに作られた束縛を参照している)ことになります。 このことを、Emacs Lispは変数がdynamic scopeを持ち、Common Lispでは(defaultでは)lexical scopeを持つと言ったりします。
しかもCommon Lispの変数は(defaultでは)無限のextentを持つので、fooの呼び出しで作られた束縛が、 その後の「(Paul Grahamの言う)アキュムレータ」呼び出しでも参照され、次々と更新されていく様子が分かるかと思います。 Emacs Lispでも(値が違うものの)一見数値がインクリメントされていますが、 更新されているのは、アキュムレータを呼び出している側のlet式の変数nです。Common Lispではlet式の変数nは更新されていません。
t-nissieさんのコメント中の定義で使われているincfはEmacs Lispでは cl.el("Common Lisp extensions for Emacs")に含まれるマクロです。 「Common Lisp拡張」という名前ではありますが、 incfのようなちょっとしたマクロをそれっぽく定義することはできても、 変数のscopeやclosureまでCommon Lisp互換にするのはさすがに実現できていません。 というわけで、Paul Grahamの言う「言語の相対的な力」というのがclosureの書きやすさで決まるのなら、 Emacs LispはCommon Lispと違い、論外の超弱々言語ということになるかもしれません。:-) まあclosureは確かに便利というか、(プログラムで高階関数を使いだすと)むしろ「無いとストレスが溜る」ものなので、 Paul Graham氏の主張も分からないではないですが。
なお、「イマドキclosureを説明するのにmutableな変数を前提とするってのはどうよ」などの突っ込みは、 Paul Grahamさんに直接おっしゃってください。 Paul Grahamの定義ではHaskellとかは相対的に弱い言語ってことになったりして? :-p
Re:標準 (スコア:2, 参考になる)
う、調べず書くもんじゃない……。
というのは嘘でした。Common Lisp extensions for Emacsを使うと、元コメントのアキュムレータは以下の様に定義できます。
Common Lisp Extensionsに含まれるlexical-letを使うというのがミソですね。 このfooを元コメントのように使ってやると、ちゃんと2, 3, 42を順に表示します。 というわけで、Emacs LispもPaul Graham氏に見放されずには済みそうです。ごめんなさい。
まとめ (スコア:2, 興味深い)
まだclosureとか本質はわかっていませんが、とりあえず各言語で試してみました。
● Common Lisp (CLISP。sbcl-1.0 は、Linux kernel の NPTL threading を使う
らしく、今使っているちょっと古めのLinux Boxにはインストールできませんでした。)
$ clisp
[1]> (defun foo (n) #'(lambda (i) (incf n i)))
FOO
[2]> (defvar bar (foo 5))
BAR
[3]> (funcall bar 3)
8
[4]> (funcall bar 7)
15
[5]> (exit)
● Scheme (Gauche)
$ gosh
gosh> (define (foo n) (lambda (i) (set! n (+ n i)) n))
foo
gosh> (define bar (foo 5))
bar
gosh> (bar 3) ; なぜfuncall等が不要なのか?
8
gosh> (bar 7)
15
gosh> (exit)
; Gaucheはreadlineがないので入力時に発狂しそうになりました。
; 通はEmacsから使うそうですが。
● Emacs Lisp (in *scratch* buffer, with C-j)
(version)
"GNU Emacs 22.0.91.1 (i686-pc-linux-gnu, X toolkit, Xaw3d scroll bars) ..."
;; Common Lisp extensions for Emacs
(require 'cl)
=> cl
(defun foo (n)
(lexical-let ((n n))
(function (lambda (i) (incf n i)))))
=> foo
(defvar bar (foo 5))
=> bar
(funcall bar 3)
=> 8
(funcall bar 7)
=> 15
● Ruby
$ irb
irb(main):001:0> def foo(n)
irb(main):002:1> lambda {|i| n += i}
irb(main):003:1> end
=> nil
irb(main):004:0> bar = foo(5)
=> #<Proc:0x40222a3c@(irb):2>
irb(main):005:0> bar.call(3)
=> 8
irb(main):006:0> bar.call(7)
=> 15
irb(main):007:0> qux = foo(1)
=> #<Proc:0x40222a3c@(irb):2>
irb(main):008:0> qux.call(2)
=> 3
irb(main):009:0> qux.call(3)
=> 6
irb(main):010:0> bar.call(1)
=> 16 (barとquxとは独立。しかし、p bar と p qux とが同じなのが謎。)
irb(main):011:0> exit
love && peace && free_software
t-nissie
Re:まとめ (スコア:1)
あ、日記 [srad.jp]でですね。 すみません、気付いてなかったので「余計なもの」になってました。(_ _)
これは、Schemeでは名前の関数値と変数値に区別がなく、 リスト先頭の要素も他の要素と同様に評価される(R5RSの"4.1.3. Procedure calls"のところ)からです。 なので、Schemeでは なんてのも書けます(R5RSより引用)。一方Common LispやEmacs Lispでは、変数値と関数値は別々に扱われます。 Common LispやEmacs Lispで、
とかやると分かると思います。 同じ名前でも関数と変数が区別される処理系では、 とか書けますが、これは逆にSchemeでは動きません。Re:標準 (スコア:1)
GooやArcは、Common Lispより後に生まれた新しいLisp方言で、正直、Common Lispに飽き足らない人向けですから、これからLispを始めようという人はCommon LispかSchemeのどちらかに手をつけるといいと思います。Common LispもSchemeも多数の処理系がありますが、どちらも標準がありますよ。
Re:標準 (スコア:0)
どうも標準化ってのが、単にいろいろ不便にしてるだけとし
か思えないまま過ぎ去った10ウン年
(defun ac (x y)
(cond ((zerop x) (+ y 1))
((zerop y) (ac (- x 1) 1))
(t (ac (- x 1) (ac x (- y 1))))]
とか
(defun factorial (n)
(cond ((zerop n) 1)