はじめに
最近関数型プログラミングが流行していることと色々な言語に触れてみたいことから「7つの言語 7つの世界 -オーム社-」を読んでみることにした。本書では最初から読み進めることを推奨しているが、並列分散処理に興味が有るのと名前からErlangを1つ目に選択した。
環境構築
brewでインストールした。
brew install erlang
これだけで環境構築は終わり。
Erlangの特徴
- Prologからの派生
- 開発者のJoe Armstrong博士はPrologで電話交換制御用ソフトウェアを開発していた。それに並列プロセス機能、エラー処理の仕組みなどを追加してErlangが完成した。
- 本書でも第3章でPrologは取り扱っているので今後触れてみたい。
- 軽量プロセス
- 前提として、JavaやCなどのスレッド方式での並行処理とは異なるアプローチをとっている。この考え方によって、共有リソースとリソースボトルネックの泥沼にハマるのを避けている。
- 分散メッセージパッシングについて調べていたらいい感じのスライドが見つかったので、リンクを貼らせて頂きます。(http://www.slideshare.net/sleepy_yoshi/erlang-1)
- 信頼性が高い(らしい)
- これには驚いたのだが、Erlangのモットーは「クラッシュさせろ」らしい。プロセスが死んだらそれに対して何かするより新しいプロセスを開始した方が楽じゃんってことらしい。
- ホットスワップというコードを停止せずにアプリケーションの一部を入れ替えるという機能で保守も簡単らしい。
1日目本編
これまで、理論や特徴について書いたがここからは実際にコードを書いていく。最後に、Erlangの4つのルールを紹介しておく。
- プログラムはすべて関数で作成する。オブジェクトはどこにも登場しない。
- 関数は通常、与えられた入力が同じであれば、同じ値を返す。
- これらの関数は通常、副作用を持たない(プログラムの状態を変更しない)。
- すべての変数への代入は1回だけに限られる。
コメント、変数、式
1> % This is a comment
2> 2 + 2.
4
3> 2 + 2.0.
4.0
4> "string"
"string"
5> [1, 2, 3].
[1, 2, 3]
6> 4 + "string".
エラー
コードを見てもらえばわかることだが、基本的な特徴は以下のようなものがある。
- コメント行は%で始める。
- 各分はピリオドで終了する。
- 基本的な型変換は自動で行われる。
- そのため、型違いでのエラーがおこる。
次に、変数に代入を行う。Erlangでは変数は大文字で始まる。
1> variable = 4.
エラー
2> Var = 1.
1
3> Var = 2.
エラー
4> Var.
1
このように、変数は大文字で始まり変更不可である。
アトム、リスト、タプル
関数型言語で最も基本的なデータ要素であるシンボルのことをErlangではアトムと呼ぶ。小文字で始める。
1> red.
red
2> Pill = blue.
blue
3> Pill.
blue
上記の例ではredとblueがアトムである。自分もアトムに関してはよくわかっていないが、実世界のものを記号化して扱うための任意の名前ということらしい。より堅牢なデータ構造体に結びつけると面白いらしく、今後使っていくらしい。
リストとタプルは特殊なところはあまりない。
1> List = [1, 2, 3].
[1, 2, 3]
2> [1, 2, "three"].
[1, 2, "three"]
3> Origin = {0, 0}.
{0, 0}
4> {comic_strip, {name, "Calvin and Hobbers"}, {character, "Spaceman Spiff"}}.
{comic_strip, {name, "Calvin and Hobbers"}, {character, "Spaceman Spiff"}}
最後の例では、ハッシュキーにアトムを、ハッシュ値に文字列を使用している。
パターンマッチング
まず、ある人物をタプルで定義する。
1> Person = {person, {name, "Agent Smith"}, {profession, "Killing programs"}}.
{person, {name, "Agent Smith"}, {profession, "Killing programs"}}
この時、名前をNameに、職業をProfessionに代入したいとする。そのようなときは次のようなパターンマッチングによって値を取り出すことが可能である。
2> {person, {name, Name}, {profession, Profession}} = Person.
{person, {name, "Agent Smith"}, {profession, "Killing programs"}}
3> Name.
"Agent Smith"
4> Profession.
"Killing program"
関数
最後に関数について説明する。例として階乗計算を行う。
-module(yet_again).
-export([another_factorial/1]).
another_factorial(0) -> 1;
another_factorial(N) -> N * another_factorial(N-1).
- 1行目はモジュール名の定義。
- 2行目はモジュールの外で使う関数の定義。/1は1つの引数を取るということである。
- 3, 4行目は関数自体の定義。
である。このようにプログラムが完成したらコンパイル、実行を行う。
1> c(yet_again).
{ok, yet_again}
2> yet_again:another_factorial(3).
6
今回は引数に3を指定したが、2000を指定した時にも一瞬で計算結果が表示されたので試してみて欲しい。
1日目で学んだこと
1日目では、Erlangの特徴・基本的文法などを学んだ。再帰的に関数を書くのでまだまだ慣れないところもあるが明日からもがんばりたい。
セルフスタディ1日目
- 再帰を用いて文字列を構成する単語の数を返す関数を書け。
-module(word_count).
-export([word_count/1]).
is_space(32) -> 1;
is_space(Anything) -> 0.
word_count("") -> 1;
word_count([Head|Tail]) -> is_space(Head) + word_count(Tail).
これを実行すると、
1> c(word_count).
word_count.erl:5: Warning: variable 'Anything' is unused
{ok,word_count}
2> word_count:word_count("Erlang").
1
3> word_count:word_count("Hello Erlang").
2
英語の文書だとスペース区切りなのでこれでいいのですが、日本語文書だと形態素解析してとかになるのかな?形態素解析したら単語の数なんて数える必要もないか。
- 再帰を用いて10まで数える関数を書け。
-module(count).
-export([count/1]).
count(1) -> 1;
count(N) -> count(N - 1) + 1.
実行する。
1> c(count).
{ok,count}
2> count:count(10).
10
- {error, Message}またはsuccessという形式の入力が与えられた時、マッチングを用いて、"success"または"error:message"のどちらかを出力する関数を書け。
-module(judge).
-export([judge/1]).
judge(success) -> "success";
judge({error, Message}) -> "error: " ++ Message.
実行すると、
1> c(judge).
{ok,judge}
2> judge:judge(success).
"success"
3> judge:judge({error, "エラーです"}).
"error: エラーです"