新規ドキュメント


忘れていく脳のための備忘録
by w_l_s
プロフィールを見る
画像一覧

プロセスの輪っかを作る

オライリーの Erlang 本にあった問題を解いてみたけど、思いのほか時間がかかった。

N 個のプロセスを作り、輪になるようにつなげましょう、という問題。
ring:start(M. N, Msg) の形に沿って無いけど、ひとまずそれっぽい動きをしてくれる。

-module(process2).
-export([start/0, child/0]).

start() ->
register(echo, spawn(process2, child, [])),
echo ! {self(), 5},
receive
{_Pid, Msg} ->
io:format("~w~n", [Msg]);
child_end ->
io:format("Return to start process.~n")
end,
echo ! stop.

child() ->
receive
{From, Count} when Count > 0 ->
Pid = spawn(process2, child, []),
io:format("self pid = ~w~n", [self()]),
io:format("child: ~w~n", [Count]),
io:format("~w~n", [Pid]),
Pid ! {From, Count - 1},
child(Pid);
{From, 0} ->
io:format("terminal~n"),
From ! child_end;
stop ->
io:format("child/0 stop"),
true
end.
child(NextPid) ->
receive
stop ->
io:format("stoping ~w~n", [self()]),
io:format("child/1 stop~n"),
NextPid ! stop
end.


実行するコードがなくなるとプロセスが終了してしまうので、作ったプロセスの ID を引数に持つchild/1 を呼び出している。こいつは終了メッセージを受け取って、自分が生成した子プロセスに終了メッセージを送信して終了する。後片付けするためだけの関数です。

プロセスが消える条件をすっかり忘れていたので、最初はすでに消えたプロセスにメッセージを送ったりして大変でした。
[PR]
# by w_l_s | 2011-08-09 01:29 | erlang

vim のプラグイン管理に vundle を使う

今まで、同僚から vim-update-bundle という Ruby 製の管理スクリプトを教えてもらい、使ってきた。使うのに Ruby をインストールする必要があるのと、スクリプトを実行しないとプラグインの取得、更新が出来ないのがちょっとネックだった。

最近知った vundleは、そんな不満を解消してくれる。

単体で動作する vim スクリプトなので、 Ruby をインストールする手間ふぁ無いこと、コマンドを実行する必要があるけど、vim の中で実行でき、実行後はインストールしたプラグインがすぐ実行できるところがいい。

インストール方法hは github から取得してきた README に書いてあるのでそれを読もう。
使い方も README でばっちりさ。

vim-update-bundle と vimrc の書き方が違うので、移行する場合は書式に気をつけよう。
これで少しはまった。コロンいらないんだね。
[PR]
# by w_l_s | 2011-05-23 00:36

関数の評価順

オライリー本の最初でつまずいた。
エクササイズの 3-3 で、偶数なら表示しろという問題が出てきたので、以下のプログラムを書いた

-module(print).
-export([print/1, print/2, even_print/1, even_print/2]).

print(N) when N > 0 ->
print(1, N).
print(C, N) when C < N ->
io:format("Number:~p~n", [C]),
print(C + 1, N);
print(C, N) when C == N ->
io:format("Number:~p~n", [C]).

even_print(N) ->
even_print(1, N).
even_print(C, N) when (C rem 2) == 0 ->
io:format("Number:~p~n", [C]),
even_print(C + 1, N);
even_print(C, N) when (C rem 2) /= 0 ->
even_print(C + 1, N);
even_print(C, N) when C == N ->
print(C, N).

これで even_print() を動かすと、無限に表示を続けてしまう。
どうやら、ソースコードの上から順番にガードを評価して関数を選んでるみたい。


-module(print).
-export([print/1, print/2, even_print/1, even_print/2]).

print(N) when N > 0 ->
print(1, N).
print(C, N) when C < N ->
io:format("Number:~p~n", [C]),
print(C + 1, N);
print(C, N) when C == N ->
io:format("Number:~p~n", [C]).

