seesaw
Webをうろいろしてたら、面白いページに行き当たった。 計算尺推進委員会
かの昔、アポロ宇宙船にも搭載されていたという古きよき時代の計算ツール。日本が誇る いにしえの計算ツール、算盤は宇宙船に載っていたという話は聞かないから、計算尺は 近代計算の礎なんだろうな。ネイピアさんに感謝しましょう。
オイラーと計算尺の関わりは、、、そう、その手の学校でした。いわゆる工学系ね。計算命って 所です。計算尺の検定2級だったかを取らされたなあ。
計算尺ってネイピアさんの発見した対数を応用した道具。掛け算は対数の足し算だよ、割り算は、 対数の引き算だよってのが基礎になってる。
基本操作は掛け算(1)あたりを 見ればいいけど、数を浮動小数点として見た場合、計算尺で計算出来るのは、仮数部のせいぜい3桁が アナログ的に求まるだけ。指数部(桁合わせ)は、人間様が頭の中でやってねって、協調動作を する事になる。
失敗学の勧めで有名な畑中先生も言ってたけど、工学ではざっくりどのくらいってのが重要で、 桁を間違える事は、非常に大きな許せない見過ごせない失敗との事。
そりゃそうだわな。65cmの長さの俵を敷き詰めて、直径4.55mの土俵を作るとしたら、俵は何個 必要? なんて問題が出された時こそ、計算尺の出番です。(暗算でやるのがスマートでしょうけど)
こういう問題は、答えが小数点以下3桁も求めたって意味無い。ざっくり、俵が1桁で済むのか 2桁(10個台)になるのかが重要。
たまには触ってみたいな。計算尺。現代コンピュータ上で再現出来ないかしらん。 JavaScript上の計算尺
読書メーター 7冊 / 2247頁 / 10076円
シーソーその前に
この間からclojureに嵌まっている。clojureの元気の元はJava。ならば、Javaでお絵かきが 出来れば、仮想計算尺だって、きっと作れるに違いない。なんたって、Javascript*でも*実現 出来ているんだから。
clojureからJavaのスイングだかを使うやつが有ったはず。シーソーとか言ったな。昔ちょっと やった事がある。まあ、その前にJavaでお絵かきとかはどうするの? ちょいと調べておく。
WikiのSwingの項の説明によれば、Javaのお絵かき第1弾はAWTとか言うやつだったらしい。 どこでもJavaを標榜してJavaが生まれたけど、Javaが載るOSによって、GUIの見え方が違っていた。 Windows上のJAvaで開発したGUIをLinuxに持ってくると、細かい調整が必要になるとか、 いろいろ不評だったんでしょうな。まるで、EXCELL対オープンオフィスみたいにね。
それじゃ、天下取れないじゃんと発奮して開発されたのが第2弾のSwing。これで天下を 取れたかと思ったけど、どこかの後塵を拝した。そこでJavaFXが開発されたって歴史に なるのかな。だから、Swingを知っておけば、まあ何とかなるか。決してFXなんて言う怪しい物に 手を出してはいけません。身の破滅を招きます。
正直、面倒くせぇー!! こんなのやってられるか。そこでだ、
seesaw
seesawの出番ですよ。Javaとclojureの間をぎっこん・ばったんしてねって 思いで、作者さんは名前を付けたんでしょうかね。残念ながら、2年ほど前に、開発が 停止しちゃってるみたいですけど。後は元気のある奴に任せるとな。
これは必然なのでしょうね。元になるSwingも止まったままですから。枯れたやつも たまにはいいか。どのぐらいいいかと言うと、
(ns hello-seesaw.core (:use seesaw.core)) (defn -main [& args] (invoke-later (-> (frame :title "Hello", :content "Hello, Seesaw", :on-close :exit) pack! show!)))
これがGUI版の例のやつです。seesawは、1.4.4版が手間隙かからずにlein depsで取り寄せ られます。とまあ、ここまではいいのですが、上記からGUIに発展させていくには至難の技。 どこかにサンプルは無いか? 丁度目立つ所にexampleが有ったのでその中のREADME.mdを 見たら、詳しくはtest/seesaw/test/examplesを参照との事。
で、その中のREADMEを見ると、
$ lein examples
で、GUIのサンプルの発射台が現れるとな。はて?
examples
leinのアーギュメントにexamplesなんてのが有ったかしら? project.cljを覗いてみたら 納得する答えが書いてあった。
; To run the examples: ; ; $ lein examples ; :aliases { "examples" ["run" "-m" "seesaw.test.examples.launcher"] }
エイリアスなんてshellまがいの機能がleinには備わっているのね。知りませんでしたよ。 ついでに、-m ってオプションを調べてみると、どうやら、(-main) を起動する特技が あるらしい。
発射台
サンプルの中にサンプルとして発射台が用意されてるって、再帰してんな。これぞLisp。
早速、Windowsから起動しようとしたら
C:\Users\sakae\Downloads\seesaw-develop>lein examples Java compiler not found; Be sure to use java from a JDK rather than a JRE by modifying PATH or setting JAVA_CMD.
なぬー! JDKが必要難解。こりゃ困った。WindowsまでにJDKなんて入れる積もりは無いんよ。 何ったって、おいらはJavaのライトユーザーですから。しゃーない、万次郎Linuxの出番だな。
起動してみると、色々なサンプルが列挙されたWindowがポップアップされてきました。どれかを 選んでおいて、最下段にある発射ボタンを押すと、サンプルのGUIが動きます。 これって、一頃のTcl/Tkにあった陳列台じゃないですか。こんな事も出来末世って自慢の種だな。
これ、どう動いているの? 発射台の設計図をちらちらと眺めましたよ。そしたら、どうやら 核はこちらの様。
(defn launch-example [s] (let [example-ns (str "seesaw.test.examples." s) _ (require (symbol example-ns)) run-fn (resolve (symbol example-ns "run"))] (println run-fn) ;; For monitor (if run-fn (run-fn :dispose) (alert (str "Couldn't find function " (symbol example-ns "run"))))))
GUIと言いつつ、核がこういう所に潜んでいるとは面白いな。GUIの華やかさに目移りしては いけません。
user> (ns seesaw.test.examples.launcher) ;;=> nil seesaw.test.examples.launcher> (launch-example 'clock) Reflection warning, seesaw/test/examples/clock.clj:79 - reference to field getSize can't be resolved. #'seesaw.test.examples.clock/run ;;=> #<JFrame$Tag$a79ba523 seesaw.core.proxy$javax.swing.JFrame$Tag$a79ba523[frame0,0,28,400x400,layout=java.awt.BorderLayout,title=Seesaw Canvas Clock,resizable,normal,defaultCloseOperation=DISPOSE_ON_CLOSE,rootPane=javax.swing.JRootPane[,5,25,390x370,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]>
これで、時計が出てきましたよ。指定したサンプルをrequireして、その中のrunという関数を 起動してるとな。でも、runなんてのは、目を皿のようにして眺めても見つからんぞ。
この秘密はいかに?
展開するぞ
思い立つのは、マクロでrunとかを生成してるぐらいでしょうかね。そう言えば、-mで起動 される(はず)の(-main)も発射台のサンプルには無いし。。。
各サンプルの一番最後に鎮座してる、defexampleっていう関数定義っぽいのが怪しいな。 こやつを調べてみると、example.cljにマクロが出てた。
マクロを一見して展開形を描ききれなかったので(すんません、修行が足りなくて)、ciderに 展開して貰おう。展開のお題は、RPN計算機。
(defexample [] (-> (layout) behave (apply-stylesheet style) ; <- NOTE use of experimental/alpha function ))
最後にカーソルを合わせて、C-c RET すると、
(do (defn run [on-close__8932__auto__ & args__8933__auto__] (let [[] args__8933__auto__ f__8934__auto__ (invoke-now (-> (layout) behave (apply-stylesheet style)))] (config! f__8934__auto__ :on-close on-close__8932__auto__) (when (= (java.awt.Dimension.) (.getSize f__8934__auto__)) (pack! f__8934__auto__)) (show! f__8934__auto__))) (defn -main [& args__8933__auto__] (apply run :exit args__8933__auto__)))
おおお、出てきたぞ。runと -mainが。-mainは、:exitってアーギュメントを付けてrunを 呼び出してる。こういう形は前回、おいらも気が付いていたな。Lisp脳が成長中だな。
runの中身は、やっぱりGUI表示の基本形に還元されてるな。決まりきった事は、マクロの 中に隠して、本筋だけをプログラミングしてねって事ですな。こういうのに出会うと、 やれオブジェクト嗜好で、継承だとかほざくのが馬鹿らしくなるよ。
ついでに、発射台を発射出来るか、検証しとく。
user> (ns seesaw.test.examples.launcher) ;;=> nil seesaw.test.examples.launcher> (-main) ;;=> #<JFrame$Tag$a79ba523 seesaw.core.proxy$javax.swing.JFrame$Tag$a79ba523[frame0,0,28,200x500,invalid,layout=java.awt.BorderLayout,title=Seesaw Example Launcher,resizable,normal,defaultCloseOperation=EXIT_ON_CLOSE,rootPane=javax.swing.JRootPane[,5,25,190x470,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]>
告白
と、ここまですいすいと進んできたように書いちゃったけど、最初は戸惑いがありましたよ。 何に心を惹かれてしまったかと言うと、leinで言うtestエリア内にexamplesが置いてあった事。
きっとテストの一環として例が書かれているんだろうって、想像しちゃったのね。 テストなんて大嫌いなんで、避けてたのさ。でも、いよいよテストに向き合わなくちゃならないかと、 気が滅入ってしまったのは事実。
そこでだ、テストを知るって事で、シーソーはどこ製のテストを使ってるか調べてみましたよ。 いわゆるテスト対策ですな。敵を知れば、きっと突破も容易だろると思ってね。
project.cljを見ると、[com.stuartsierra/lazytest "1.1.2"]なんて業者名が書いてあったよ。 lazytestが、それらしい。 斜め読みした所、Rspec風に書けるとな。Rubyの時にしっかりやっとくんだったわい。
(describe + "with integers" (it "computes the sum of 1 and 2" (= 3 (+ 1 2))) (it "computes the sum of 3 and 4" (= 7 (+ 3 4))))
こんな風に自然に書けるとな。これを使った専用ドライバーが用意されてた。
[sakae@manjaro seesaw-develop]$ cat lazytest.sh #!/bin/sh JAVA_OPTS="-XX:MaxPermSize=512m -Xmx512m" java $JAVA_OPTS -cp `lein classpath` lazytest.main test
走らせてみる。
[sakae@manjaro seesaw-develop]$ sh lazytest.sh : Namespaces seesaw.test.event #'seesaw.event/unappend-listener can remove a listener #'seesaw.event/listen-to-property registers a property change listener : seesaw.test.icon #'seesaw.icon/icon returns nil given nil returns its input given an Icon returns an icon given an image returns an icon given a URL returns an icon given a path to an icon on the classpath returns an icon given a File returns an icon given a i18n keyword Ran 990 test cases. 0 failures.
でも、どうも可笑しいって事で、ソースを再度読んだら、上記の仕組みに辿りついた次第。 思い込みは恐いな。
Windowsでもサンプル実行
しようと思ったんだ。それには、発射台にセットされてる各種ロケットをバラして持って来るのが 手っ取り早い。
[sakae@manjaro gr]$ diff -u clock.clj clk1.clj --- clock.clj 2014-09-23 06:07:43.000000000 +0900 +++ clk1.clj 2014-09-23 06:13:01.000000000 +0900 @@ -8,11 +8,24 @@ ; the terms of this license. ; You must not remove this notice, or any other, from this software. -(ns seesaw.test.examples.clock +(ns clk1 (:use seesaw.core seesaw.graphics - seesaw.color - seesaw.test.examples.example)) + seesaw.color)) + +(defmacro defexample + [arg-vec & body] + `(do + (defn ~'run [on-close# & args#] + (let [~arg-vec args# + f# (invoke-now ~@body)] + (config! f# :on-close on-close#) + (when (= (java.awt.Dimension.) (.getSize f#)) + (pack! f#)) + (show! f#))) + + (defn ~'-main [& args#] + (apply ~'run :exit args#)))) ; A very rudimentary example of (canvas) that draws an analog clock
こんな風に、マクロを輸入してきておけば楽だ。起動は
clk1=> (run :dispose)
するか、-mainを起動してあげればよい。どうしても、マクロを持ってくるのがイヤなら
[sakae@manjaro gr]$ diff -u clock.clj clk2.clj --- clock.clj 2014-09-23 06:07:43.000000000 +0900 +++ clk2.clj 2014-09-23 06:15:54.000000000 +0900 @@ -8,11 +8,10 @@ ; the terms of this license. ; You must not remove this notice, or any other, from this software. -(ns seesaw.test.examples.clock +(ns clk2 (:use seesaw.core seesaw.graphics - seesaw.color - seesaw.test.examples.example)) + seesaw.color)) ; A very rudimentary example of (canvas) that draws an analog clock @@ -76,7 +75,7 @@ ; Draw a little circle in the middle (draw g (circle 0 0 3) tick-style))) -(defexample [] +(defn clk2 [] (let [cvs (canvas :id :canvas :background "#BBBBBB" :paint paint-clock) t (timer (fn [e] (repaint! cvs)) :delay 1000)] (frame
強引にdefexampleをdefnにしてしまい、
clk2=> (invoke-later (-> (clk2) pack! show!))
とでもするかだな。で、実際に上記を走らせてみると、clockがメニューの中に張り付いて しまって、姿を表してくれなかった。同じ事をrpnの計算機でやってみたら、こちらは問題無し。 この差は何なの? こういう事に悩むのは無駄。素直にマクロを輸入しておけば問題無し。
Windowsでも発射台
一旦はWindowsで発射台強いてはexamplesの実行を諦めてしまった。けど、よく考えたら examplesの環境を決めてる、project.cljを調整すれば、発射台を設置出来るんではなかろうかと、 想像を巡らすようになった。
実験は1分もあれば出来るんで、やってみんべ。
C:\Users\sakae\Downloads\seesaw-develop>cat project.clj (defproject seesaw "1.4.4-SNAPSHOT" :aliases { "examples" ["run" "-m" "seesaw.test.examples.launcher"] "clock" ["run" "-m" "seesaw.test.examples.clock"] "rpn" ["run" "-m" "seesaw.test.examples.rpn"] } :dependencies [[seesaw "1.4.4"]] )
こんなのを作っておいて、
C:\Users\sakae\Downloads\seesaw-develop>lein examples C:\Users\sakae\Downloads\seesaw-develop>lein clock C:\Users\sakae\Downloads\seesaw-develop>lein rpn
発射台も時計も計算機も無事に動いた。後は、GUIの技を盗むだけだな。