トップ 最新 追記

じじぃの日記、ツッコミ可

Twitter: @jijixi_org
Xbox Live: jijixi

初心者が書いた OCaml 入門
Spotlight tips サイト内リンク集
1970|01|02|
2003|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|11|
2011|05|
2012|01|

2007-05-06 [長年日記]

% [Erlang] おぶじぇくと指向

なんつーかさー、変に話題になっちゃうと途端に冷めちゃうってのが、わしのダメなところだと思うんだけども、そんな感情的な話で離れてしまうのは、あまりにもったいないと思うわけで、冷めムードではあるけど無理にでも盛り上がってみようと思ってネタプログラミングとか。

や、これ自体はたしかにネタだけど、何らかのフレームワークを作る時の叩き台にはなりそうな気はするよ。 要するに適当な粒度でもって処理をプロセス化していく仕組みにしておけば、CPU のコア数が増えたときに自動的にスケールしてくれるという感じで。

ま、それはともかく。 まずクラスを作りますヨ。

% cat oop.erl
-module(oop).
-export([counter/1]).

counter(Num) ->
   put(num, if Num == undefined -> 0; true -> Num end),
   receive
      incr -> put(num, get(num) + 1);
      print -> io:format("~p\n", [get(num)]);
      {get, Pid} -> Pid ! get(num);
      {[set, N], Pid} -> Pid ! put(num, N)
   end,
   counter(get(num)).

counter というクラスには incr, print, get, set というメソッドがあります。 読み込んでみましょう。

% erl
Erlang (BEAM) emulator version 5.5.4 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.4  (abort with ^G)
1> c(oop).
{ok,oop}

インスタンス化します。

2> Obj1 = spawn(oop, counter, [10]).
<0.37.0>
3> Obj2 = spawn(oop, counter, [20]).
<0.39.0>

メソッドを呼んでみます。

4> Obj1 ! incr.
incr
5> Obj2 ! incr.
incr
6> Obj2 ! incr.
incr
7> Obj2 ! incr.
incr
8> Obj1 ! print.
11
print
9> Obj2 ! print.
23
print

ちゃんと状態を保持してるし、各インスタンスごとに別の状態を持ってます。

10> Obj1 ! {[set, 100], self()}.
{[set,100],<0.30.0>}
11> receive N -> ok end.
ok

set メソッドの使い方はこんな感じ。 返り値は書き換え前の値ですが、ここでは捨ててるだけ。

12> Obj1 ! print.
100
print
13> Obj2 ! print.
23
print

以上。

GUI の各部品が一つのプロセスになってるような GUI フレームワークとか作ると Smalltalk っぽくておもしろそうかも、と思った。 直接 X プロトコルを喋るポートドライバとか作れば (これ自体はすでにあるかもだけど) できそうかな、とか思うけど、誰か作んないかな。 そんな暇人はいないかね。

% [Erlang] ex11 (Jungerl)

shiro さんが存在を教えてくれたので、ちょっと調べてみた。 そもそも X プロトコル喋るだけならソケットが使えれば良いんだから、ポートドライバとかわざわざ作らなくても、Erlang だけで書けるんだなーと気付いたときには、「おまえポートドライバ言いたいだけちゃうんか」と脳内ツッコミがぁ〜...orz

すんません、にわかもんは語りたがるんすよ。

Eshell V5.5.4  (abort with ^G)
1> cean:search(ex11).
[{"x11","New flavour of the ex11"},
 {"ex11",
  "This is an Erlang-X11 binding, i.e an implementation of the X11 protocol in Erlang"}]

とりあえず、cean には登録されてるみたいなので、入れてみた。

2> cean:install(ex11).
+ ex11 md5=<<211,131,209,44,14,178,244,85,176,108,187,55,208,99,117,145>>

……けど、何をして良いのかわからんので、まずは erlang/lib/ex11-1.5/bin/test のまねをして、rcube:start を実行してみる。

1> rcube:start().
<0.32.0>

……ダンマリ。 仕方ないのでソース読もうと思ったら cean ではソースがインストールされないみたいなので、cvsweb で閲覧。 とりあえずよくわからんのだけど、最初のとっかかりであるはずの、

4> ex11:start(localhost).
** exited: {error,econnrefused} **

ex11:start がこんな感じで失敗してるので、うちの環境の問題なのかもしれない。

% [Erlang] x11 - New flavour of the ex11.

懸命なる読者の方々はお気付きかもしれないが、先程 cean:search(ex11). を実行した時に ex11 の他に x11 というものもリストアップされていたのである。

というわけでさっそくインストールしてみたのだが……ナゼか中身が空っぽ。 しかたなく Jungerl のレポジトリからチェックアウトして demo.erl を動かしてみた。

% cd jungerl/lib/x11/src
% make
% erlc -I ../include demo.erl
% erl -pa ../ebin demo.beam -s demo xev  
Erlang (BEAM) emulator version 5.5.4 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.4  (abort with ^G)
1> 
=ERROR REPORT==== 6-May-2007::20:07:47 ===
Error in process <0.30.0> with exit value: {{badmatch,{error,bad_auth}},[{demo,xev0,1}]}

うーん、やっぱだめか。

なんかソース斜め読みした感じからすると、ex11 にしても x11 にしても UNIX domain socket じゃなく IP で X サーバに接続してるように思えるんだけど、確か Mac OS X の X11.app って IP での接続を許可してなかった気がすんなー。

% xauth generate localhost:0 .
xauth: (argv):1:  unable to open display "localhost:0".

これって、その証拠になるんだっけ?

なんか以前は X11.app の環境設定にそこら辺の項目があったような気がするんだけど、今見ると無いね。 そろそろめんどくさくなってきたんで、これ以上の追求は気が向いたらいずれ。

% [Mac] とりあえず X11.app で IP での接続を許可するには

~/Library/Preferences/com.apple.x11.plist の nolisten_tcp プロパティに No を設定する。 あとまあ、その時は no_auth も No にしといた方が良さそうな予感。 とりあえず、これで、

1> ex11:start(localhost).
{ok,{xlib,<0.32.0>,<0.30.0>,16}}

てな感じで接続は可能になった。 ……けど、まだサンプルは思うように動いてくれないんで、もっと調べにゃあかんなー。

本日のツッコミ(全2件) [ツッコミを入れる]

