文字列

文字列を数値に変換する

list_to_integer ( "123" ). % 123 list_to_integer ( "-10" ). % -10

n進数の文字列を数値に変換する

u は指定した基数で変換、# は文字列が表現している基数で変換します。

io_lib:fread( "~16u" , "100" ). % {ok,[256],[]} io_lib:fread( "~2u" , "100abc" ). % {ok,[4],[abc]} io_lib:fread( "~36u" , "100%%%" ). % {ok,[1296],"%%%"} io_lib:fread( "~#" , "16# 100 " ). % {ok,[256],[]} io_lib:fread( "~#" , "2# 100abc " ). % {ok,[4],[abc]} io_lib:fread( "~#" , "36# 100 %%%" ). % {ok,[1296],"%%%"}

部分文字列を取得する

Latin-1 の文字なら問題ないのですが、日本語は完全にバイト単位です。 次の例は UTF-8 で実行した場合です。 なにかいいライブラリがあるといいのですが。

string:substr( "abcd" , 2). % "bcd" string:substr( "abcd" , 2, 1). % "b" string:substr( "あいう" , 1, 3). % [227,129,130] string:substr( "あいう" , 2, 1). % [129]

文字列の前方一致でパターンマッチングする

文字列の前方一致、プレフィックスでパターンマッチングが可能です。

1> fun ( "Hello" ++ _ ) -> ok; ( _ ) -> ng end ( "Hello World!" ). ok 2> fun ( "Hello" ++ _ ) -> ok; ( _ ) -> ng end ( "Fello World!" ). ng 3> fun ([ $H , $e , $l , $l , $o | _ ]) -> ok; ( _ ) -> ng end ( "Hello World!" ). ok

"Hello" ++ _ は [$H, $e, $l, $l, $o | _] と同じということです。

なので、後方一致のパターンマッチングはできません。

文字列が文字列の中に含まれているか判定する

string:str/2 は頭から探し、string:rstr/2 は後から探します。 返り値は何文字目で見つかったかで、見つからない場合は 0 が返ります。 Erlang は 1 オリジンです。

1> string:str( "Hello element" , "el" ). 2 2> string:rstr( "Hello element" , "el" ). 7 3> string:rstr( "Hello element" , "xel" ). 0

数値

数値を16進数で表記

16#ff. % 255 16# A1 . % 161

数値をn進数で表記

2進数から36進数まで表記可能です。

2#101. % 5 3#211. % 22 36# zz . % 1295

数値を文字列に変換する

