newLISPでプログラムを書く

アマチュア無線局を開局してからもう少しで5年。免許期間は5年で、切れる1年前から 1月前までに再申請しないと無効になる。忘れないように案内が来た。酷試に必ず出る定番 問題なんだけどね。

案内の葉書によると、申請書を地上メールで送ると3050円、電子申請だと1950円。30%OFFで お徳ですってさ。だったら、電子申請でいいじゃん。

手順は、新規ユーザー登録、1週間程するとIDとパスワードが郵送で届く。これってさりげなく 住所確認だな。IDとパスワードでログインして、申請。審査が進んでOKとなると、手数料払えの最速が 今度は電子メールで届く。ペイジー対応のATMから入金。免許状は直接取りに入ってもいいし、郵送してもらってもいいとな。

電子申請にはWindowsパソコンが必要。携帯、スマホ、タブレット、MACはお使いになれませんって、 どういう作りしてんだよー。 電波利用電子申請届出システムLiteには、 動作環境チェックをやれとな。

やってみた。ポップアップブロックがロックされるから駄目と言われた。解除したらOKに なった。使ってるWindowsとブラウザな名前、ポップアップブロックの状態とクッキーの 設定状況に○が付いたよ。一応、最初の関門はPASSだな。

それにしても、誰もがWinなパソコンなんて持ってないだろう。若い人はスマホやMACだろうに。 年寄りにターゲットを絞っておりますと、暗に主張してんだな。

TPPの障壁になると、アプル・ジュークボックス屋だかアプル時計店から文句が来るぞ。ついでに、韓国のサムスンを ないがしろにするのはけしからんと、あのおばちゃんが文句を垂れてくるぞ。さーどうする。 ソームのおばちゃん。

それよりも、国内のユーザーはこの不利益に対して、訴訟でも起さんかね。Winマシンを 持ってる人のみが優遇されてますって。8月には恒例のハムフェアがあって、今年も電子申請の 勧めなんてのをやるだろうに。その場を借りて、プラカードを掲げてデモしろよ。 行動を起さんと、舐められるぞ。

支払いは、インターネットバンキング。そんな危ない物には加入してないぞ。どうやら 郵貯のATMも使えるようだ。案内を見ようとしたら、バグ満載のアドビのやつが入ってないから 駄目と抜かしやがる。そして、ここをクリックするとフラッシュプレーヤを入れられますと、 提灯リンクが有ったぞ。

それは止めて、生身の人間に聞いてみた。昔の無線仲間。最近は空に出る事はないけど、 局免だけは維持してるとか。その人の話では、郵貯に口座があればOKとの事。 今度、郵便局へ記帳に行った時に、詳しい事聞いてこよう。

プログラム書き用の環境

ややnewLISPにも慣れてきたので、プログラムを書こうと思う。で、プログラム書き用の 環境を作っておこう。

多分、emacsにもそれ用のモードが有るだろうと思って探したら有った。 newlisp-mode 作者さんは日本人の方。2008年末から作り始めておられるようだから、オイラーは超遅い とっかかりだな。ありがたく使わしていただく。

で、OpenBSD用に少し改変。

;; $ html2txt $NEWLISPDIR/newlisp_manual.html -o newlisp_manual.txt
;; or use www-browser [File] -> [Save Page As (Text)]
(defvar newlisp-manual-text "/usr/local/share/doc/newlisp/newlisp_manual.txt")

