(top)  (memo)  (rss)
各方面で大絶賛の嵐。というより Lisp を叩くのが人気。
こうして不人気の再生産に入るのか?
追記:1/28 Shiro さんのところに素晴しいエントリが。
S式のメリットをLisperに尋ねれば、エディタがどうの、マクロがどうの、といった 回答が真っ先に返って来ると思うんですが、そういう理屈をいくら理解しても S式がダメな人がS式を好きになったりはしません。どうも、もっと根本的な 感覚に大きな隔たりがあるような気がします。非Lisperから理解しがたい、 Lisperの持つ感覚とはどんなものなんでしょうか。Lisp脳から見た世界はどん なものなのでしょうか。
ちょっと前に S 式の利点(エディタがどうの)を説明しようと必死になっていましたがその通りですね。 しかも、このエントリは(マクロがどうの)といった話をしてしまいました :-(
Matz 日記での批判は Lisp のカタログスペック的利点を知った上で指向の違いからきています。 「Lisp である事自体が罪」というようなスタンスなので反論不能です。 orz
そういえば、 Matz 日記 (http://www.rubyist.net/~matz/20061218.html ) にのった記念。なるほど、それで Ruby 方面の人からメールがちらほら来るようになったのか。いろいろツッコミ下さった方々ありがとうございます。
一部誤解もあったので念のため訂正しておきますが、「最高にキモい〜」というのは end の使い方の話です (元ネタ: http://jijixi.azito.com/cgi-bin/diary/index.rb?date=20060328 )。 「Lisper でも Ruby ライクに書くなら end は改行する!! 一行に end を連続させたりしません!!」という主旨だったんですが suck-lisp とか書いたせいで、誤解を生じたようです。
括弧がいばり過ぎ。算数や自然言語などの学習において、 括弧はあくまで補助的役割を果たすものだという思考法が染み着いています。 にもかかわらず、Lispでは括弧に大きな意味があるため、思考のギャップが 生じます。
括弧がうざい。人間は多くの場面で結合の優先順位によって楽をすることを 知ってます。Lispはその辺をさっぱりよしなに扱ってくれないので、優しくありません。
よくある表現が分かりにくい。言語の構成要素がプリミティブ過ぎて、 よくある道具が言語機能になっていないから、ぱっと見で理解できるような書き方をするのに苦労します。 インデントの付け方で工夫できるとは言え、やっぱり辛いです。
よくある表現やよくある道具が欲しいのなら、作ればいいのです!!
(if (= x 0) y (if (> 1 y) (+ x 1) (+ x y)))
Lisper にもこれがわかり難いと感じて、かつ cond も嫌いという人はいます。 この世界では高名な Franz の John Foderaro 氏が作成した有名なマクロに if* があります。
(if* (= x 0) then y elseif (= 1 y) then (+ x 1) else (+ x y))
こんなの。まぁ cond に展開されるんですけどね。ヒマな人は自作してみると 面白いかもしれません。私は http://www.franz.com/~jkf/coding_standards.html を読んで知ったんですけどね。
;; 簡単な仕様 (if a b c) => (if* a then b else c) (if c1 a (if c2 b c)) => (if c1 then a elseif c2 then b else c)
正解はこちら → http://www.franz.com/~jkf/ifstar.txt
でも if*
使ってるプログラムってほとんど見たことないんだよなぁ…。
大多数の Lisper はやっぱり cond でいいじゃんと思ってしまうのでしょう。
なので、C並に速いLispコードは見た目も安全性もC並になる、というのが 私 (Shiro) の経験です。 もっとも、Lispの場合、大量の型宣言はマクロで隠せるのでCよりずいぶん 楽です。また、別のところでも書きましたが、最適化の途中段階ではいく つものアイディアを実装してベンチマークを取ることが重要ですが、マク ロを利用することでアルゴリズムの一部をパラメタライズしたバージョン を簡単に作れるので、より多くのアイディアを試しやすい=結果としてよ り速いコードに到達できる、ということは言えます。
ここで、「大量の型宣言をマクロで隠せるので楽です」とサラっと書いてありますが ほんとうに小さなマクロで実践できます。手元のコードから実例を探してみました。
;; はじめての版(推定) (defun magnitude (x y z) (+ (* x x) (* y y) (* z z)))
;; 今の版 (defun magnitude (x y z) (declare (optimize (speed 3) (safety 0) (debug 0) #+lispworks (float 0))) (declare (type single-float x y z)) (with-type single-float (+ (* x x) (* y y) (* z z))))
この with-type マクロですね。出典がどこか忘れましたが、(追記: cllib にあるとの指摘を受けました。どーも。) ちなみにコレの展開結果例は↓こんなかんじ。
[Before] (with-type single-float (+ (* x x) (* y y) (* z z))) ↓ [After] (the single-float (+ (the single-float (* (the single-float x) (the single-float x))) (the single-float (* (the single-float y) (the single-float y))) (the single-float (* (the single-float z) (the single-float z)))))
大量の型宣言が。
CL-USER 72 > (disassemble 'magnitude) 239D02DA: 0: 55 push ebp 1: 89E5 move ebp, esp 3: 83EC10 sub esp, 10 6: C7042486100000 move [esp], 1086 13: 8B7D0C move edi, [ebp+C] 16: 8B5D08 move ebx, [ebp+8] 19: D94701 flds [edi+1] 22: D95DF4 fstps [ebp-C] 25: 89DF move edi, ebx 27: D94701 flds [edi+1] 30: D95DF8 fstps [ebp-8] 33: D94001 flds [eax+1] 36: D95DFC fstps [ebp-4] 39: D945F4 flds [ebp-C] 42: D84DF4 fmuls [ebp-C] 45: D95DF4 fstps [ebp-C] 48: D945F8 flds [ebp-8] 51: D84DF8 fmuls [ebp-8] 54: D845F4 fadds [ebp-C] 57: D95DF4 fstps [ebp-C] 60: D945FC flds [ebp-4] 63: D84DFC fmuls [ebp-4] 66: D845F4 fadds [ebp-C] 69: D95DF4 fstps [ebp-C] 72: C7450886080000 move [ebp+8], 886 79: D945F4 flds [ebp-C] 82: D95D0C fstps [ebp+C] 85: B501 moveb ch, 1 87: C9 leave 88: FF253CDC1220 jmp [2012DC3C] ; SYSTEM::RAW-FAST-BOX-SINGLE NIL
パフォーマンスというと C/C++ 方面の人からツッコミが入りそうなのであらか じめ白状しますが、確かにこれでは C で書くよりは遅いです。実数のストアと ポップを行う FST 系の命令はペアリングできず、並列実行されないという事を 意味します。この場合は人間が書くなら FXCH とかでスタックトップを入れ替 えて演算するでしょう。実際 GCC でも最適化オプションをつけると FXCH を使っ てスタックトップを入れ替えるようなコードを生成します。Lisp コンパイラで も改善されないかなー
posted: 2007/01/25 20:50 | permanent link to this entry | Tags: LISP