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ファイルも読めるし、グラフを書けるし、おいらが 使う統計ぐらいなら十分に用が足りそう。

統計解析ツールIncanterことはじめ

Clojureで帳票処理(csv読み書き、RDBMS利用、グラフ描画、PDF出力)

統計解析アプリ「Incanter」入門

そして、 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象限オシロを使って○になるか確認した。 それを見てた人が、たまげた顔してたっけ。普通の人の感覚だと、オシロって波形を 見る装置だからね。

SSBこらむ

AF PSN

オールパス・フィルタを使ったPSNのSSB発生器を作る

懐かしく思い出しながら、マクロでも書いてみる。スキャン範囲は、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月に退職しましたって人が居た。慣れないサンデー毎日が 続いているって言ってた。そんなに暇なら、オイラーの所に遊びにでも来て、こういう数学 談義はいかがですか?