incanter
clojureの盆栽本は、Webを やる人の必携本らしい。オイラーは、Webをとっくに卒業してしまったので、年寄りの趣味で、 正真正銘の盆栽本を読んでみた。『盆栽の誕生』(大修館書店)
盆栽は遠い昔、奈良時代にその根があるとか。坊さんの趣味だったらしい。やがて武士の 時代になって殿様に献上されるようになったとか。徳川の将軍も大好きな人がいて、いろいろ 珍しいものを集めていたらしい。
江戸時代のそれは、徹底的に人工物で、松の枝を蛸の足のようにくねくねさせる造りが 持てはやされたらしい。お茶とか生け花にはいろいろ流派があるけど、盆栽にはそういう ものが無いのが特徴とか。
明治の時代になると何とか財閥とか政府高官達が、一種の社交の場として、それぞれの ものを持ち寄ったりして楽しんだらしい。岩崎弥太郎の実の弟は、三菱財閥の2代目とかで ご他聞に洩れず、大量の盆栽を収集そてたとか。をれを見学に行こうとしたさる高管、 そままま頼んだんじゃ、ちょっと確固わるい。
出入りの植木屋に頼んで、植木屋の格好をさせてもらって、こっそり見学に行った。 余りのりっぱさに、盆栽の前で、枝ぶりがどうのと品評会を始めちゃった。それを女中に 見つかり、主人に報告された。主人がよって来て、正式に招待しますよ。高管は自分の家に 正装を取りに行かせ、改めて見学したとか。面白いねぇ。
東京には武士の屋敷が多かったゆえ、植木職人が多数、巣鴨のあたりに住んでいた。 副業として、盆栽とかも売っていたらしんだけど、関東大震災で壊滅的な被害を受けた。 そこで、広い地を求めて大宮の氷川神社のあたりに盆栽村を作ったそうだ。
そして、そこには、大宮盆栽美術館が出来て、一大 メッカになってるらしい。
海外では、日本文化と言うと、生け花やお茶よりも、盆栽に人気が有るとか。何となく 大人のクラフトと言うか、凝りだすと盆とかから始まって止めを知らない世界らしいです。
水やりに3年、剪定は2年から7年に一回ぐらい。世代を超えて造って行くものらしいです。 相手が生き物なので、ずっと収蔵される事も少なく、結構いろいろな人の間を渡り歩くとか。 オーナーが変わると、盆栽の裏表が逆になるように改造することも多いそうな。生きた 美術品ですなあ。
7冊 / 1850頁 / 12100円
FreeBSD でも clojure
Windowsでもclojure出来るようにしたんなら、FreeBSDでも出来るようにしておかないと、片手落ち。
ports/devel/leiningenの案内を見ると
Leiningen is for automating Clojure projects without setting your hair on fire.
なんて誇らしげに書いてある。本当なら何もしなくても動くとな。 pkg install leiningen したら、java関係者を連れてやってきた。後は、lein replする だけでいい。んだけど、オイラーが可愛そうなんで、サイダーをおまけに付けてあげて、emacs からも動くようにしておいた。楽でいいわい。
lein help faq
でも、起動が事の他遅いのよね。lein help faqしたら、その言い訳と対策らしいのが書いてあった。
**Q:** What can be done to speed up launch? **A:** The main delay involved in Leiningen comes from starting two JVMs: one for your project and one for Leiningen itself. Most people use a development cycle that involves keeping a single project REPL process running for as long as they're working on that project. Depending on your editor you may be able to do this via its Clojure integration. (See [nrepl.el](https://github.com/kingtim/nrepl.el) or [foreplay](https://github.com/tpope/vim-foreplay), for example.) Otherwise you can use the basic `lein repl`. **Q:** Still too slow; what else can make startup faster? **A:** The wiki has a page covering [ways to improve startup time] (https://github.com/technomancy/leiningen/wiki/Faster).
おいらも文句言っておこう。まだまーーーだ遅いぞ、、と。それはそうと、leinを起動 すると、javaが2つ上がってくるらしい。本当か調べて見れ。
[sakae@fb10 ~]$ ps awx : 2956 1 I+ 0:00.01 /usr/local/bin/bash /usr/local/bin/lein repl :headless :port 4005 2961 1 I+ 0:24.89 /usr/local/openjdk7/bin/java -client -XX:+TieredCompilation -Xbootclasspath/a:/usr/local/share/java/classes/ 2991 1 I+ 0:26.75 /usr/local/openjdk7/bin/java -classpath /usr/home/sakae/src/testclj/test:/usr/home/sakae/src/testclj/src:/us
これ、emacs上でjack-inすると、その度に待たされてしまうので、サーバーモードで動かして います。これなら、何時emacsを落としても、再接続で直ぐに使えるようになりますから。
先に見たFAQの通り、javaのプロセスが2本立ち上がっています。親子関係はどうなん?
[sakae@fb10 ~]$ pstree 2956 -+= 02956 sakae /usr/local/bin/bash /usr/local/bin/lein repl :headless :port 40 \-+- 02961 sakae /usr/local/openjdk7/bin/java -client -XX:+TieredCompilation - \--- 02991 sakae /usr/local/openjdk7/bin/java -classpath /usr/home/sakae/src
javaがjavaを呼ぶと言う、ジャヴァ・ジャヴァな関係に有るのね。それにしても、親子の pidが離れているな。どゆ事? linuxのpsで確認してみる。
[sakae@manjaro testclj]$ ps awx : 405 pts/2 S+ 0:00 bash /home/sakae/.lein/lein repl :headless :port 4005 414 pts/2 Sl+ 0:20 java -Xbootclasspath/a:/home/sakae/.lein/self-installs/leiningen-2.4.2-standalone.jar -XX:+TieredCompilat 456 pts/2 Sl+ 0:16 java -classpath /home/sakae/testclj/test:/home/sakae/testclj/src:/home/sakae/testclj/dev-resources:/home/ [sakae@manjaro testclj]$ pstree -p 405 bash(405)───java(414)─┬─java(456)─┬─{java}(462) │ ├─{java}(463) │ ├─{java}(464) │ ├─{java}(465) │ ├─{java}(466) │ ├─{java}(467) │ ├─{java}(468) │ ├─{java}(469) │ └─{java}(470) ├─{java}(415) ├─{java}(416) ├─{java}(417) ├─{java}(418) ├─{java}(419) ├─{java}(420) ├─{java}(421) ├─{java}(422) ├─{java}(455) ├─{java}(457) ├─{java}(458) ├─{java}(459) ├─{java}(460) └─{java}(461)
で、出たー!! 子供プロセスと言うか、スレッドいっぱい居る。近頃は子供を沢山作るのは Javaだけと限りません。
どこかの御曹司も15人ぐらい子供を作ってます。でも彼はまだ世界記録に届いていないぞ。 出産記録が凄い事になってる。そして、 ギネスに挑戦?!12ツ子を妊娠した女性。(チュニジア) とかも。無事に生まれたのでしょうか?
[sakae@fb10 ~]$ lsof -p 2961 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 2961 sakae cwd VDIR 0,98 512 964034 /usr/home/sakae/src/testclj java 2961 sakae txt VREG 0,98 56821 10914 /usr/local/openjdk7/bin/java java 2961 sakae 15r VREG 0,98 2901 965600 /usr/home/sakae/.m2/repository/org/clojure/java.classpath/0.2.0/java.classpath-0.2.0.jar :
親の方は、基本的なjava環境を用意してる雰囲気(多分leinが起動するやつでしょう)、そして 子のjavaはと言うと
[sakae@fb10 ~]$ lsof -p 2991 : java 2991 sakae 56r VREG 0,98 35336 965597 /usr/home/sakae/.m2/repository/cider/cider-nrepl/0.7.0-SNAPSHOT/cider-nrepl-0.7.0-SNAPSHOT.jar java 2991 sakae 57r VREG 0,98 6395 965602 /usr/home/sakae/.m2/repository/org/clojure/tools.trace/0.7.8/tools.trace-0.7.8.jar java 2991 sakae 68u IPv6 0xc5ae35e0 0t0 TCP localhost:4005 (LISTEN)
のように、ユーザーがuseしたものとか、通信用のポートが口を開くようだ。それじゃ、leinが どのようにjavaを起動するか見ておくか。
[sakae@fb10 ~/src/testclj]$ bash -x /usr/local/bin/lein repl : + for f in '"$LEIN_HOME/leinrc"' '".leinrc"' + '[' -e .leinrc ']' + grep -E -q '^\s*:eval-in\s+:classloader\s*$' project.clj + LEIN_JAR=/usr/local/share/java/classes/leiningen.jar + '[' repl = compile ']' + CLASSPATH=:/usr/local/share/java/classes/leiningen.jar + BOOTCLASSPATH=-Xbootclasspath/a:/usr/local/share/java/classes/leiningen.jar + '[' -f .lein-classpath ']' + '[' ']' + export JAVA_CMD=java + JAVA_CMD=java + export LEIN_JAVA_CMD=java + LEIN_JAVA_CMD=java ++ basename java + [[ java == *drip* ]] + export JVM_OPTS= + JVM_OPTS= + '[' '' '!=' '' ']' + '[' -r .lein-fast-trampoline ']' + TRAMPOLINE_FILE=/tmp/lein-trampoline-3228 + trap 'rm -f /tmp/lein-trampoline-3228' EXIT + '[' '' '!=' '' ']' + export TRAMPOLINE_FILE + java -client -XX:+TieredCompilation -Xbootclasspath/a:/usr/local/share/java/classes/leiningen.jar -Dfile.encoding=UTF-8 -Dmaven.wagon.http.ssl.easy=false -Dleiningen.original.pwd=/home/sakae/src/testclj -Dleiningen.script=/usr/local/bin/lein -classpath :/usr/local/share/java/classes/leiningen.jar clojure.main -m leiningen.core.main repl nREPL server started on port 59446 on host 127.0.0.1 REPL-y 0.3.0 Clojure 1.6.0 Docs: (doc function-name-here) (find-doc "part-of-name-here") Source: (source function-name-here) Javadoc: (javadoc java-object-or-class-here) Exit: Control+D or (exit) or (quit) Results: Stored in vars *1, *2, *3, an exception in *e user=>
どうやら、/tmpの下にもファイルを作ってる。
[sakae@fb10 /tmp]$ ls -a : .leiningen-cmdline8596284861756544567.tmp form-init425855288638091669.clj hsperfdata_sakae
form-initxxx.cljはクラス関係者をロードするスクリプトぽい。.leingen-cmdlinexxxは、 javaの為のパスを表している。こんなにjarを呼んでたら、そりゃ遅くなるよ。最近のjavaは、 やたら検閲が厳しいからね。そして最後のhsperfdata_sakaeは、dirになってて、pidを ファイル名にしたキャッシュっぽい。
clojure profiler
お約束でclojureのプロファイラーを眺めておく。軽く検索してみると Clojureコードを性能測定する3つの方法 が出てきた。普通にtimeしろとな。javaの領域まで踏み込んで調べたいなら、 VisualVM 入門 ? Java.net が、有るとな。専門的過ぎるんで、timeのソースでも眺めてお茶を濁す事にする。
user=> (source time) (defmacro time "Evaluates expr and prints the time it took. Returns the value of expr." {:added "1.0"} [expr] `(let [start# (. System (nanoTime)) ret# ~expr] (prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs")) ret#))
ふーん、Javaの領域からナノ秒単位のデータを引っ張ってきて、ミリ秒単位に変換してるとな。 Javaはそんなに高精度で時間を採れるようにしてるけど、こりゃ精度病にかかってるな。 FXでしのぎを削る用途まで考えているの。今の時代は、ミリ秒以下の戦争らしいですから、 そういう時に必要になるのかな。
戯言はともかく、タイムさん(time 3)という、世界で一番簡単そうなマクロを評価してみる。 カーソルを右括弧の右側に置いて、C-x C-e する。結果はminibufferに、実行時間は、replの バッファーに表示される。オイラーの環境では、"Elapsed time: 0.029885 msecs" となったぞ。
そして、マクロ展開は、やはり括弧の右にカーソルを置いて、C-c RETだ。押しやすい キーバインドになってるって事は、よく使う、よくマクロで書けって事だ。Lispを使う 醍醐味は、マクロ多用でオレ様言語を作る事に尽きるな。苦労じゃ本では、マクロは 最後の手段、出来たら使うなって指導してるから真逆だな。
誰かが『C++は使えるようになったら神だけど、大抵の人はその前に仏になる」』と言った そうだけど、C++の所をマクロに置換しても、この文は真なのだろうか。
(let [start__4481__auto__ (. java.lang.System (nanoTime)) ret__4482__auto__ 3] (prn (str "Elapsed time: " (/ (double (- (. java.lang.System (nanoTime)) start__4481__auto__)) 1000000.0) " msecs")) ret__4482__auto__)
これを見て、逆変換出来る人は神に違いない。これは言い切れ事です。本当か? start#なんてのが、start__4481__auto__ に変形されてて一見難しそうに見えるけど、 シンボルの後ろにあるシャープさんは、衝突防止の為に、絶対にシンボル名が衝突しない ようにしてるだけですよ。落ち着いて考えれ!
clojure file io
ちょいとした事を調べる時、逆引きが出来ると便利。 逆引きClojureを挙げておく。それから、 Working with Files and Directories in Clojure とかも、良いかな。全体を見るなら、 Clojure 1.3-1.6 Cheat Sheet (v13)だな。
まずは、入力と出力を押さえておかんとな。Javaを使ったバージョン。
(use 'clojure.java.io) (with-open [fin (reader "hoge.txt")] (doseq [str (line-seq fin)] (println str)))
(use 'clojure.java.io) (with-open [fout (writer "hello.txt" :append true)] (.write fout (str "hello" " world")))
そして、こちらは、clojureだけでやっちゃおうという催し。
(spit "hoge.txt" "This is writing value") (println (slurp "hoge.txt"))
spitが書き出し用関数で、slurpが読み込み用の関数。これって、昔のcontribに有ったのが 昇格して、正式に取り込まれたんだな。slurpって、確かファイルだけじゃなくURLも指定 して、読めたはずだな。
incanter
もう少し高級なioが無いか思って探してた時に、偶然見つけたやつ。 おいしいClojure入門
clojure png グラフ なんてので検索したら、行き当たった。おいしい本の冒頭 部分を結構読めるようにしてくれているので、有り難い。
そうか、incanterって手が有ったな。csvファイルも読めるし、グラフを書けるし、おいらが 使う統計ぐらいなら十分に用が足りそう。
Clojureで帳票処理(csv読み書き、RDBMS利用、グラフ描画、PDF出力)
そして、 ncanter本家へ行って、詳細内容を確認しろとな。
グラフを書くぞ
簡単にグラフが書ける事が分かったので、おいらも書いてみる。project.cljの主要設定は 次のようにした。
:plugins [[cider/cider-nrepl "0.7.0-SNAPSHOT"]] :dependencies [ [org.clojure/clojure "1.5.1"] [incanter "1.5.5"] ]
たまたま読んでいた、結城さんの本『数学ガールの秘密ノート 丸い三角関数』に、 リサージュ波形が出てきてた。手を動かして、日経パソコンご推薦のEXCEL方眼紙 相当品 に点 (cosθ、sin2θ) の軌跡を 描きなさいとかね。
思い起せば自由研究で、SSBジェネレータを作った事があるんだ。抵抗とコンデンサを 組み合わせて、90度位相がずれた信号を発生させたんだ。(名前は、そのものずばりで、 フェーズ・シフト・ネットワーク PSNと称していたな)
ちゃんと位相がずれてるか確認する時、2象限オシロを使って○になるか確認した。 それを見てた人が、たまげた顔してたっけ。普通の人の感覚だと、オシロって波形を 見る装置だからね。
懐かしく思い出しながら、マクロでも書いてみる。スキャン範囲は、2*Math/PI も有れば 十分だよねって事で、決め打ち。(省略する事Railsの如しって、武田信玄が出て来る、真田3代風雲録を読んだ影響だったりします)
XとYの成分計算は、無名関数で与える事にした。マクロ楽しい。
(use 'clojure.repl) (use '(incanter core stats io charts datasets)) ; (view (function-plot sin -10 10)) (defmacro pg [fx fy] `(let [wide# (range 0 6.3 0.01) x# (map ~fx wide#) y# (map ~fy wide#)] (view (xy-plot x# y# :auto-sort false)))) (pg cos #(sin (* 2 %))) (pg #(cos (* 2 %)) #(sin (+ (* 3 %) (/ (* 60 Math/PI) 180))))
(cosθ, sin2θ)と (cos2θ, sin(3θ+60°))の軌跡を描く活用例を載せてみた。 最初、xy-plotのオプション auto-sortがデフォでtrueになってる事を知らなくて、 変なグラフに成っちゃったけど、ソースを覗いてみたら解決したよ。
折角なので、普通のオシロスコープみたいに波形を表示出来るようにしておくか。ああ、 リアルタイムなオシロを想像した人は御免。オシロと言うよりストレージオシロ。
それには、X軸にランプ波形を加えればOK。ランプ信号の発生は、マクロ内でrangeが賄って くれているから、それをそのまま使おう。こういう時は、Haskellで言うidって関数が有れば いいんだけど、clojureの相当品はidentity。こんな単語は覚えにくいな。今となっては、大学受験を 目指す訳でもないからね。そんじゃ代用品を考えてみると、#(+ %) でもいいけど、 ちゃんと、それらしい名前で登録しておく。(本当はオシロっぽくsweepなんてのにしたかったけど、 incanter.statsだかの登録商標になってて、定義しようとすると、clojureからお目玉を喰らった。)
(defn ramp [x] x) (pg ramp sin)
これで目出度く、ストレージオシロが完成。サイン波が1周期分表示されるよ。 本当なら、これをファイルに落としてWebに載せれば親切ってものだけど、CUI屋な もので、そういうは省略します。
そうだ、ソースを眺めていて、楽しい技が披露されていたので、忘れないように 載せておこう。
([] `(xy-plot [] [] :x-label "x" :y-label "y")) ([x y & options] `(let [opts# ~(when options (apply assoc {} options)) group-by# (:group-by opts#) title# (or (:title opts#) "") x-lab# (or (:x-label opts#) (str '~x)) y-lab# (or (:y-label opts#) (str '~y)) series-lab# (or (:series-label opts#) (if group-by# (format "%s, %s (0)" '~x '~y) (format "%s, %s" '~x '~y))) args# (concat [~x ~y] (apply concat (seq (apply assoc opts# [:group-by group-by# :title title# :x-label x-lab# :y-label y-lab# :series-label series-lab#]))))] (apply xy-plot* args#))))
linuxではエラッタ
万次郎Linuxでやってみると、こんなエラーが出てくる。
Show: Clojure Java REPL Tooling Duplicates All (23 frames hidden) 1. Unhandled java.awt.HeadlessException (No message) GraphicsEnvironment.java: 207 java.awt.GraphicsEnvironment/checkHeadless Window.java: 535 java.awt.Window/<init> Frame.java: 420 java.awt.Frame/<init> JFrame.java: 218 javax.swing.JFrame/<init> ChartFrame.java: 76 org.jfree.chart.ChartFrame/<init> ChartFrame.java: 64 org.jfree.chart.ChartFrame/<init> charts.clj: 3356 incanter.charts/eval13193/fn RestFn.java: 410 clojure.lang.RestFn/invoke MultiFn.java: 227 clojure.lang.MultiFn/invoke REPL: 1 user/eval13369 :
Windowsに入ってるやつは本家のやつ。万次郎の方は、OpenJDK
[sakae@manjaro gr]$ java -version java version "1.7.0_60" OpenJDK Runtime Environment (IcedTea 2.5.0) (Arch Linux build 7.u60_2.5.0-2-i686) OpenJDK Client VM (build 24.60-b09, mixed mode)
エラーの内容からすると、java.awtが動いていないっぽい。若しくは、そんなの入って いない? もう少し調べてみるか。
調べてみたら、入っているのはheadlessとかのやつだった。フルバージョンのやつを 入れないと駄目なのね。
[sakae@manjaro gr]$ sudo pacman -S jdk7-openjdk [sudo] password for sakae: 依存関係を解決... 内部衝突を確認... パッケージ (2): jre7-openjdk-7.u60_2.5.0-2 jdk7-openjdk-7.u60_2.5.0-2 合計ダウンロードサイズ: 15.27 MiB 合計インストールサイズ: 19.77 MiB
これで動いた。やれやれ。
回転行列
結城さんの本に、回転行列なる解説が出てた。点(x,y)を角度θだけ回転させると、 回転後の座標(x',y')は、どうなるか? 答えは、回転行列なるものと、点のベクトル(x,y)を 積算すれば良いらしい。
x' | | cosθ -sinθ | x | = | | y' | | sinθ cosθ | y same as x' = x cosθ - y sinθ y' = x sinθ + y cosθ
この行列とベクトルの掛け算は、こういう風に定義しましたから、深く考えないでね(意訳) ってものらしい。そう、cosの定義を底辺/斜辺と勝手に決めた事のようにね。。。
この間の同級会の時、数学の教師を今年3月に退職しましたって人が居た。慣れないサンデー毎日が 続いているって言ってた。そんなに暇なら、オイラーの所に遊びにでも来て、こういう数学 談義はいかがですか?