% shiro [あるみたいですよ>GUIの各部品がプロセス、直接Xプロトコル http://www.erlang.se/worksh..]

% jijixi [ありがとうございます。 わりと誰もが考えそうだなーとは思ってましたけど、やっぱり先人が歩いた後だったんですね(苦笑]


2007-05-08 [長年日記]

% [雑談] 夜更かしなのか早起きなのか、よくわからん状態

4 時すぎに寝て、5 時半に起きた。 レム睡眠の訪れる間隔は 1 時間半ほどだと言われているので、つまり入眠後最初のレム睡眠時に何らかの刺激があって覚醒してしまったと思われる。

……つーか納得行かねー。

% [Erlang] erl コマンドの -remsh オプション

多分レム睡眠とは関係無い。 erl コマンドのマニュアルを眺めてて見つけた。

-remsh Node
    Starts Erlang with a remote shell connected to Node. 

ということで、リモートノードをいじるための Erlang シェルを起動できるということらしい。 なわけでテスト。

% cat test.erl
-module(test).
-compile(export_all).
test() -> io:format('test.\n').

こんなコードを用意しておいて、適当なノードに読み込ませる。

% erl -sname hoge@localhost
Erlang (BEAM) emulator version 5.5.4 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.4  (abort with ^G)
(hoge@localhost)1> c(test).
{ok,test}

んで、別のノードを -remsh オプションを使って立ち上げる。

% erl -sname fuga@localhost -remsh hoge@localhost
Erlang (BEAM) emulator version 5.5.4 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.4  (abort with ^G)
(hoge@localhost)1> 

別ノードと会話をする都合上、-sname なり -name なりで名前を付けておく必要がある模様。 でもプロンプトの表示が fuga じゃなく hoge なところに注目。

(hoge@localhost)1> test:test().
test.
ok

VM 自体は fuga なんだけど、いじってるのはあくまで hoge の方なんで、hoge の方でしか読み込んでない test モジュールが普通に使える。 まあ、あたりまえっちゃーあたりまえ。

ちなみに ^G で使える User switch command からでもリモートシェルは起動できるので、

% erl
Erlang (BEAM) emulator version 5.5.4 [source] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.5.4  (abort with ^G)
1> net_kernel:start([piyo@localhost, shortnames]).
{ok,<0.32.0>}
(piyo@localhost)2> 
----- ここで ^G を入力している -----
User switch command
 --> ?
  c [nn]   - connect to job
  i [nn]   - interrupt job
  k [nn]   - kill job
  j        - list all jobs
  s        - start local shell
  r [node] - start remote shell
  q        - quit erlang
  ? | h    - this message
 --> r hoge@localhost
 --> c
Eshell V5.5.4  (abort with ^G)
(hoge@localhost)1> test:test().
test.
ok

こういうこともできる。

さらにちなみに、最近気付いたんだけど、Atom としては "_" 以外に "@" も特別扱いなので、hoge@localhost みたいのはシングルクォートで囲まなくても Atom になる。

% [Erlang][Ruby] Erlang で引数に fun() を求められているときの苦悩

…が何日か前に解消された。 教えてくれたのはここのページ(実際ははてなの方だったけど、今はもう消えてるみたい)。 ここは他にも色々参考になるのでありがたいです。

で、具体的にどういうことかというと、OCaml だと

List.map string_of_int [1;2;3]

などと書く場面で、Erlang の関数をそのまま渡す方法がわからなかったので、以前は

lists:map(fun(X) -> integer_to_list(X) end, [1,2,3]).

とか冗長なことを書いてたわけ。 んで、今回これが、

lists:map(fun erlang:integer_to_list/1, [1,2,3]).

と書けるようになってわ〜い、と。 ……って微妙に短くなってないけど、これはたまたま使った関数がモジュール名を省略できるヤツだったからで、他の関数ならもっと嬉しいと思う。 どっちにしろクロージャにするとスタックが一段無駄だし。

で、唐突に Ruby の話になるけど、関数型言語でこういう書き方に慣れてくると、Ruby で

[1,2,3].map { |x| x.to_s }
              ^^^

この変数宣言の部分がすごくウザく感じるんだよね。 一応、関数的メソッドの場合なら、

[1,2,3].each &method(:p)

みたいなのもアリかも知れないが、これはこれで色んな意味であんまりだし、上記の to_s みたいな場合はどうしようもない。 こういうときは何か暗黙の変数が欲しくなる (どこぞの言語の it みたいな)。 実際にそれが導入されるのが良いことなのかは、微妙に美しくない気もして、何とも言えないんだけど。

でも、やっぱりブロックの仮引数は、ブロック外の変数と同一スコープだから名前には多少なりと気を使うわけだし、それが実質ほとんど意味の無い変数だったりするとストレスだよね。 まあ、スコープルールに関しては改善される予定はあるみたいだし、今後に期待しておこう。

実は引数を囲むパイプ文字 (正式名称知らず) も嫌いなんだよな。 位置的に打ちづらいし、キーボードによって場所が違ったりするし(苦笑

% [Erlang] 対話環境で色々やるにあたって、まず読んどいた方が良いもの

c モジュールとか shell モジュールとか。 特に憶えておいた方が良いのは、

f()        全ての変数束縛を解除
f(X)       変数 X の束縛を解除
flush()    シェルプロセスのメールボックスに溜ってるメッセージを消去 (ついでにそのリストを表示)
pid(X,Y,Z) <X,Y,Z> というプロセス ID を返す (list_to_pid 使うより楽)

この辺かね。 特に f 関数を憶えておくと、いちいち新しいシェルを起動したり VM を再起動したりしなくても色々実験ができて楽。

% [Erlang] エラー報告の見方を訓練してみよう

Erlang のエラーメッセージは意味がわからんという話を色んなところで目にした。 まあ、実際慣れてないとわけわからん気はするんだけど、ある程度の肝を押さえてしまえば、わりと何てことない気もする (それでも "わかりやすい" かは微妙だけど)。 つーことで、エラーの報告の見方について、とりとめもなく書いてみる。 って言うか、わしもまだまだ勉強中だから、いつものようにテディベアへの説明だってことで。

まず強く意識しておかなきゃならないのは、手続き型なら「代入」、関数型なら「束縛」などと称される…

X = 1.

こういう式は、Erlang ではパターンマッチであるということ。 まあ、OCaml の let とかもそうなんだけど、OCaml の場合どんどん元の束縛値を隠して新しい値を同じ変数に束縛していけるのに対して、Erlang では「束縛は一度きり」なので「一度何かを束縛した変数は定数と同じ」扱いになる。 上記の例で言えば、X に何も束縛されていない状態であれば、X に 1 が束縛されて両辺が等しくなるのでパターンマッチが成功するけど、もし X に 2 とかが束縛されていると…

1> X = 2.
2
2> X = 1.

=ERROR REPORT==== 8-May-2007::19:37:44 ===
Error in process <0.30.0> with exit value: {{badmatch,1},[{erl_eval,expr,3}]}

** exited: {{badmatch,1},[{erl_eval,expr,3}]} **

このようにパターンマッチに失敗してエラー (他の言語で言うところの例外と思って良い) になる。 この 2 行目の X = 1 は 2 = 1 と同じことだからだね。 どうせだから、このエラーから見方を考えていこう。

通常、エラーを起こしたプロセスはその時点でエラーを示す終了値を持って終了する。 この例で言えば、<0.30.0> というプロセス中で {badmatch,1} というエラーを起こして終了している。 では、この終了値の残りの部分 [{erl_eval,expr,3}] てのは何かと言うと、スタックトレースである。 もう少し詳しく説明すると (等幅フォント推奨)、

{ { badmatch, 1 },
              ^ ←エラーを起こした式 (厳密には badmatch の場合は "値")
    ^^^^^^^^ ←エラーの種類
  ^^^^^^^^^^^^^^^ ←エラーメッセージ (Exit Reason)
  [ { erl_eval, expr, 3 } ] }
                      ^ ←アリティ (引数の数) もしくは直近の呼び出しだと引数のリストになる場合も
                ^^^^ ←関数名
      ^^^^^^^^ ←モジュール名
    ^^^^^^^^^^^^^^^^^^^^^ ←一段分のスタック
  ^^^^^^^^^^^^^^^^^^^^^^^^^ ←スタックのリスト

こんな感じ。 「1 という式で badmatch (パターンマッチ失敗) というエラーが起きた」ということを示すタプルと、エラーが起きたときに呼ばれていた関数を示すタプルのリストを要素にするタプルが終了値になっているわけ。

Erlang の対話環境では (ソース読んだわけじゃないので、動作からの想像だけど)、入力したコードを別プロセスで erl_eval:expr/3 という関数を使って評価して、その結果を対話環境側に戻しているんだと思う。

さて、この例だとスタックが一段だけでつまらないので、もう少し派手なエラーを出させてみる。

3> io:format('hello ~s~n', "world").
** exited: {badarg,[{io,format,[<0.23.0>,'hello ~s~n',"world"]},
                    {erl_eval,do_apply,5},
                    {shell,exprs,6},
                    {shell,eval_loop,3}]} **

…って言うほど派手じゃなかったけど、ともあれ。 この場合、badarg がエラーメッセージで、それに続くリストがスタックトレースだ。 さっきの例と違って別プロセスを立てずに直接関数を呼び出してるので、Error in process なんちゃらという表示は無い。 badarg はもちろん「引数がおかしいぞ」という意味である。

io モジュールのソースを読めばわかると思うけど、io:format/2 は第一引数に standard_io を補って io:format/3 に丸投げされる。 その呼び出しが {io,format,[<0.23.0>,'hello ~p~n',"world"]} の部分。 ちなみに、なぜ erl_eval:do_apply/5 から io:format/2 ではなく io:format/3 が直接呼ばれてるかは定かではないが、多分コンパイル時に最適化されているんではないかと思う (つーか単に末尾呼び出しだからスタックトレースに現れるわけが無いということかも)。

ところで上記の式が badarg エラーなのが、なぜなのかわからない人は io モジュールのマニュアルをよく読むこと。 わしは Erlang 触り始めの頃には毎回のように、この間違いをしてたな。 最近ようやく慣れた。

えーと、こんな感じで。 あと一応書いとくと、エラーが起きたときにエラーが伝播するのは link しているプロセスだけで、link していないプロセスがエラーを起こしても何も起こらない (ただし、monitor している場合には 'DOWN' メッセージが届く)。 例えば、こんな感じだと…

15> spawn(io,format,['hoge ~s~n', "fuga"]).
<0.54.0>

spawn 関数だと link はされないので、あきらかに生成したプロセスではエラーが起きているはずだけどエラーは報告されない。 これを、spawn_link 関数に変えると、

17> spawn_link(io,format,['hoge ~s~n', "fuga"]).
<0.57.0>
** exited: {badarg,[{io,format,[<0.23.0>,'hoge ~s~n',"fuga"]}]} **

このようにエラーが報告される。 さらに厳密に言うと、シェルプロセス側でエラーの処理をしていないので、実はこの時点でシェルプロセスも死んでいる。

18> self().
<0.59.0>
19> spawn_link(io,format,['hoge ~s~n', "fuga"]).
<0.61.0>
** exited: {badarg,[{io,format,[<0.23.0>,'hoge ~s~n',"fuga"]}]} **
20> self().                                     
<0.63.0>

このように、何事もなくシェルプロセスが続いているように見えるけど、実は一度死んで新たに起動させられているだけだったりする。 きっとシェルプロセスをモニタしているプロセスが、いろいろよろしくやってくれてるんだろう。

と、ここまで書いて気付いたが、この例だと ERROR REPORT が出てないな (** exited ... というのは self() の終了メッセージだと思う)。 きっとエラーを掴まえて何かをするプロセスがどっかにいるんだろう。 そんで、単に spawn_link を使うと、それを経由しないから ERROR REPORT が表示されないとか、そういう感じだろうか。

なんか毎度毎度締まらないけど、この辺で終了。 あ、エラーメッセージ (正しくは Exit Reason) についてはマニュアルに表があるので読んでおくとためになる。

本日のツッコミ(全3件) [ツッコミを入れる]

% yhara [> この変数宣言の部分がすごくウザく感じるんだよね Ruby界でもそういう話が盛り上がったことがあります(to_pr..]

% jijixi [あ、なんか見た覚えがあります。 多分、Ruby 本体に取り込まれたら使うでしょうけど、個人的な趣味で言うと微妙に字面..]

% TrackBack [http://jijixi.azito.com/cgi-bin/diary/index.rb?date=200705..]


2007-05-09 [長年日記]

% [雑談] fizzbuzz 問題 (わりとどうでもいい話)

公倍数がどうのこうの言ってる人は、こういう↓解答は認めないということなんだろうか。

(1..100).each do |n|
   buf =  if n % 3 == 0 then "fizz" else "" end
   buf << if n % 5 == 0 then "buzz" else "" end
   print(if buf.empty? then n.to_s else buf end + " ")
end
print "\n"

こういうアプローチの場合は公倍数とか出てくる余地は無いと思うが。 ……わしはヘタレなので、微妙に自信が無い。

あと、鍋谷さんとこで見た、剰余使うなんてダメ(?)という意見も、なぜなのかわからない。 まあ、そのコメントをした人の意図がそもそもよくわからないので、以下は余談。

剰余を使わずに、それ以上に読みやすいように (コードから意味を汲み取りやすいように) 書く方法ってあるかな。 例え剰余の演算が多くの環境であまり効率の良くないものだとしても、いきなりそれを使うのを否定して読みにくいコードを書くのは早すぎる最適化というやつではないのかな。 まあ "つまらない" のは確かだけど。


2007-05-10 [長年日記]

% [Erlang] エラーネタの続き

この前の話の補足みたいな。

最後の方で ERROR REPORT が出ない云々という話があったけど、あれってどうも ERROR REPORT を吐く役目を持ってるプロセス (たぶんシェルプロセス) が、それを吐く前に exit の伝播によって死んでるのが原因っぽい。 だから、例えば↓のようにすると…

Eshell V5.5.4  (abort with ^G)
1> process_flag(trap_exit, true).
false
2> spawn_link(io,format,"hoge").
<0.33.0>

=ERROR REPORT==== 10-May-2007::18:45:26 ===
Error in process <0.33.0> with exit value: {undef,[{io,format,"hoge"}]}

ちゃんと ERROR REPORT が出る。

何をやってるか一応説明しておくと、1 行目では他のプロセスから exit が伝播してきたときにそれをトラップするかどうかのフラグを true に設定している。 返り値が false なのは設定に失敗したってことじゃなくて、書き換え前の設定が戻ってきてるだけ。 で、この状態になると link したプロセスが exit しても、いきなり死なずに 'EXIT' というメッセージを受けて、それに対して何らかの処理をすることができるようになる (もちろん無視することも)。 なので、2 行目を実行した段階では、spawn したプロセスだけが死んで、シェルプロセスに対してはメッセージが飛んできてるだけな状態。

3> flush().
Shell got {'EXIT',<0.33.0>,{undef,[{io,format,"hoge"}]}}
ok

実際、こういうメッセージが届いている。

ところで 2 行目に対するエラーが badarg じゃなく undef なのは、文字列が実際には数値のリストと同じものだから。 つまり、

spawn_link(io,format,"hoge").

は、

spawn_link(io,format,[$h,$o,$g,$e]).

と解釈されて、新しく作ったプロセスでは、

io:format($h,$o,$g,$e).

として呼び出されてる状態。 で、io:format/4 という関数は存在しないので undef というわけ。


2007-05-11 [長年日記]

% [Erlang] Erlang で FizzBuzz

すでにいくつか見かけたけど (例えばここ)、Erlang で何か書きたかったんで書いた。 とはいえ、めんどいんで単純に剰余使ってるが。

まず、この前の Ruby 版を踏襲したバージョン。

% cat fb.erl
-module(fb).
-export([main/0,f/1]).

main() -> f(100).

f(Max) -> f(1,Max).
f(Num,Max) when Num =< Max ->
   Buf = case Num rem 3 of
      0 -> "Fizz";
      _ -> ""
   end ++ case Num rem 5 of
      0 -> "Buzz";
      _ -> ""
   end,
   case length(Buf) of
      0 -> io:format("~b ",[Num]);
      _ -> io:format("~s ",[Buf])
   end,
   f(Num+1,Max);
f(_,_) -> io:nl().

これだと Erlang の意味ナッスィングっぽいので、無理にでもプロセス作るバージョン。

% cat fb2.erl
-module(fb2).
-export([main/0,f/1]).

main() -> spawn(?MODULE,f,[100]), ok.

printer() ->
   S = receive
      {_,0,0} -> "FizzBuzz";
      {_,0,_} -> "Fizz";
      {_,_,0} -> "Buzz";
      {N,_,_} -> integer_to_list(N);
      exit -> io:nl(), exit(normal)
   end,
   io:format("~s ",[S]),
   printer().

f(Num) ->
   Pid = spawn(fun printer/0),
   lists:foreach(
      fun(N) ->
         Pid ! {N, N rem 3, N rem 5}
      end,
      lists:seq(1,Num)),
   Pid ! exit.

ついでに、これはちょっとやりすぎだと思うバージョン。 あと、これだと厳密にはちゃんと順番に表示されるとは限らない (まあこの程度の処理で順番が崩れることは無いと思うけど)。

% cat fb3.erl
-module(fb3).
-export([main/0,f/1]).

main() -> spawn(?MODULE,f,[100]), ok.

printer(Num) ->
   io:format("~s ",[
      case {Num, Num rem 3, Num rem 5} of
         {_,0,0} -> "FizzBuzz";
         {_,0,_} -> "Fizz";
         {_,_,0} -> "Buzz";
         {N,_,_} -> integer_to_list(N)
      end]).

f(Num) ->
   [spawn(fun() -> printer(N) end) || N <- lists:seq(1,Num)],
   io:nl().

% [Erlang] エラーの見方、コンパイルエラー編

神経質なほどに曖昧さを排除した文法のおかげで、コンパイルエラーのほとんどは "." と "," と ";" の整合性を間違えてる場合に違いない。

  • 複数の式を繋ぐのはカンマ(,)
  • 複数のパターンを繋ぐのはセミコロン(;)
    • 引数の数が同じ関数は、複数のパターンを持つという扱いなのでセミコロンで繋ぐ
  • 関数の終わりはピリオド(.)
  • おしりに余計なカンマやセミコロンを付けるとエラー

これさえしっかり憶えておけば、あとは C なんかと同じく最初のエラーから順番に潰してけば、すぐに直せる。 syntax error に関しては、大体これでオッケーなはず。 っていうか、これ以外で syntax error が出るときは、もっとちゃんとマニュアル読み直した方が良いと思う。

…で、他の言語の感覚だと「なぜコンパイルエラーになるかわからん」というケースも実はある。 実際、わしもかなり悩んだ経験があったり。 例えばこんなコード (非常にでっちあげな例だが)。

% cat test.erl
-module(test).
-compile(export_all).

f(X) ->
   if g(X) > 0 -> positive;
      g(X) < 0 -> negative;
      true -> zero
   end.

g(X) -> X.
1> c(test).
./test.erl:5: illegal guard expression
./test.erl:6: illegal guard expression
error

不正なガード式って、どこがだよ?…と思うよね。わしは思った。 まず if の条件式がガード式扱いってのが予想外なんだけど、まあそれはそれとして。 実はガード式には、ごく一部の組み込み関数を除いて『関数が使えない』という制限があるのであった。 な、なんだってーー!!

なので、上記のコードは以下のようにすると通る。

f(X) ->
   Y = g(X),
   if Y > 0 -> positive;
      Y < 0 -> negative;
      true -> zero
   end.

結構罠なので、憶えておくと、うっかりやっちゃったときに焦らないで済むと思うよ。

% [Erlang] 結局こういうのは痛し痒しやんね

これはこれで意味があるっていうか。

でもErlang のエラーメッセージは ghc 以上にひどいと思いますよ!

[イネムリネズミ日記(2007-05-11)より引用]

ひどいっちゃーひどいけども(苦笑)、束縛済みの変数を定数としてパターンマッチに使えるというのは利点でもあるわけで、そういう意味では、あそこで badmatch ってエラーになるのは仕方がないんじゃないかなーと。 こんな話もあるし。 まあ、初学者泣かせであることは間違いねぃですが。

% [Haskell] わしが Haskell で一番イヤンと思う所とか (半分くらいはネタです)

わけわかんなくたって、コンパイル時にエラーになってくれた方が良いっすよ。 今でっちあげたコードだけど…

main = putStrLn (hoge ["hoge","fuga","piyo"] "")

hoge [] r = r
hage (h:t) r = hoge t (h++r)

とか。 わしは Haskell には慣れてないのでツッコミどころは多々あると思うけど、それは置いといてもらってだ。 とにかく、このコードが何の警告も無くコンパイルできてしまって、そして実行時にエラーになるのがもうガッカリなの。 もうね、これは Haskell の文法的欠陥だと思うんだな。 Erlang みたいに、同じ関数のパターン違いは全部 ";" で繋ぐみたいな規制があれば、コンパイル時に間違いに気付けるわけよ。 せっかく静的型付けな言語なのに、こんなちょっとした typo をコンパイル時に検出できないなんて切なすぎる。

や、ちゃんと警告バシバシ出すようにしとけばわかるだろうけど、こんなミスをするのは初学者であって、初学者はコンパイル時にオプションとか付けねぃですよ。 って言うか、ghc は何でデフォルトだとこんなにも警告出さないのかがわからん。

% [OCaml] 初学者泣かせで、もう一品

初学者泣かせと言えば、OCaml でちょっとカッコ忘れたりして、想定外に型推論されちゃったときのコンパイルエラーのわけわからなさなんかも定評があるよね。 最初の内は、とにかく細かく細かく分けてトップレベルに定義していくようにしないと、ほんとどこで何が起こってるのかわからない。

まあ、そうこうしてる内に慣れてくると、そのわけわからないエラーでも不思議とどこが悪いのか見当が付くようになるんだから、人間ってすごいね。(ほんとは処理系がすごくなるべきだと思います)

% [雑談] 某ワニは…

わしもさっぱりわかんなかったことを告白しておきます。 できれば、わしの脳がゆるすぎるからだとは思いたくないです(泣

% [Haskell] jhc

via イネムリネズミ日記

ガベージコレクターがない

って何それ?と思ったんで調べてみた。 どうもこのページによると、

No garbage collector. A variant of Region Inference is in the works.

ということで、Region Inference とかいうものが使われていると。 んで、これは要するに、それぞれのデータが必要とされる領域を解析して、全部まとめてスタックに積むから必要なくなると勝手に消えるということかな。 まあ、Haskell みたいに mutable なオブジェクトが存在しない言語ならではの方法って気がするね。 それでも、

It leaks memory. The Region inference algorithm is still in the tweaking stage and programs are known to leak memory.

ってことみたいだから、相当難しいものなんだろうな。 まあ、苦労してるなーというのは朧げながらもわかる気がする。 っていうか最初 Lightweight Language C の GC ネタを思い浮かべてしまったのは、大変失礼だったと思う(苦笑


2007-05-12 [長年日記]

% [Erlang] デバッガに突撃

なんかさりげなくデバッガとか含まれてたりしてね。

Eshell V5.5.4  (abort with ^G)
1> ih().
iv()         -- print the current version of the interpreter
im()         -- pop up a monitor window
 ...長いので略...
ok

ってことで、おもむろに、

2> im().
Error in startup script: couldn't read file "/opt/cean/erlang/lib/gs-1.5.6/priv/
darwin-powerpc/gstk.tcl": no such file or directory

なんすかそりゃ。 どうも CEAN て微妙にアテになんないなー。 しゃーないのでとりあえず、こんなして、

% cd /opt/cean/erlang/lib/gs-1.5.6/priv/
% mkdir darwin-powerpc
% cd darwin-powerpc
% ln -s ../gs* ./

めんどくさいから erl を再起動。

Eshell V5.5.4  (abort with ^G)
1> im().
<0.32.0>

これで、こんな↓感じでウインドウが表示されるはず。

erlang_monitor.jpg

Tcl/Tk を使ってるみたいなんで、適当に用意しておくこと。 まあ、Mac OS X には元から入ってるし、他の UNIX 系システムでも知らん間に入ってることが多かろう。 Windows は、とりあえずオフィシャルサイトのインストーラで入れた場合は、Tcl/Tk ごと入ってるのか、何事もなく動いてた。 あ、Mac OS X with MacPorts とかだと、X Window 版の Tk が知らん間に入ってるかもしれないので、そういう場合は X11.app 起動したり、+aqua 付けて tk をインストールし直したり、PATH の頭に /usr/bin を持ってきたりして対処。

さて、そいじゃーこの前の単純なコードを利用して試してみよー。

% cat test.erl
-module(test).
-compile(export_all).

f(X) ->
   Y = g(X),
   if Y > 0 -> positive;
      Y < 0 -> negative;
      true -> zero
   end.

g(X) -> X.
3> c(test).
{ok,test}

まずは ii 関数でデバッグするモジュールを登録っと、

4> ii(test).
** Invalid beam file or no abstract code: test
error

いきなり挫折。

5> int:interpretable(test).
{error,no_debug_info}

要するにデバッグ情報がネーという話な模様。 てことで、コンパイルし直す。

8> c(test,[debug_info]).   
{ok,test}
9> int:interpretable(test).
true

うむ。今度こそ登録。

10> ii(test).               
{module,test}

さっき立ち上げたモニターの左側の欄に test と表示されてればオッケー。 この辺からコマンドラインでやるのは面倒になってきたので、おもむろにその test という部分をダブルクリック。 View Module test というタイトルのウインドウが立ち上がるはず。

erlang_view_module_test.jpg

長くなってきたんで、一旦区切り。

% [Erlang] デバッガ探検、ブレークポイント編

さて、さっきの View Module test ウインドウの 6 行目をおもむろにダブルクリック。 行番号のところが、"6:" から "-@-" に変わればブレークポイントとして登録されたことになる。

11> ipb().
Module          Line      Status       Action       Condition            
test            6         active       enable                            
ok

このように。 あと Break メニューから Function Break... を選べば、関数にブレークポイントを設定することもできる。 コマンドラインからやるなら、

12> ib(test,g,1).
ok

こんな感じで、ib 関数にモジュール名、関数名、アリティを渡す。 行で指定する場合は、ib 関数にモジュール名、行番号を渡す。 Conditional Break というのは、ブレークポイントのところで指定した関数 (true か false を返す) を実行して true ならブレークするという動作をする模様。 試してないが、ibc 関数の説明を見るかぎり、そんな感じ。

そんなこんなで実行してみよう。 とりあえずさっき付け足した分は余計だから消しとく。

14> ir(test,g,1).
ok
15> ipb().
Module          Line      Status       Action       Condition            
test            6         active       enable                            
ok

んで……普通のデバッガには存在する run コマンドみたいのが無いよな。どうすんの? ……と一瞬思ったが、何のことは無い。 元々 Erlang のプログラムにエントリポイントのようなものは無いので、デバッガに登録した状態 (ii 関数を使った状態) で普通にそのモジュール (の関数) を動かせば良い。 だから、ここではこうする↓

16> test:f(42).

そうすると、こういう↓ウインドウが立ち上がるはずだ (Option で Auto Attach の設定をしてるときだけかも)。 立ち上がらない場合は、モニターウインドウに今呼んだ関数に関する情報が出てきているはずなので (Initial Call の欄が test:f/1 になってるやつ)、そいつをダブルクリックしてやろう (コマンドラインからなら ia 関数)。

erlang_breakpoint.jpg

あとはもう、なんつーか普通にデバッガだよね。 右下には変数の中身が表示されてるし、左下の Evaluator: のところに適当に式を打ち込めば、色々その場所での状態を確かめることができる。 Step,Next,Continue,Finish,Where,Up,Down というボタンも、gdb とか使ったことあれば普通に機能の連想が効くでしょう。 つーか、わしもまだちゃんと詳しく試してみてるわけじゃないので、その程度のことしか言えないのでありますよ(苦笑

ともかく、ここで表示されたウインドウはプロセスごとのもので、プロセスは平行して実行されるものなので、デバッグも平行して行なうことができるのである。 理論上は。 もちろん人間はそんな風にできてないけども。 まあ、複数人でやりゃ良いわけで、できることはできるはず。 ローカルノードのプロセスも、リモートノードのプロセスも、Erlang の世界では大した違いは無いわけで。

% [clip][Prolog] FizzBuzz (【見覚え】koichikのひとりごと【あります】)

きむらさんの「是非とも Prolog での解を」を見て、そういやどっかで見かけたなーと思って履歴とかほじくりかえして発見。 Prolog がどーのこーのより、その後のネタがおもしろかった。

CPU が 400 コくらいあれば (^^;

% [Erlang] toolbar モジュール

とりあえず一度くらいは toolbar:start() して、いろいろボタン押してみると良いと思った。

% [Scala][Ruby] Implicit anonymous functions

2.5 は RC が取れたら入れようと思ってたんだけど、なんとなく Change Log を眺めてたら、ちょっとスルーしかねる要素が見えたんで試してみた。

scala> val f = (x:Int) => x + 1
f: (Int) => Int = <function>

これがさー、

scala> val f = (_:Int) + 1
f: (Int) => Int = <function>

これは良いんじゃね? 対話環境で試してるから型指定してるけど、型推論が効くシチュエーションなら _ + 1 って書けるわけでしょ。

scala> List(1,2,3).map(_ + 1)
unnamed9: List[Int] = List(2, 3, 4)

scala> List(1,2,3).map(_.toString)
unnamed10: List[java.lang.String] = List(1, 2, 3)

良いなあ。Ruby も、これで良いんじゃない? アンダースコア一個の変数名が予約されても、困るのって irb くらいでしょ (まったく確認せずに言ってますよ)。 わしは irb でも使ってないし (というか、すぐ存在を忘れる)。

本日のツッコミ(全2件) [ツッコミを入れる]

% クエック [toolbar:start(). オセロで完敗です。]

% jijixi [にひ、おまけ発見おめでとうございます。 ただガッカリなことに、Mac OS X だとコラムスくらいしかまともに動いて..]


2007-05-13 [長年日記]

% [Erlang] SMP で実験できる環境が欲しいなあ

などと思ってみても、無い袖は振れないのでとりあえず、おもむろに…

% erl -smp

とかしてみる。

Erlang (BEAM) emulator version 5.5.4 [source] [smp:1] [async-threads:0] [hipe] [kernel-poll:false]

なんやようわからんが、一応 SMP モードに入ってる模様。 まあ、CPU は一個だがナ。

 PID COMMAND      %CPU   TIME   #TH #PRTS #MREGS RPRVT  RSHRD  RSIZE  VSIZE
3185 beam.smp     0.0%  0:00.33   5    35    36  9.27M  1.78M  11.3M  44.8M
3257 beam         0.0%  0:00.28   2    31    31  9.00M  1.64M  10.2M  43.3M

…このように、普通に起動したの (下の方) とは、それなりに違いがある。 特に違うのはスレッド数 (#TH) で、多分 SMP モードのときはこの多めに用意されたスレッドにプロセスを割り振るようになってるんだろうと思う。 CPU の数によってスレッド数が増減するかは知らないけど、動的に数が変わるようなことは無さそうな予感。

漠然とした想像だけど、CPU を占有するような忙しいプロセスが複数あるようなシチュエーションじゃないと性能が出なさそうな気がする。 すぐ死ぬプロセスをボコボコ量産するようなプログラムだと、逆に遅くなったりとかありそう。 プロセスを各スレッドに割り振るってことは、spawn の時にいちいちスレッド間通信でデータを送るんだろうけど、その分のコストは無視できるほど小さいのかって言うと微妙な気がするし。


2007-05-15 [長年日記]

% [雑談] らき☆すた 6 話

貝殻から出てきたアレが無駄にリアルすぎて、トラウマになりそうな件。 もう今回はアレの印象が強すぎて、他のネタが頭に残ってねーよ、という。

% [雑談] インターネット依存症

どうも NTT 側のトラブルで 3 時間ほど IP unreachable だったわけだが。 いやー、なんつーの、出かけてたりしてパソコンが手元に無いときは、まあ諦めが付くのかそれなりに平気なんだけど……

目の前にパソコンがあるのに、インターネットに接続できないときの焦燥感ったら無いよね。

ね。そう思いませんか。

% [Erlang] ++ 演算子は、どうもドーピングされてるっぽい件

Erlang では文字列がリストなだけに、結構頻繁に ++ 演算子を使うことになるんだけど、どうも貧乏性の人間から見ると、こいつは効率が悪そうに見えてならない。 でもまあ、イメージだけでダメ出しするのも何なので、テストしてみた。

% cat list_test.erl
-module(list_test).
-export([plusplus/2, append/2, trfunc/2, test/1, main/0]).

plusplus(A,B) -> A ++ B.

append([],L) -> L;
append([H|T],L) -> [H|append(T,L)].

trfunc(A,B) -> trfunc(A,B,[]).
trfunc([],[],Ret) -> lists:reverse(Ret);
trfunc([],[H|T],Ret) -> trfunc([],T,[H|Ret]);
trfunc([H|T],L,Ret) -> trfunc(T,L,[H|Ret]).

test(N) ->
   {L1,L2} = {lists:seq(0,N), lists:seq(0,N)},
   {TC1,_} = timer:tc(?MODULE,plusplus,[L1,L2]),
   io:format('~b : ~b elems with ++ operator.~n',[TC1,N]),
   {TC2,_} = timer:tc(?MODULE,append,[L1,L2]),
   io:format('~b : ~b elems with append function.~n',[TC2,N]),
   {TC3,_} = timer:tc(?MODULE,trfunc,[L1,L2]),
   io:format('~b : ~b elems with trfunc function.~n',[TC3,N]).

main() ->
   L = [1000,10000,100000,1000000],
   lists:foreach(fun(N) -> test(N), io:nl() end, L).

まあ見ればわかると思うけど、plusplus が ++ 演算子を使ったやつで、append は素直に実装したもの。 あとおまけで trfunc が末尾再帰にして最後に逆転というバージョン。一応スタックオーバーフロー対策。 ……なんだけど、まあ、これは後で書く。 ともあれテスト。

1> c(list_test).
{ok,list_test}
4> list_test:main().
61 : 1000 elems with ++ operator.
204 : 1000 elems with append function.
456 : 1000 elems with trfunc function.

949 : 10000 elems with ++ operator.
4203 : 10000 elems with append function.
3920 : 10000 elems with trfunc function.

13802 : 100000 elems with ++ operator.
19007 : 100000 elems with append function.
19711 : 100000 elems with trfunc function.

40975 : 1000000 elems with ++ operator.
101866 : 1000000 elems with append function.
325950 : 1000000 elems with trfunc function.

GC かなんかの関係か、何度か実行してみても多少バラけるところがあるんだけど、だいたいはこんな感じ。 ++ が圧倒的に速い。 試しに native コードでも試してみる。

5> c(list_test,[native]).
{ok,list_test}
8> list_test:main().
64 : 1000 elems with ++ operator.
290 : 1000 elems with append function.
416 : 1000 elems with trfunc function.

573 : 10000 elems with ++ operator.
1289 : 10000 elems with append function.
4877 : 10000 elems with trfunc function.

9771 : 100000 elems with ++ operator.
20045 : 100000 elems with append function.
35777 : 100000 elems with trfunc function.

40859 : 1000000 elems with ++ operator.
97946 : 1000000 elems with append function.
323722 : 1000000 elems with trfunc function.

やっぱり ++ が速い。 何かのタイミングで ++ が append と同程度になったり、append と trfunc が逆転したりもするけど、とにかく ++ が平均して速いのは間違いない。

きっと真っ当な手続き (append 関数のようなね) を踏まずにインチキしてリストを繋いでるんだろうな。 O(n) っぽいからメモリコピーか何かは起きてるみたいだけど、余計な部分をすっ飛ばしてるから結果的に append より速くなるんだろう。

んで、せっかくスタック溢れ対策で末尾再帰バージョンを作ったのに、百万要素でも溢れなくて立場無しなわけで、この際だからもう一桁行ってみようじゃないの…と。

9> list_test:test(10000000).
635971 : 10000000 elems with ++ operator.
7447358 : 10000000 elems with append function.
5314239 : 10000000 elems with trfunc function.

さすがにここまで来ると、append より trfunc の方が速いようだけど、どっちみち ++ とは比べものにならないので意味無し。 って言うか、なんでスタック溢れないんだよ(苦笑

どうやら Erlang はスタックが自動伸長するようですな。 一千万要素のテストの実行前はメモリ使用量が 20MB ちょいだったのに、実行後は 500MB 近くまで膨れあがってたよ。

まあ、何はともあれ、Erlang でリストを繋ぐときは何も考えず ++ 演算子を使いましょうってことで終了。

% [Erlang][Mac] CEAN だと HiPE が動かない

配布されてるバイナリがダメなのか、どっかの設定の問題なのか、とにかく native オプション付けてコンパイルしようとするとエラーを吐くので、微妙にがっかりしつつ MacPorts 版の Erlang を入れ直したのであった。 ショボン。

% [Erlang] 例外処理

Erlang の例外には 3 つの種類 (クラス) があって、

  • throw
    • throw/1 で投げる
  • error
    • 何かエラーが起きると発生
    • 自分で起こすなら erlang:error/1,2
  • exit
    • exit/1 で自分自身 (プロセス) を終了したとき発生
    • リンクしたプロセスが normal 以外の Exit Reason で終了したとき発生

こんな感じになっている。 で、これらは catch 式か try 式を使うことで捕捉できるんだが、try を使う場合には、このクラスを分けて掴まえることができる。 具体的には try 式の catch 節に使うパターンの頭にクラスを指定する。

1> try
1>   throw(hoge)
1> catch
1>   exit:E -> 'exited.';
1>   throw:E -> E 
1> end.
hoge

このように、E は未束縛の変数なので、クラス名の指定が無い場合は最初のパターンにマッチして 'exited.' が返るはずだが、実際にはクラスが違うのでマッチせず、二つ目のパターンにマッチして、結果 hoge を返している。 もちろん E の部分のパターンには定数を指定しても良い。 というか、クラス名の指定以外は普通のパターンと全く同じだ。

んで、これの何が嬉しいかと言うと、例えば Exit Reason と、うっかり同じ atom を使ってしまっても、throw と exit をきちんと使いわけてれば、意図しない例外を捕捉してしまうことが無くなったりとか、そういうことかと。 それこそ exit や error はどこから飛んでくるかわからないので (それに error なんかは下手に掴まえない方が良い場合も多いだろう)、throw は自分で投げて自分で掴むような用途 (大域脱出とか?) に使うことに決めておくと良いと思う。

ちなみに catch 式というのはこういう↓

3> catch throw(hoge).
hoge

ものだが、パターンによる取捨選択ができないし、もし同じ効果が欲しいときは try 式で、

4> try
4>   throw(hoge)
4> catch
4>   E -> E
4> end.
hoge

とでも書けば良いので、ぶっちゃけ存在を忘れてしまっても良いような気がする。


2007-05-16 [長年日記]

% [雑談][Erlang][vim][Emacs] Ctrl+U とか Ctrl+W とか

vim でもシェルでも、書いてる途中でそこまで書いたものを消したいときには普通に Ctrl+U を使うんだけど、Erlang の対話環境って一般的な readline 的 emacs 風ごちゃまぜキーバインドじゃなく、ゲンミツに emacs 的なキーバインドのようで、Ctrl+U が効かないのがどうにもストレスが溜まる。

まあ、無いものは無いで仕方ないので、今は Ctrl+U の代わりに Ctrl+A して Ctrl+K とかやってるんだけど、そうやって Erlang をいじりまくってたりすると、今度は vim の挿入モードでもついつい同じことをしてしまってストレスが溜まる...orz

ともあれ、これはこれで代替手段があるから良いんだけど、Ctrl+W で単語消去ができないのがツラい。 一応 Meta+B して Ctrl+K とかってのもアリみたいだけど、Terminal.app だと Meta は Option キーなんで、普段 Option キーなんてほとんど使わなくてキーボードのはじっこ (左下隅) に追いやってるわしとしては、押しづらくてにんともかんとも。

ちなみに ledit 使えば?というのは却下。 と言うか、ledit 使うと Erlang シェルの Ctrl+G が効かなくて不便すぎる。 rlwrap だと Erlang 側の readline 設定に引きずられるのか Ctrl+U や Ctrl+W が使えないのは同じなので、何の意味も無い。

なんかうまいこと設定する方法は無いもんかな。

% [clip][Erlang][vim] Erlang plugin package (vim online)

vim に付属する syntax/erlang.vim は最終更新が 2002 年と古いせいか、いくつかの演算子や予約語がハイライトされなくて気持ち悪かったので、これを入れてみた。 まあ、なんというか、pre-release などと書かれてることから推して知るべしだが、いろいろやっつけ仕事な部分が多いのも事実。 でも付属のものよりは多少マシになったから良しとしよう。

とりあえず、syntax/erlang.vim で erlangException にハイライトの設定がされてないので、適当に 206 行目辺りに、

hi link erlangException    Keyword

とでも追加しておく。 あとは ~/.vimrc に、

" for erlang plugin.
hi link ErlangFunHead Define
let g:erlangHighlightBif = 1
let g:erlangHighlightFunHead = 1

くらいを追加して終了。

……erlangDefine を erlengDefine と typo ってるのに気付いたが、整合性は取れててちゃんと動いてはいるのでスルーした。

% [Erlang] ++ どころか lists:reverse までドーピングされてるとな?

++ が最適化されてるという話に関して、向井さんとこで言及されてたのだが。

注意: リストを逆転させたいときは lists:reverse を使うべきであり、ほかのものは使わないこと。 lists モジュールのソースコードを見れば、 reverse の定義を発見できる。しかし、この定義は単に説明のために使われる。コンパイラは lists:reverse の呼び出しを発見すると、もっと効果的な最適化版の関数を呼び出す。

++ の件から考えて、演算子や特別扱いされてる組み込み関数 (ガードに使えるやつ) 辺りに関しては内部でいろいろ最適化されてるんだろうなあと想像してたんだけど、こんなとこまでこっそり最適化してるなんて、なんてエロいんだ。

% [Erlang] ついでなんで、文字列の話

向井さんとこで Erlang の言語仕様で個人的に気になることとして、

「ASCII(7bit)の範囲内の整数のみで構成されているリスト」が「文字列」とみなされること(「文字」というのはないし、マルチバイトはおろか8bit clean ですらないんじゃないかなあ)

と書かれてますが、文字扱いになる整数は 8bit 値だと思います。 少なくとも、現在の実装はそうなってるみたいです。 Character Set として full ISO-8859-1 が使えるとなってますし。 実際、

1> [$\376,$\377].
"\376\377"
2> [254,255].
"\376\377"
3> [16#FE,16#FF].
"\376\377"

こんな感じで、表示されるときに文字列扱いになります。 あと $ を使ったリテラル表記が許してるのが 0o377 までだとか。

4> $\377.
255
5> $\378.
** 1: syntax error before: 8 **    ←このエラーはいかがなものかとは思う(苦笑

まあ、どっちにしても、文字列っぽく見えるものは結局は整数のリストでしかない、って事実は変わりませんけど。 通信関係で使われることが多い処理系だけに、変に凝った構造にするよりも単純にバイト列でいてくれた方が色々便利だということなんじゃないかと思いますね。 マルチバイト文字列として処理したいなら、適当にライブラリで何とかしろと、そういうことかと。


2007-05-17 [長年日記]

% [clip][雑談] イチローとイージス艦が戦ったらどっちが勝つの? (もみあげチャ〜シュ〜)

全盛期のイチローはスゴまじすぎるw

% [Erlang] OCaml の as に相当する記法

要はひとかたまりのパターンを別の変数に束縛する書き方。

# match 1,2 with
    (a,b) as c -> (a,b,c)
  ;;
- : int * int * (int * int) = (1, 2, (1, 2))

こういう感じのもの。 あると中々便利なんだけど、Erlang には無いっぽくてがっかりしてた。 ところが、最近 stdlib 辺りのソースを読んでて発見。

1> case {1,2} of
1>   {A,B} = C -> {A,B,C}
1> end.
{1,2,{1,2}}
2> {X,Y,Z} = T = {1,2,3}.
{1,2,3}
3> T.
{1,2,3}
4> X.
1
5> Y.
2
6> Z.
3

わーい、これで色々楽になる。 結局 Erlang においては "=" も含めて全て『パターン』だということのようで、ある意味一環していると言えるのか。 でもこういうのは、マニュアルに書いといて欲しいよ。

% [Erlang] UTF-8 の文字列を UTF-8 として扱うモジュール

…を書いてみた。 UCS や、その他の文字コードとのエンコードとかデコードとかは知らない。 あくまで UTF-8 のバイト列として正しいか調べて、文字ごとのリストに組み直すだけのもの。 あとまあ、その状態で string モジュールと同じ操作ができるように同名の関数をテケトーにでっちあげたりもしてある。 ぶっちゃけ使い道あんの?という気もするけど、まあ欲しい人はどうぞ。 配布用に整備してないので、当面 erl ファイルだけ。 おざなりなテストしかしてないので、あんましアテにしないように (アテにする人はあんましいないだろうが)。

utf8.erl

ライセンスは用意してないけど、一応修正 BSD ライセンスのつもり。

なんかこういうのって仕様を考えてるときは妙にわくわくすんだけど、実際書き始めてみると泥くさいことこの上ないというか。 つーか、こんなものより、もっと作るべきものがあるんだが、そっちは色々調べるのがめんどくさくて手を付けてない...orz

実際のところ、このモジュールは中身がどうのこうのより、edoc コメントを書く練習台としての役割が大きかった気がする。 ちなみに edoc は erl を起動して、

1> edoc:files(["utf8.erl"],[{dir,"destdir"}]).
ok

とかやればよろしい。 細かいファイルがごちゃごちゃできるんで、適当にディレクトリを用意して、そこに突っ込んだ方が吉。

span/cspan 関数だけは実装してないんだけど、なぜかと言うとマニュアル見てもソース見ても何をする関数なのかよくわからなかったから。 まあ、string モジュールのソース見ながら書けば、それっぽいのは書けると思うけど、これがあると何が嬉しいのかがどうにもわからんので。

% [Erlang] 一応 utf8.erl の使い方とか

えーと、早い話が…

  • utf8:check/1 で UTF-8 な文字列を {utf8, List} という値に直す。List は UTF-8 での一文字 (char() か string()) のリスト。
  • check/1 に不正な UTF-8 文字列を渡した場合は {not_utf8, {SubString,OrigString}} という値を返す。頭から逐一検査していくので、SubString はエラーになった時点での残り文字列。OrigString は check/1 に最初に渡したときの元の文字列。
  • utf8:of_string/1 は基本的には check/1 と同じだけど、エラーになった場合にその値を例外として投げる。
  • utf8:to_string/1 で元のフラットな文字列に戻す。string() を要求する関数に渡すときはこれを使えということ。
  • コメントの英語がインチキすぎる件は、華麗にスルーするように。

…以上。

% [Erlang] 単一代入と辞書

ここやらここやら。 関数型言語使うなら、やっぱそこは fold じゃないすか。

1> D = lists:foldl(fun({Name,Age,Sex}, Dict) -> 
1>                   dict:store(Name, [Age,Sex], Dict)
1>                 end,
1>                 dict:new(),
1>                 [ {"ton",27,male}, {"peke",21,female} ]).
{dict,2,
  ... (省略)
2> dict:find("ton", D).
{ok,[27,male]}
本日のツッコミ(全2件) [ツッコミを入れる]

% 向井 [うおっ、わたしも Erlang にはアズパターンはないのだと思っていました。すげーキモい(笑)]

% jijixi [慣れないと、読むときにどこで区切って良いかわからない気がします(苦笑]


2007-05-18 [長年日記]

% [雑談] 関数型言語の常識

いわゆる関数型言語 (LISP なんかも含むので "広義の" と注釈を付けよう) における『リスト』というのはおしなべて連結リストのことであるというのは、ある意味『常識』だと思ってたんだけど、それって実はかなり狭い世界の常識なのかなーと、ここのページを読んでて思ったのである。 要するに、これを読んだ瞬間…

要素数が数万にもなるリストに一個ずつ要素を加えるのに、愚直にケツに追加するやつなんてイネーよ! …… Haskeller 以外は。

などと思ったのね。 まあ、その要素を加える操作が、ごくまれにしか起こらないのであればそれでもアリかも知れないけど、この例みたいに 3 万回も繰り返すようなシチュエーションでは絶対にそんなことしない。 そんなの当たり前でしょ……と思ったんだけど、ちょっと待て、と。

自分が関数型言語にハマる以前のことを思い返してみると、実際のところ関数型言語のリストが単方向連結リストとして実装されてることなんて知らなかった気がする。 まあ、わしの場合は、リストのアタマに追加する構文が特別に用意されてる時点で、その辺に疑問を持って調べたからすぐに気付いたけど (あと、最初に本気でいじったのが OCaml で、こいつにはリストとは別に配列があるから気付きやすいというのもあるかも)、人によってはあまり疑問を持たずに配列と同じものだと思って進んでしまう場合もあるかもしれない。 ましてや、配列のことをリストと呼ぶような言語も存在するし (某 P 言語とか)、その辺から入ってくるとますます混乱するだろう。

なんか、わしもすっかり関数型言語脳に冒されているのかなーと、そんなことを思ったできごとであった。

% [雑談][Erlang] 関数型言語脳の常識 (…かどうかはわからない) fold 編

つい何年か前までは「fold って何?うまいの?」とか言ってたわしだけど、すっかり関数型脳になった今では fold が無い生活なんて考えられない。

かつて、わしが最初に fold の使い方を知ったときの例は sum だった。 要するにこんな↓の (コード例は昨今のブームを反映して Erlang)

1> lists:foldl(
1>   fun(A,B) ->
1>     A + B
1>   end,
1>   0, [1,2,3,4,5,6,7,8,9,10]).
55

でもね、はっきり言って、こんな例見せられても fold の醍醐味は絶対理解できない。 fold というのは関数型言語にとっての for ループなのだ。 つまり (例は Java)、

int[] a = {1,2,3,4,5,6,7,8,9,10};
int buf = 0;
for (int i = 0; i < a.length; i++) {
   buf += a[i];
}

こんな感じのものと上記の fold の例は対応すると言っても良い。 もう少し一般化して言うと、

fold というのは、変化する "状態" を保持したままループを回す機能である。

と言えるかも知れない。 上の Java の例で言うと、buf が状態である。 なんかカッコつけて書きすぎたが、簡単に言えば、C やその類いの言語で変数を破壊的に操作するようなシチュエーションは、大抵の場合 fold を使って再現することができるということだ。

だから、昨日のネタでもあるけど、檜山さんのとこからコピペ↓して利用させてもらうと、

sample() ->
  D0 = dict:new(),
  D1 = dict:store("板東トン吉", [27, male], D0),
  D2 = dict:store("大垣ペケ子", [21, female], D1),
  Val = dict:find("板東トン吉", D2),
  Val.

こういうシチュエーションは、

  • 破壊的に変更したいのは辞書 (D0) である
    • D0 を fold の種にしよう
  • D1,D2 の部分は dict:store の繰り返しである
    • 関数にまとめてしまおう
    • 引数はリストに詰めよう

てな感じの論理展開によって、結果昨日の例のように、

1> D = lists:foldl(fun({Name,Age,Sex}, Dict) -> 
1>                   dict:store(Name, [Age,Sex], Dict)
1>                 end,
1>                 dict:new(),
1>                 [ {"ton",27,male}, {"peke",21,female} ]).

こんな感じに変化するのである。 この例では foldl の第一引数に渡している関数の Dict という引数が破壊的に変更する変数にあたる。 んで、第二引数の dict:new() は Dict の初期値。 イメージとしては、リストの中身を順番に関数に渡していって、その都度結果で Dict の中身を書き換えていく感じ。

関数型脳においては、共通した処理を見つけたら、まず関数化するというのが基本パターンのような気がするけど、それと同時に上記のような『同じ関数の呼び出し』を見つけたら、まずその引数 (処理対象) をリストにしてしまって、後でまとめて処理するというのもパターンじゃないかと思う。 なんか、この辺の考え方が、手続き型から関数型に移行する際のブレークスルーな気がするね。

% [OCaml] OCaml 3.10.0 が出たようだ

今回の目玉はやっぱ ocamlbuild かね。 と言いつつ、どんな感じのものなのかさっぱりわかってないんだけど。 caml-list で色々議論してたのは眺めてたけど、中身を全然読んでなかったからなー(ほんとに眺めてただけかい)。 とりあえず、三行でまとめると、

  • 今まで手書きしてたライブラリの依存関係なんかは勝手に解決してくれて、
  • コンパイルしたファイルなんかもそこら辺に撒き散らかさず、
  • いちいち Makefile 書かなくても大抵のことはうまいことやってくれる。

てな感じなのかね。 や、まだ使ってみたこと無いんでよくわかんないけどさ。

つーか、メジャーバージョンアップしたときには常に悩むことになるんだけど、通常 GODI で OCaml を入れている人間としては、GODI が対応するのをおとなしく待つべきか、それともとっとと入れてみて遊ぶべきか、あーもーどーしたら。

いや、入れる。今回はとりあえず手で入れる。 ocamlbuild 触ってみたいし。 っていうか、この前 ocamlbuild のために current を入れようと思ったら、なぜかビルドできなかったんだよなー。 さすがにリリース版でビルドできないってことは無いだろうし、今度こそーー。

% [OCaml] ocamlbuild 事始め

結局 ppc64 な環境は自動じゃ見っけてくれなくて、./configure -cc "gcc -arch ppc64" とかせにゃならんのだなー。 だとすると GODI 版も 32bit 版でインストールされちゃうのかねぇ。 それはちとせつないな。

……などというグチは置いといて、ocamlbuild を使ってみますよーと。 まず適当なディレクトリ掘って、適当にファイルを用意。

% cat main.ml
let main () =
   Hoge.print ();
   Fuga.print ()
let () = if not !Sys.interactive then main ()

% cat hoge.ml
let print () = print_endline "hoge"

% cat fuga.ml
let print () = print_endline "fuga"
% ls -A
fuga.ml hoge.ml main.ml

……で。

% ocamlbuild main.byte
Finished, 7 targets (0 cached) in 00:00:01.
% ls -FA
_build/         _log            fuga.ml         hoge.ml         main.byte@      main.ml
% ls -l main.byte
lrwxr-xr-x   1 jijixi  jijixi  16  5 18 19:22 main.byte -> _build/main.byte
% ./main.byte
hoge
fuga

ちょwww 何これwww 簡単すぎwwww

もうなんつーか、今までの苦労って一体何だったのって感じだよね。 なんか笑うしかない。 例えばここで、main.ml をライブラリにしたいときは…

% ocamlbuild main.cma
Finished, 7 targets (6 cached) in 00:00:00.
% ocaml -I _build main.cma
# Main.main();;
hoge
fuga
- : unit = ()

ちなみに、

% ocamlbuild -clean

で、お掃除。 あと、ナニゲにステキすぎると思う機能。 まず、さっきの三つの ml ファイルを foo というディレクトリ掘って突っ込む。 んで、こんなファイルを用意する。

% cat foo.mlpack
Main
Hoge
Fuga

結果、こんな感じの構成になってる状態。

% ll -l
total 4
drwxr-xr-x   5 jijixi  jijixi  170  5 18 19:48 foo
-rw-r--r--   1 jijixi  jijixi   15  5 18 19:50 foo.mlpack
% ls foo
fuga.ml hoge.ml main.ml

で、

% ocamlbuild foo.cma
Finished, 8 targets (0 cached) in 00:00:00.
% ocaml -I _build foo.cma
        Objective Caml version 3.10.0

# module M = Foo;;
module M :
  sig
    module Fuga : sig val print : unit -> unit end
    module Hoge : sig val print : unit -> unit end
    module Main : sig val main : unit -> unit end
  end

ヤバい、もう鼻血出そう。

あとはインストールなんかもやってくれるようになると、ほんとに make とかいらなくなるんだが、その辺はどうなるんだろう。 別に ocamlfind との連携とかでも良いけど。 一応、ocamlbuild -help とかやると、

-install-lib-dir <path>     Set the install library directory
-install-bin-dir <path>     Set the install binary directory
-where                      Display the install library directory

こんなのはあるけど、実際にインストールするためのターゲットとかは見当たらないっぽい。 逆に ocamlfind の方で ocamlbuild 対応してくるって流れとかあるのか?

% [Perl] Perl のリスト (not 配列)

えーと、この話に至るまでの流れはツッコミ欄参照。

てことで、調べてみたんだけど、どうもハッキリしないというかよくわからない。 こうなると通常は「ソース読むか」となるんだが、以前にちょろっと覗いたときのトラウマがあって、正直読むのは勘弁していただきたい(苦笑

んで、適当にググっていくつか解説ページとか眺めた感じだと、リストが変数に代入されると、それはもう配列だと言うじゃない。 つーことは、リストというものが存在できるのは名前が付いていないときだけだということになる。 例えば、もしリストが連結リストとして実装されていたとしても、名前を付けたら配列になっちゃうってことは、連結リストとしての存在意義は無いってことで、要はリストがどんなデータ構造かを云々するのは意味の無いことなのかも。

リストとは何かの値がリストコンテキストで評価されるときに、一瞬だけ存在する蜃気楼のようなものなのかもしれない。 ……などと、ちょっと気取った言い方をしてみたり。 コンテキストなどという、わけのわからん文学的な代物が存在する Perl にはお似合いじゃなかろうか。

……すんません、ちょっとやさぐれてしまいました。 ほんと、わしにとって Perl は性に合わんです。 文脈によって同じ変数でも返す値が違うとか、正気ですか? まあ、こんな不思議な言語を考えたラリーウォールは、ある意味天才ですね。

や、これはほんとバカにしてるんじゃなくて、本気でそう思う。 好き嫌いは別にして (残念ながら、わしは好きにはなれないが)、Perl には何か不思議な魅力があるのは確かだと思うんで。

本日のツッコミ(全6件) [ツッコミを入れる]

Before...

% H.I. [えっと、Perlのリストってのはスタック上に存在する配列だ、ってのが近いかも。はい、ある意味蜃気楼です。んでその蜃気..]

% jijixi [ふむふむ。そうするとやっぱ、データ構造としては配列と同じようなもんなんですかね。 ネストしたリストは自動的に平たく展..]

% きむら(K) [あー別のP言語の話だったとは。 ところでタイムリーにも、弾さんの今日のエントリ http://blog.livedo..]


2007-05-19 [長年日記]

% [雑談] fold, map, for-each この中から一つ選ぶとしたらどれ?

リストを扱う関数三つのうち、どれか一つしか使っちゃダメと言われたら、どれを選ぶ?という話。 タイトルで使ったのは Scheme での名前だけど、まあ好きな言語に置き換えて考えよう。 Ruby なら inject, map or collect, each とか、Python なら reduce, map, for とか。 OCaml なら fold_left, map, iter だし、Erlang なら foldl, map, foreach かな。

まあ、結論から言って、一つしか使っちゃいけないなら、当然一番汎用性の高いものを選ぶべきだ。 じゃあ、どれが一番高いの?と言えば、fold なわけだね。

まず、for-each なんていらないのはすぐわかるはず。 map して返り値捨てれば良いだけだから。 で、map と fold を比べた場合、実はこれらはほとんど互角だ。 ただし、「fold は単体で map の機能を再現できる」のに対し、「map が fold の機能を再現するには、破壊可能な変数が必要」だという違いがある。 だからまあ、破壊的に変数を書き換えられる言語なら (上で挙げたもので言えば Erlang 以外そうだ。Erlang でもプロセス辞書とか使えばできないことはない) どっちでも良いと言えばどっちでも良い。

ただ、

関数型脳的に見ると、map で fold のマネをするのは、ひじょーーに美しくない。 試しに map で inject と同等の操作をする Ruby のコードを書いてみる。 ほんとは Scheme で書こうと思ったけど、とっさにコードが浮かばないので日和った。

irb(main):001:0> [1,2,3,4,5].inject(0) { |acc,x| acc + x }
=> 15

これを map で書くと、

irb(main):002:0> acc = 0
=> 0
irb(main):003:0> [1,2,3,4,5].map { |x| acc += x }
=> [1, 3, 6, 10, 15]
irb(main):004:0> acc
=> 15
irb(main):005:0> 

汚い。行数が増えることはともかく、実質 map の中でしか必要のない acc という変数が map の外にあるのが汚すぎる。 関数型脳には許せない。

逆に inject で map をマネしてみよう。

irb(main):007:0> [1,2,3,4,5].map { |x| x * 2 }
=> [2, 4, 6, 8, 10]
irb(main):008:0> [1,2,3,4,5].inject([]) { |acc,x| acc + [x * 2] }
=> [2, 4, 6, 8, 10]

ほら、すっきり。 fold サイキョー。

……まあ、なんつーの、実際のところ fold しか使えないなんて状況は無いし、map で済むところを fold で書くなんて無駄だし、for-each で済むところを map で書くのも無駄だし、現実には適材適所ってことで終了なわけだが。 ……なんと身も蓋もない。

や、あれさ、fold の表現力の高さをもっと知るべきだと、そういう話なんで。 つーか、fold の弱点として、言語によって引数の順番がまちまちで、正直憶えきれないってのがあるんだよな。 誰か対応表とか作ってくれんもんか。

% [雑談] アキバ Blog に、もってけなんちゃらの歌詞が出てたが……

これは何とも……想像以上に電波(笑

% [雑談][OCaml] そう言えば ksk さんのツッコミで思い出したんだけど

末尾再帰の最適化をする言語は数あれど、ライブラリのマニュアルで、末尾再帰になっていない関数にいちいち "Not tail-recursive" などと説明を付けてるのは OCaml くらいではなかろうか。

なぜ OCaml 使いが、こんなにも末尾再帰に神経質かはわからんけど、スタックの上限が低くて、ちょっとしたことであっさりスタックオーバーフローしたりするから……とかなのか? ともかく List.rev_map みたいな関数、他の言語では見たことない気がする。 単なる集合としてリストを使うことってよくあるし、そういうとき便利だと思うんだけどね。

今ちょっと調べてみたら、SML には OCaml の List.rev_append に相当する List.revAppend ってのがあるみたいだけど、rev_map に相当するのは無いみたい。

% [Perl] Pugs を入れてみた

まあなんつーの、Perl 5 に比べて色々良い感じになってるっぽいしさ、Perl 6 は。 個人的に、Perl の一番キモいところだと思うコンテキストは残ってるし、それどころか数が増えてるって言うんで、まあわしが Perl 使いになることは無い気がするけど、その他の部分は色々興味深くはあるので。

てな感じで Scheme:Lisp プログラマのためのPerl入門とか Perl 6 入門とかいうページを眺めつついじる。

pugs> my $f = -> $x { $x + 1 };
pugs> $f(2).say;
3

あー、なんか普通に関数型言語っぽい。 プレースホルダーってのも、まあ字面はちょっとアレだけど便利だろうね。

pugs> $f = { $^x + 2 };
pugs> say $f(3);
5

でも…

pugs> $f = { .pop };
pugs> say $f((1,2,3));
3

さすがにこれはキモいな。$_ を使うのすらキモいのに、さらにそれまで省略って、どこまで省略するの好きなんだよ(苦笑

pugs> my @ary = (1,2,3);
pugs> say @ary[1];
2

この変更は GJ だね。以前の $ary[1] とかは納得行かなすぎた。 でもなんかさ、

Perl5ではスカラーコンテキストで配列要素数が取得できましたが、Perl6では自身のリファレンスが帰ってきますので注意してください。

とか書いてあんだけど……

pugs> my $ary = (1,2,3);
pugs> say $ary[1];
2

……なんか @ いらなくネ?

pugs> my $hash = { 'a' => 1, 'b' => 2 };
pugs> say $hash{'a'};
1

ついでに % もいらなくネ?

メタオペレータとかいうもの。

pugs> say (1,2,3) >>+<< (2,3,4);
357

うーん、リストを二つ取る map みたいなもんか?

pugs> say map(sub($x,$y){$x+$y}, (1,2,3), (2,3,4));
357

ありがたみがよくわからんな。こんなの作るなら演算子を関数として扱えるようにした方が map で共通に使えてありがたいんじゃないのかね?

pugs> say [+](1,2,3,4,5);
15

それなんて fold? まあ短く書けるのは嬉しいのかな。 そう言えば、Perl に fold に相当する手続きってあったっけ?

んーと、あとはジャンクションか。 まあ、これは便利かな。短く書くことにこだわらなければ、関数型言語ならほとんどの部分をカバーできる気がするけど。

# let a = 1;;
val a : int = 1
# if List.exists ((==) a) [1;2;3] then true else false;;
- : bool = true

こんなとかね。 でもまあ、見た目はジャンクションの方がわかりやすいわな。 ジャンクションに対する演算だの連鎖比較だのって話になると、上記のような方法じゃ真似するのはちょっと面倒だし。 メタオペレータのありがたみはよくわからんけど、ジャンクションは普通に嬉しい感じ。

ざっと見たところ、今までの Perl に馴染めなかった人にとっての一番の目玉は、あの忌々しい sigil をほとんど気にしなくてもコードが書けそうだってことじゃなかろうか。 まあ、どっちにしても $ だけは書かなきゃいかんけど (わし Shift キーは左のしか押せない人なんで、$ 記号は打ちにくいんだよなー)。 なんか、あいかわらず Write Only な言語になりそうな要素だらけって気もするけど、少なくともこれまでの Perl に比べればずっと好きになれそうな予感。

% [clip] inject, map, each (はじめてのにき)

実に shinh さんらしい判断基準で笑った。

% [雑談] 世の中には色んなリファレンスが存在する

よね。 ……と思っていくつかリストアップしてみたんだけど、思ったよりも数は無かった(苦笑)。 というか、わしが好んでいる二つの言語 (OCaml と Erlang) にたまたまリファレンスというものが存在して、それが通常「リファレンス」と言った場合にポピュラーだと思われる C++ や Perl のものと随分毛色が違う代物だから、たくさん種類があるような気がしただけみたいだ。

ちなみに英辞郎によると、リファレンスには…

  1. 参照、参考、参考文献、引例、出典
  2. 基準、見本
  3. 言及、論及、指示
  4. 照会、信用照会先、問い合わせ
  5. 関連、関係
  6. 証明書、保証書、推薦状
  7. 回付、委託

こんな感じの意味があるらしい。 では、それぞれの言語のリファレンスはこれらの内のどれに当てはまるのか。

  • C++
    • ポインタの偉いやつ。
    • まあ、これは普通に『参照』だろう。
  • Perl
    • これもポインタみたいなもんだと思う。
    • てことで『参照』。
  • OCaml/SML
    • 書き換え可能な変数。まあ、実際の中身は mutable なレコードだけど。
    • 『参照』かな。使うときの事を考えると『照会』とかも当てはまりそうではある。
  • Erlang
    • VM 内でユニークであることが保証された値。Common Lisp の無名シンボルみたいなもんかな。使い道は違う気がするけど。
    • ユニークであることが第一義なので、『証明書』とか『保証書』とかが近い?

なんか、もう少し種類があったような気もするんだがなあ。 あ、あと、こういうのとは別に、リファレンスカウンタとかウィークリファレンスとかいうのはよく出てくるよね。 『リファレンス』で検索してもリファレンスマニュアルばっか出てくるから、他にリファレンスって名前の何かを持った言語があったかどうか探すのは容易じゃないので、このネタはこの辺で。 まあ、すごくどーでもいー話だし(苦笑

本日のツッコミ(全11件) [ツッコミを入れる]

Before...

% TrackBack [http://d.hatena.ne.jp/KeisukeNakano/20070519/1179568491 λx..]

% soda [C++ はあまり普通じゃないと思います。C++ のように、参照に対する代入文で、ポインタの張りかえではなく、値のコピ..]

% jijixi [や、単に「リファレンス」という言葉に訳語を付ける場合、何が適切かって話なんで、参照として普通かどうかは言及したつもり..]


2007-05-20 [長年日記]

% [Oz] プロセス嗜好

sumii さんのプロセス志向言語脳というネタを見て、そういや、こういうのって Oz だと超簡単にできるんだよなーと思い出した。 要するに、sumii さんの例のようにスレッドが何かを計算した結果が同じところに戻ってくる場合、単に thread 式 end と書くだけで、その部分がスレッドとして動くので、

X = thread 式1 end
Y = thread 式2 end
式3 (ここで X と Y を使ってる)

こんな感じだと X と Y を求める計算が平行して動く (はず)。 もう少し詳しく言えば、通常のシーケンシャルな処理の場合は X の値を得るのに時間がかかる場合、当然 X のところで処理が止まるんだけど、スレッドにした場合はどんどん先に進んで、値が得られるまで待つのは実際に X が使われる部分 (式3) になる。

感覚としては Haskell とかの遅延評価に近い感じ。 まあ、『必要になったら計算する』じゃなくて、『必要になったらブロックする』ってことだけど、その特徴を有効に利用しようとする場合のコードの書き方って、結構共通点がある気がする。

と、この辺まで書いてから、さっきの例がほんとにそうなるのか試してみたくなったのでやってみる。 oz を起動してこんな感じのコードを書いた。

declare Fib FibPrint in
fun {Fib N}
   case N of
      0 then 1
   [] 1 then 1
   else {Fib N - 1} + {Fib N - 2}
   end
end

fun {FibPrint N}
   local R in
      R = {Fib N}
      {Show R}
      R
   end
end

local X Y in
   X = {FibPrint 30}
   Y = {FibPrint 3}
   {Show X + Y}
end

んで、これを実行。

M-x oz-feed-buffer

すると、*Oz Emulator* ってバッファに、

1346269
3
1346272

てな感じで表示されるはず。まあ普通。 そんで次は local ブロックを、

local X Y in
   X = thread {FibPrint 30} end
   Y = thread {FibPrint 3} end
   {Show X + Y}
end

こんな風に書き換える。

3
1346269
1346272

めでたく並列処理されたようだ (たぶん)。

% [Fortress] Oz の例を Fortress でもやってみる

Fortress ってやつは and が ∧ で or が ∨ だったりすることになってるんだけど、今のところその辺の部分は実装されていないのか、そのまま書いてもシンタックスエラーになってしまうのであった。 んで、結局それらの ASCII での代替表記を探すのに苦労して、ものすごく無駄に時間を過ごしたよ...orz

とりあえず、∧ は AND で ∨ は OR 。 詳しくは The Fortress language specification の Appendix F の辺りをどーぞ。

そんなわけで本題。

% cat par_test.fss
component Par_test
   export Executable
   fib(n) = do
      if n = 0 OR n = 1 then
         1
      else
         fib(n-1) + fib(n-2)
      end
   end
   fib_p(n) = do
      r = fib(n)
      print r
      print "\n"
      r
   end
   run() = do
      x = fib_p(20)
      y = fib_p(3)
      print (x + y)
      print "\n"
   end
end
% fortress par_test.fss
10946
3
10949

まずはこんな感じ。 で、Fortress ではタプルの各要素は並列に計算されるはずなので、run() のところを…

run() = do
   (x, y) = (fib_p(20), fib_p(3))
   print (x + y)
   print "\n"
end 

こんな風に変えると…

3
10946
10949

てな感じになる。めでたしめでたし。 タプルの要素が右から計算されてるだけって可能性もあるじゃないか…ってツッコミはナシの方向で。 だって検証がめんどくさいし。…って順番入れかえれば良いだけか。つーか良いの、タプルの各要素は並列に計算されるってことになってるんだから、もしそうなってなかったら実装が良くないの。

% [雑談][vim] Esc か ^C か ^[ か

otsune さんのブックマーク経由で「vi 使いはいちいち Esc とか押してウザくないのか?(意訳)」という疑問を見かけた。 で、それに対する反応として ^[ 使ってるとか、^C 使ってるとかってのがあったんだけど、わしの場合は愚直に Esc を押しております。 別に ^[ と ^C を知らなかったってわけじゃなくて、Esc を押すのが苦じゃないというか、むしろ Esc を押すことが変なリズムを生んで妙に楽しいというか(謎

つーかね、基本的にわしコンパクトなキーボードしか使わないんだよね。 ホームポジションから手を離したくないと言っても、それって実際のところは「手の平の位置が変わらなければ良い」ってことだと思うんだ。 まあ、リターンキーすら押すの面倒だって人もいるから、人それぞれなんだろうけど、ともかくわしはそうなのね。 で、コンパクトキーボード使ってて、しかもわしは手がでかい方なので、ホームポジションの状態から普通に指を伸ばせば問題なく Esc に届くんだよね。 手の平の位置は動かないから、普通にホームポジションに戻ってこれるし、何の問題も無い。 って言うか、むしろ Esc のたびに Ctrl キー押す方がわしとしてはツラい。 ただでさえ SKK で小指酷使してるってのに、これ以上わしに小指を使わせないでくれ、と(笑

まあ、ご立派なフルキーボードとか使ってる人はたしかに Esc が果てしなく遠くにあったりするだろうし、そういう人は ^C や ^[ が手軽だと思うけど。 要は環境によるってことだね。

% [Fortress] Fortress の地味におもしろいところ

さて問題です。以下のコードは端末に何を表示するでしょう?

component Test
export Executable
   run() = do
      println(2 3 4)
   end
end

println は改行付き端末出力です。さっき気付きました (遅い)。 では正解を見てみましょう。

% fortress test.fss
(省略)
24
(省略)

省略した部分は、何を実行しても表示される部分なので気にしないでください。 さて、fortress のコードにおいて、

2 3 4

が何を意味しているか、わかりましたか? ちなみにこの部分を…

2 3 / 4

にすると、

1.5

になります。

2 + 3 4

こうすると、

14

です。

……正直無茶だと思いました。


2007-05-21 [長年日記]

% [雑談] 関数的なコレクション構造について云々

檜山さんのイミュータブル・データとミュータブル・オブジェクトの境界線というエントリを読んで、色々と書きたくなったんだが、うまくまとまらなかった。 けどまあ、せっかくだから書き散らしておく。

ぶっちゃけて言って、変更可能な (関数的でない) データ構造というのは、『時間』というモデルを持ち出すなら、常に『現在の状態』を扱うものである。 そして、変更不可な (関数的な) データ構造というのは、時間軸上の『ある瞬間』をその都度切り取るものである。 結局、手法は違っても『現在』を扱うことには違いは無い。

ただ、関数的でないデータは変更すれば、もう『現在』を見れるだけなのに対し、関数的なデータは毎回『現在』を切り取っているので、そうしたいと思うなら、いくらでも『過去』を残しておける (『現在』を切り取った時点で、それまでの『現在』は『過去』になる)。

関数的でないデータにおいては『過去』と『現在』は同じもの (『過去』を見ようとしても『現在』しか見えない)。 関数的なデータにおいては『過去』と『現在』が別々に存在する (『過去』から新たに『現在』が作られる)。 どちらのデータも時間軸に沿って変更されているんだけど、関数的なものに慣れていないと、たぶん違うものに見えるんだろう。 慣れてさえいれば、fold を使おうが何しようが、時間軸に沿って順番に処理していると認識できるんじゃないのかな。

まあ、要するに慣れですよ、慣れ。

……なんてグダグダな...orz


2007-05-22 [長年日記]

% [雑談][Erlang] なぜメッセージ送信に ! という記号を使うのか

向井さんのとこの話とか (コメント含む) 読んでちょっと考えたので。

実はこの前の sumii さんのエントリでこっそりリンクされている (はてなキーワードかと思ったら、ある言語のページへのリンクだったという)、Pict という言語をちょろっといじってみたりしてるんだけど、この言語もなぜかメッセージ送信に ! 記号を使ってるんだよね。 そんな感じで、どうもパイ計算に ! 記号の原点がありそうだぞと思って Wikipedia の Pi-culculus というページを見てみたんだけど……

……よくわかりません...orz

少なくともパイ計算に ! という記号が出てくるのは間違いないみたいだけど、そもそもパイ計算が理解できないので何だかよくわかんない。 どうも !P と書くと『P のコピーを持つことのできるプロセス』(謎訳) を意味するらしくて、なんとなく P をメッセージと考えると意味が通るような通らないような、やっぱよくわからん。

ちなみに Pict では、カッコで囲んだ式がいちいちプロセスになっていて、パイプ記号で繋ぐと並列処理される。 んで、プロセス間通信はチャンネルを通して行なうんだけど、そのときに

chan ! msg

という形で送信、

chan ? var

という形で受信 (var に msg が束縛される) という感じになっている。 パイ計算ではメッセージ受信は c(x).P とか書くみたいなので (プロセス P の実行前にチャンネル c からメッセージを受信して x に束縛するという意味らしい)、? という記号がどこから出てきたのかはイマイチ不明だけど、単に ! の仲間という連想なのかもしれない。

そんなこんなで、Erlang のメッセージ送受信の構文が現在のようになるに至った流れを想像すると……

  • メッセージ送信に ! 記号を使うのは Pi-culculus 由来っぽい。
  • じゃあ、なぜメッセージ受信は receive .. end とか書くの?
    • Pi-culculus にはメッセージ受信に関して特徴的な記号が出てこない。
    • 話の順番はどうなのかわからないけど、受信メッセージをパターンマッチで扱おうとするなら、必然的に受信のための構文はブロック構造にならざるを得ない。
    • なら当然他のパターンマッチを使う構文 (case とか) と見た目的に似たものになるのがベターだよね。
    • じゃあ終端は end でしょ。
    • receive なんて長いけど、rec だとレコードとか末尾再帰とまぎらわしいし、recv なんつーアレゲな略語は使いたくないし、receive で良いんだよ。

……とこんな感じなのではないだろうか。 もちろん、97% くらい妄想なので、あんまり信用しないように。

ところで、『パイ計算』とか『π計算』とかでググると、少なからず『円周率の計算』に関するページがヒットするのでがっかり感強し。

% [Pict] こんにちは世界

Pict という言語はあまりにも文法にクセが強すぎて、正直なかなかまともにコードを書けるようになれないんだけど、ともあれ世界にあいさつするくらいはできたので書いてみる。

さて、まず Pict のサイトから 4.1 というバージョンのソースをもらってきて Mac OS X でビルドしてみた。 ちなみにビルドには OCaml が必要。あと、Pict コンパイラは C のコードを吐くものなので、使うには C のコンパイラも必要。 最終更新が 1998 年などとなってるので、今の環境ですんなりビルドできるのか心配だったんだけど、結果的にはまったく問題無くビルドできた。 ただ、インストールされる pict コマンドのファイル (シェルスクリプト) に信じられないミスがあってしばらく悩んだが……

% diff -u pict.orig pict
--- pict.orig   2007-05-22 20:09:00.000000000 +0900
+++ pict        2007-05-22 20:08:42.000000000 +0900
@@ -13,4 +13,4 @@
 export X11LIB
 GCC=gcc
 export GCC
-exec /usr/local/lib/pict/pict *
+exec /usr/local/lib/pict/pict $*

さすがに目を疑ったよ(苦笑

ともあれ、この試練を乗り越えればあとは普通に使える。 まずは普通な Hello World を。

% cat hello.pi
(pr "Hello world\n");
% pict hello.pi
Hello world

どういうわけか関数適用はカッコで囲む。 この例だと pr が関数ね。 この時点だと、なんつーかちょっと変わった LISP か? って気がすんだけど……

さて、これだけだといくらなんでもつまらなすぎるので、メッセージ通信とかさせてみる。

% cat hello_p.pi
new x:^String
run ( x ? a = print ! a
    | x ! "Hello World!")
% pict hello_p.pi
Hello World!

ほーら、いきなりわけわからなくなった。 関数適用は S 式みたいなのに、その他の部分はもうわけのわからない構文だらけ。 わし、このコードがコンパイルできるようになるまで 2 時間近くかかったよ(疲

そもそもセミコロンが、どこで必要でどこで必要ないかがわからんのだよな。 hello.pi の方はセミコロン省くとシンタックスエラーなんだよね。

ちなみに一応 hello_p.pi の説明をすると、一行目で x という名前のチャンネルを宣言している。^ がチャンネルを表わす印。 要はこの場合、String 型の値をやり取りするチャンネルという意味。

そんで、二行目の run は何なのかよくわからないんだけど、とりあえず適当な理解で言うと、カッコで囲まれた部分はプロセスとして動くんだけど、その部分が関数適用でない場合にはこう書かないとダメってことみたい。 で、カッコ内で | 記号で区切られた部分はそれぞれ並列して動く。 つまりこの例だと二行目と三行目は、別々のプロセスとして動いているということ。

二行目は、x というチャンネルから受信したものを a という変数に束縛して、print というチャンネル (端末出力をする、あらかじめ用意されているチャンネルらしい) に a を送信している。 三行目は、x というチャンネルに "Hello World!" という String 型のメッセージを送信している。 もしこれがシーケンシャルに動くとすると、二行目の受信のところでブロックして止まってしまうけど、実際にはパラレルに動いているので、ちゃんと動くという寸法。

とりあえず、わしができてるのはこの程度。 関数定義とか型の指定なんかは ML っぽかったりとか、色々ごちゃまぜっぽい文法でなかなか妙な感じにエキサイチング(笑

文法的な共通点はほとんど無いけど、思想的(?)な部分では Occam にかなり近い気がする。 まあ、あれもパイ計算と縁が深い言語みたいだしね。 しかし、Occam といい、こいつといい、この手の言語は文法がヘンテコなの多いなあ(苦笑

% [Scala][Erlang] アクター

結構お気に入りの言語であるはずの Scala なんだけど、最近はなでる程度にしかいじってないなーと思っていたので、Scala と Erlang でリングベンチマークというのを、後でじっくり読もうと思ってブックマークしておいたのであった。 で、アクターってなんじゃろー?と思いつつ、まあざっと見た感じスレッドとかその類いのものなんだろうと適当な納得をしてしばらく放置してたんだけど、なんか Matz さんのところで、

話題のActorも使ってないし。

なんて言われていて、え?アクターって話題なの?と思って、あわてて調べてみた (Actor クラス) …… そしたら、なんかアクターにメッセージ送るのが ! メソッドだったり、Actors that Unify Threads and Events (PDF) をちょっと眺めてみると何だか、「それなんて Erlang ?」と思うようなことがいろいろ書いてあるっぽくて、なんじゃこりゃー…と。

…で、ここで気を取りなおして、アクターモデル (Wikipedia-ja) というページを熟読。 なんかやっぱり「それなんて Erlang ?」という感想。

アクターモデルはメッセージパッシングの意味論でもある。

うーん、つまり Erlang がやってるようなことを、ちゃんとした言葉に直したものってことかな、たぶん。 なんか関連項目のところに Erlang もリストアップされてるし。

さて、そうすると Scala のアクターというのは Erlang のプロセスのように使えるものなのか。 まあ件のベンチマークの結果からするとパフォーマンスに関しては Erlang に並ぶようなものではないようだけど、同じ考え方で物を作れるというのは良い事だよな。 元々、JavaVM で動く言語の個人的ランキングでは圧倒的一位だった Scala だけど、この件でさらに株が上がったな。

……まあ、問題はそもそも JavaVM 自体があんま好きじゃないという点なのだが……

% [clip] π-calculus 超入門

sumii さんが教えてくれたので見てみたが……

超わかりやすい!!

なんだか、わしでもπ計算の基礎が解った。 いや、解った気がするだけかもしれないけど、とりあえず、さっぱり意味不明だった Pict のチュートリアル (Pict のソースアーカイブに ps ファイルが含まれてる) が、それなりに意味を把握できるようになったから、大きな進歩だと思う。

ほんと感謝感激です。

本日のツッコミ(全2件) [ツッコミを入れる]

% sumii [「?が受信で!が送信」は、僕が知る限りCSP (http://en.wikipedia.org/wiki/Commu..]

% jijixi [おお、情報ありがとうございます。 # 実は適当なことでも書いとけば、事情通の人が反応してくれるんでは…と期待してたり..]


2007-05-23 [長年日記]

% [Pict] プロセスよりもチャンネルを強く意識すべきなんだと悟った件

π計算にとっては、実はプロセスよりチャンネルの方が重要なんじゃないかと思ったのだった。 極論すると、π計算において全ての値はチャンネルであって、チャンネルにチャンネルを送ったり、チャンネルからチャンネルを受け取ったりしてごちゃごちゃやることのひとかたまりがプロセスなんだよ。 ……たぶん。 プロセスというのを頭からはずして、チャンネルに絞って見ていけば、なんとなく流れが掴みやすい気がする。

π-calculus 超入門の最後の方で紹介されている、

!(fact?[n,r] . (if n=0
                   then r![1]
                   else (new c . (fact![n-1,c] |
                                  (c?[x] . r![n * x])))))

というのも、一見 fact という名前の関数があるように見えるけど、fact はあくまでチャンネルで (つまり、本来はこの定義の手前に new fact という宣言があるはず)、fact から [n,r] という型のメッセージを受けて、if 以下の処理をする…というプロセスが無限個あるということを意味しているにすぎない。……はず。

それで、そこら辺を踏まえつつ Pict のお勉強に戻ると、色々見えてきて楽しい。 Pict では、他の言語で言うところの関数を定義する場合 def というキーワードを使うんだけど、これはチュートリアルでは Process Definitions という項で説明されている。 つまり、関数 (のようなもの) とは実は上記の fact のように無限個のプロセスであると言えるだろう。 ただ、実装の関係か、実用の関係かはわからないけど、純粋な意味での process replication というものは無いみたいで、上記の例をそのまま再現するのは無理っぽい。 ちなみに、Pict の配布物に含まれるサンプル (Examples/Simple/seq.pi) では、fact は…

def fact (x:Int):Int =
  if (== x 0) then 1 else (* x (fact (- x 1)))

となっている。 これはπ計算的に見ると、fact というチャンネルを用意して、(ちなみに Pict では [hoge fuga] はリストではなくタプル)

new fact : ^[Int ^[Int]]

それを利用する、

(fact ? [x r] = if ...

というプロセスの replication を作成して、

(fact n)

という呼び出しを、

(new r:^[Int]
 ( fact ! [n r]
 | r ? [result] = result ))

みたいな感じで扱っていると見做せるような気がする。

% [雑談] あートラックバックスパムウゼー

tDiary を開発版に入れ替えるかなー。 でもめんどくさいなー。 この際、トラックバックを無効にしてしまうかー、などとも思うけど、少ないながらもまともなトラックバックも来るしなー。

とりあえず、今のところ tDiary のバージョンアップのめんどくささ (いや、入れるのは一瞬だろうけど、絶対なんだかんだで細かい調整は必要になると思うし) に対するスパムのウザさは、まだ閾値を越えてないんで、もう少し現状でがまんしとくか。

% [雑談][Erlang] モジュール内の関数を全部エクスポートする方法が -compile(export_all) な理由

理由というのは嘘かもしれない。 結果的にそうなっているからってだけで、順番は逆かもしれないから。 まあ、それはともかく。

Erlang ではファイルが一つのモジュールになっていて、モジュール内の関数を他のモジュールからも使えるようにするには、

-export([hoge/1]).

などというように、-export というモジュール属性にエクスポートする関数のリストを指定することになっている。 そして、これとは別に、モジュール内の全ての関数をエクスポートする方法として、

-compile(export_all).

と書けるわけだ。 さて、それじゃあ、この -compile というモジュール属性は一体何なのだ?というと、要するにコンパイルオプションである。 つまり、こう書いておくことによって、このモジュールがコンパイルされるとき、暗黙的に export_all というコンパイルオプションが使用されることになるということ。 そうするとだ、この事を逆から考えると、

コンパイル時に export_all というオプションを使えば、無理矢理全ての関数をエクスポートさせることができる

ってことだね。 これが何を意味するかと言うと、例えばデバッグの時とかユニットテストの時に、そのための仕掛けをソース側に施しておかなくても、プライベートな関数を自由にテストできるということだ。 と言うより、元々 export_all というのはそういうことをするために用意されたもので、それをたまたま遊びの時とかプロトタイピングのときにいちいち -export を書かなくて済むから利用している…というのが正解なんじゃないかなあ、と思うのだがいかがだろう。

ちなみに、件の無理矢理 export_all する例。

% cat test.erl
-module(test).
-export([f/1]).
f(X) -> io:format("~b~n", [g(X)]).
g(X) -> X + 1.
Eshell V5.5.4  (abort with ^G)
1> c(test).
{ok,test}
2> test:module_info(exports).
[{f,1},{module_info,0},{module_info,1}]
3> c(test, [export_all]).
{ok,test}
4> test:module_info(exports).
[{g,1},{f,1},{module_info,0},{module_info,1}]

こんな感じ。

% [Debian] aptitude の罠

新しいパッケージを入れようと思って aptitude を起動して、まずはアップデートだよねと u を押して、なぜかその勢いでうっかり F10 → p とか押してしまって、気付いたら一時間くらい経っていたってことありませんか? 特にセキュリティアップデートが出た直後とかで、サーバが重くてなかなかアップデートが終わらないときとか、しばらく放置した sid のアップデートのときとか、ついついやっちゃいますよね。 かまけすぎちゃいますよね?

本日のツッコミ(全1件) [ツッコミを入れる]

% 向井 [ああー、なるほど。 Makefile とかに書いておけばもっと楽にスイッチできますからねえ。 export_all ..]


2007-05-24 [長年日記]

% [Erlang] なんという単純かつ明快なルール

向井さんのとこへのクエックさんのコメントに目から鱗が落ちた。

カンマは and で、セミコロンは or です。

カンマで区切られているものは全て実行されますが、セミコロンで区切られているものはそのいずれかが実行されます。ガードでもカンマは and で、セミコロンは or ですよね。

% [Erlang] Erlang でπ計算

あー……こんなことやっとる場合じゃないんだが……すんごい逃避行動……

ということで、Erlang でπ計算を再現するモジュールを書いてみた。 えーと、まあ、ソースはこんな感じ。

% cat pi.erl
-module(pi).
-export([new/0,
         send/2,
         recv/2,
         display/1,
         replication/2,
         replication/3]).
-define(REPL_NUM, 10).

channel(Queue) ->
   receive
      {get, Pid} when is_pid(Pid) ->
         {Val, Rest} = case queue:out(Queue) of
            {{value, V}, R} -> {V, R};
            {empty, Q} ->
               receive
                  {put, Msg} -> {Msg, Q}
               end
         end,
         Pid ! {channel_response, Val},
         channel(Rest);
      {put, Msg} ->
         channel(queue:in(Msg, Queue))
   end.

new() -> spawn(fun() -> channel(queue:new()) end).

sendSeq(Channel, Msg) when is_pid(Channel) ->
   Channel ! {put, Msg}.

send(Channel, Msg) ->
   spawn(fun() -> sendSeq(Channel, Msg) end).

recvSeq(Channel) when is_pid(Channel) ->
   Channel ! {get, self()},
   receive
      {channel_response, Val} -> Val
   end.

recv(Channel, Fun) when is_function(Fun, 1) ->
   spawn(fun() ->
      V = recvSeq(Channel),
      Fun(V)
   end).

display(Channel) -> io:format("~p~n", [recvSeq(Channel)]).

replication(Channel, Fun) -> replication(?REPL_NUM, Channel, Fun).

replication(Num, Channel, Fun) when is_integer(Num),
                                    is_pid(Channel),
                                    is_function(Fun, 1) ->
   lists:map(
      fun(_) ->
         spawn(fun() -> mk_repl_proc(Channel, Fun) end)
      end,
      lists:seq(1, Num)).

mk_repl_proc(Channel, Fun) ->
   {Pid, Ref} = erlang:spawn_monitor(
      fun() ->
         V = recvSeq(Channel),
         Fun(V)
      end),
   receive
      {'DOWN', Ref, process, Pid, Reason} ->
         case Reason of
            normal -> mk_repl_proc(Channel, Fun);
            ErrReason -> erlang:error(ErrReason)
         end
   end.

んで、こんな感じで書く。

Eshell V5.5.4  (abort with ^G)
1> c(pi).
{ok,pi}
2> X = pi:new().
<0.37.0>
3> pi:send(X, 123), pi:recv(X, fun(V) -> erlang:display(V) end).
123
<0.40.0>
4> C = pi:new().
<0.42.0>
5> pi:send(X,C), pi:recv(C,fun(V)->erlang:display(V)end), pi:recv(X,fun(D)->pi:send(D,"abc")end).
<0.46.0>
"abc"

例はπ-calculus 超入門から拝借。 あとはまあ、こんな感じ↓

6> f().
ok
7> Fact = pi:new().
<0.50.0>
8> pi:replication(Fact, fun({N,R}) ->
8>   if N == 0 -> pi:send(R, 1);
8>      true -> C = pi:new(),
8>              pi:send(Fact, {N-1, C}),
8>              pi:recv(C, fun(X) -> pi:send(R, N*X) end)
8>   end
8> end).
[<0.52.0>,
 (略)
 <0.61.0>]
9> R = pi:new().
<0.73.0>
10> pi:send(Fact, {5, R}).
<0.75.0>
11> pi:display(R).
120
ok
12> pi:send(Fact, {10, R}).
<0.105.0>
13> pi:display(R).         
3628800
ok

そこそこ遊べるんじゃないだろうか。 replication の辺りが手抜きで、用意したプロセスを消す方法を用意していなかったりするけど。

% [雑談] 猫にはある種の超能力があるのではないかと、本気で思うことがある

なかなか集中できなくて困っているときは静かなのに、やっとのことで集中し始めた頃に「あお〜ん」と一声。 せっかくの集中力は霧散。

なかなか眠れなくてうだうだしてるときは静かなのに、ようやくうとうとし始めた頃に「がお〜ん」と一声。 ばっちり目が覚める。

偶然では片付けたくないほど頻繁に、こういうことがある。 もうちょっと集中しきってからなら平気なのに。 もうちょっと眠りが深くなってからなら平気なのに。 どう考えてもタイミングが良すぎるのだ。 ここ以外に無い、ってタイミングで鳴くんだもの。

きっと何かあるんだよ、猫ってやつには。 なんかこう、人が出すα波とかを感じとって、それに反応するとかさ。


2007-05-25 [長年日記]

% [雑談][Erlang] タプルとリスト

どこでだったかは忘れてしまったんだけど、しばらく前に「OCaml みたいに、リストに何でも入れられない言語だとタプルは重要だと思うけど、Erlang ではリストに何でも入れられるのでタプルの存在意義がわからない」みたいな話を読んだのである。 わしなんかはむしろ、動的型付けだってリストとは別にタプルがあった方が便利じゃないか…などと感覚的に思ってしまうんだけど、具体的にどう便利なのかはちゃんと言葉にしたことが無かったなあ、と思って、ちょっと考えてみた。 実装の問題だとかは置いといて、もっと意味的な話というか、そんな感じで。

で、考えた結果としては、きっとパターンマッチが関係している。 だって、もし Erlang にタプルが無かったらすごく困るだろうと思うのに対して、Python にタプルが無くてもあんまり困らないだろうと思うんだな。 Python のタプルって、なんつーか結局使うときってリストと同じなんだよね。

var[2]

とか書いてあっても、var がリストなのかタプルなのかなんて区別が付かない。 まあ、Python の場合はむしろ区別しなくて良いように実装されてるんだろうけど、そうすると結局、細かいことを無視すれば、全部リストで良いじゃんってことになっちゃう。 Ruby にタプルに相当するものが無いのはそういうことでしょう。 だって、配列で全部間に合うんだもの。

ただ、Erlang の場合は事情が違う。 Erlang はパターンマッチを使いまくる言語だ。 そして、パターンマッチとは、データ構造を常に可視化して扱うものだ。

[A,B,C]

と、

{A,B,C}

は全く違うものだと意識しなくちゃならない。 だって、リストとタプルは違うものだから。 リストは同じようなものを並べたもので、タプルは全然違うものをひとまとめにしたものなのだ。 データとしての意味が全然違う。

これはもちろん「そうじゃなきゃだめ」と決まっていることではないけど、暗黙的了解事項としてこういう分類の仕方は存在すると思う。 そして、このルールに従ってコードを書けば、それを他人が読んだときにもコードの意図が伝わりやすいはずだ。 Python なんかと違って、パターンマッチのおかげでリストを扱ってるかタプルを扱ってるか、コードからはっきり読み取れるしね。

あと、Erlang 特有の事情として、メッセージの問題がある。 例えば静的型付けされたチャンネルを利用して、メッセージを送りあうような言語なら問題は無いんだけど、Erlang のようにどんな型のデータでも送れるし受け取れる言語では、何らかの形でメッセージを選り分けないといけない。 そして、Erlang ではそれをパターンマッチを使うことで何とかしている。 もしそこでタプルが使えなかったとしたら、きっとすごくややこしいことになるはずだ。

……と、ここでほんとは、ややこしくなる例を示そうと思ったんだけど、思い付かなかったんで省略。 とりあえず {protocol, {Data1, Data2}} みたいなメッセージプロトコルがあったとして、それをタプルの代わりにリストで表現して、かつ Data1 とかがリストである例なんかを考えてみると良い。 少なくともわしはそんなパターンは書きたくないな(苦笑)。 例えリストとタプルの意味云々を置いておいたとしても、その二種類のパターンがあるおかげで、メッセージプロトコルがわかりやすいものになるのは確かだろう。 一種類しか無いと、どの部分がプロトコルで、どの部分がデータか区別が付かない。

……と、これくらいが人に説明するなら…ということで考えた分なんだけど、なんつーか個人的にはもっとこう一言で終わるというか、ぶっちゃけ、

  • 「後でまとめて処理するためのひとかたまり」がリスト
  • 「バラバラにしとくと扱いが面倒だから、ひとまとめにしたもの」がタプル

ぐらいの話なんだけどね。

% [clip][Python] Evolution of a Python programmer (4chan BBS)

via programming reddit.

おもしろい。 First year programmer, studied C の例が一瞬意味不明だったがじわっと来た。

ところで SICP とか Python hacker の例で出てくる @tailcall っていう魅惑的なデコレータは、どうやったら使えるんだろうか。

本日のツッコミ(全2件) [ツッコミを入れる]

% Dubhead [Pythonでも「リストは同じようなものを並べたもので、タプルは全然違うものをひとまとめにしたもの、という暗黙的了解..]

% jijixi [> 事情は同じ もちろんそうだと思いますけど、パターンマッチがある言語だとそこがより際立つよという話です。 > ..]


2007-05-27 [長年日記]

% [clip][Python] Collection of tail call optimization decorators for Python (prgramming reddit のコメント)

この前の Evolution of a Python programmer に対する、「いつから Python は末尾呼び出しの最適化をするようになったん?」というコメントとそれに対する反応。

要するに、あのネタにおける @tailcall ってデコレータは、いんちきだけど (少なくとも何も import せずに使えるような機能ではない)、ただ、本当の意味での末尾呼び出し最適化は無理でも、末尾再帰する関数を自動的にループに変換するようなことならできるよ……というような話かな? 一応実装例へのリンクがいくつか挙がってたんでメモ。

まあ、どっちにしてもスタックが溢れなくなるという以外に利点があるかというと、たぶん無いだろうね。 つーか、リンク先の例を斜め読みしても、なんだかややこしくて、ほんとにこれでスタックが溢れないのかよく理解できないという(苦笑

……まあ、あとでゆっくり読もう。

% [clip] PHPの奇妙なround関数 (hnwの日記)

sumii さんとこ経由で。

PHP っておもしろい言語だなあ。何かいろいろと違った意味で。 それはそれとして、これを書いている時点ではてなのブックマーク数が 28 もあるのに、「これはひどい」が一つしか付いてないのはどういうことだ。

え、もしかして「これはひどい」って結局定着しなかったの? や、わし、はてなユーザじゃないからよくわかんない。

% [clip][OCaml] 幽霊型を使ってウェブアプリで安全に文字列を使う (Rainy Day Codings)

constraint ってオブジェクトに使うものっていう思い込みがあったんだけど、こういう使い方もあるんだあと感心した。

% [雑談][OCaml] OCaml の大嫌いなところ

正直 OCaml で嫌いなところってほとんど無いんだけど、どうしても一つだけ我慢ならない部分があって、それは何かというと『文字列が mutable』だってこと。 Buffer モジュールとかあるんだから、破壊的な操作はそっちでやれよと言いたい。 例えば単純な例として、こんな↓のを作ったとするじゃない。

# class hoge (s:string) = object
    val v = s
    method get = v
  end;;
class hoge : string -> object val v : string method get : string end

でもさ、

# let s = "hoge";;
val s : string = "hoge"
# let o = new hoge s;;
val o : hoge = <obj>
# o#get;;
- : string = "hoge"
# s.[1] <- 'i';;
- : unit = ()
# o#get;;
- : string = "hige"

こんなことが起こる可能性を考えるとゾッとしないから、結局…

# class hoge (s:string) = object
    val v = String.copy s
    method get = v
  end;;
class hoge : string -> object val v : string method get : string end

こんな風に書くはめになる。 他にも、

# class fuga = object
    val v = "fuga"
    method get = v
  end;;
class fuga : object val v : string method get : string end

こんなのを書いて、

# let s = new fuga;;
val s : fuga = <obj>
# let t = s#get;;
val t : string = "fuga"
# t.[3] <- 'e' ;;
- : unit = ()
# s#get;;
- : string = "fuge"

こんなことになる可能性を考えるとゾッとして、

# class fuga = object
    val v = "fuga"
    method get = String.copy v
  end;;
class fuga : object val v : string method get : string end

こんな風に書くとか。 もう String.copy なんてウンザリだ。 String.copy v を v ^ "" って書けば短かいね…とか考えたこともあったけど、そんなことよりも、いちいち「この文字列は書き換えらえるとマズい」とか「マズくない」とか考えるのがめんどくさいんだっての。

極端な話、文字列が mutable な言語なんて滅んでしまえ!とか思わなくもないんだけど、例えば Ruby なんかだと (mutable な) 文字列の代わりに (immutable な) シンボルが使えるわけで、お願いですから OCaml にもその手の安心して定数として使える、文字列と可換なデータ型を用意してください。本気で。

ちなみに、そういう視点で見ると、文字列が immutable な F# はサイコー。 つーか F# は最近どんどん OCaml とは違う言語に成長してるっぽいので、わりと注目してますよ。 いつまで経っても Mac OS X with Mono で使えるようにならないんで、ほとんどいじれてないんだけども(苦笑

% [Ruby] Lazy Array でっちあげ版

弾さんの Perl 版に触発されたんだけど、tie ってどういうものかよくわからなくて、何をやってるかイマイチ理解できなかったんで、結局何も参考にはせずに適当にやってみた。 つーか、遊びなんでね。 ちゃんと使い物になるのが欲しいなら、誰かがちゃんと作ってくれるのを待ちましょう。

% cat lazy_array.rb
class Array
   alias original_init :initialize
   def initialize(size=nil, val=nil, &block)
      @is_lazy = false
      @generator = nil
      if size.nil? and !block.nil?
         @is_lazy = true
         @generator = block
      else
         if block.nil?
            if val.nil?
               original_init(size)
            else
               original_init(size, val)
            end
         elsif val.nil?
            original_init(size, &block)
         else
            raise ArgumentError
         end
      end
   end

   alias original_get :"[]"
   def [](index, length=nil)
      unless @is_lazy
         if length.nil?
            original_get(index)
         else
            original_get(index, length)
         end
      else
         if index.class != Range and length.nil?
            tmp = original_get(index)
            if tmp.nil?
               new_val = @generator.call(index)
               self[index] = new_val unless new_val.nil?
               new_val
            else
               tmp
            end
         else
            range =
               if length.nil?
                  # index is range object.
                  index
               else
                  # index is start.
                  (index...(index+length))
               end
            range.to_a.map do |i|
               self[i]
            end
         end
      end
   end

   def lazy?
      @is_lazy
   end
   
   def force!
      return nil unless @is_lazy

      idx = 0
      val = self[idx]
      until val.nil?
         idx += 1
         val = self[idx]
      end
      @is_lazy = false
      @generator = nil
      self
   end
end

実は元のメソッドに丸投げするところとかで、微妙に苦労があったり。 lazy になるのは、Array.new にブロックだけ渡した場合のみ。 まあ、こんな↓感じで。

irb(main):001:0> require 'lazy_array'
=> true
irb(main):002:0> a = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):003:0> a.lazy?
=> nil

ここで nil が返っちゃうのはご愛嬌。 この辺はいろいろと事情が。

irb(main):004:0> b = Array.new([1,2,3,4,5])
=> [1, 2, 3, 4, 5]
irb(main):005:0> b.lazy?
=> false

これなら大丈夫。

irb(main):006:0> c = Array.new {|i| if i<10 then i else nil end}
=> []
irb(main):007:0> c[3]
=> 3
irb(main):008:0> c
=> [nil, nil, nil, 3]
irb(main):009:0> c[1] = 10
=> 10
irb(main):010:0> c
=> [nil, 10, nil, 3]
irb(main):011:0> c.force!
=> [0, 10, 2, 3, 4, 5, 6, 7, 8, 9]

c[3] した時点での nil 三つが余計だと思うような人は、あきらめてハッシュ使うように。 force! すると nil に当たるまで値を計算していく。 絶対に nil が返らないようなブロックを与えていると、ぶっちゃけ無限ループ。

irb(main):012:0> d = Array.new {|i| i}
=> []
irb(main):013:0> d[5]
=> 5
irb(main):014:0> d
=> [nil, nil, nil, nil, nil, 5]
irb(main):015:0> d[1..3]
=> [1, 2, 3]
irb(main):016:0> d
=> [nil, 1, 2, 3, nil, 5]
irb(main):017:0> d[5,4]
=> [5, 6, 7, 8]
irb(main):018:0> d
=> [nil, 1, 2, 3, nil, 5, 6, 7, 8]
irb(main):019:0> d.force!
^CIRB::Abort: abort then interrupt!!
        from /opt/local/lib/ruby/1.8/irb.rb:81:in `irb_abort'
        from /opt/local/lib/ruby/1.8/irb.rb:243:in `signal_handle'
        from /opt/local/lib/ruby/1.8/irb.rb:66:in `start'
        from ./lazy_array.rb:35:in `call'
        from ./lazy_array.rb:35:in `[]'
        from ./lazy_array.rb:69:in `force!'
        from (irb):19

こんな感じで、無限配列にしちゃうと force! は危険。 まあ、Haskell なんかでも無限リストに length とか使えば同じことだから、こんなのはプログラマが注意しなさいってことでしょ。 あと、force! してない遅延配列に push とか使うの禁止な。 恥かしいことになるから。や、でっちあげだって言ってるでしょ(笑

しかし、これ作ってて思ったんだけど、これってあんまり有用じゃない気がするなあ。 つーか、配列である限り旨味が少ないというかさ。 遅延リストってのは、常に『先頭の値』と『残りのリスト』しか存在しないから、map 関数とか普通にリストを扱うように書くだけで結果のリストも自然と遅延リストになるけど、遅延配列の場合は map なんかもそれ用に考えて書かないといけないと思うんだよね。 で、まあ、それが map だけで済むなら良いけど、配列を返すメソッド全てに……ってことになってくると、えらいことでしょ。

少なくとも、わしには『配列を返すメソッドをいじらなくても、遅延配列の恩恵を受けられる』ような実装方法は思い付かなかったな。 例え C レベルでいじるとしても。

本日のツッコミ(全2件) [ツッコミを入れる]

% しーやん [ぐは、@tailcall 存在してなかったのか. 騙されて何を import すればいいんだろうと調べてました or..]

% jijixi [実はわしもそうです(苦笑]


2007-05-28 [長年日記]

% [clip] JASRAC、勝訴…ネット上に音楽データ保存できる「ストレージ」サービスに、「著作権侵害」判断 (痛いニュース)

なんつーか……もう笑い話にしか聞こえないな……

172 名前:名無しさん@七周年[sage] 投稿日:2007/05/25(金) 22:28:21 ID:fKgU5bWP0

自分で買った本をどこで読もうがコインロッカーに入れておいて

取り出して読もうが著作権の侵害のわけないのに

なんでこんなアホな判決が出てくるの?

196 名前:名無しさん@七周年[] 投稿日:2007/05/25(金) 22:31:21 ID:UOLSByJ50

>>172

本は合法だろ、

CDをコインロッカーに入れたら違法

261 名前:名無しさん@七周年[sage] 投稿日:2007/05/25(金) 22:37:46 ID:7vhkJgW80

>>196

しかも捕まるのは駅の人だってんだから驚きだ

もう国内には、どんなサーバも置けないね。 つーか、この裁判官クビにする方法無いの?

% [Smalltalk] asBag

某所で sumim さんが使ってる asBag ってなんじゃらほい?と思って、試してみたんだが…

% gst                                                                  ~
GNU Smalltalk ready

st> #('foo' 'bar' 'foo' 'baz' 'bar' 'foo') asBag printNl !
Bag('bar':2 'foo':3 'baz':1 )

引っくり返った。 何このまんまな機能(笑

本日のツッコミ(全1件) [ツッコミを入れる]

% とおりすがり [一太郎のときもこの人らしいですね. ひどいものです.]


2007-05-29 [長年日記]

% [clip][Erlang][Emacs] Distel = Emacs erlang-mode++ (Bill Clementson's Blog)

via programming reddit. Emacs の erlang-mode を超便利にしてしまうツール、Distel の紹介記事。

入れてみて、ちょろっと触ってみたけど、結構良い感じ。 Emacs ユーザなら是非ってところ。 まあ、わしの場合は vim 命だから常用はしないけど。 独自にデバッガを持ってるので、Tk 使ってるデフォルトのデバッガが好きになれない、って人にもお薦めかもしれない。

% [Ruby] でっちあげシリーズ、遅延リスト

なんかね、なんとなくぼんやりと lazylist のソース眺めてたんだけど、なんかこう……車輪の再発明とかしてみたくなってさ。 まあ、基本的な部分だけなら、大した手間でもないだろーという感じでテケトーに書いてみた。 ……とは言うものの、行き当たりばったりに始めたせいで、いろいろとツギハギで美しくないのであった...orz

でもまあ、せっかく書いたので晒しとく。 当初は 100 行くらいで書けんじゃね?と思ってたんだけど、見通し甘くて結局 300 行近くになってしまったんで、別ページに→ lazy_list.rb

使い方は…

  • list() で空リスト
    • 所により :null というシンボルも空リストとして使える (なんか適当)
  • list(1) で 1 から無限大までの自然数
    • ブロックも使える
  • list(1,tail) は 1.cons(tail) と同じ
    • Object とか Kernel にメソッド追加してごめんなさい
  • list(n..m) は (n..m).to_a のリスト版
  • list(array) は配列をリストに
  • list(hoge) で hoge が整数でも配列でもレンジでもない場合は list(hoge,:null) 相当 (たぶんこんな機能はいらなかった気が…)
  • list{ ... } は遅延評価用のラッパー
    • 引数無しでリストを返すようなブロックを与えてちょうだい
  • ファイル名がギリギリな線なのは許してね
    • そもそも誰も使わんだろうという前提の元に作ってるんで

こんな感じで。 あとは実行例とか。

irb(main):001:0> require 'lazy_list'
=> true
irb(main):002:0> l1 = 1.cons(:null)
=> #<List: head = 1, tail = null>
irb(main):003:0> l2 = 2.cons(list())
=> #<List: head = 2, tail = null>

:null でも list() でもどっちでも良い。

irb(main):004:0> l3 = l1 + l2
=> #<List: head = 1, tail = lazy>
irb(main):005:0> l3.to_a
=> [1, 2]
irb(main):006:0> l4 = list(1..10)
=> #<List is lazy: head = ???, tail = ???>
irb(main):007:0> l4.to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
irb(main):008:0> l5 = list(1..10) {|x| x*2}
=> #<List is lazy: head = ???, tail = ???>
irb(main):009:0> l5.to_a
=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

レンジ式の例。

irb(main):010:0> l6 = list(1) {|x| x*3}
=> #<List is lazy: head = ???, tail = ???>

この辺は無限リスト。

irb(main):011:0> l7 = l6.take(10)
=> #<List: head = 3, tail = lazy>
irb(main):012:0> l7.to_a
=> [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
irb(main):013:0> l6.to_a(10)
=> [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
irb(main):014:0> l6.to_a {|x| x > 20}
=> []
irb(main):015:0> l6.to_a {|x| x < 20}
=> [3, 6, 9, 12, 15, 18]
irb(main):016:0> l8 = l6.map {|x| x + 10}
=> #<List: head = 13, tail = lazy>
irb(main):017:0> l8.take(5).to_a
=> [13, 16, 19, 22, 25]

結局 Kernel に list メソッドみたいなのを追加するんなら、initialize で色々対応できるようにがんばる意味なかったよなー。 適当にシンボルでも引数に取って、それで分岐すりゃ良かったんだよ、ぐっすん。 っていうか、もうなんつーの?脳がパターンマッチの存在を期待した方向にしか向かってくれなくて、書きにくいったらなかったよ。 どうせ動的型付け言語で書くなら Erlang で書いときゃ良かった…とか思ったのは内緒だ。 他にも末尾再帰ですっきり行くところを、while に書き換えたりしてぐったりとか、なんかもう関数型言語脳の呪いが……

本日のツッコミ(全1件) [ツッコミを入れる]

% TrackBack [http://jijixi.azito.com/cgi-bin/diary/index.rb?date=200706..]


2007-05-30 [長年日記]

% [独り言] 大溜息大会

はあ……………………………………(溜息終了)。 お・か・ね・が・無〜〜い。 まあ、どっかから湧いて出るもんじゃあないしな。

実はふとした拍子に、道内企業では初めて「この会社なら入りたい」と本気で思えるとこの求人を見つけた (まあ、WEB サイトに書いてることを鵜呑みにするなら…だけども)。 …てことで、さっそく応募してみたんだけど……落ちたらヘコむよなー。 こんなにもウマが合いそうな会社にすら拒否されたら、ほんとにわし、いらない子やん。 ってか、面接にすら呼んでもらえなかったら、一週間くらい立ち直れないかも。

ということで、この件に関してはこれ以降、事態が好転した場合のみ書く。 一向に音沙汰無いなーと思ったら、まあそういうことなので、心の中でなぐさめてやってください。

% [clip][OCaml] The JoCaml system

via programming reddit.

OCaml に平行、並列プログラミングのための概念である join calculus のサポートを追加した処理系…ということらしい。 join calculus ってのが何なのかよくわかんないけど、ドキュメントをちらっと眺めてみた感じ、π計算の仲間みたいに見える。 あと、static type-checking とか書いてるから、型付きのπ計算て感じなのかも。

さっそくいじってみようと思ったんだけど、残念なことに Download のリンク先は空っぽでションボリ。 しかたないので説明書きとかマニュアルとか読んでガマン。

join calculus のための構文と予約語の追加以外は、かなりの割合で OCaml と互換性が確保されている…ということのようだ。 あと、適合するバージョン同士ならバイナリレベルの互換性もあるようなこと書いてある? ってホンマかいな?

#def echo(x) = print_int x; 0
 ;;
val echo : int Join.chan = <abstr>

def でチャンネルの定義。 print_int の型は int -> unit なので、echo は int を受け取るチャンネルとして推論されている。 ケツに付いてる ; 0 は型を合わせるための付け足しかな? よくわからんが、送受信それぞれ同じ型じゃないとだめってことか? あと、echo(x) の部分のカッコは省略不可みたい。

#spawn echo(1)
 ;;
- : unit = ()

これでチャンネル echo に 1 を送っている状態。 spawn 構文を通して実行される式は、平行動作するプロセスになる。

#spawn echo(1) & echo(2)
 ;;
- : unit = ()

こんな感じにすると、& の前後が別々のプロセスになるみたい。 普通の OCaml みたいに ; で区切るとシーケンシャルに動く。

と、この辺まで読み進めてようやく気付いたけど、π計算みたいにチャンネルに対する送信と受信があるわけじゃなくて、join のチャンネルは要するに呼び出されるとプロセスとして動作する関数みたいなもんみたいだな。 π計算で言えば replicate されたプロセスみたいな。 チャンネルは基本的に値を返すことはなくて、返り値が必要な場合はチャンネルを渡してそこに結果を投げさせる。 ただ、それだけだとやっぱ不便なので、値を返すようにする方法も用意されている。

#def succ(x) = print_int x; reply x+1 to succ
 ;;
val succ : int -> int = <fun>

こんな感じで reply 返り値 to 自分の名前、と書けば良いようだ。

さて、この後 Join patterns というのが出てきて、だんだんおもしろくなりそうなんだけど、長くなるんでとりあえず終了。 単純には、& を使って複数のチャンネルを定義すると、同期チャンネルとして作用するとか、or でパターンマッチが使えるとかそんな感じの話。 他にも Bi-directional channels という項でπ計算の真似事をする例とか書いてるみたいだけど、この辺まで来ると実物いじりながらじゃないとイマイチ理解しにくいなあ。 早いとこソース拾えるようになると良いんだが。


2007-05-31 [長年日記]

% [雑談][OCaml] OCaml をお忘れなく

そういえば静的言語でキーワード引数をサポートしているのってあったかな。メジャーなところではあまりない気がする。

[odz buffer - キーワード引数より引用]

はーい、OCaml にはあります。ありますよー。

……………ええ、ええ、わかってますよ。メジャーじゃないっていうんでしょ。返す言葉もございませんよ。どーせね(しくしく

% [JoCaml] JoCaml 妄想日記

いまだ処理系が手に入らないので、マニュアルを眺めつつ妄想するよ。

とりあえず、昨日の例の…

print_int x; 0

; 0 の部分は……というか、0 は JoCaml において「何もしないプロセス」を表わしているようだね。 def 構文でチャンネルを定義する場合、右辺は式ではなくプロセスじゃないといけないんだけど、print_int x だけだと単なる式でプロセスではないので、それを無理矢理プロセスにするために、空プロセスである 0 を付け足しているということのようだ。

とりあえず join-calculus におけるプリミティブな要素はプロセスとチャンネルだけのようだ。 まあ、π計算でもそうなんだろうけど、join の場合のチャンネルはπ計算におけるチャンネルとはちょっと意味が違ってる感じ。 π計算だとチャンネルって、プロセス間で値をやり取りするための経路のようなもので、「チャンネルに送信」という操作と「チャンネルから受信 (して、プロセスを実行)」の二種類の操作があるが、join のチャンネルは「値を受信してプロセスを実行する関数」と考えた方がしっくり来そう。 昨日も書いたけど、π計算で言えば replication にあたるもの。

んで、そんな感じで考えると、要するに「基本はプロセス」というのを「基本は関数」という関数型な考え方に素直にマッピングできそうな気がする。 なんつーか、普通に関数型な書き方をしていけば、それが自然と並行プログラミングになっているというか。 例えば想像で map 関数を書いてみたりすると……

def map(f, lst) =
   match lst with
   | [] -> reply [] to map
   | x::xs ->
      def hd(y) & tl(ys) =
         reply f y to hd & reply map(f, ys) to tl
      in
      reply hd(x) :: tl(xs) to map
  

こんな感じかな。もしかしたら動かないかもだけど、まあ、たぶん。 とりあえず今は確かめられないんで、そこは置いといて。 多少冗長だけど、基本的にはごく普通の map 関数を定義しているだけ。 でも、たぶん、各要素に f を適用するのは並行動作するはず。 あと、結局 hd の結果と tl の結果を繋ぐところで待ち合わせが入るから、結果はちゃんと順番に並ぶはず。

……つーか、ほんと早く実物が触りたい。 机上プログラミングなんてやりたくないよ...orz

個人的には Concurrent programming よりも Distributed programming の方が興味を惹かれるんだけど、そっちはなおさら実物が無いと感触が掴みづらいしなあ。 基本的にわし、実際に動かして感触掴むタイプだから、処理系が無いとほんとつらい。

% [clip][Perl] Perlの罠 - =>とコンテクスト (工夫と趣向と分別と。)

怖いよ、Perl 怖いよ。

OCaml のような言語を好む人間としては、bare word の扱いとか context とかって正気の沙汰とは思えないんだよなー。 どう考えても、便利さよりも面倒に巻き込まれる不安の方が大きい。

% [JoCaml] JoCaml 妄想日記その2

マニュアルの Distributed programming のページを斜め読み。 ぶっちゃけ、Erlang ほど洗練されてはいないという印象。 まあ、この点に関してはむしろ Erlang がうまくできすぎてるというか、比べるのが酷な部分はあるけども。

ノード間の接続は IP ベースで、各ノードが持つネームサーバにそれぞれチャンネルを名前 (文字列) をキーにして登録し、その名前からリモートノードのチャンネルを検索して使用するという流れ。 Erlang における epmd みたいな仲介者はいなくて、各ノードが直接サーバとしてリクエストを待つための用意をしなきゃならない。

とりあえず、ノード間の接続をして、必要なチャンネルの情報を取り込んでしまえば、あとは普通にローカルなチャンネルを使うのと同様に扱えるみたいだけど、そのリモートのチャンネルを取り込むのが色々と泥くさい。

一番がっかりなのが、別ノードのチャンネルは型情報無しでやり取りされるということ。 もちろん、取り込む時点で型は指定するんだけど、その指定した型が正しいかどうか判断する方法は無い。 要は、まともに運用しようと思えば、プログラム本体とは別に、各チャンネルの型情報を記したファイルか何かをノード間で共有しないといけないわけだ。 せっかく静的型付けなのに、人間が助けてあげないと型安全が保証できないのは、ちとさみしいなあ。

まあなんつーか、元になってる OCaml が、そもそもコンパイルした時点でポータブルな状態での型情報を持ってないっぽいから、そこら辺からどうにかしないことには、この問題は解決できないのかもしれず。 でも、せめて型情報共有の助けになるようなツールは用意してほしい気がするね。 とりあえず、共有するチャンネルは別ファイルに分けるようにして、mli ファイルが実質同じになるようにするっていう工夫とかはできそうではあるけど。 んで、そのファイルを共有すると。

% [Erlang] 大きなお世話だと思うけど、すごく添削したくなった

……これ

や、細かいっちゃ細かいけども、receive のところの書き方があまりにも気になるもんで。

receive
  false -> ... ;
  true  -> ...
end

と書きましょうよ。 Result って変数はともかく (説明的に意味が無いとは言いきれない) if 文はまるっきり余計。

あとまあ、厳密に見ると、全ての条件が true を返さない場合に一つでも無限ループが含まれてたりすると receive のところでブロックしっぱなし……とかね。

本日のツッコミ(全1件) [ツッコミを入れる]

% TrackBack [http://jijixi.azito.com/cgi-bin/diary/index.rb?date=200706..]


トップ 最新 追記

日記ってのは本来、自分で読み返すためにあるもんだよなあ……
もしくは有名人になったら死後に本になったりとかか?

RSS はこちら

jijixi at azito.com