Erlang のおはなし (2)

Erlang をぼちぼちと使ってみるお話の第2回目です。前回は Erlang のシェルを使って簡単な計算をしたり、変数をご紹介しました。今回は、Erlang で扱うデータの「型」として、「アトム」と「タプル」と「リスト」をご説明します。それから、筆者が Erlang を動かしている環境もご紹介しておきます。今回も少々長いのでご了承ください。

アトム

アトム(atom)」は、Erlang で使う定値のことです(数値は除く)。アトムの値は、英数字やアットマークあるいはアンダースコアが使えます。ちなみに大文字で始めると変数として解釈されてしまうため、シングルクォートで囲む癖をつけるとよいです(シングルクォートで囲むと、アットマークやアンダースコア以外の文字も使えます)。例えば、

1> Lang = ‘japanese’.
japanese

2> Resid = Osaka.
* 1: variable ‘Osaka’ is unbound
(Osaka を変数と解釈するので、まだ値で束縛されていないというエラーが出る)

3> Resid = ‘Osaka’.
‘Osaka’

となります。

アトムも Erlang の式として扱われ、値を持ちます。当然、アトムの値はアトム自身なので、’japanese’ というアトムは japanese という値を持つと考えてください。したがって、上記の 1> では、Lang という変数を ‘japanese’ というアトム(これは japanese という値で束縛されている)で束縛していることになります。

4> ‘kwsn@kwsn.com’.
‘kwsn@kwsn.com’ # アトムはアトム自身を値としてもっています

タプル

さて、数値や浮動小数点数あるいはアトムを一つのまとまったデータとして扱いたいときがあると思います。例えば、個人のプロフィールを、

氏名、性別、現在地、キャリア、機種

という項目でまとめて扱いたいとき、

kwsn, Male, Dojima, au, W51H

という幾つかのデータの集まりに名前をつけて扱えたら、

kwkw, Male, Dojima, au, W51H

という他の集まりと比較したときに、「氏名だけが違う」との結果から、例えば他人であると判断できたりするわけです。Erlang では、ひとまとまりの値(表記するときは変数やアトムなのですが)を取り扱う方法として、「タプル(tuple)」と「リスト(list)」という二つの方法が用意されています。

まず、上記のプロフィールからタプルを作ってみましょう。

5> Tuple = { ‘kwsn’, ‘Male’, ‘Dojima’, ‘au’, ‘W51H’ }.
{ kwsn, ‘Make’, ‘Dojima’, au, ‘W51H’ }
#(大文字で始まるアトムは変数と解釈されないように、シングルクォートで囲まれて表示されます)

これで、Tuple を5つの値から成るタプルで束縛したことになります。タプルは波括弧(ブレース)で囲んで表記するという点に注意してください。また、タプルは入れ子にもできるので、{式, {式, {式, 式}, 式, 式}, 式, 式}のようにも作れます。

リスト

リストも同じように幾つかの値で作れますが、タプルと違う特徴があります。試しにリストを作ってみましょう。

6> List = [ 'kwsn', 'Male', 'Dojima', 'au', 'W51H' ].
[ kwsn, 'Male', 'Dojima', au, 'W51H' ]

表記としては、値を鍵括弧(ブラケット)で囲むのがタプルとの違いです。ここまでは括弧の違いだけですが、タプルは要素の個数を変えられません。いちど作ったら、中身はずっと同じままです。これに対して、リストは後から要素を先頭に追加できます。

それからリストには、パイプ「|」で、先頭の要素である「ヘッダ」と先頭以降の要素からなる「テイル」に、それぞれリストとして分割できるという特徴があります。

7> [ ListHead | ListTail ] = List.
[ kwsn, 'Male', 'Dojima', au, 'W51H' ] # 見た目は同じ
8> ListHead. # 先頭
kwsn
9> ListTail. # 残り
[ 'Male', 'Dojima', au, 'W51H' ]

のように、先頭と残りに分割できます。そこで、次のような事例を見てみましょう。

