clojure
昨日はコーヒー豆が切れたので、近くのコーヒー店に豆を買いに行った。
そしたら、そこには、JavaBeans大特価の看板がでかでかと出ていた。
JavaBeansって苦いんだよなぁーと、敬遠しましたよ。
どのぐらい苦いかと言うと、、、まだ、私がMac使いだった頃、鳴り物入りで
Java 1.01が出たんです。苦労して落としてきて、起動してみましたが、余りの
遅さに即、ごみ箱へ叩きこみましたよ。
それから、時代は経過し、女房用のWindows機が搬入された時、Ecripseを入れて
みましたが、こやつも起動したら、タバコを吸いに行くのが常体と成りました。
それで、余り使う事もなく、時間が経過し、、
どこからか、JavaBeansがいいらしいと聞きつけ、入れてみましたが、やっぱり
遅い。
それ以来、JRE環境を除いて、全てのJava開発環境を捨ててしまいました。
JREは、時々Netをうろうろしてると、サーバーサイドか何かのページにぶち
当たる事があるので、さすがに消すのは忍びないと温存してます。
そんな苦さを味わって来た訳ですが、昨日の看板で再びJava熱が。
今なら、Jrubyでしょうか。でも、ありきたり過ぎて面白くない。(Sunのあの人、
ごめんなさい、です。)
刺激を求める技術者に捧げるScala講座ぐらいが、丁度いいかなあ。
でも、有名になりすぎと言う事で今回はPASS。変わりに見つけてきたのが、
clojure
そそられる名前だ。それに、多分、Lisp発祥の地ボストン生まれっぽいのも
よさそうだ。(ほんとかどうかは、知らないけど)
Getting Started に則り、試運転してみる。
c:\app\clojure>java -version
java version "1.6.0_11"
Java(TM) SE Runtime Environment (build 1.6.0_11-b03)
Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode, sharing)
c:\app\clojure>java -cp clojure.jar clojure.lang.Repl
Clojure
user=> (+ 1 2 )
3
user=> (. javax.swing.JOptionPane (showMessageDialog nil "Hello World"))
nil
いきなり、メッセージ箱が出てきたのには、びっくりしましただ。
javax.swing って、Tcl/Tkの Tkみたいなものだな、と、無知をさらけ出して
おこう。でも、すごいな、1行のS式で、GUIをやれるなんて。
user=> (cons 1 '(2 3 4))
(1 2 3 4)
user=> (car '(1 2 3))
java.lang.Exception: Unable to resolve symbol: car in this context (NO_SOURCE_FILE:4)
user=> (cdr '(1 2 3))
java.lang.Exception: Unable to resolve symbol: cdr in this context (NO_SOURCE_FILE:5)
えぇ? car も cdr も無いの? そんなん Lispじゃないぞ!!
まあ、括弧の無い lispである、ruby にも、car,cdr は、無いからなぁ。
それじゃ、ruby 風ならいいんかいな?
user=> (first '(1 2 3))
1
user=> (rest '(1 2 3))
(2 3)
user=> (last '(1 2 3))
3
ははは、ビンゴじゃん。ruby芸がこんな場面で役に立つとは。
所で、この REPL を終了するには、どうするの?
user=> (exit)
java.lang.Exception: Unable to resolve symbol: exit in this context (NO_SOURCE_FILE:13)
user=> (quit)
java.lang.Exception: Unable to resolve symbol: quit in this context (NO_SOURCE_FILE:14)
user=> (bye)
java.lang.Exception: Unable to resolve symbol: bye in this context (NO_SOURCE_FILE:15)
user=> (sayonara)
java.lang.Exception: Unable to resolve symbol: sayonara in this context (NO_SOURCE_FILE:16)
終了できんぞ。
user=> ^Z
c:\app\clojure>
ああ、やっと抜けたわい。
このページをつらつらと見ていくと、emacs とかとも連携出来るみたい。
後で、取ってきておこう。
後は、メインページから、面白そうな所を見つけ出して、適当に見ていく。
user=> (def factorial
(fn [n]
(loop [cnt n acc 1]
(if (zero? cnt)
acc
(recur (dec cnt) (* acc cnt))))))
#'user/factorial
user=> (factorial 5)
120
Scheme 基準に考えると、define の変わりが def で、lambda の変わりが
fn と思われるな。また、上記の loopは、Schemeの場合の let と思われ。
cnt = n, acc = 1 としておいて、再帰してねか。
ちょいと癖がありそうな感じがするぞ。
まあ、ポール・グレアムの Arc よりは、想像が働くな。そう言えば、Arcの
消息はどうなったのでしょうか?
PGが、奥さんの尻に敷かれてしまったとか、赤ん坊のおむつ換えに忙しくて
パソコンに向かう余裕がないとか?
user=> (defn note [f]
(let [mem (atom {})]
(fn [& args]
(if-let [e (find @mem args)]
(val e)
(let [ret (apply f args)]
(swap! mem assoc args ret)
ret)))))
#'user/note
user=> (defn fib [n]
(if (<= n 1)
n
(+ (fib (dec n)) (fib (- n 2)))))
#'user/fib
user=> (time (fib 35))
"Elapsed time: 12755.383156 msecs"
9227465
user=> (def fib (note fib))
#'user/fib
user=> (time (fib 35))
"Elapsed time: 4.177067 msecs"
9227465
12秒かかっていた計算が、Noteを取ると(Webにある、memoizeは、使われていて
エラーになったので改名した)、4msですよ。SICPは偉大だぞ。
user=> (time (fib 35))
"Elapsed time: 0.247238 msecs"
9227465
ノートを見るだけなら、更に早い(先の4msのは、ノートを取りつつ、見てた
ので、ちと遅かったのだ)
おまけで、素のfibを petite でやってみると
> (time (fib 35))
(time (fib 35))
no collections
4516 ms elapsed cpu time
4713 ms elapsed real time
0 bytes allocated
9227465
でしたよ。ちょいと涙が出てきますね。
また、手続きを定義した時、#'user/fib と出てくるけど、これって aclっぽい。
ぶれない人、黒田さんも、納得されるかしらん?
ついでなので、もう少し遊んでみます。Hosted on the JVM のリンクに
あった、swing しましょの例。
さすが、プログラミング言語Cの最初の例ばかりでは、馬鹿にされると
思ったかどうか知りませんが、この本の2番目の例だったはず。
そう言えば、石田先生が最近、逝去されたとか、お世話になりました。合掌。
摂氏を華氏に変換するんだから、安直に下記のスクリプトを cf.clj とでも
しておくと、
(import '(javax.swing JFrame JLabel JTextField JButton)
'(java.awt.event ActionListener)
'(java.awt GridLayout))
(defn celsius []
(let [frame (JFrame. "Celsius Converter")
temp-text (JTextField.)
celsius-label (JLabel. "Celsius")
convert-button (JButton. "Convert")
fahrenheit-label (JLabel. "Fahrenheit")]
(.addActionListener convert-button
(proxy [ActionListener] []
(actionPerformed [evt]
(let [c (Double/parseDouble (.getText temp-text))]
(.setText fahrenheit-label
(str (+ 32 (* 1.8 c)) " Fahrenheit"))))))
(doto frame
(.setLayout (GridLayout. 2 2 3 3))
(.add temp-text)
(.add celsius-label)
(.add convert-button)
(.add fahrenheit-label)
(.setSize 300 80)
(.setVisible true))))
(celsius)
java -cp clojure.jar clojure.lang.Script cf.clj
これで楽しめる。窓を閉じてもスクリプトが終了しないので、最後は ^C で
終了させましょう。