integer_to_list (123). % "123" integer_to_list (-123). % "-123" integer_to_list (16#10). % "16" integer_to_list (36#0z). % "35"

数値をn進数の文字列に変換する

io_lib:format( "~.16b" , [31]). % ["1f"] io_lib:format( "~.36B~.2B" , [71, 15]). % ["1Z","1111"]

リスト

リストを連結する

1> [1, 2] ++ [3, 4]. [1,2,3,4]

リストから要素を取り除く

1> [1, 2, 3, 1, 2, 3] -- [3, 2, 1, 2]. [1,3] 2> [ X || X <- [1, 2, 3, 1, 2, 3], X /= 3]. [1,2,1,2] 3> [ X || X <- [1, 2, 3, 1, 2, 3], X /= 3, X /= 1]. [2,2]

リストの要素1つずつに対して処理を行う

lists:foreach/2 はリストの要素1つずつに処理を行い ok を返します。 lists:map/2 はリストの要素1つずつに処理を行った結果をリストにして返します。

2> lists:foreach( fun ( N ) -> io:format( "~p~n" , [ N ]) end , [1,2,3]). 1 2 3 ok 3> lists:map( fun ( N ) -> N * N end , [1,2,3]). [1,4,9]

ドットリストを作る

5> [1|[2|[]]]. [1,2] 6> [1|[2|3]]. [1,2|3] 7> tl (v(6)). [2|3] 8> tl (v(7)). 3

タプル

タプルの要素数を取得する

リスト [1, 2] は [1|[2|[]]] の構文糖です。 最後が [] になっていますが、それを [] 以外にすると Lisp でいうところのドットリストになります。

erlang:size/1 でタプルの要素数を取得できます。

size ({a, b}). % 2 size ({}). % 0 size ({{}, {1, {1, 2}}}). % 2

タプルの要素を取得する

erlang:element/2 でタプルの要素を取得できます。 どの要素を取得するかは第1引数の1から始まるインデックスで指定します。 範囲外のインデックスを指定した場合は badarg ランタイムエラーが発生します。

element (1, {a, b, c}). % a element (2, {a, b, c}). % b

タプルに要素を追加する

4> erlang:append_element({a, b}, c). {a,b,c}

タプルに値を設定する

5> setelement (2, {a,b,c}, two). {a,two,c}

ディクショナリ

dict モジュール

Erlang には組み込みではディクショナリやハッシュといったデータ型はありませんが、 STDLIB に dict というモジュールがあります。 Erlang は単一代入なので、値を入れるたびに変数をかえてやる必要があります。

D1 = dict:new(). % 作成 D2 = D1 :store(key1, value1). % キーと値のペアを入れる D3 = D2 :store({key2, key2}, [value2, value2]). % キーも値も任意の方を使える D4 = D3 :append({key2, key2}, value3). % append で値を追加 D4 :find(key1). % {ok,value1} D4 :find(xxxx). % error が返る D4 :fetch({key2, key2}). % [value2,value2,value3] D4 :fetch({key2, xxxx}). % ランタイムエラー（function_clause）が発生 D4 :to_list(). % リストに変換（[{key1,value1},{{key2,key2},[value2,value2,value3]}]）

バイナリ

文字列リテラルをバイナリで表記する

任意の文字列リテラルをバイナリとして表記できます。

1> << "Hello Bin." >>. << "Hello Bin." >> 2> << "Hello あいう." >>. << "Hello \343\201\202\343\201\204\343\201\206." >>. <<72,101,108,108,111,32,227,129,130,227,129,132,227,129,134,46>>

バイナリのパターンマッチング

リストやタプルと同様にバイナリでもパターンマッチングが可能です。

IPTuple = {192, 168, 1, 1}, { A , B , C , D } = IPTuple , << IPDecimal :32>> = << A :8, B :8, C :8, D :8>>.

関数

"fun モジュール名:関数名/引数の数" で関数を参照

"fun モジュール名:関数名/引数の数" で関数を参照できます。

1> fun erlang:tuple_to_list/1. # Fun <erlang.tuple_to_list.1> 2> fun erlang:tuple_to_list/1({a, b}). [a,b] 3> lists:map( fun erlang:tuple_to_list/1, [{a, b}, {a}, {}]). [[a,b],[a],[]]

日付と時刻

日付と時刻の取得する

日付と時刻の取得に関しては次のような BIF があります。 また、この他に calendar モジュールがあります。

1> date (). % ローカル日付 {年, 月, 日} {2007,5,28} 2> erlang:localtime(). % ローカル日時 {{年, 月, 日}, {時, 分, 秒}} {{2007,5,28},{6,46,28}} 3> erlang:localtime_to_universaltime(v(-1)). % ローカル日時を UCS に変換 {{2007,5,27},{21,46,28}} 4> now(). % {MegaSecs, Secs, MicroSecs} 1970/1/1からの経過 {1180,302477,518328} 5> time (). % ローカル時刻 {時, 分, 秒} {6,50,30} 6> erlang:universaltime(). % UTC 日時 {{年, 月, 日}, {時, 分, 秒}} {{2007,5,27},{21,51,6}} 7> erlang:universaltime_to_localtime(v(-1)). % UTC 日時をローカルに変換 {{2007,5,28},{6,51,6}}

曜日を取得する

calendar:day_of_the_week の返り値は 1 が月曜、7 が日曜日です。

2> calendar:day_of_the_week({2007,6,1}). 5 3> calendar:day_of_the_week(2007,6,3). 7

エラー処理を行う

Erlang にも try catch があります。 catch は 例外クラス:例外パターン となり、 例外クラスには次の3つがあります。

error ランタイムエラー。erlang:error/1,2 または erlang:fault/1,2によって発生する。 exit exit/1 が呼ばれた場合に発生する。 throw throw/1 が呼ばれた場合に発生する。

8> try (1 + a) catch _ : Reason -> io:format( "1 + a is failed, reason: ~p~n" , [ Reason ]) end . 1 + a is failed, reason: badarith ok 9> try (1 + a) catch error: Reason -> io:format( "1 + a is failed, reason: ~p~n" , [ Reason ]) end . 1 + a is failed, reason: badarith ok 10> try (1 + a) catch throw: Reason -> io:format( "1 + a is failed, reason: ~p~n" , [ Reason ]) end . = ERROR REPORT ==== 30- Apr -2007::07:49:40 === Error in process <0.222.0> on node 'emacs@localhost' with exit value: {badarith,[{erl_eval,expr,3}]} ** exited: {badarith,[{erl_eval,expr,3}]} **

ファイル

ファイルの内容を一括でバイナリとして取得します。

get_file_contents ( File ) -> {ok, Binary } = file:read_file( File ), Binary .

ファイルの内容を一括で文字列として取得します。

get_file_contents ( File ) -> {ok, Binary } = file:read_file( File ), binary_to_list ( Binary ).

一行ずつ読み込む

print_with_line ( File ) -> {ok, IoDevice } = file:open( File , read), print_with_line( IoDevice , 1), file:close( IoDevice ). print_with_line ( IoDevice , LineNumber ) -> case io:get_line( IoDevice , "" ) of eof -> ok; Line -> io:format( "~b ~s" , [ LineNumber , Line ]), print_with_line( IoDevice , LineNumber + 1) end .

ファイル情報を取得する

file:read_file_info(FileName) で file_info レコードを取得できます。

% file_info レコードをインクルード -include_lib ( "kernel/include/file.hrl" ). % ファイル情報取得 {ok, FileInfo } = file:read_file_info( FileName ). FileInfo # file_info .size. % ファイルサイズ FileInfo # file_info .type. % device | directory | regular | other FileInfo # file_info .access. % read | write | read_write | none FileInfo # file_info .atime. % 最終アクセス時間 FileInfo # file_info .mtime. % 最終更新時間 FileInfo # file_info .ctime. % 作成日時 FileInfo # file_info .mode. % 8# 00400 のようなファイルパーミッション FileInfo # file_info .links. % このファイルへのリンク数。 FileInfo # file_info .major_device. % 0: Aドライブ, 1: Bドライブ FileInfo # file_info .minor_device. % Unix のキャラクタデバイスでのみ有効 FileInfo # file_info .inode. % inode 番号 FileInfo # file_info .uid. % 所有者のユーザID FileInfo # file_info .gid. % 所有者のグループID

リンクかどうか判定する

あるファイルがリンクかどうか判定するには file:read_link(Name) または file:read_link_info(Name) を使用します。

1> file:read_link( "/cdrom" ). {ok, "media/cdrom" } 2> file:read_link( "/usr" ). {error,einval} 3> file:read_link_info( "/cdrom" ). {ok,{file_info,11, symlink, read, {{2007,4,6},{5,38,13}}, {{2005,1,9},{21,20,27}}, {{2005,1,9},{21,20,27}}, 41471, 1, 2051, 0, 12, 0, 0}} 4> file:read_link_info( "/usr" ). {ok,{file_info,4096, directory, read, {{2007,4,5},{6,4,23}}, {{2005,11,9},{21,16,44}}, {{2005,11,9},{21,16,44}}, 16877, 14, 2051, 0, 12500993, 0, 0}}

パスがファイルかディレクトリか判定する

filelib:is_dir でディレクトリなら true。 filelib:is_file でディレクトリまたはファイルなら true。 filelib:is_regular でファイルなら true。

4> filelib:is_dir( "/etc/yaws" ). true 5> filelib:is_dir( "/etc/yaws/yaws.conf" ). false 6> filelib:is_file( "/etc/yaws" ). true 7> filelib:is_file( "/etc/yaws/yaws.conf" ). true 8> filelib:is_regular( "/etc/yaws" ). false 9> filelib:is_regular( "/etc/yaws/yaws.conf" ). true

ファイルをコピーする

1> file:list_dir( "/tmp/bano" ). {ok,[ "a.txt" ]} 2> file:copy( "/tmp/bano/a.txt" , "/tmp/bano/b.txt" ). {ok,1} 3> file:list_dir( "/tmp/bano" ). {ok,[ "a.txt" , "b.txt" ]}

ファイルのリネーム

1> file:list_dir( "/tmp/bino" ). {ok,[ "a.txt" ]} 2> file:rename( "/tmp/bino/a.txt" , "/tmp/bino/b.txt" ). ok 3> file:list_dir( "/tmp/bino" ). {ok,[ "b.txt" ]}

ファイルの改行コードを変更する

/tmp/a.txt の改行コード

を、\r

に変更して /tmp/aa.txt に出力します。

{ok, Bin } = file:read_file( "/tmp/a.txt" ), Str = binary_to_list ( Bin ), {ok, NewStr , RepCount } = regexp:gsub( Str , "

" , "\r

" ), file:write_file( "/tmp/aa.txt" , list_to_binary ( NewStr )). %% 一行で書いてみると、こうなります。 %% 変数を使わない分、element でタプルから目的の値を取り出す必要があります。 file:write_file( "/tmp/aa.txt" , list_to_binary ( element (2, regexp:gsub( binary_to_list ( element (2, file:read_file( "/tmp/a.txt" ))), "

" , "\r

" )))).

現在のディレクトリ（current working directory）を取得・設定する

file:get_cwd/0, file:set_cwd/1 で取得・設定します。 Windows の場合は file:get_cwd("C:") のようにドライブごとのワーキングディレクトリを取得することもできます。

7> file:get_cwd(). {ok, "/home/ancient/public_html/erlang" } 8> file:set_cwd( "/tmp" ). ok 9> file:get_cwd(). {ok, "/tmp" }

絶対パスを取得する

filename:absname/1 は現在のディレクトリを基準に絶対パスを返します。 filename:absname/2 は基準となるディレクトリを第2引数に指定します。

1> file:get_cwd(). {ok, "/tmp" } 2> filename:absname( "a.txt" ). "/tmp/a.txt" 3> filename:absname( "a.txt" , "/var" ). "/var/a.txt"

標準入出力

io モジュールでは、IoDevice 引数を省略、または standard_io を指定することで標準入出力を使用できます。

1> io:get_line( "? " ). "? " hello. "hello.

" 2> io:get_line(standard_io, '? ' ). ? worlde . "worlde.

" 3> io:fwrite( "hello

" ). hello ok 4> io:fwrite(standard_io, "hello

" , []). hello ok

file モジュールでは erlang:group_leader/0 の返り値を IoDevice に指定することで標準入出力を使用できます。

全ての IO はグループリーダー経由で行われるため、標準入出力 = グループリーダーとなるのです。

#!/usr/bin/env escript main ( _ ) -> loop(file:read( group_leader (), 1)). loop (eof) -> ok; loop ({ok, Data }) -> file:write( group_leader (), Data ), loop(file:read( group_leader (), 1)).

実行例。C-d で終了します。

~ % cd ~/public_html/erlang/sample ~/public_html/erlang/sample % chmod +x standard_io.erl ~/public_html/erlang/sample % ./standard_io.erl Hello ? Hello ? あいう あいう

filelib:fold_files/5 という関数があります。 1番目の引数がディレクトリ。 2番目が処理対象か否かを判定するための正規表現。 3番目がサブディレクトリも再帰的に処理するか否か。 4番目が各ファイルに適用する関数。 5番目が初期値。 4番目の引数である関数は引数を2つとります。最初の引数は処理対象のファイル。次は最初は5番目に指定した引数で、それ以降はこの関数の返り値です。

サフィックスが erl のファイルをリストにして取得するには次のように書きます。

>1 filelib:fold_files( "/home/ancient/letter/erlang" , "\\.erl$", true, fun(X, Acc) -> [X|Acc] end, []). [" /home/ancient/letter/erlang/a/fib.erl ", " /home/ancient/letter/erlang/chat/ _darcs /pristine/chat.erl ", " /home/ancient/letter/erlang/chat/ _darcs /pristine/test_chat.erl ", " /home/ancient/letter/erlang/chat/chat.erl ", [...]|...]

ワイルドカード指定でファイルリストを取得する

filelib:wildcard/1 を使います。?（任意の1文字）、*（任意の文字列）、{Item,..}（いずれかに一致）をワイルドカードとして使用できます。

1> filelib:wildcard( "/us?/*/lib{GL,Xa*}.so" ). [ "/usr/lib/libGL.so" , "/usr/lib/libXau.so" , "/usr/lib/libXaw.so" , "/usr/lib/libXaw3d.so" , "/usr/lib/libXaw7.so" ]

Web

Web ページを取得する

まずはじめに inets:start() を呼び出しておく必要があります。

1> inets:start(). 2> {ok, { Status , Header , Body }} = http:request( "http://www.google.co.jp" ).

HTML エスケープを行う

Yaws の API があります。

1> yaws_api:htmlize( "<br>&" ). "<br>&"

ネットワーク

サービス名からポート番号を取得する

inet:getservbyname/2 を使用します。サービス名はアトムで指定する必要があります。文字列で指定した場合はエラーになってしまいます。

25> inet:getservbyname(telnet, tcp). {ok,23} 26> inet:getservbyname(echo, udp). {ok,7}

FTP でファイルをアップロードする

make から手軽に呼べるので escript で作成します。 <div class="file-name">upload.es</div>

#!/usr/bin/env escript main ( _ ) -> inets:start(), {ok, Pid } = inets:start(ftpc, [{host, "ftp.example.com" }]), ftp:user( Pid , "user" , "password" ), ftp:cd( Pid , "/public_html/erlang" ), lists:foreach( fun ( File ) -> ftp:send( Pid , File ) end , [ "cookbook.html" , "cookbook.css" , "index.html" ]), inets:stop(ftpc, Pid ).

コンカレントプログラミング

プロセスを生成する

spawn でプロセスを生成します。返り値は pid() です。

spawn ( Fun ). spawn ( Node , Fun ). spawn ( Module , Function , Args ). spawn ( Node , Module , Function , Args ).

メッセージを送信する

Pid（プロセスID）または登録名に ! を使用してメッセージを送信します。

Pid ! Message . registered_name ! Message .

メッセージを受信する

receive でメッセージを受信します。

receive Message -> Message end .

タイムアウトを指定してメッセージを受信する

receive で after を使います。タイムアウトはミリ秒で指定します。 タイムアウトに0を指定した場合は、メッセージがなければ即時タイムアウトします。

receive Message -> Message after 1000 -> no_message end .

プロセスを登録する

プロセスを登録することにより、Pid を知らなくても、登録時の名前を指定し てそのプロセスにメッセージを送信することができます。

register (foo, spawn ( fun () -> receive { Client , Message } -> Client ! Message end end )). foo ! { self (), "Hello foo." }. receive M -> M end .

プロセスをリンクする

あるプロセスでエラーが発生した場合、 リンクされている全てのプロセスが終了します。

リンクされていないプロセスは無関係に処理を継続します。

spawn_link でリンクしたプロセスを開始できます。

次の例では start_without_link はリンクをしていないため、 エラーを発生させても spawn したプロセスは処理を継続します。 start_with_link はリンクをしているため、 エラーを発生させると spawn_link したプロセスも停止します。

-module (sample.link). -compile (export_all). start_without_link () -> %% リンクしない spawn (? MODULE , loop, [10]), .timer:sleep(3000), .io:format( "Let's stop!~n" ), .erlang:error(stop). start_with_link () -> %% リンクする spawn_link (? MODULE , loop, [10]), .timer:sleep(3000), .io:format( "Let's stop!~n" ), .erlang:error(stop). loop (0) -> ok; loop ( N ) -> .io:format( "hello~n" ), .timer:sleep(1000), loop( N - 1).

プロセスをグルーピングする

pg2 モジュールでプロセスをグルーピング化できます。 プロセスが終了した場合、そのプロセスは自動的にグループから削除されます。

-module (process_group). -compile (export_all). start () -> pg2:create(group), % グループ作成 erlang:display(pg2:which_groups()), % グループリストを取得 Pid1 = spawn ( fun p/0), pg2:join(group, Pid1 ), % グループ参加 Pid2 = spawn ( fun p/0), pg2:join(group, Pid2 ), % グループに参加 Pid3 = spawn ( fun p/0), pg2:join(group, Pid3 ), % グループに参加 erlang:display(pg2:get_members(group)), % グループメンバを取得 pg2:leave(group, Pid1 ), % グループから抜ける erlang:display(pg2:get_members(group)), % グループメンバを取得 Pid2 ! error, % エラー終了 timer:sleep(100), erlang:display(pg2:get_members(group)), % グループメンバを取得 Pid3 ! finish, % 普通に終了 timer:sleep(100), erlang:display(pg2:get_members(group)), % グループメンバを取得 pg2:delete(group), % グループ削除 erlang:display(pg2:which_groups()). % グループリストを取得 p () -> receive error -> 1 + a; finish -> ok end .

実行結果

6> process_group:start(). [group] [<0.65.0>,<0.64.0>,<0.63.0>] [<0.65.0>,<0.64.0>] = ERROR REPORT ==== 5- May -2007::16:58:31 === Error in process <0.64.0> on node 'emacs@localhost' with exit value: {badarith,[{process_group,p,0}]} [<0.65.0>] [] [] true

ローカルノードのプロセスだけ返す pg2:get_local_members/1 や、ローカルノードのプロセスがあればそれ、なければリモートノードのプロセスを返す pg2:get_closest_pid/1 等もあります。

プロセスを監視する

Erlang では通信相手のプロセスが停止していても ! と receive は失敗しません（登録名で receive を行う場合で、 登録名のプロセスが存在しない場合はエラーになります）。 通信相手のプロセスが停止していないかどうか検知するには erlang:monitor/2 を使います。

1番目の引数は process 固定です。 2番目の引数はプロセスID、登録名、{登録名, ノード名} のいずれかです。 返り値はリファレンスです。 2番目の引数で指定したプロセスが停止していた場合、次のメッセージを受信します。

{ 'DOWN' , monitorの返り値と同一のリファレンス, process, monitorの2番目の引数, プロセスダウン理由}

2番目の要素に monitor の返り値と同一のリファレンスが設定されるのど、 通常それを receive のパターンマッチングに使用します。

-module (monitor_sample). -compile (export_all). start () -> Pid = spawn ( fun loop/0), Ref = erlang:monitor(process, Pid ), % 監視を開始する RevFun = fun ( Ref ) -> receive { 'DOWN' , Ref ,process, Pid , Reason } -> io:format( "process is down, reason: ~p.~n" , [ Reason ]); Any -> io:format( "~p~n" , [ Any ]) after 1000 -> io:format( "timeout.~n" ) end end , Pid ! { self (), "Noko" }, RevFun ( Ref ), % ここでは Down していない Pid ! { self (), "Quit" }, % Pid が終了 RevFun ( Ref ), % ここでは Down している Pid ! { self (), "Hitsuji" }, RevFun ( Ref ), % ここではタイムアウト Pid ! { self (), "Hitsuji" }, Ref2 = erlang:monitor(process, Pid ), % 再度監視を開始する RevFun ( Ref2 ). % ここでも Down している loop () -> % サーバプロセスループ receive { _Pid , "Quit" } -> ok; { Pid , Any } -> Pid ! "Hi, " ++ Any , loop() end .

実行例

3> monitor_sample:start(). "Hi, Noko" process is down, reason: normal. timeout. process is down, reason: noproc. ok

ポートスキャン

私の環境では50プロセスぐらいがちょうどいいみたいです。 1プロセスだと約19秒。 2プロセスだと約13秒。 50プロセスだと約8秒。 1000プロセスだと約10秒。 10000プロセスだと約60秒。

-module (port_scan). -compile (export_all). m ( ProcCount ) -> timer:tc(? MODULE , main, [ ProcCount ]). main ( ProcCount ) -> lists:foreach( fun ( _ ) -> spawn (? MODULE , client, [ self ()]) end , lists:seq(1, ProcCount )), init(). init () -> loop(lists:seq(1, 16#ffff)). loop ([ H | T ]) -> receive {get, C } -> C ! H , loop( T ) end ; loop ([]) -> io:format( "finish!~n" ). client ( Server ) -> Server ! {get, self ()}, receive Port -> scan( Port ), client( Server ) after 1000 -> ok end . scan ( Port ) -> case gen_tcp:connect( "localhost" , Port , []) of {ok, Socket } -> gen_tcp:close( Socket ), io:format( "~b~n" , [ Port ]); _ -> ok end .

分散プログラミング

分散ノードを開始する

分散ノードを開始するには -name か -sname オプションでノード名を指定して erl を実行します。 または、net_kernel:start を使用します。

ancient@vmubu:~ % erl -name ubu@172.22.10.22 Erlang ( BEAM ) emulator version 5.5.4 [source] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5 .5.4 (abort with ^ G ) (ubu@172.22.10.22)1>

1> net_kernel:start([ 'master@172.22.10.15' , longnames]). {ok,<0.32.0>} (master@172.22.10.15)2>

分散ノードを停止する

net_kernel:stop().

分散ノードへの ping

net_adm:ping で分散ノードの接続確認ができます。 接続できたときは pong、接続失敗時は pang が返ってきます。

net_adm:ping( 'ubu@172.22.10.22' ).

RPC

rpc:call で分散ノードの任意の関数を実行することができます。 引数は ノード, モジュール, 関数, 引数 です。

(master@172.22.10.15)78> rpc:call( 'ubu@172.22.10.22' , file, list_dir, [ "/usr" ]). {ok,[ "src" , "share" , "sbin" , "local" , "lib" , "include" , "games" , "bin" , "X11R6" ]}

リモートノードへのモジュールロード

リモートノードへローカルでロードしたモジュールをロードさせることができます。 ローカルでモジュールのオブジェクトコードを取得し、それを RPC でリモートノードにロードします。

(cho@ Macintosh )37> { Module , Binary , Filename } = code:get_object_code(todo). {todo,<<70,79,82,49,0,0,16,144,66,69,65,77,65,116,111,109,0,0,3,102,0,0,0,82,4, 116,111,...>>, "/Users/ancient/letter/tex/erlang/sample/mnesia/todo.beam" } (cho@ Macintosh )38> rpc:call( 'babi@localhost' , code, load_binary, [ Module , Filename , Binary ]). {module,todo}

Yaws

動的なページを作成する

ファイル名の拡張子を yaws にします。 <erl>タグの間に out(Arg) 関数を定義します。 f とい関数を io_lib:format のかわり使用できます。

<html> <head> <meta http-equiv= "Content-Type" content= "text/html; charset=UTF-8" > <title> 最初のページ </title> </head> <body> <h1> 最初のページ </h1> <erl> out(Arg) -> {{Y, M, D}, {H, Mi, S}} = erlang:localtime(), {html, f("今は~b年~b月~b日~b時~b分~b秒です。", [Y, M, D, H, Mi, S])}. </erl> </body> </html>

セッションデータを使う

yaws_api:find_cookie_val/2 でクッキーセッションを取得し、 yaws_api:cookieval_to_opaque/1 でセッションに保存したデータを取得します。 セッションデータを書きかえるには yaws_api:replace_cookie_session/2 を使用します。

クッキーセッションの新規作成は yaws_api:new_cookie_session/1 で引数はセッションデータです。 クッキーセッションを新規作成した場合は、yaws_api:setcookie/3 でクッキーを設定する HTTP ヘッダの作成し、それをレスポンスの html と一緒にリストにして返す必要があります。

-define ( myopaque , {aaa, bbb}). out ( Arg ) -> { Myopaque , NewHeader } = get_session_data( Arg ), case NewHeader of undefined -> {html, "contents" }; _ -> [{html, "contents" }, NewHeader ] end . %% 受信プロセスIDを取得します。 get_session_data ( Arg ) -> H = Arg # arg .headers, C = H # headers .cookie, case yaws_api:find_cookie_val( "COOKIE_KEY" , C ) of [] -> % クッキーセッションなし Myopaque = # myopaque {aaa=init, bbb=init}, Cookie = yaws_api:new_cookie_session( Myopaque ), NewHeader = yaws_api:setcookie( "COOKIE_KEY" , Cookie , "/" ), { Myopaque , NewHeader }; Cookie -> % クッキーセッションあり case yaws_api:cookieval_to_opaque( Cookie ) of {ok, Myopaque } -> { Myopaque , undefined} end end .

外界とのつながり

os:cmd/1 を使います。引数は文字列か、アトムです。

1> io:format( "~s" , [os:cmd(pwd)]). /home/abc/public_html/erlang ok 2> io:format( "~s" , [os:cmd( "dir" )]). ドライブ C のボリューム ラベルは IBM_PRELOAD です ボリューム シリアル番号は 68F0-7B18 です c:\home\abc\public_html\erlang のディレクトリ 2007/05/18 17:25 < DIR > . 2007/05/18 17:25 < DIR > .. 2007/05/18 17:34 33,564 cookbook.html 2007/05/18 17:17 33,281 cookbook.html~ 2007/05/18 17:08 441 erlang.css 2007/05/18 17:08 130 index.html 2007/05/18 17:08 34 Makefile 2007/05/18 17:08 < DIR > sample 2007/05/18 17:08 309 upload.es 2007/05/18 17:08 < DIR > _darcs 6 個のファイル 67,759 バイト 4 個のディレクトリ 4,458,496,000 バイトの空き領域 ok

環境変数を取得する

引数がない場合全ての環境変数を返します。

6> os:getenv( "SHELL" ). "zsh" 7> os:getenv(). [ "windir=C:\\WINDOWS" , "VS80COMNTOOLS=C:\\Program Files\\Microsoft Visual Studio 8\\Common7\\Tools\\" , [...]|...]

環境変数を設定する

8> os:getenv( "ENVVV" ). false 9> os:putenv( "ENVVV" , "HELLO" ). true 10> os:getenv( "ENVVV" ). "HELLO"

メタ

Beam ファイルのパスを取得する

code:which/1 にモジュール（atom）を渡せばそのモジュールの Beam ファイルのパスを取得できます。ただし、プレロードされているモジュールは preloaded、カバレジコンパイルされているモジュールは cover_compiled が返されます。存在しないモジュールの場合は non_existing が返されます。

1> code:which(io). "/usr/lib/erlang/lib/stdlib-1.14.4/ebin/io.beam" 2> code:which(erlang). preloaded 3> code:which(no_such_module). non_existing 4> c( "/tmp/a" , [{outdir, "/tmp/" }]). {ok,a} 5> code:which(a). "/tmp/a.beam" 6> cover:compile( "/tmp/a.erl" ). {ok,a} 7> code:which(a). cover_compiled

Beam ファイルからソースコードを取得（再構築）する

1> { _Module , Beam , _File } = code:get_object_code(base64), 1> {ok,{ _ ,[{abstract_code,{ _ , AC }}]}} = beam_lib:chunks( Beam , [abstract_code]), 1> io:fwrite( "~s~n" , [erl_prettypr:format(erl_syntax:form_list( AC ))]). -file ( "./base64.erl" , 1). -module (base64). -export ([encode/1, decode/1, mime_decode/1, encode_to_string/1, decode_to_string/1, mime_decode_to_string/1]). encode_to_string ( Bin ) when is_binary ( Bin ) -> encode_to_string( binary_to_list ( Bin )); encode_to_string ( List ) when is_list ( List ) -> encode_l( List ). （以下略）

文字列をコンパイルする

文字列で作ったモジュール（ソース）をコンパイルする方法です。

erl_scan:tokens で1文ずつスキャン。

erl_parse:parse_form で1文ずつパース。

compile:forms でモジュール単位でコンパイル。

code:load_binary でコンパイルしたものをロード。

改行コードは

にしておかないとだめです。

-module (compile_string). -export ([compile/1, test/0]). compile ( SrcString ) -> Forms = mk_forms(lists:flatten( SrcString )), %% コンパイルする {ok, M , B } = compile:forms( Forms ), %% コンパイルしたものをロードする {module, M } = code:load_binary( M , atom_to_list ( M ), B ), M . mk_forms ( String ) -> mk_forms( String , []). mk_forms ([], Acc ) -> lists:reverse( Acc ); mk_forms ( S , Acc ) -> %% 1文（ピリオドまで）をスキャン {done, {ok, Tokens , _Line }, Rest } = erl_scan:tokens([], S , 0), %% パースする {ok, Parsed } = erl_parse:parse_form( Tokens ), mk_forms( Rest , [ Parsed | Acc ]). %% テスト関数 test () -> %% ソース S = "-module(fib). -export([fib/1]). fib (1) -> 1; fib (2) -> 1; fib (N) -> fib(N-1) + fib(N-2). " , %% 文字列をコンパイル compile( S ), %% コンパイルしたモジュールの関数を呼び出す fib:fib(10).

関数情報を取得する

erlang:fun_info/1 で関数の情報を取得できます。

1> erlang:fun_info( fun erlang:display/1). [{module,erlang},{name,display},{arity,1},{env,[]},{type,external}] 2> fun () -> X = 1, Y = fun ( Y ) -> X + Y end , erlang:fun_info( Y ) end (). [{pid,<0.59.0>}, {module,erl_eval}, {new_index,2}, {new_uniq,<<146,128,248,136,99,188,48,7,216,172,210,56,139,244,145,225>>}, {index,6}, {uniq,72228031}, {name, '-expr/5-fun-2-' }, {arity,1}, {env,[[{ 'X' ,1}], none, {eval,# Fun <shell.21.66499203>}, [{clause,1,[{var,1, 'Y' }],[],[{op,1, '+' ,{var,1, 'X' },{var,1, 'Y' }}]}]]}, {type,local}]

取得できる情報は次のとおりです。

pid 関数を作成したプロセスID。ローカル関数のみ。 module モジュール名。 new_index モジュールの関数テーブルのインデックス。ローカル関数のみ。 new_uniq 関数のユニーク値（バイナリ）。ローカル関数のみ。 index モジュールの関数テーブルのインデックス。ローカル関数のみ。 uniq 関数のユニーク値（整数）。ローカル関数のみ。 name 関数名。 arity 引数の個数 env 関数の環境。ローカル関数のみ。 type ローカル関数の場合は local、外部関数の場合は external。

DB

ODBC 接続

多くの場合オプションに {scrollable_cursors, off} を指定しないとエラーになります。 connect する都度 odbcserver.exe が起動され disconnect しないとそのプロセスが残ります。

{ok, Ora } = odbc:connect( "DSN=bino;UID=hr;PWD=password;" , [{scrollable_cursors, off}]), {selected, _ , [{ Aiu }]} = odbc:sql_query( Ora , "select 'あいう' from dual" ), io:format( "~s~n" , [ Aiu ]), odbc:disconnect( Ora ).

色々

関数の実行時間を計測する

シェルの time コマンドのように実行時間を計測するには timer:tc を使用します。 返り値は {実行時間（マイクロ秒）, 関数の返り値} です。

5> timer:tc(fib, fib, [1]). {3,1} 6> timer:tc(fib, fib, [40]). {8957808,102334155}