10> List = [ 1, 2 ].
[1,2]
11> [ Prior | Posterior ] = List. # List を前後に分割します
[1,2]
12> Posterior.
[2]
13> [ PrePosterior | PostPosterior ] = Posterior. # 残りを更に前後に分割します
[2]
14> PostPosterior.
[]

ここで分かることは、要素が一つの場合でも作れることと、要素がない場合でも作れるということです。もちろんタプルについても、要素が一つだけでもかまいませんし、要素がなくてもかまいません。

では次に、前後に分割できるという特徴を使って、次のようにどんどん分割するとどうなるでしょうか。

15> A = [ 1, { 2, 3 } ].
[ 1, { 2, 3 } ]
16> [ B | C ] = A.
[ 1, { 2, 3 } ]
17> [ D | E ] = C.
[ { 2, 3 } ]
18> [ F | G ] = E.
** exception error: no match of right hand side value []

最後にエラーが出てしまいました。この理由は、「リストの分割」とは、或るリストを前半のリストと後半のリストに分けることだからです。16> では、A というリストを分割して、1 と [ { 2, 3 } ] という二つのリストにしています。それから、17> では、[ { 2, 3 } ] というリストを分割して、[ { 2, 3 } ] と [] という二つのリストにしているのです(実際、E. を入力すれば、E が [] であると分かります)。したがって、18> では空のリストを更に分割しようとしているため、エラーとなっているわけです(リストとして分けなければならないので、タプルである { 2, 3 } は分割できません)。

タプルとリストはどちらも、お互いの要素として使えます。つまり、タプルの要素としてリストがあってもよいですし、その逆も可能です。

19> A = [ 1, 2, { 3, 4 } ].
[ 1, 2, { 3, 4 } ]
20> B = { 1, 2, [ 3, 4 ] }.
{ 1, 2, [ 3, 4 ] }

タプルとリストから値を取り出す

次に、タプルとリストから値を一つ取り出す手順についてご紹介しておきましょう。要素を取り出せたら、要素ごとに比較したり、他の目的に値を使えますね。それでは、まずリストからお話しすると、これはさきほどの「先頭とそれ以外にリストを分割する」という操作を繰り返して値を取り出します。いま、[ 1, 2, 3 ] というリストがあって、2番目の要素(「2」)を取り出したいとすれば、

21> TestList = [ 1, 2, 3 ].
[ 1, 2, 3 ]
22> [ L1 | L2 ] = TestList.
[ 1, 2, 3 ]
23> [ L3 | L4 ] = L2.
[ 2, 3 ]
24> L3.
2

として、目的の2を取り出せます。また、取り出したい要素の数が分かっているなら、

25> [ Li1, Li2, Li3 ] = TestList.
[ 1, 2, 3 ]
26> Li2.
2

のように、元の TestList というリストと同じ数の要素でパターンを作り、変数を取り出したい値の束縛対象として指定してもよいでしょう。あるいは、元のリストの要素が分かっていれば、他の要素を全く同じ値にして、

27> [ 1, List2, 3 ] = TestList.
[ 1, 2, 3 ]
28> List2.
2

のようにしても、取り出せます。

後半の二つの方法は、Erlang のプログラミングでは「=」記号を使ったパターンマッチングと呼ばれていて、左辺と右辺のパターンを照合するという操作を行います。この照合が正しく行えたら、左辺の変数やリストなどは、右辺で該当している変数の値などによって束縛されるのです。ですから、もともとの要素が分かっていて、それを単独の変数に割り当てたいときは、

29> [ One, Two ] = [ 295, 'foo' ].
[ 295, foo ]
30> One.
295
31> Two.
foo

とすれば、One, Two それぞれにリストの値を割り当てられます。

次に、タプルの場合はどうでしょうか。タプルの場合は、ヘッダとテイルに分割できないので、このパターンマッチングを使います。

