pureでag
ニュートン誌のバックナンバーを見ていたら、音声でパソコン入力なんて言うCMが出てた。 それによると、アメリカの医学界で鍛えられ、絶大な人気を誇っているとか。 おいらが行ってる町医者も、診断結果をマイクに向かって喋っていたな。
パソコン入力の世界記録保持者が、42秒で160単語を入力するのを尻目に16秒で音声入力 しちゃったとか。こう素晴らしいものなら、ツイター大好き人間は、ツイターの語源の ごとく、このソフトを使わななりません。ツイター社が一括ライセンスして、使える ようにしませんかね?さあさあ、つぶやけ、つぶやけ、ツイター社へつぶやけ攻撃!!
ぐぐる様は暫く前から、音声検索出来るようになったので、ブームなんですかね? かの 広告にも、音声入力の歴史は1950年台から始まりIBMあたりが熱心に研究してたなんて説明 されてた。1次ブームは1997年頃に、確率モデルを取り入れる事によってよってもたらされ、2次ブームは 当社が作ったと豪語してました。
当然、ライバルは居るわけで、比較をやってる方 とか、ドラゴンの応援団 が居たりして、面白い。
この分野もいろいろな雑誌に取り上げられていたな。Haskellで何とかしようという人も居て Haskellで作る超コンパクト音声認識システム:Husky なんてのも公開されてます。
でも、トータルで進んでいるのは、今の所アプルなんだな。ひょっとしてアプルでも Haskellを使っているのかしらん?
そうそう、冒頭に町医者の話が出てきたけど、最近は医者もコンピュータの御託を採用 する方向で動いているみたい。クイズ番組で名をはせた ワトスン君が、医療分野でも いい味を出しているようですよ。無駄知識も特定分野に絞れば人を超える英知を発揮する らしいですよ。
agの下調べ
agってのは、おいらが勝手に付けた名前。CUI端末で星を並べて棒グラフを書きましょってやつ。 前回の続きだ。まずは、先輩がどうやってるか見てみる。
main = mapM_ go (histogram list) where go (h, l) = putStrLn $ show h ++ " " ++ replicate l '*' -- ^ h : head; l: length list = [1,2,3,4,2,5,0,0,8,9,1,7,8,9,7,4,7,7,7]
画面に表示とは、副作用です。副作用を起せるのはmainって決まっているので、関数名は 自動的にmainに決定。
表示の為のデータは、histgram関数が佳きにあしらってくれるんで、それをcarしながら、 棒を一本づつ書いて行く。cdrを取りながら、リストが空になるまで繰り返せばいいんで、 そんな関数あったよねと言う事でmapを思い出す。但し扱うデータがHaskellさんからは 不浄に見えるので、不浄なデータを扱うため恐怖のモナド。って事で、モナド対応のmapM。
で、mapの返り値はリストになるんだけど、どうせ()しか返ってこないんで、そんなものを 見せられたって嬉しくない。いらないものは棄ててね機能を付けた、mapM_ 。 なかなか、 都合よい関数を用意してるな。
各棒を書くのは、下請けのgoにお任せ。引数の受け渡し方法に注目。carしたやつを分解 してから渡しているので、後が楽になる。某SIみたいに丸投げしない所が偉いぞ。
下請けさんは、文字列を表示してから改行してねっていうアクション(と言う、詐欺みたいな 名前で納得させられるマジック)を定義、++ で、文字列を結合。hって言う、棒のタイトルを 見せ、スペースを挟んでから、データ分だけ星印を生成。なんとも分かり易い動作だ。
ああ、histgram関数から渡ってくるリストは、こんなんだった。
[(52,51),(57,247),(62,57),(67,5)]
これってLispで言う、連想リストだよな。そうするとassoc一族は有るのかな? 軽く探して みたけど、見つからなかった。プログラミングGaucheでも参考に、移植してみるか。とか言いながら パラパラ見てたら、foldなんてのが出てきたぞ。やっぱりと言うか当然と言うか。。。(以下省略)
素朴なag
下調べが終わったので、Haskellのそれをpureに移植してみる。移植って足りない分や 挙動が違う部分を補ったり、補正したりする作業だな。
Haskellに有ってpureに無いものとして、replicateがある。エラーになってしまったので、 間違いない。念の為、マニュアルを眼grepしたけど、無かったぞ。pureって最小主義なんだな。 ある意味、昔のSchemeに通じる所がある。ああ、昔のSchemeってのは、R5RSまでの事ね。 6以降は、肥大主義と言うか実用主義に走っちゃって、面白みに欠ける。
無いものは適当に寄せ集めて、でっち上げればいいんだ。こんなのでどうだ。
replicate n = (join "" . repeatn n) ;
関数合成+ポイントフリースタイルで、書いてみました。 Haskell Advent Calendar 2013の 4日目、arrowの話には負けるけど。
取り合えず、足りない分は揃ったので、pure流に書いてみる。
ag xs = do go xs with go (k,v) = puts (str k+" "+replicate v "*"); end;
pureはモナドなんて有るかどうかは知らないけど、doが用意されてた。rubyで言うeachね。 mapの親戚。putsを使う時は、systemをインポートしとく事。showの代わりにstrを使って、 整数を文字列に変換してる。以上で、移植終了。
と、簡単に書いちゃったけど、マイナーなトラブルが発生してたんだ。gplotのnamespaceを 有効にしてたものだから、そのスペースにあるputsが使われちゃっててねぇ。おかしな事に なってたのさ。最初、それに気付かずオロオロしちゃったけど、show ag してみたら、 何処の関数が使われているか表示してくれたんで、事無きを得ました。
圧縮付きag
概要が分かったので、barが1画面に収まるような圧縮機能付きの棒グラフ作成関数を 書いてみる。均等にデータをスケーリングしちゃうと、棒が表示されない事もありうる。
どうする? RのCUIグラフにヒントを得て、そういう時は、データを文字にエンコード しちゃえってのにいたる。先に作ったagの変形なので、Haskell宜しくag' とでもしたい んだけど、pureでは、そんな文字使っちゃだめって言われるので、(ドイツだとウムラウト 文字を許すのかな?)、変形の印として、aGにした。
aG xs = do go xs with sv v = if (dm * v div vm) > 0 then replicate (dm * v div vm) "*" else chr (ord "0" + v) ; go (k,v) = puts (str k+" "+ (sv v)+" "+str v); end when vm = foldl1 max $ unzip xs ! 1; dm = 50 ; end ;
最大星印は、50文字まで。いきなりマジックナンバーが出てこないように、バインドしといた けど、意味不な名前だな。我ながら反省!
> aG (hist 5 hi); 101 **** 9 106 ************** 29 111 ***************************** 60 116 ************************************************** 103 121 **************************************** 83 126 ************************ 50 131 ********** 21 136 * 4 141 1 1 ()
まあ、目的達成って事で。
> aG $ hist 4 $ zipwith (-) hi low; 24 * 3 28 **** 8 32 ********************* 41 36 ************************************ 68 40 ************************************************** 94 44 ********************************************* 86 48 ******************* 36 52 ********** 19 56 ** 5
こちらは、血圧差のヒストグラム。じぇじぇじぇ~ 差が24って測定の不確かさから 来てるのだろうか? 素人目に、超やばそうな気がするんだけど。。
こいつのQって幾らぐらいなんだろう? (おっと、これは無線用語か。ちなみにQの定義って、 同調周波数を、そこより6dBダウンした帯域幅で割った数値だったな) 尖っている程、 標準偏差が小さいって、なんかQと通じるものがあるな。
慣れてくると、ヒストグラムの形を見るだけで、標準偏差が予想出来るって言うけど、今の オイラーには、まだ無理。ばらつきが小さければ、相関係数が高いってのは、確信を持って 言えるけど。
練習帳を作って、ヒストグラムから標準偏差を推定したり、相関係数を予測したりするのも 面白いかも知れないな。
do一族
上で活躍してくれた、do、Haskellではモナドの構文糖衣だけど、pureにはそんなのが 無いんで、関数として実装されているはず。どういう実装なんだろう?
mapみたいなものとすれば、headを取ってそれに関数を適用。それとtail側の再帰結果を何らかの 演算子(mapなら、consがそれに当たる)で結合してくはず。その演算子が思い浮かばない。
しょうがないので、答えをちょろ見。
do f [] = (); do f (x:xs) = f x $$ do f xs; dowith f (x:xs) (y:ys) = f x y $$ dowith f xs ys; dowith f _ _ = () otherwise; dowith3 f (x:xs) (y:ys) (z:zs) = f x y z $$ dowith3 f xs ys zs; dowith3 f _ _ _ = () otherwise;
doにはzipみたいに複数のリストを同時に扱う奴も用意されてるのね。今後の為に頭の 片隅に入れておこう。 で、今回の肝は、$$ですよ。何だろうこれ?
infixl 1000 $$ ; // sequence operator
順番に実行するんだな。shellでよく configure && make && make install とかやるけど、 それより制限が緩いんだな。
> 3 $$ 4 $$ 5 ; 5 > true $$ false ; 0
最後に実行した結果を返すとな。Lispのprognか。
上の定義を見ると、doの最終結果は、()って事で、意味無 を強調してる。
まとめ
pureって統計用にうってつけ。統計で多用するシグマってのは畳み込み、これ簡単。 グラフはgnuplotにお任せよ。変なGUIを導入する事無く使える。疎結合はunixの思想。 データの取り込みも簡単。haskellみたいに、やれモナドとか言い出さなくて良い。
今まで、何回かに分けてコードを書いてきたけど、最後に一挙公開。将来の自分の為に。 凝った事はしていない、部品の寄せ集めだけどね。
using system; // for __show__ time puts using math; // for sqrt ceil using gplot; using csv; using namespace csv; getcsv f = v when d = dialect {quote_flag=>MINIMAL}; p = open (f, "r", d); v = fgetr p; z = close p; end; putcsv f x = #x when d = dialect {quote_flag=>MINIMAL}; p = open (f, "w", d); v = fputr p x; z = close p; end; group n [] = []; group n xs = (take n xs) : (group n (drop n xs)); sum xs = foldl (+) 0 xs; avg xs = sum xs / #xs ; ss xs = sum [(x - avg xs) ^ 2 | x = xs;] / ( #xs - 1) ; sd xs = sqrt $ ss xs ; median xs = if #xs mod 2 == 1 then head $ drop (#xs div 2) $ sort (<) xs else (rs ! 0 + rs ! 1) / 2 with rs = drop (#xs div 2 - 1) $ sort (<) xs end; cor xs ys = (sum $ map tmc $ zip xs ys) / (#xs - 1) with tmc z = ((z!0 - ax) / sx) * ((z!1 - ay) / sy) ; end when ax = avg xs ; ay = avg ys ; sx = sd xs ; sy = sd ys ; end ; hist s xs = mk n (sort (<) xs) with n = s + (foldl1 min xs) ; len xs = # xs ; cs n xs = (n - s, (len $ takewhile (<n) xs)); mk n [] = [] ; mk n ss = cs n ss : mk (n+s) (dropwhile (<n) ss) ; end ; let gp = gplot::open "/usr/bin/gnuplot"; lg xs = gplot::plot gp xs ("style", "lines") ; ltg ttl xs = s when a = gplot::title gp ttl; s = str (time mod 86400) + ".png"; b = gplot::output gp "png" s; e = lg xs ; c = gplot::output gp "x11" ""; d = gplot::title gp ""; end; replicate n = (join "" . repeatn n) ; ag xs = do go xs with go (k,v) = puts (str k+" "+replicate v "*"); end; aG xs = do go xs with sv v = if (dm * v div vm) > 0 then replicate (dm * v div vm) "*" else chr (ord "0" + v) ; go (k,v) = puts (str k+" "+ (sv v)+" "+str v); end when vm = foldl1 max $ unzip xs ! 1; dm = 50 ; end ; // __show__ x::double = sprintf "%0.1f" x; let m = getcsv "2013.csv"; let am = [x | x = m; x ! 0 ! 7 == "0"] ; let pm = [x | x = m; x ! 0 ! 7 == "2"] ; let hia = [x!1 | x = am; ]; let lowa= [x!2 | x = am; ]; let plsa= [x!3 | x = am; ]; let hip = [x!1 | x = pm; ]; let lowp= [x!2 | x = pm; ]; let plsp= [x!3 | x = pm; ];
祝 FreeBSD 10.0
今回はRC4まで行く、難産であったけど、無事にリリースされた。 早速、入れるだけしてみた。
$ uname -a FreeBSD fb10 10.0-RELEASE FreeBSD 10.0-RELEASE #0 r260789: Fri Jan 17 01:46:25 UTC 2014 root@snap.freebsd.org:/usr/obj/usr/src/sys/GENERIC i386 $ df Filesystem 1K-blocks Used Avail Capacity Mounted on /dev/ada0p2 19278684 2269360 15467032 13% / devfs 1 1 0 100% /dev
CDからみんな入りってやってportsとsrcを入れただけ。おいおい、ユーザーランドを追加して、 PCBSDから引越ししよう。
pkgの在り処が、http://pkg.freebsd.org/freebsd:10:x86:32/latest/ なの?
root@fb10:~ # pkg install pure Updating repository catalogue The following 9 packages will be installed: Installing gmp: 5.1.3 Installing gettext: 0.18.3.1 Installing perl5: 5.16.3_6 Installing libltdl: 2.4.2_2 Installing mpfr: 3.1.2 Installing python27: 2.7.6_1 Installing python2: 2_2 Installing llvm32: 3.2_2 Installing pure: 0.58_1 The installation will require 202 MB more space 41 MB to be downloaded Proceed with installing packages [y/N]: y
なんか、linuxっぽくなってきたな。/var/db/pkgの下も、とうとうsqliteで占められる ように、なっちまったい。これも時代の流れか。