haskell and pure

『維新銃姫伝』(中央公論新社)なんて本を読んでみた。作者は藤木さんと言う方。 帯を見るまでもなく、ははんと来ましたよ。帯には、ヒロイン八重って出てましたから。 それより、2013年NHK大河ドラマってのが受け狙いですかね。去年の11月発行。書き下ろし だそうです。

このドラマ、東北復興を願って放映されたはずなんで(海女ちゃんもそうだった)、これに 合わせて大急ぎで書き下ろししたんですかねぇ。それにしても巻末に付いてた参考資料が 半端じゃなかったぞ。

という事は、他に原作者が居るの? Webで調べてみたら脚本が誰それってのは出てたけど 原作者ってのは調べた限りでは見つからず。

ドラマは八重の幼少の頃から丁寧に描写してると思うのだけど(見てないので想像で書いてる) 銃姫伝の方は、会津城が落城する所から話は始まる。この差は何?

2人がほぼ同時に同じ人物を素材に小説を書いたって事で宜しいでしょうかね? ドラマは 1年かかるけど、小説の方は数日で読めてしまって、中弛みがなくてよかったよ。 たまには、会津にでも行ってみたいぞ。旨い酒を求めて!

haskell

今までずっとML系をやってきた。更に続けてもいいんだけど、秋田んで、同系のHaskellに 鞍替えしてみる。 家には献本されたRWHとかがあるんだけど、真面目にやってなかったんで、反省の意味も込めて。。。 で、Haskellって言うと、pythonみたいに、インデントで構造を表すってのが印象に のこってるんだ。余り良い印象が無かったなあ。某板を見てたら

Q -----------------------------------------------------------------------------

Haskellのインデントスタイルが微妙にわからない Pythonみたいにガチガチでもなくセミコロンとかで区切るでもなくて

A -----------------------------------------------------------------------------