32> Tup = { ‘die’, ‘der’, ‘den’, ‘die’ }.
{ die, der, den, die }
33> { D1, D2, D3, D4 } = Tup.
{ die, der, den, die }
34> D3.
den

上記の例は、三つ目の “den” を取り出そうとした場合です。もちろん、

35> { die, der, DX, die } = Tup.
{ die, der, den, die }
36> DX.
den

として、取り出したい要素だけに変数を割り当ててもOKです。

無名変数

それでは、今回最後に、「無名変数」をご紹介しておきます。さきほどのようにパターンマッチングでリストやタプルから要素を取り出せますが、全ての要素に一時的であれ変数を割り当てるのはメモリの無駄遣いですし・・・それよりなにより面倒くさい(笑。そこで、取り出したい要素以外は「どうでもよい要素」ですから、どうでもよい要素はどうでもいいように扱えたら便利ですね。そこで、「どうでもよい変数」というのが用意されています。実際、無名変数を ”don’t care variable”(どうでもよい変数)と呼んでいる解説ドキュメントがあります。

無名変数は、アンダースコア(_)で表されます。そこで、上記の 35>, 36> を無名変数を使って書き換えると、

37> { _, _, DX, _ } = Tup.
{ die, der, den, die }
38> DX.
den

となって、一つだけ要素を取り出すのが楽になります(ちなみに、このようにパターンマッチングを使って値を取り出す方法を「ユニフィケーション」と呼びます)。リストやタプルの要素が幾つあるのかわかっていて、ユニフィケーションで値を取り出すときは、無名変数を使うと楽ですね。

以上で今回のお話は終わりです。次回は、簡単なプログラムを書いてみたいと思います。

おまけ: Erlang の環境を作ってみる

会社で使っているテスト用のマシンは、MacMini(PowerPC 1.4GHz, MacOS X 10.4)です。Erlang をインストールするには、Linux 用のソースファイルから自力でコンパイルする方法と、MacPorts というインストールソフト(GUI でインストールするために、別途 Porticus というソフトも使えます)を使う方法があります。MacPorts でインストールするよりも好きな場所へ自力でインストールする方が、僕個人の精神衛生によいため(笑、自力でインストールしています。MacPorts か自力のどちらにしても、インストールした後は、bin の中にある erl という Erlang の実行ファイルにパスが通るように(フルパスを入力しなくても erl と入力するだけで起動するように)、環境変数に Erlang のフォルダを追加しておきましょう。なお、MacMini は社内の LAN にぶら下がっているのですが、モニターは繋いでいないので、別のマシン(僕が業務用に使っている個人マシン)からアクセスする際は TeraTerm や Putty などの ssh ターミナルを使っています。ファイルを送るときは共有アクセスできるフォルダに入れています。

Erlang を入れるだけだと、Windows 用の Erlang を個人マシンに入れてシェルを使っているのとさほど使い勝手は変わりません。そのため、Yaws という名前の、Erlang(一部 C )で書かれたウェブサーバを入れています。これは Linux 用のソースをダウンロードしてから、make, make install で、

** etc files went into /usr/local/etc
** executables went into /usr/local/bin
** library files went into /usr/local/lib/yaws
** var files went into /usr/local/var
** default docroot went into /usr/local/var/yaws/www

という場所にそれぞれインストールされます。設定ファイルは /usr/local/etc/yaws.conf にありますので、<server>~</server> で指定する、ちょうど Apache の VirtualHost 設定のような項目を次のように書いています。

<server 192.168.1.***>
port = 8080
listen = 0.0.0.0
docroot = /Users/*******/htdocs
appmods = <cgi-bin, yaws_appmod_cgi>
</server>

設定をご覧頂くと、8080 ポートを使って他のマシンからアクセスすると、/htdocs がドキュメントルートになります。通常の 80 ポートは Apache 用に使っているため、LAN でのブロードキャストされた IP で 80 番ポートにアクセスすると、Apache で使っているドキュメントルートが表示されるので、使い分けができます。

コメントをどうぞ

トラックバック URL

トラックバック

このブログのフィード