even_print(N) ->
even_print(1, N).
even_print(C, N) when C == N ->
print(C, N);
even_print(C, N) when (C rem 2) == 0 ->
io:format("Number:~p~n", [C]),
even_print(C + 1, N);
even_print(C, N) when (C rem 2) /= 0 ->
even_print(C + 1, N).

順番を変えて、最初に C == N かどうかを確認させたら意図した動きになった。

ガードの中身を考え直すか、関数で条件分岐するべきかな?
[PR]
# by w_l_s | 2011-05-20 01:05 | erlang

vim script 覚書

カーソルキーの移動

今の位置から左に一文字移動。

今の位置から右に一文字移動。
vim scriptでカーソル位置を移動したい時に使う。
<>でくくる特殊な設定は他にもいっぱいある。
とかとか(Ctrl + r のこと)
詳しくは
:help <>
:help
:help

キーバインド設定
imap [ []
上の設定を行うと、[ と入力するしたとき [] にしてくれて [ | ] の | の位置にカーソルを移動してくれる。
ノーマルモード時の設定ならnmap、編集モード(インサートモード)時ならimapと、種類がいろいろ。
細かいことは:help mapで見てみよう。

キーバインド削除
iunmap [
上の設定を行うと、[ に登録していたキーバインドが削除される。
imap [ []を設定していた場合、解除後は普通に [ だけ入力される。
これまた nunmapとかiunmapとかいろいろなので、詳しくは:help unmapで調べよう。

こいつらを使えば、 [ と入力すると [] にしてくれてその中にカーソルが入るキーバインドが書ける。
こんな感じ

:inoremap [ []<Left>
:inoremap { {}<Left>
:inoremap ( ()<Left>
:inoremap " ""<Left>
:inoremap ' ''<Left>
:inoremap ` ``<Left>


でもいつでも補完して欲しいわけじゃない。
例えば、vimrcのコメント記号が " だけど、こんなときに補完してもらっても嬉しくない。
という訳で、使わないときは削除しよう。
:iunmap [
:iunmap {
:iunmap (
:iunmap "
:iunmap '
:iunmap `

以上で " を "" に補完出来る。
でも、いちいちコマンドを打って設定するのもめんどくさいので、.vimrcに書いておこう。
そして、好きなときに上記の設定を入れ替えられるようにしよう。
まずは、キーバインドを設定する関数 ClosePairOn を定義する。
vim scriptでの関数は先頭を大文字にしないと行けないことに注意が必要だ。

function! ClosePairOn()
let b:closepair = 0
inoremap [ []
inoremap { {}
inoremap ( ()
inoremap " ""
inoremap ' ''
inoremap ` ``
endfunction

次に、削除する関数 ClosePairOff を定義する。ClosePairOnにもある!マークは、同じ名前の関数があっても
エラーを出さずに、新しい関数で上書きすることを示している。vimrcを再読込してもエラーが起きないようにね。

function! ClosePairOff()
iunmap [
iunmap {
iunmap (
iunmap "
iunmap '
iunmap `
endfunction

後はこれを交互に実行する関数 ToggleClosePair を定義する。
今はどの状態なのかを覚えておくために変数を使う。変数の定義はこんな感じ。
let g:closepair_status = 0
letで変数を宣言する。
g:とあるのは、この変数はグローバル変数ですよという意味だ。
この変数の値が0で off , 1で on ということにしよう。
実行時に、今の状態が表示されると分かりやすくていいと思う。
状態を管理し、交互に実行する関数 ToggleClosePair はこんなのでどうだろう。

function! ToggleClosePair()
if !exists("g:closepair_status")
let g:closepair_status = 1
endif

if(g:closepair_status)
let g:closepair_status = 0
echo "Current: Closing Pair On"
call ClosePairOn()
else
let g:closepair_status = 1
echo "Current: Closing Pair Off"
call ClosePairOff()
endif
endfunction


あとはこれにキーバインドを設定し、コマンドを打ち込まなくても実行できるようにしよう。
nnoremap { :call ToggleClosePair()
というのは実行したコマンドをコマンドラインに表示させないオプションだ。
これがないとToggleClosePair()と一瞬表示される。目触りだったので消した。
上記の設定だと、Ctrlを押しながらpを押し、その後すぐに{を入力すれば切り替えられる。

出来れば、バッファやタブごとの設定にしたかったけど、b: や t: の挙動がわからなかったので今回は諦めた。

vimはヘルプが充実してるので、helpコマンドで調べるとコマンドの意味がすぐわかってとてもやりやすい。
香り屋さんがhelpの日本語訳を公開してくださっているので、ぜひいれておこう。


xxxするときはどうすればいいの?という、やりたいことからコマンドを見つけるのは、
ヘルプの量が多いので大変かも。逆引きサイトとかあるかな?
[PR]
# by w_l_s | 2010-11-24 01:26

erlangでproject eulerを解く

project euler 1問目
1000未満の数のうち、3か5の倍数の和を求める。

まるで手続き型言語のようなソース!

int_arrayで数字のリストを作り、euler_01_solverでリストから一個ずつ数字を取り出す。取り出した数字をadderに突っ込んで、条件にあえば足し算して結果を返す。

euler_01_solverは、リストが空になるまで自己再帰を行う。
リストが空になると最後の数字をadderに突っ込んで計算を行う。
その結果を一個前のeuler_01_solverに返し、一個前に取り出した数字とその結果をadderに入れて計算し、というのを繰り返す。

  1 -module(euler_01).
2 -export([int_array/2, int_array/3]).
3 -export([euler_01/2, euler_01_solver/2, adder/2]).
4
5 int_array(X, Y) ->
6 if
7 X < Y ->
8 int_array(X + 1, Y, [X]);
9 true ->
10 false
11 end.
12
13 int_array(X, Y, List) ->
14 if
15 X < Y ->
16 int_array(X + 1, Y, [X | List]);
17 X == Y ->
18 lists:reverse([X | List]);
19 true ->
20 false
21 end.
22
23 euler_01(X, Y) when is_integer(Y) == true ->
24 List = int_array(X, Y),
25 euler_01_solver(0, List).
26
27 euler_01_solver(All, [X | Y]) when is_list(Y) == true ->
28 if
29 Y == [] ->
30 adder(All, X);
31 Y /= [] ->
32 Value = euler_01_solver(All, Y),
33 adder(Value, X)
34 end.
35
36
37 adder(All, X) ->
38 if
39 X rem 3 =:= 0; X rem 5 =:= 0 ->
40 All + X;
41 true ->
42 All
43 end.


追記
会社でいいアルゴリズムを教えてもらったのでそれでといてみた。
ソースがだいぶ短くなったよ。

  1 -module(euler_01_another).
2
3 -export([euler_01_ask/1, sequence_adder/1]).
4
5 euler_01_ask(X) ->
6 sequence_adder(trunc(X / 3)) * 3 +
7 sequence_adder(trunc(X / 5)) * 5 -
8 sequence_adder(trunc(X / 15)) * 15.
9
10 sequence_adder(X) ->
11 if
12 X > 0 ->
13 Value = sequence_adder(X - 1),
14 Value + X;
15 true ->
16 X
17 end.


1 ~ 1000までの数値のうち、3の倍数の数は 1000 / 3 = 333(個)
その和は(1 + 2 + ... + 333) * 3で求められる。
同じように5の倍数の時を計算して足し合わせ、3と5の最小公倍数である15の倍数の和を引けば出来上がり。
[PR]
# by w_l_s | 2010-06-18 06:07 | erlang

カテゴリ

全体
erlang
Python
c言語
作って遊ぼう
シェルスクリプト
OS
javascript
jQuery
反省
vim
未分類

お気に入りブログ

メモ帳

最新のトラックバック

ライフログ

検索

タグ

ファン

記事ランキング

ブログジャンル

画像一覧