(defvar newlisp-manual-html
  (or (dolist (path (list "/usr/local/share/doc/newlisp/manual_frame.html"

マニュアルが引けるようになってるんだけど、原稿の位置が違うので修正。 emacsからw3mを呼び出すと、レンダリングに時間がかかるので、newlisp-lookupを多用 する事にした。それ用のテキストは、w3m -dump で作ったよ。だってhtml2txtなんて コマンド持ってないもん。

その為、lookupすると、目的語が見つからんと言われた。で、newlisp-lookup-manual関数内を 下記のように修正したよ。

              (format "^syntax: (%s" (regexp-quote keyword))

よくlookupするので、キーバインドに登録した。

    (define-key map (kbd "C-c C-d") 'newlisp-lookup-manual)

これで、目的語の所で、C-cC-dすれば、語を拾って検索してくれる。timeの結果分解能が いくつになってるか引いたら、第二引数が有る事を知った。思わぬめっけものと喜んで います。

F5キーから起動出来るrunコマンドが装備されてるんだけど、昔の馴染みでオイラー流のrunも 登録しといた。

(defun newlisp-run (query)
  "newlisp run on buffer *newlisp-run-output*"
  (interactive (list (buffer-file-name)))
  (unless (string= query "")
    (save-buffer)
    (start-process-shell-command "newlisp-run" "*newlisp-run-output*"
                                 (concat "newlisp " query " "
                                         (read-from-minibuffer "args: ")))
    (switch-to-buffer "*newlisp-run-output*")
    nil))

(defun newlisp-clear-screen ()
  (interactive)
  (erase-buffer))

(define-key newlisp-mode-map (kbd "\C-c\C-r") 'newlisp-run)

F5とかのキーはWindowsな人の好み。きっとWindowsの入ってるemacs23系でも動くんだろうな。 それ以前に、javaのGUIで動く、newlisp-editが有るだろうと言われそう。

JavaとフラッシュはもうWindowsで使わないと、硬く誓ったのであります。ウブとかで使うのはどうよ? portをしっかり閉めているし、使い方を限定してるから。本当にportが閉っているか? newLISPのサンプルにport-scanが付いていきたぞ。

[ob: examples]$ ./scan localhost
scanning: localhost
open port: 22 ssh
1024

sniffなんて言う盗聴器も用意されてた。libpcap.so.6.0 なんてのが縁の下の力持ちとして 使われてたけど、OpenBSD5.7ではいかに?

[ob: examples]$ locate libpcap.so
/usr/lib/libpcap.so.8.0

デフォで入ってた。びっくりするなあと思ったてmanしたら、関連にtcpdumpが挙げられていた。 そうか、デフォで盗聴器が装備されてるんだな。

なにはともあれ、Windows上のnewLISPも、emacsから使えたんでほっとしていますよ。

constant

先のsniffのコードを見てたら、constantなんてのを使ってた。 早速マニュアルを引いてみると、へぇ、関数も保護出来るとな。それ以外のも、 『組込関数をシンボルに割り当てたり、組込関数名に他の違う関数を割り当て再定義した りすることも可能です。関数名を変更しても、性能は変わりません。』なんて書いてある。

例で、

> (+ 1 2.123)
3
> (constant '+ add)
add@16D7EA90
> (+ 1 2.123)
3.123

普通のプラスは、整数演算のみ、それをconstantでaddに拡張すると、schemeっぽくなる。 これは嬉しい。是非、init.lspに入れておけ。ああ、schemeには定数として、πとかeが 定義されてるけど(これって、RnRSの要求事項?)newLISPには無い。

目指す所がPHP的だから、デフォでは無いんだな。でも普通の人が余り使わない(であろう) 財務関数や統計関数があったり、極めつけは、高速フーリエ変換とその逆関数が有ったり、 アンバランス感覚が楽しい。

そう言えば、newLISPのソースを読んでて、やたらEMSCRIPTENってのが出てくる。なんじゃらほいと思って 調べてみると、 newLISP as a JavaScript libraryって事らしい。 newlisp語をjavascript語に変換出来れば、見かけ上Webの上でnewlispを実行してる(気分に 事になる。同ページの紹介で、 ntroduction to newLISPなんて所へも 行ってみたよ。

ともあれ、定数は自分で定義しとけ

> (constant 'pi (mul 2 (acos 0.0)))
3.141592653589793
> pi
3.141592653589793
> (constant 'e (exp 1))
2.718281828459045

血圧表示プログラム

去年さんざんやったのに、またかと言うなかれ。何か小さいスクリプトを書いておくのは、 通過儀式ですから。元データは、こんなデータ

14010121,114,66,69
14010203,116,70,57

測定日時(yymmddhh)、最高血圧、最低血圧、脈拍数が、日時順に並んだデータ。

;; bld script

(module "stat.lsp")
(constant 'How-many -60)  ;; show last 60 data's

(define (csv-read file)
  (let ((rv '()) (es nil) (fp (open file "read")))
    (while (read-line fp)
      (set 'es (parse (current-line) ","))
      (push (map (fn (e) (int e)) es) rv))
    (close fp)
    (reverse rv)))

(define (apm sw)
  (cond
   ((= sw 'am) (fn (e) (<  (% (first e) 100) 12)))
   ((= sw 'pm) (fn (e) (>= (% (first e) 100) 12)))
   (true       (fn (e) true))))

(define (ssf xfn hlp)
  (join (map (fn (v) (format "%6.1f" (apply xfn v))) hlp) ""))

(define (ssv xfn hlp)
  (join (map (fn (v) (format "%6.1f" (xfn v))) hlp) ""))

(define (show ds)
  (let ((hlp (rest (transpose ds))))
    (println "Min:  " (ssf min hlp))
    (println "Mean: " (ssv stat:mean hlp))
    (println "Max:  " (ssf max hlp))
    (println "Sdev: " (ssv stat:sdev hlp))))

(define (graph ds)
  (let ((hlp (rest (transpose ds)))
	(nl  (fn (x) (map (fn (v i)(string i " " v)) x (sequence 0 (length x)))))
	(gp  "plot '-' w l\n"))
    (push (join (nl (hlp 0)) "\n") gp -1)  ;; high
    (push "\n\n" gp -1)
    (push (join (nl (hlp 1)) "\n") gp -1)  ;; low
    (push "\nend\n" gp -1)
    (exec "gnuplot --persist" gp)))
  
(set 'cv (csv-read "current.csv"))
(println "at Wakeup") (show (How-many (filter (apm 'am) cv)))
(println "at Night")  (show (How-many (filter (apm 'pm) cv)))
(println "Now Wakeup graph ... close then Night graph.")

(graph (How-many (filter (apm 'am) cv)))
(graph (How-many (filter (apm 'pm) cv)))
(exit)

Windows7での実行結果。グラフは省略です。

C:\Users\sakae\Documents\blood>newlisp ana.lsp
at Wakeup
Min:   107.0  67.0  52.0
Mean:  127.2  73.1  56.3
Max:   140.0  79.0  65.0
Sdev:    6.5   2.7   2.8
at Night
Min:    97.0  52.0  59.0
Mean:  112.0  63.7  62.7
Max:   130.0  75.0  70.0
Sdev:    8.5   5.3   2.3
Now Wakeup graph ... close then Night graph.

OpenBSDで実行すると、グラフ画面が2つ表示されてすぐにプロンプトへ戻るんだけど、 Windowsでは、最初のグラフ画面を消さないと、次の画面が表示されない。

gnuplotのhelpによれば、Windows用でも、 --persist が使える事になってるんだけど。 ちなみに、 --persistってのは

       -p, --persist lets plot windows survive after main gnuplot program
       exits.

gnuplotが終了しても表示画面が残るって設定。一画面表示した所でブロックしちゃうって 事は、表示した時にgnuplot内のイベントループを回っているんだな。それを証拠に、グラフ 内でマウスを動かすと、それに応じて座標位置がリアルタイムに更新される。もしやこの 機能をoffに出来れば、すーっと抜け出してくれるかな。

もしそうなって、WindowsとUNIX系の違いでスクリプトを分ける必要が生じたら、どうする? newLISPには、そんな事もあろうかと、実行してる土台を調べる変数が用意されてる。

[ob: ~]$ newlisp
newLISP v.10.6.2 32-bit on BSD IPv4/6 UTF-8, options: newlisp -h

> ostype
"BSD"
c:\>newlisp
newLISP v.10.6.2 32-bit on Win32 IPv4/6 libffi, options: newlisp -h

> ostype
"Win32"

ウブとかFedoraとかのLinux系は、"Linux" って言ってくるぞ。大雑把だけど、土台が 分かるのは有り難い。

移植は、去年のGaucheのそれを引っ張ってきて修正して行った。そこはかとなくschemeチックな 所があって、相性ばつぐんでしたよ。

ただ、違う所も有ったけどね。CSVデータをカンマで分解する所。splitを使うかと思ったら、 parseなのね。

文字列を数値に変換する時は、intとかを使う。逆に数値を文字列に変換する場合、stringを 使う。このstringは汎用性があって、リストでもシンボルでも、文字列に出来る。丁度あの Haskellの表示出来るものって機能だな。(なんて言う用語だったか、すっかり蒸発してる)

zipが有るかと思ったら、mapで代用せいとな。最初のリストが尽きた所で、mapが終了する。 だから、短いリストを先に持ってくるのが常套手段。逆にすると、不足分はnilで補われる。

pushは指定が無いと、cons相当の動きをするんだけど、オプションで、-1を付けると、 最後に付け足されるようになる。(append相当)これも常套手段っぽい。

上記はオイラーが血圧監視によく使うパラメータをコード化したものだ。昔、goで汎用の やつを書いたけど、使い方は限られているから、lispに落とした次第。

ちゃんとやろうとすると、Module: getopts.lsp あたりを使って、やらなきゃならない。

統計遊び

前回もやったけど、統計関数が用意されてるんで使ってみる。

> (set 'pm (rest (transpose (How-many (filter (apm 'pm) cv)))))
((108 117 118 102 ...)
 (57 75 68 64 ...)
 (66 70 65 64 ...))

最近の就寝時のデータを取り出した。transposeなかなか便利。次は基本統計量。桁数が長いので、 roundで丸めてみた。

> (map (fn (v) (round v -3)) (stats (pm 0)))
(60 112.017 7.151 8.516 72.52500000000001 0.222 -0.774) ;最高血圧
> (map (fn (v) (round v -3)) (stats (pm 1)))
(60 63.667 4.256 5.303 28.124 0.136 -0.552)       ;最低血圧
> (map (fn (v) (round v -3)) (stats (pm 2)))
(60 62.7 1.847 2.287 5.231 0.531 0.175)         ;脈拍
; N mean       sdev        skew  kurt

skewがいずれも正になってるから、度数分布は左偏りだな。kurtが正だと、正規分布に 比べて尖ってる。負だとなだらか。

> (map (fn (v) (round v -3)) (corr (pm 0) (pm 1)))
(0.654 18.029 0.407 6.588 58 0)
> (map (fn (v) (round v -3)) (corr (pm 0) (pm 2)))
(-0.259 70.49299999999999 -0.07000000000000001 -2.043 58 0.046)
> (map (fn (v) (round v -3)) (corr (pm 1) (pm 2)))
(0.043 61.511 0.019 0.33 58 0.742)

次は、相関係数を求める。ただ一つの数が返ってくるから思ったら沢山返ってきたので、 慌てて意味を調べてみた。

name description
r    相関係数
     Correlation coefficient
b0   回帰切片係数
     Regression coefficient offset
b1   回帰傾き係数
     Regression coefficient slope
t    優位検定の(訳注:スチューデントの)t 統計値
     t - statistic for significance testing
df   自由度
     Degrees of freedom for t
p    帰無仮説下での(訳注:スチューデントの)t の尾部両側の確率
     Two tailed probability of t under the null hypothesis

相関係数を信頼するかどうかはp値の大きさによるとな。pがZEROに近ければ、信頼してよしとな。 また、

  Y = b1 * X + b0

と言う、フィット直線で、一次近似が行えるともな。これ以上は統計本参照か。 統計と言えばRだけど、最近は派手な事が出来るみたいだ。 R Markdownで楽々レポートづくり

落穂拾い

上で統計関数を使った時、少数点以下が多数桁表示された。これらの表示桁数をシステム・ワイドに 変更するコマンドが用意されてる。

> (stats a)
(17 3.352941176470588 1.474048442906574 1.800735143996343 3.242647058823529 0.3457226379960801 -1.039116833875074)
> (pretty-print)
(80 " " "%1.16g")
> (pretty-print 80 " "  "%1.4f")
(80 " " "%1.4f")
> (stats a)
(17 3.3529 1.4740 1.8007 3.2426 0.3457 -1.0391)

次は、コマンドラインオプションの入手方法

[ob: ~]$ newlisp hoge.lsp aa bb cc
newLISP v.10.6.2 32-bit on BSD IPv4/6 UTF-8, options: newlisp -h

> $main-args
("newlisp" "hoge.lsp" "aa" "bb" "cc")

さすが、スクリプトをさっと書けるように、手厚い変数を用意してるな。

次はostypeの代わりになるやつ。サービス精神旺盛です。

> (sys-info)
(455 268435456 410 1 0 2048 0 28432 10602 130)

統計情報だな。使ってるセル数とか、バージョン番号が得られる

offset description
0      Lispセルの数
       Number of Lisp cells
1      Lispセルの最大数、定数
       Maximum number of Lisp cells constant
2      シンボル数
       Number of symbols
3      評価/再帰レベル
       Evaluation/recursion level
4      環境スタック・レベル
       Environment stack level
5      最大呼び出しスタック数、定数
       Maximum call stack constant
6      親プロセスの PID または 0
       Pid of the parent process or 0
7      走っている newLISP プロセスの PID
       Pid of running newLISP process
8      バージョンを整数で表したもの、定数
       Version number as an integer constant
9      オペレーティング・システムを表す、定数:
       Operating system constant: (see manual)

最後に、システムが持ってる変数。正規表現でマッチした値を保持する、$n とか、 何番目の値を処理してるかを示す、$idx。この $idxはmapと組み合わせると便利。

移植したgraphの中のletで

 (nl  (fn (x) (map (fn (v i)(string i " " v)) x (sequence 0 (length x)))))

こんなのを定義してた。sequenceで、連番を作っていたけど、これが$idxを使うと不用に なる。

> (map (fn (v) (string $idx " " v)) '(130 122 126 120 123 129 128))
("0 130" "1 122" "2 126" "3 120" "4 123" "5 129" "6 128")

こんな具合にgnuplotに送り込むx,yデータを作成出来る。(y軸データは、血圧値等だ)

なお、$idxは色々な所で(dolist,until,while等)使われているんで、もし番号が必要と 思ったら参照してみると良い。

最近のJS

最近はJavascriptで面白い事出来るのね。

Web Audio API + Web MIDI API】ブラウザで電子楽器を作ってみよう!

いきなり、そんなに飛ばさなくたって、と言うむきには、newLISPでそろりと始めるのが よかろう。まずは、お絵書きからだな。

Module: canvas.lsp