gaucheでも
『タコの教科書』なんて本を読んだ。決してLinuxの本ではない。だってさ、古い人なら 超ど級の素人さんをタコともてはやして、Linux陣営へ引きすり込むために、某仕掛け人が 大事にしてたから。
正真正銘のオクトパスの本である。たこ足配線とか、オクタルなんて用語は、たこの足が 8本ある事が由来になってる。
たこは原生期には、身を守る甲羅があったそうな。それが甲羅を棄て、完全な軟体生物に 進化。身を守る方法は、体色を素早く変化させて、回りに溶け込む。タコの墨、足を 切って、敵がそれにくらいついている間にスタコラ逃げる作戦。足は再生するそうだ。
子孫を残すと、雄も雌も死んでしまう。メスは、卵を生むと、餌を食べずにかいがいしく 卵の世話をする。そして死んでしまう。オスも交尾の疲れで、2ヶ月ぐらいで死んでしまう とか。
サッカーの優勝を見事に予想して有名になったタコは、3年程で死んでしまったけど、 大体寿命はそんなものらしい。
食べるたこでは、マダコが上位を占めているけど、養殖は難しいらしい。卵から孵化した ものが、海中に漂うプランクトンの生活を送る為、その時期の管理が難しいとか。 頑張って、三重大とか近大あたりが、養殖にチャレンジしませんかね。ああ、マグロの方が 儲かるから、だめか。
日本では淡路島あたりの物が有名らしいけど、地元で消費されちゃって、中々市場には 出回らない。大体、口に出来るのはアフリカあたりからやって来るやつだな。
西洋人は、デビル・フィッシュと言って毛嫌いしてたようだけど、寿司とかの文化で 段々と食べる人が多くなってるそうな。タコも国際争奪戦ですかね。
大阪で1935年に遠藤留吉さんと言う人が、たこ焼きを発明したそうな。もっと古い歴史が あるものと思っていたけど、意外に新しいのね。知らんかったよ。
タコはエロスと死の象徴らしい。葛飾北斎の 蛸と海女 と言う版画は、その道の人には余りにも有名だそうだ。 ゴッホとかが大いに刺激されたとか。知らんかったぞ。
読書メーター 7冊 / 2159頁 / 12200円
gaucheの環境作り
新しい版が出たと言うのに入れてなかった。ウブなマシンは余裕があるので、ソースから 自前で入れた。そしてその残骸を残しておいた。後でgdbにかけられるようにね。万次郎Linuxの方は、 素直にパッケージを入れておいた。(おまけでslibとguileも付いてきた。slibは有り難いけど、 guileはいらない子。まてまて、RMSの壮大な夢に投資してみるか)
これを機会にgaucheでも補完と思って、どなたかからのを頂いてきた。
;; complete (when (require 'auto-complete nil t) (global-auto-complete-mode 1) (defun ac-next-or-next-line (arg) (interactive "p") (if (/= (length ac-candidates) 1) (ac-next) (ac-abort) (next-line arg))) (defun ac-previous-or-previous-line (arg) (interactive "p") (if (/= (length ac-candidates) 1) (ac-previous) (ac-abort) (previous-line arg))) (define-key ac-complete-mode-map "\C-n" 'ac-next) (define-key ac-complete-mode-map "\C-p" 'ac-previous) (custom-set-faces '(ac-candidate-face ((t (:background "dark gray" :foreground "blue")))) '(ac-selection-face ((t (:background "blue" :foreground "white")))))) ;; eldoc (require 'eldoc-extension) (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode) (add-hook 'lisp-inteeraction-mode-hook 'turn-on-eldoc-mode) (setq eldoc-idle-delay 0.2) (setq eldoc-minor-mode-string "") ;; scheme-mode-hook (defvar ac-source-scheme '((candidates . (lambda () (require 'scheme-complete) (all-completions ac-target (car (scheme-current-env)))))) "Source for scheme keywords.") ;; Auto-complete-mode config (add-hook 'scheme-mode-hook '(lambda () (make-local-variable 'ac-sources) (setq ac-sources (append ac-sources '(ac-source-scheme))))) (autoload 'scheme-smart-complete "scheme-complete" nil t) (eval-after-load 'scheme '(progn (define-key scheme-mode-map "\t" 'scheme-complete-or-indent))) (autoload 'scheme-get-current-symbol-info "scheme-complete" nil t) (add-hook 'scheme-mode-hook (lambda () (make-local-variable 'eldoc-documentation-function) (setq eldoc-documentation-function 'scheme-get-current-symbol-info) (eldoc-mode t) (setq lisp-indent-function 'scheme-smart-indent-function)))
コピペ野郎なんで、初回にemacsに怒られた。eldoc-extensionが無いとな。これはパッケージに なってたぞ。それから、scheme-completeも無いとな。こちらは、http://www.emacswiki.org/emacs-ru/download/scheme-complete.el あたりを貰ってくれば、取り合えず動く。mini-bufferに関数の引数の説明も出てきた。 (補完用データが更新されていないのが残念)
今まで使ってたgoshを起動するルーチンが、emacsの時代にそぐわなくなっていたので、 下記のようなシンプルなやつにした。最近のrun-schemeって、自動で窓を分割してくれるのね。
(setq scheme-program-name "/usr/local/bin/gosh -i") (defun runme () (interactive) (run-scheme scheme-program-name)) (define-key global-map "\C-cG" 'runme)
万次郎Linuxの方は、以前racketがらみで入れたgeiserが幅を利かせていて、上記が動かなかった。 しょうがないので、racketとは縁を切る事にしたよ。
emacsからgoshを使うんじゃなくて、ターミナルから使う場合、rlwrapと連携させておくと便利。 それ用の設定が、 改めてGaucheとrlwrapの連携についてとか、 REPLでヒストリと補完を使う (rlwrap) に載っているので、設定しておくと吉。
gaucheでcsvファイルの読み書き
しようと思って使い方をinfoしてみた。性懲りもなく、前回からやってる血圧アプリを schemeで書いたらどうなるかと思ってね。schemeも色々あるけど、日本語充実って事で、 gaucheなんです。
-- Function: make-csv-reader separator :optional (quote-char #\") 入力ポートを省略可能引数として取る手続きを返します。 手続きが呼ばれ ると、ポート(省略された場合は現在の入力ポート)からレコードを1つ読み 込み、 フィールドのリストを返します。入力ポートが EOF に達すると、 EOF を返します。 -- Function: make-csv-writer separator :optional newline (quote-char #\") 出力ポートとフィールドのリストの2つの引数を取る手続きを返します。 手 続きが呼ばれると、SEPARATOR で区切られたフィールドを 正しくエスケー プして出力ポートに出力します。レコードの区切り文字列を NEWLINE で指 定することもできます。例えば、ファイルが Windows の プログラムでも 読めるように、‘"\r\n"’ を渡すことができます。
これしか無くて、高レベルの手続きはいつか、、、って説明だった。 何となくpythonのそれと似てるんで、使い方は想像出来るんだけど具体例を思い付かない。 こういう時は、あの人に聞いてみるしかないな。
直交世界の地図 に、schemeの精神が表明されてた。またgaucheに特化した、 Gaucheクックブックにも説明が 出てた。
まずは基本を押さえておけって事です。csv-readerなんて名前が付いているぐらいだから、 schemeの一般手続き read を置き換えるものと推測。その裏を取ってみる。
gosh> (use text.csv) #<undef> gosh> (define in (open-input-file "2013.csv")) in gosh> in #<iport 2013.csv 0x86158c0> gosh> ((make-csv-reader #\,) in) ("13010103" "118" "80" "60") gosh> ((make-csv-reader #\,) in) ("13010121" "107" "67" "72")
csvデータだと思ってポートから一行読み出してくれ! で、その読み出し仕様は、なんちゃら、 かんたらってのをmake-csv-readerに指定すると供に、読み出しを実施。
CSVファイルにヘッダーが付いていたりして、それをどうするってとかあるけど、一気読み かつ、余り色々なライブラリーを必要としないのがいいな。 こちらが、そのオイラーの我がまま仕様を満たすやつ。
gosh> (call-with-input-file "2013.csv" (lambda (in) (port->list (make-csv-reader #\,) in))) (("13010103" "118" "80" "60") ("13010121" "107" "67" "72") .....)
我がままついでに言わせて貰うと、返ってきたデータが文字列になってるのが気にくわん。 これじゃ、使う前に数値にしないといけないな。面倒な事は読み取り時にやっておくれよぅ。 お願いだからさ。
所で、port-listってどう実現されてる? オイラーなら、読み込んだデータをどんどんと cons してって、最後に反転するかな。ソースがあるから、こっそり覗いてみる。これが隠れた 楽しみだったりします。
求めるものは、lib/gauche/portuil.scm に有った。
(define (port->list reader port) (with-port-locking port (^[] (let loop ([obj (reader port)] [result '()]) (if (eof-object? obj) (reverse! result) (loop (reader port) (cons obj result)))))))
正に予想した通り。予想が外れたのは、reverse! て、びっくりマークが付いている事だった。 で、良いものにお目にかかったので、これを少々改造させて貰おう。ついでに、csvなファイルを 与えて、それを数値に変換して読み込んでくれるのも定義しとく。
(use text.csv) (define (csv-num->list reader port) (with-port-locking port (^[] (let loop ([obj (reader port)] [result '()]) (if (eof-object? obj) (reverse! result) (loop (reader port) (cons (map string->number obj) result))))))) (define (myload cfile) (call-with-input-file cfile (lambda (in) (csv-num->list (make-csv-reader #\,) in))))
結果の確認は、slibにあるpretty-printを使ってみた。便利なやつ、traceとかも あるので、積極的に使うべし。
gosh> (define aa (myload "2013.csv")) aa gosh> (use slib) #<undef> gosh> (require 'pretty-print) #t gosh> (pretty-print aa) ((13010103 118 80 60) (13010121 107 67 72) : (13011003 121 79 60) (13011021 112 68 65)) 23
でも、何となく釈然としない。CSVファイルの内容を数値にしたい事は煩雑にあるはず。 それなのに、おいらみたいに新しく手続きを定義しなければならないなんて。。。
port->listの説明をinfoで見た時、そのすぐ近くにport-mapってのがある事に気が付いて いたんだ。それの引数を見ると、読んだデータを加工する為の関数と、readerを指定する ようだ。これならぴったりだろう。但しreaderは、current-input-portを利用するとな。
ファイルポートを一時的にcurrent-input-portに出来れば事が済みそう。久しぶりに、 Gauche本を引っ張り出してみましたよ。そして、書いたのが下記。
(define (mycvsr cfile) (with-input-from-file cfile (lambda () (port-map (cut map string->number <>) (make-csv-reader #\,)))))
Gauche本をパラパラしてる時に、部分適用 cut なんてのが目に入ったので使ってみた。 これ、伝統のlambda式の代替えになるのね。程よく使うと便利だなあ。
そんじゃ、ファイルへの書き出しはどうするかと言うと
(define DAT (mycvsr "2013.csv")) (define (to-csv-file cfile) (call-with-output-file cfile (lambda (out) (for-each (^(e) ((make-csv-writer #\,) out (map number->string e))) DAT))))
こんな具合に、csvファイルを読み込んで出来たリスト(であるDAT)を、ファイルに 書き出せば良い。make-csv-writerがwriteの代わりを務めてくれる。
CSVファイルの扱いはこれぐらいでいいか。面倒なヘッダーは無い事にしたし、文字列ー数値 変換も、全フィールド対象って事でmap一発で済ませたから、手抜き感はありますけど。
現実は、こうも単純にはいかないんだろうね。亡社の健康診断データには、男の場合、身長、体重、 腹回りのデータが、女子の場合は、身長、それにBWHのデータが付いてます。
この特製CSVファイルで、読み込み時に、男子の場合は、BMIとビア腹係数を、女子の場合は ボイン係数を計算して下さい。こういう無理っぽい要求が、クライアントから有る訳ですよ。 ね、なかなか複雑でしょ。
ああ、複雑と言えば、、、某携帯電話メーカーD社では、契約内容が 複雑になり過ぎて、人間では矛盾をチャック出来ないらしい。そこでPROLOGを導入して コンピュータにチェックを任せたとか。自慢げにニュースになってた。
そんなの何か間違ってると思うぞ。単純にせんかい。総務省と公正取引委員会が、契約は これだけって、単純版を行政指導せんかね。 複雑にして、ユーザーを欺き、メーカー丸儲け、競争相手を蹴散らす手段を取り上げられる んで、メーカーはぶーーーーーいんぐだな。裏で天下り受け入れとか献金とか必死等々。
gaucheでグラフ
で、グラフ表示はどうする? 入力系の目処がついたので今度は出力の見える化。
そりゃ、グラフ書き職人に任せちゃうのが、正しいunix屋さん ってもんです。racketみたいに抱え込んで太るのは御免って訳。 以前ちょいとネットから拾っておいたコードを見ると、謎のマークが有った。
(define (plot d) (display "plot '-' w l\n") (force d) (display "end\n")) (define (calc n) (dotimes (i n #f) (display (format "~a ~a\n" i (sqrt i))))) (plot (delay (calc 100))) ;;sakae@uB:~/src$ gosh plot.scm | gnuplot -persist
何これって 調べてみたら、特別なファイル名 (special-filenames) だそうだ。ついでに、wはwithの、lはlineplotの省略系らしい。丁寧にwithとかやると、 職人さんが臍を曲げてしまうので、注意。
gnuplotとはパイプで接続して、データを送りこんでいる。この方法は、データ受け渡しの ファイルを作らないので、すっきりしてるんだけど(不測の事態でファイルが残る心配無し) 、残念ながら using 1:3 とかやって、複数のグラフを一度に書けない。gnuplotが新しくなると(?) 名前付きインラインデータ (inline data) が使えるらしいけど、おいらの所では、エラーになってしまった。(gplot 4.6.5) 5系 からのサポートになるんかな。
そこで、少し冗長になるけど、インラインデータの区切りを空行で行うようにした。 上記のcalcなら
(define (calc n) (dotimes (i n #f) (display (format "~a ~a\n" i (sqrt i) ))) (display "\n") (dotimes (i n #f) (display (format "~a ~a\n" i (log (+ 1 i))))))
最初に平方根の計算値を吐き出し、空行を置いてから対数の計算値を出力してる。plot関数の 方は、全くいじる必要が無いので、こういう仕様も有りかな。
後は、スクリプトの中にgnuplotを呼び出すパイプを格納すればいいな。えと、どうやって やったかな? 昔、同じような事をやったな。 そして、こんなのも出てきた。pngに落とすやつばっかだな。
(use gauche.process) (define plot-command " set terminal png color set grid set ylabel \"Output \" set yrange [0:20] set xlabel \"Date\" set xdata time set timefmt \"%Y%m%d%H%M%S\" plot \"data.txt\" usi 2:1 ti \"output\" with impulses ") (display plot-command (open-output-process-port "gnuplot > out.png" ))
これが参考になりそうなので、ちょっと実験
gosh> (use gauche.process) #<undef> gosh> (display "plot 'data.txt' u 1:2 w l, '' u 1:3 w l\n" (open-output-process-port "gnuplot -p")) #<undef> gosh> (call-with-output-process "gnuplot -p" (lambda (out) (display "plot 'data.txt'\n" out))) #<undef>
ここまでは、ちゃんとgnuplot画面が出てきた。そして今回の本命、、、
(with-output-to-process "gnuplot -p" (lambda () (display "plot 'data.txt'\n")))
これも大丈夫。今度は
(with-output-to-process "gnuplot -p" (lambda () (plot (delay(calc 50)))))
を、スクリプトとして実行すると、何もplotされずに終了。はてはて、delayとかが悪さ してるんかな?
よく考えたら、goshの起動の仕方が悪かった。
[sakae@manjaro ~]$ gosh -l ./plot.scm
としてやらないと、goshが終了しちゃうものだから、起動されたgnuplotも殺されちゃうのね。 だから、表示されないように見えたとな。スクリプトを実行してからreplに入って もらえば、生きながらえるとな。ちょっとした盲点でしたな。
マクロだよ
グラフを書かせる度にwith... うんたらと書かせられるなんてウンザリ。そこでマクロに 登場して貰おう。
gaucheのマクロ と言うかSchemeのマクロには2大潮流がある。一つはSchemeの優位性を自慢するために、 世界のSchemerが知恵を絞った健全なマクロ。もう一つはコモンリスプから脈々と受け継がれて いる伝統のマクロ。
健全なマクロは、変数名の衝突を未然に防ぐ対策が施されたもの。車の衝突防止機能と 一緒だな。車と違ってリコールとかないのかね。設計の苦労の一端が、ビューテフル・コード って本に解説されてる。何度か読んだけど、難しくて途中で挫折してんだよな。もう一度 読んでみるか。
一方、伝統マクロは、変数の衝突を逆手に取った技が開発されてて、伝道師ポール・グレアムが、 『On Lisp』なんて惑う書を配布してたりする。Gauche本のマクロの章には、両方のマクロの 解説が出てたから、再読してみれ。
前書きが長くなったけど、伝統マクロにしてみる。delay/forceは無しね。
(define-macro (gp form) `(with-output-to-process "gnuplot -p" (lambda () (display "plot '-' w l\n") ,form (display "end\n"))))
リコールされない事を祈りつつ、、ですな。 使い方は、
gosh> (gp (calc 200)) #<undef>
たったこれだけ。省タイプに役立っています。
これが、どう展開されるかは、
gosh> (pretty-print (macroexpand-1 '(gp (calc 123)))) (with-output-to-process "gnuplot -p" (lambda () (display "plot '-' w l ") (calc 123) (display "end ")))
綺麗に清書しようとしたら、文字列内も綺麗になっちゃったぞ。抑止方法って有るの かしらん。/usr/shere/slibをミロ。
おまけ
gnuplotで開かれた窓にグラフの結果保存メニューが有った。ふむふむPDFに落とせるか。 さすがデフォになったな。
そしてもう一つは、SVGフォーマットでも保存出来るとな。このフォーマットって不遇の ものらしい。 いまさら聞けないSVG、なぜ知られていないのか? とか ベクター形式のグラフィックを扱うSVGの基本 が出てきた。どこの世にも覇権争いが。
ファイルはテキスト形式なんで、読もうと思えば読めます。XMLやだやだですが。 ブラウザの画面にファイルをドロップすれば、表示されます。見るにはいいかも。