・do, of, where, letの四つの予約語が特別扱いされてる ・上記の予約語の後が{でない場合、インデントに従って{;}が補われる の二点を押さえれば分かり易いと思う

------------------------------------------------------------------------------

本当の所はどうなの?やさしい Haskell 入門 (バージョン98)とか Haskell 98 言語とライブラリ 改訂レポートの レイアウトとかオフサイドルールって所を見ればいいのかな。まあ、そういうメンドイ事は haskell-modeに任せてしまうのが一番楽ですよ。

そして、今流行ってるHaskell本は、すごいH本。書店で買うのが恥ずかしいなら、親本である Learn You a Haskell for Great Good!を、堂々と読もう。 そんなに時間を掛けられないよって人は、10分で学ぶHaskell が、お手軽だ。Clojure すごい Haskell 楽しく学ぼう なんて事で、clojureな人も注目してるしね。

おいらは、Haskellの特殊用語と記号の克服の為、 ポイントフリースタイル入門 とか、 Haskellの演算子 なんてのをこっそりと覗いてみるのさ。

そして、幾らこういうのを見て立って畳上の水練と思うなら、がっつりと Haskell: 初期リリースのPugs 6.0.0をビルドしてみよう とか、 Scheme in 48Hr を見ればよい。 飽きたら、 バラエティー豊かにアラカルトに楽しめばよい。 浮気症な方は、FP: 関数型プログラミング 等もお勧め。

hugs

業界のデファクト・スタンダードはghcなんだけど、DISK大食い、cabal地獄が待ってるので 避ける事にします。まあ、cabalもrubygemも同じようなもので、gemが出てきてレールが 流行だした時から積極的に使わなくなりましたけどね。

で、

[sakae@pcbsd ~]$ hugs
__   __ __  __  ____   ___      _________________________________________
||   || ||  || ||  || ||__      Hugs 98: Based on the Haskell 98 standard
||___|| ||__|| ||__||  __||     Copyright (c) 1994-2005
||---||         ___||           World Wide Web: http://haskell.org/hugs
||   ||                         Bugs: http://hackage.haskell.org/trac/hugs
||   || Version: September 2006 _________________________________________

Haskell 98 mode: Restart with command line option -98 to enable extensions

Type :? for help
Hugs>

Hugsを攻略するなら、何を置いても :b Hugs.Prelude しましょ。実際にやってみると 、余りに表示すべき定義が多すぎて、画面が流れてしまいます。そんな時はどうするか?

環境変数、EDITORをemacsとかに設定しといて、:f map とかすると、ファイル毎閲覧出来るよ。 これがら何処に入っているかと言うと、/usr/local/lib/hugs/packages/hugsbase/Hugs とかに、 入ってました。

demosが置いてありました。HGLの中に有ったやつ。

module Main(main) where

import Graphics.HGL

main :: IO ()
main = runGraphics $ do
  w <- openWindow "Hello World Window" (300, 300)
  drawInWindow w (text (100, 100) "Hello")
  drawInWindow w (text (100, 200) "World")
  getKey w
  closeWindow w

そしてOpenGL系って事でGLUT版もありました。こちらは作法がちと面倒で、Helloを出すにも いろいろ設定しなければいけないみたい。折角なので、どうやると歯車が回せるか見て おくかな。

type View = (GLfloat, GLfloat, GLfloat)

data State = State {
   frames  :: IORef Int,
   t0      :: IORef Int,
   viewRot :: IORef View,
   angle'  :: IORef GLfloat }

makeState :: IO State
makeState = do
   f <- newIORef 0
   t <- newIORef 0
   v <- newIORef (20, 30, 0)
   a <- newIORef 0
   return $ State { frames = f, t0 = t, viewRot = v, angle' = a }

Refって単語が出てきたけど、これってML系に有ったやつですかね。ああ、こんな風に使われてた。

   when (t - t0' >= 5000) $ do
      f <- get (frames state)
      let seconds = fromIntegral (t - t0') / 1000 :: GLfloat
          fps = fromIntegral f / seconds
      putStrLn (show f ++ " frames in " ++ show seconds ++ " seconds = "++ show fps ++ " FPS")
      t0 state $= t
      frames state $= 0
      when ((t >= 999 * autoexit) && (autoexit /= 0)) $
         exitWith ExitSuccess

5秒毎に、性能を表示してくれるんだな。と、グラフィック系は、無理にHaskellでやらなくても いいんで、Haskellならではのやつは無いか。出てきたのは、parsecですよ。パーサーコンビネータってやつね。

あの人が好きだったなあ。あの人はどうしているやら、風の噂にも流れてこないぞ。まあ、 おいらが居るところは田舎だからね。

いきなりのparsecでは辛いので、まずは予習をば。

Parsecというパーサーライブラリで電卓を作る簡単な方法

Parsec の実例

Parsecを使ってみる で、hugsを動かすと、Token.hsでエラーだよとのたまう。

[sakae@pcbsd /usr/local/lib/hugs/demos/parsec/UserGuide]$ hugs Main.hs

Haskell 98 mode: Restart with command line option -98 to enable extensions

ERROR "/usr/local/lib/hugs/packages/parsec/Text/ParserCombinators/Parsec/Token.hs":64 - Syntax error in data type declaration (unexpected `.')
Text.ParserCombinators.Parsec.Expr>

嗜好錯誤(ナイス誤変換)の上

[sakae@pcbsd /usr/local/lib/hugs/demos/parsec/UserGuide]$ hugs -98 Main.hs

Hugs mode: Restart with command line option +98 for Haskell 98 mode

Main> run price "123.45"
12345

Main> run price "12.3456"
1234

Hugsモードに切り替えて使う事にした。これで、いろいろ試せるな。

Parsecを使ってパーサを作ってみます とか kazさんのWiLiki を見たよ。 kazさんと言えば、おいらもPiki -- コマンドライン WIKIでお世話になってます。 ソース読みの参考は、 最強のパーザー、Parser Combinator ですかね。それとも、Parsec 再入門 parse 関数 Parsecを使ってApacheのログをパースしてみる(1)Parsecを使ってApacheのログをパースしてみる(2) とかが、面白いかな。あああ、 Haskell Lectureにも、Parsec が出てたぞ。こんなのもあるな。Parsec, a fast combinator parser

いろいろ検索してると、日が暮れてしまうので、お前も少しコードを書いて見れって言われ そうなんで、何か書きます。暫くぶりで買った虎妓を見てたら、dBmの事が出てた。ベテランでも 間違える事がある、dBm表記された値の平均を求める例。

dBmってのは、1mWを基準(0dBm)にした、電力での対数比の事だ。対数で圧縮されているので そんな数値を、単純に足してはいけない。伸張して真数に直してから演算し、それをまた 圧縮しなければいけない。

これをHaskellでやろうとすると、log10が必要になる。けど、Hugsにはそんな都合のよい ものは用意されていない。どうするか。

log10 n = logBase 10 n

logBaseなんてのを見つけたので、それを使えばよい。そして、全体は、

dbm a = todbm $ esum a / (fromIntegral (length a))
  where
    esum a = foldl (+) 0 $ map (\ n -> 10 ** (n / 10.0)) a
    todbm n = 10.0 * (log n / log 10)

こんな感じになる。logBaseは止めて、自前のやつを使ってみた。esumは、真数に直しての 合計計算、それを母数で割って、それのlog10を取ればよい。dBmの他に、dBμとかdBVとかの 電圧系の比もあるけど、そういう時は、上記で10.0ってなってる係数を20.0に変更すれば OK。これで、1尼受験が出来るぞ。

pure

ずっとhugsと戯れていてもいいんだけど、例によって他にも面白そうなものはないかと さがしてみる自分が居る。FreeBSDのカタログを見てたら面白そうなのを見つけたよ。 Haskellの純粋性に着目して名前は pure って、付けられています。

ああ、Haskellの遅延評価に関しては

遅延評価だけに後回し後回しの人生です
________________________________________
Haskellは必要なときには過不足なく仕事してるんだよ
お前は必要な時もサボってるだろ
________________________________________
よくいるよね
高価な文房具を買いそろえ、勉強はあまりしない
高価なギターやエフェクター類をそろえるが練習しない
________________________________________
10年ぐらいたって突然それらが遅延評価されて練習が始まるのです

ははは、何となく、当たっているな。まあ、めげないで、

[sakae@pcbsd /usr/ports/lang/pure]$ cat pkg-descr
Pure is a modern-style functional programming language based on term
rewriting. It offers equational definitions with pattern matching, full
symbolic rewriting capabilities, dynamic typing, eager and lazy evaluation,
lexical closures, built-in list and matrix support and an easy-to-use C
interface. The interpreter uses LLVM as a backend to JIT-compile Pure
programs to fast native code.

WWW:     https://code.google.com/p/pure-lang/

上記のURLはちと古くて、今は、http://purelang.bitbucket.org/ へ引越ししてるよ。

FreeBSDのportsになっているのは、ちと古い。新しいのをいれよう。それには前提としてLLVMが 必要。最初fedoraで試したんだけど、LLVMのとなるincludeファイルが無いとか言われて、家捜し させらせそうになったんで、fedora19から逃亡。もうすぐfedora20も出るしね。 そんな訳で、万次郎ですよ。

[sakae@manjaro ~]$ pure

 __ \  |   |  __| _ \    Pure 0.58 (i686-pc-linux-gnu)
 |   | |   | |    __/    Copyright (c) 2008-2013 by Albert Graef
 .__/ \__,_|_|  \___|    (Type 'help' for help, 'help copying'
_|                       for license information.)

Loaded prelude from /usr/local/lib/pure/prelude.pure.

> help
START /usr/bin/exo-open --launch FileManager "file:/usr/local/lib/pure/docs/index.html"
exo-open: ディスプレイをオープンできません: .

どうも見慣れないexo-openってのは、ファイルマネージャ兼ブラウザーみたいだ。X上じゃ ないと動かんとは不便なものだのう。何とかせいよってんでmanを家捜し。

       The full Pure manual can be read inside the interpreter, by typing help
       on the interpreter's command line. This requires w3m(1) to  work.  (You
       can also set the name of another html browser to use with the PURE_HELP
       or the BROWSER environment variable, or read the manual online  at  the
       Pure website.)

pureを作ってる所がドイツの国内みたいで、真面目に資料を更新してくれている。さすが グーテンベルグ大学。書籍命って所ですからね。

ソースツリーの中のetcの下には、vimやらemacsやらのインターフェースが置いてある。 勿論ドイツの人はemacs便利だよーーーん と言う事でインストール時にemacsが入って いれば、勝手にそれをインストールしてくれる。後は、設定をしてあげるだけ。

;; Register pure-mode
(autoload 'pure-mode "pure-mode" "Major mode for editing Pure code." t)
(add-to-list 'auto-mode-alist '("\\.pure$" . pure-mode))
(add-hook 'pure-mode-hook 'turn-on-font-lock)
(add-hook 'pure-mode-hook '(lambda () (require 'w3m-load)))
(add-hook 'pure-eval-mode-hook 'turn-on-font-lock)

pureなんて普遍的過ぎる名前を付けるなよ。ぐぐる先生に聞いても頓珍漢な答えしか 返ってこなくて苦労したぞ。shiroさんが、gaucheって名前を決める時、一生懸命に辞書を 引いて検討したそうだ。辞書に載ってるような単語は、辞書攻撃を受けるんで注意が必要! pureを半面教師にしよう。Windows、お前もだぞ。

なんやかんややってたら、 関数型言語 Pure マニュアル日本語版 とか、プログラミング言語「Pure」をインストール なんてのが出てきたぞ。

それでは、例によって例のごとく例を見てく。何はなくともhello.pureあたりからだな。 ふむふむ、インタープリタの内部コマンドがあるとな。run hello.pureでも走るんか。 他にどんな内部コマンドが有るのかな。help run とかすればいいんだな。そして、run の 説明の近辺に、ほかの内部コマンドもクラスタを作っているんで、簡単に俯瞰出来る。

show とか、tracdとかstatsとかbreakとか。内部コマンドには文末にセミコロンを付けない事。

> run hello.pure
Hello, world!
> show fact1
fact1 n = n*fact1 (n-1) if n>0;
fact1 n = 1;
> fact1 10;
3628800
> stats -m on
> fact1 10;
3628800
2.9e-05s, 23 cells
> fact1 30;
1409286144
4.3e-05s, 63 cells
> fact1 30L;
265252859812191058636308480000000L
0.021047s, 64 cells

factが色々と陳列されてて、下記が面白かった。

// Using lambda ("pointless style").

fact5 = \n -> if n>0 then n*fact5 (n-1) else 1;

// Using fixed points. This technique allows you to define a recursive
// function without referring to the name of the function in its body. See
// http://en.wikipedia.org/wiki/Fixed_point_combinator for an explanation.
// The (normal order) fixed point combinator 'fix' is defined in the prelude.

fact6 = fix (\f n -> if n<=0 then 1 else n*f (n-1));

そして、haskellに対抗すべく、ドイツ方言丸出しで頑張ってくれてます。ああ、ドイツ方言 おいらは好きですよ。昔、ドイツに駐在してた時、コーラの瓶にkoraって書いてあって、すごく 納得した覚えがあります。

qsort p []      = [];
qsort p (x:xs)  = qsort p [l | l = xs; l<x] + (x : qsort p [r | r = xs; r>=x])
                  with x<y = p x y; x>=y = ~p x y end;

extern int rand();
qsort (<) [rand|i = 1..20]; // sort 20 random numbers in ascending order
qsort (>) [rand|i = 1..20]; // sort 20 random numbers in descending order

上の例でさりげなく extern なんてのが出てきた。これって、C側の関数をpureに取り込める って事だろうな。help extern したら、ぞろぞろと説明が出てきたぞ。例が無いかと 探してみたら、有った。ドイツ人親切だな。どんな人? manとかに署名してるかな?

AUTHOR
       Albert  Graef  <aggraef@gmail.com>,  Dept.  of Computer Music, Johannes
       Gutenberg University of Mainz, Germany.

コンピュータミュージックの人なんか。どうりで、Grame’s functional DSP programming language なんていう言語のI/Fまで用意してんだな。勿論C,C++,Fortran90,LLVMもサポートしてる。

で、例だけど

[sakae@manjaro hellomod]$ cat hello.c

#include <stdio.h>

int hello(const char *s)
{
  return printf("Hello, %s\n", s);
}
[sakae@manjaro hellomod]$ cat hello.pure

using "lib:hello";
extern int hello(char*);

// Example:
hello "world";

自前のCモジュールを作って、それをpure側から呼び出してみようって戦略だな。Makefileが 付いていたのでやってみる。

[sakae@manjaro hellomod]$ make
Package pure was not found in the pkg-config search path.
Perhaps you should add the directory containing `pure.pc'
to the PKG_CONFIG_PATH environment variable
No package 'pure' found
  :
Makefile:48: recipe for target 'hello' failed
make: *** [hello] Error 1

例によって、pkg-configのDBが見つけられないという、/usr/local/lib/pkgconfigなんて 知らない攻撃だな。不親切な事だ脳。

[sakae@manjaro hellomod]$ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
[sakae@manjaro hellomod]$ make
cc -shared -o hello.so  -I/usr/local/include  -g -O2  hello.c -L/usr/local/lib -lpure
[sakae@manjaro hellomod]$ pure -x hello.pure
Hello, world

Makefileがごちゃごちゃやってるけど、コツは -shardに有りなんだな。

[sakae@manjaro hellomod]$ pure -q -i hello.pure
Hello, world
> hello "hage" ;
Hello, hage
12

ちゃんと、あの人(って、誰よ? 想像するのは自由、誰も傷付けませんから)にも、挨拶出来たよ。 最後に、hugsでやったdBmでの演算をpureでも書いておこう。

// dBm calc.
using math;

todBm n    = 10.0 * log n ;

fromdBm d  = 10 ^ (d / 10.0) ;

dBm_avg ds = todBm avg with
               sum ds = foldr (+) 0 $ map fromdBm ds ;
               avg    = sum ds / # ds ;
             end;

toV r d    = sqrt $ r * 0.001 * fromdBm d ;

実行例

> todBm 1000;             // 1000mW は、何dBm ?
30.0
> fromdBm 20 ;            // 20dBm は、何mW ?
100.0
> dBm_avg [10, 20, 30];   // 10,20,30dBm の平均は ?
25.6820172406699
> toV 50 30;              // 50オーム負荷に30dBm(1W)を供給するには、何V加えるか?
7.07106781186548

まとめて計算しちゃいたい場合は、下記のようにすると良い。関数型便利ーーて 思う瞬間ですよ。

> map (toV 75) (-20:-10..10) ;
[0.0273861278752583,0.0866025403784439,0.273861278752583,0.866025403784439]
> -20:-10..10;
[-20,-10,0,10]

75オームの例で、色々なワット数に対して演算してる。点々を使うと、等差数列の リストを作成出来る。Haskellだと、[A,B..C]と書く所をpureでは、A:B..C と書く。 ちなみに、Aが初項、Bが次項、Cが終項の意味です。

上で等差数列が出てきたんで、逃避数列はどうするってがある。例を例によって虎妓から いただこう。抵抗には、E6系とかE12系とか呼ばれる抵抗値が決まっている。詳細は、 E6系列 E12系列 E24系列 E48系列 E96系列 E192系列の一覧表と概略計算方法 を参照。pureでこれを計算してみる。

> iterate (* (10^(1/6))) 1 !! (0..5);
[1,1.46779926762207,2.15443469003188,3.16227766016838,4.64158883361278,6.81292069057961]
> map (\r -> (r, toV r 30)) $ iterate (* (10^(1/6))) 10 !! (0..5);
[(10, 3.16227766016838)         // 見やすいように整形してる
,(14.6779926762207, 3.83118684955729)
,(21.5443469003188, 4.64158883361278)
,(31.6227766016838, 5.62341325190349)
.(46.4158883361278, 6.81292069057961)
,(68.1292069057961, 8.25404185268018)]

E6系を自動生成してみた。大体合ってるかな。そして、それを合成して、10オームから 始まるE6系に1Wを加えると両端に何V発生するか計算。(世の中に、定電圧(流)源が有る のは承知してるけど、定電力源って有るのかしらん?)

おいらへのメモ 上記で出てきた、2つのびっくりは、リストのスライス。びっくりの前 までが、iterateって事で無限等比数列を発生させてる。そしてその数列の最初から5番目 まで取り出せって事。(最初は0ってのは、この業界のお約束です)

上の例なんて正に例の為の例だな。もっと実用的な例は無いの? しばし考えてたら 今使ってるNotePC、無線LANでつないでる。無線の状況をモニタするアプリを入れてて 電界強度を得られるようになってた。それによると、おいらが受信してる電波は、-63dBm との事。アンテナのインピーダンスが50オームと仮定したなら、アンテナに励起される 電圧は何マイクロボルトになるか? こういうの1尼の酷試に出そうだな。

> 1e6 * toV 50 (-63);
158.301489826734

答え一発、pureの威力。間違っても

> 1e6 * toV 50 -63;
1000000.0*toV 50-63

マイナスの数値を使う場合、括弧でくくる事がHaskell界隈の慣わしです。くくらなくても、エラーだよって 文句を垂れない所がpureの寛容な所さ。hugsとかだと、ブースカ文句を言ってくる。 でも、この寛容さは、気味が悪いな。

ドイツ方言健在也。面白いから、少し自習して三日な。