「7つの言語 7つの世界」第6章の学習のために、Erlang(アーラン)言語をインストールします。
7つの言語 7つの世界
- Ruby
- Io
- Prolog
- Scala
- Erlang
- Cloijure
- Haskell
書籍では、バージョン R13B02
をインストールして、Eshell 5.4.13
を使っているよ
Erlang公式サイト
最新は、Erlang/OTP 23.0
Windows 10
では、Erlang/OTP 23
。macOS 10.15.4
では、Erlang/OTP 23
。Ubuntu 18.04
では、Erlang/OTP 20
。
書籍内のコードはひととおり動いたよ
Windows 10
https://www.erlang.org/downloads を表示して、Windows用インストーラのリンクをクリックしてダウンロードします。
インストーラを起動します。次のダイアログが表示されたら、「詳細情報」をクリックしてください。
「実行」ボタンをクリックしてください。
「この不明な発行元のアプリがデバイスに変更を加えようとしています」ダイアログが表示されたら、「OK」ボタンをクリックしてください。
インストーラが表示されました。「Next」ボタンをクリックしてください。
インストール先の場所です。変更しないで「Next」ボタンをクリックしてください。
スタートメニューの設定です。「Install」ボタンをクリックしてください。
インストールが開始されました。
途中、Visual C++のダイアログが表示されたら、「ライセンス条項および使用条件にに同意する」にチェックを付けて、「インストール」をクリックします。
PCを再起動して、あらためてインストーラを起動します。
インストールが完了しました。
コマンドプロンプトのerl
環境変数PATH
に、C:\Program Files\erl-23.0.1\bin
を追加します。筆者は学習用フォルダに env.bat
を作りました。学習のたびにコマンドプロンプトを起動して、学習用フォルダにcd
して、env.bat
を実行することにしました。
:env.bat
PATH=C:\Program Files\erl-23.0.1\bin;%PATH%
Code language: DOS .bat (dos)
C:> cd study_7lang\erlang
C:> env.bat
Code language: CSS (css)
erlで起動します。Ctrl + C で終了します。Ctrl+ Gでは終了しませんでした。
C:> erl -version
Erlang (SMP,ASYNC_THREADS) (BEAM) emulator version 11.0.1
C:> erl
Eshell V11.0.1 (abort with ^G)
1> <-- Ctrl + C を押した
C:>
Code language: CSS (css)
コマンドプロンプトのwerl
コマンドプロンプト + erl の場合と同じように、env.bat を実行します。
C:> cd study_7lang\erlang
C:> env.bat
Code language: CSS (css)
werlコマンドで、Erlangウィンドウが起動します。
C:> werl
C:>
Erlangウィンドウを終了するには、Fileメニュー、Exitです。
Eshell内で、Ctrl + Gを押して、q [Enter] でもErlangウィンドウが閉じます。
コマンドプロンプトから起動した場合、そのフォルダに translate.erl などのファイルを作り、Eshell で、c(translate).
とコンパイルします。
スタートメニューのErlang
環境変数PATHの変更は不要です。
スタートメニューのErlangから起動しても、werlウィンドウが表示されます。この場合、作業フォルダーが C:\Program Files\erl-23.0.1\usr
になってしまいます。このフォルダは読み込み専用なので、translate.erl などのファイルをここに作成できません。
そこで、エクスプローラで、スタートメニューのショートカット、C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Erlang OTP 23 (x64)\Erlang
を表示します。右クリックでプロパティを表示して、作業フォルダ欄を学習用フォルダパスに変更してください。
macOS(Mac OS X) 10.15.4
homebrew
でインストールします。
$ brew search erlang
==> Formulae
erlang erlang@20 erlang@21 erlang@22
Code language: Bash (bash)
$ brew info erlang
erlang: stable 23.0.2 (bottled), HEAD
Programming language for highly scalable real-time systems
https://www.erlang.org/
Not installed
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/erlang.rb
==> Dependencies
Build: autoconf ✔, automake ✘, libtool ✔
Required: openssl@1.1 ✔, wxmac ✘
==> Options
--HEAD
Install HEAD version
==> Caveats
Man pages can be found in:
/usr/local/opt/erlang/lib/erlang/man
Access them with `erl -man`, or add this directory to MANPATH.
==> Analytics
install: 23,838 (30 days), 88,466 (90 days), 375,810 (365 days)
install-on-request: 5,993 (30 days), 22,505 (90 days), 91,694 (365 days)
build-error: 0 (30 days)
Code language: Bash (bash)
$ brew install erlang
$ which erl
/usr/local/bin/erl
Code language: Bash (bash)
Erlang emulator version 11.0.2
、Erlang/OTP 23
でした。
$ erl -version
Erlang (SMP,ASYNC_THREADS,HIPE) (BEAM) emulator version 11.0.2
$ erl
Erlang/OTP 23 [erts-11.0.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
Eshell V11.0.2 (abort with ^G)
1>
Code language: Bash (bash)
Ubuntu 18.04
apt
でインストールします。
$ apt show erlang
Package: erlang
Version: 1:20.2.2+dfsg-1ubuntu2
Priority: optional
Section: universe/interpreters
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Debian Erlang Packagers <pkg-erlang-devel@lists.alioth.debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 51.2 kB
Depends: erlang-base | erlang-base-hipe, erlang-dev, erlang-asn1, erlang-common-test, erlang-corba, erlang-crypto, erlang-debugger, erlang-dialyzer, erlang-diameter, erlang-edoc, erlang-eldap, erlang-erl-docgen, erlang-et, erlang-eunit, erlang-ic, erlang-inets, erlang-megaco, erlang-mnesia, erlang-observer, erlang-odbc, erlang-os-mon, erlang-parsetools, erlang-public-key, erlang-reltool, erlang-runtime-tools, erlang-snmp, erlang-ssh, erlang-ssl, erlang-syntax-tools, erlang-tools, erlang-wx, erlang-xmerl
Recommends: erlang-jinterface, erlang-ic-java, erlang-mode, erlang-src, erlang-examples
Suggests: erlang-manpages, erlang-doc
Homepage: http://www.erlang.org/
Download-Size: 13.7 kB
APT-Sources: http://jp.archive.ubuntu.com/ubuntu bionic/universe amd64 Packages
Description: 並行、リアルタイム、分散関数型言語
Open Source Erlang は、エリクソンのコンピュータ科学研究所で設計された、関数型 プログラミング言語です。
.
Some of Erlang main features are:
* Clear declarative syntax and is largely free from side-effects;
* Built-in support for real-time, concurrent and distributed programming;
* Designed for development of robust and continuously operated programs;
* Dynamic code replacement at runtime.
.
この Erlang の配布には、豊富なライブラリとアプリケーションを提供する OTP (Open Telecom Platform:
オープンテレコムプラットフォーム) も含んでいます。
.
このパッケージはダミーパッケージです。Erlang と OTP のランタイム、 アプリケーション、ソース、ソースコード例と Emacs 用の
Erlang 編集モードを インストールします。
N: 追加レコードが 1 件あります。表示するには '-a' スイッチを付けてください。
Code language: Bash (bash)
$ sudo apt install erlang
$ which erl
/usr/bin/erl
Erlang emulator version 9.2
、Erlang/OTP 20
でした。
$ erl -version
Erlang (SMP,ASYNC_THREADS) (BEAM) emulator version 9.2
$ erl
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]
Eshell V9.2 (abort with ^G)
1>
Code language: JavaScript (javascript)
Eshellの終了方法
Eshell
を終了するには、Ctrl + G
を押して、q
を押します。
$ erl
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]
Eshell V9.2 (abort with ^G)
1> <-- Ctrl + Gを押した
User switch command
--> h
c [nn] - connect to job
i [nn] - interrupt job
k [nn] - kill job
j - list all jobs
s [shell] - start local shell
r [node [shell]] - start remote shell
q - quit erlang
? | h - this message
--> q
$
Code language: Bash (bash)
もうひとつの方法は、Ctrl + C
を押して、a
を押すことでも、Eshell
を終了します。
$ erl
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]
Eshell V9.2 (abort with ^G)
1> <-- Ctrl + Cを押した
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
(v)ersion (k)ill (D)b-tables (d)istribution
a
$
Code language: Bash (bash)
セルフスタディ
筆者の解凍例をうのみにしないほうがいいわよ
1日目
・再帰を用いて文字列を構成する単語の数を返す関数を書け。
ヒントをください〜
文字列内の空白文字を数えたらどう?
文字列内の空白文字を数えることにします。簡単にするために、文字列の先頭や末尾に空白文字はない、単語区切りの空白文字は1文字とし、2文字以上連続しない、とします。
文字列 | 単語数 |
---|---|
"" | 0 |
"hello" | 1 |
"hello world" | 2 |
%% wc.erl
-module(wc).
-export([wc/1]).
is_space(32) -> 1;
is_space(_) -> 0.
wc_body("") -> 0;
wc_body([Head|Tail]) -> is_space(Head) + wc_body(Tail).
wc("") -> 0;
wc(Anything) -> wc_body(Anything ++ " ").
Code language: Erlang (erlang)
1> c(wc).
{ok,wc}
2> wc:wc("").
0
3> wc:wc("hello").
1
4> wc:wc("hello world").
2
Code language: Erlang (erlang)
・再帰を用いて10まで数える関数を書け。
「数える」のヒントをください〜
1, 2, 3 と表示してみたら?
まず、10, 9, 8, 7, 6, 5, 4, 3, 2, 1 と表示するものです。
%% count_down.erl
-module(count_down).
-export([count/1]).
count(1) -> io:format("~p~n", [1]);
count(N) -> io:format("~p~n", [N]), count(N-1).
Code language: CSS (css)
23> c(count_down).
{ok,count_down}
24> count_down:count(10).
10
9
8
7
6
5
4
3
2
1
ok
Code language: Erlang (erlang)
次に、1, 2, 3, 4, 5, 6, 7, 8, 9, 10 と表示するものです。
-module(count_up).
-export([count/1]).
count(N) -> count_up_to(1, N).
count_up_to(M, N) when M =:= N -> io:format("~p ~n", [M]);
count_up_to(M, N) -> io:format("~p ~n", [M]), count_up_to(M + 1, N).
Code language: JavaScript (javascript)
28> c(count_up).
{ok,count_up}
29> count_up:count(10).
1
2
3
4
5
6
7
8
9
10
ok
Code language: Erlang (erlang)
・{error, Message} または success という形式の入力が与えられたとき、マッチングを用いて、"success" または "error: message" のどちらかを出力する関数を書け。
%% print_result.erl
-module(print_result).
-export([print_result/1]).
print_result(success) -> io:format("success~n");
print_result({error, Message}) -> io:format("error: ~p~n", [Message]).
Code language: Erlang (erlang)
39> c(print_result).
{ok,print_result}
40> print_result:print_result(success).
success
ok
41> print_result:print_result({error, "Hoge"}).
error: "Hoge"
ok
Code language: Erlang (erlang)
2日目
• [{erlang, "a functional language"}, {ruby, "an OO language"}]
のようなキーワード値タプルのリストを考える. リストとキーワードを受け取り,そ
のキーワードに対応する値を返す関数を書け.
> List = [{erlang, "a functional language"}, {ruby, "an OO language"}].
としたとき、> selfstudy2_1:map_get(erlang, List).
"a functional language"
> selfstudy2_1:map_get(ruby, List).
"an OO language"
となるような関数を作ります。
リストのHead|Tailを使ったバージョン。
Head|Tailで考えようとすると、頭がねじれる感じがするよ。
-module(selfstudy2_1).
-export([map_get/2]).
-export([test/0]).
map_get(Key, [{Key,Value}|_]) -> Value;
map_get(Key, [_|Tail]) -> map_get(Key, Tail);
map_get(_, _) -> not_found.
assert(BoolExpr, Comment) ->
case BoolExpr of
true -> true;
_ -> io:format("assert failed: ~p~n", [Comment])
end.
test() ->
List = [{erlang, "a functional language"}, {ruby, "an OO language"}],
assert(map_get(erlang, List) =:= "a functional language", "#1"),
assert(map_get(ruby, List) =:= "an OO language", "#2"),
assert(map_get(scala, List) =:= not_found, "#3").
Code language: Erlang (erlang)
リスト内包表記を使ったバージョン。他の言語のfilterとmapに相当します。
リスト内包表記はすぐに思いつかないよ
-module(selfstudy2_1).
-export([map_get/2]).
-export([test/0]).
map_get(Key, List) ->
ValueList = [ V || { K, V } <- List, K == Key ],
val_get(ValueList).
val_get([]) -> not_found;
val_get([Value]) -> Value.
assert(BoolExpr, Comment) ->
case BoolExpr of
true -> true;
_ -> io:format("assert failed: ~p~n", [Comment])
end.
test() ->
List = [{erlang, "a functional language"}, {ruby, "an OO language"}],
assert(map_get(erlang, List) =:= "a functional language", "#1"),
assert(map_get(ruby, List) =:= "an OO language", "#2"),
assert(map_get(scala, List) =:= not_found, "#3").
Code language: Erlang (erlang)
12> List = [{erlang, "a functional language"}, {ruby, "an OO language"}].
[{erlang,"a functional language"},{ruby,"an OO language"}]
13> c(selfstudy2_1).
{ok,selfstudy2_1}
14> selfstudy2_1:map_get(List, erlang).
"a functional language"
15> selfstudy2_1:map_get(List, ruby).
"an OO language"
16> selfstudy2_1:map_get(List, scala).
[]
Code language: PHP (php)
•[{item quantity price}, …] という形式の買い物リストを考える.このとき, [{item total_price}, …] という形式の items のリストを構築するリスト内包表記を書け.ただし, total_price は, quantity×price とする.
> List = [{pencil, 4, 0.25}, {pen, 1, 1.20}, {paper, 2, 0.20}].
としたとき、> xx:xx(List).
[{pencil,1.0},{pen,1.2},{paper,0.4}]
となるような関数を作ります。
-module(selfstudy2_2).
-export([total_price/1]).
%% > List = [{pencil, 4, 0.25}, {pen, 1, 1.20}, {paper, 2, 0.20}].
%% > selfstudy2_2:total_price(List).
%% > [{pencil, 1.0}, {pen, 1.20}, {paper, 0.4}]
total_price(List) -> [{Item, Quantity * Price} || {Item, Quantity, Price} <- List].
Code language: Erlang (erlang)
1> List = [{pencil, 4, 0.25}, {pen, 1, 1.20}, {paper, 2, 0.20}].
[{pencil,4,0.25},{pen,1,1.2},{paper,2,0.2}]
2> c(selfstudy2_2).
{ok,selfstudy2_2}
3> selfstudy2_2:total_price(List).
[{pencil,1.0},{pen,1.2},{paper,0.4}]
Code language: PHP (php)
• 長さ 9 のリストまたはタプルとして表現された三目並べのボードを読み込むプロ
グラムを書け.勝者が確定している場合はその勝者( x または o のどちらか)を,
これ以上指し手がない場合は cat を,勝負がついていない場合は no_winner を返
すようにせよ.
何も置いていないセルを b、片方の指し手を x、もう片方の指し手を o としました。ボードは、要素が9個あるタプルにしました。
testは通るけど、他にいい方法があるわね
-module(selfstudy2_3).
-export([judge/1]).
-export([test/0]).
-export([test_is_blank/0]).
-export([test_count_blank/0]).
-export([test_is_full/0]).
-export([test_find/0]).
-export([test_judge/0]).
assert(BoolExpr) -> assert(BoolExpr, "").
assert(BoolExpr, Comment) ->
case BoolExpr of
true -> true;
_ -> io:format("assert failed: ~p~n", [Comment])
end.
judge({C11, C12, C13,
C21, C22, C23,
C31, C32, C33}) ->
List1 = [{C11, C12, C13}, {C21, C22, C23}, {C31, C32, C33},
{C11, C21, C31}, {C12, C22, C32}, {C13, C23, C33},
{C11, C22, C33}, {C13, C22, C31}],
List2 = [C11, C12, C13, C21, C22, C23, C31, C32, C33],
WonByX = won_by_x(List1),
WonByO = won_by_o(List1),
IsFull = is_full(List2),
if
WonByX -> x;
WonByO -> o;
IsFull -> cat;
true -> no_winner
end.
won_by_x(List) -> find({x, x, x}, List).
won_by_o(List) -> find({o, o, o}, List).
find(_, []) -> false;
find(Item, [Item|_]) -> true;
find(Item, [_|Tail]) -> find(Item, Tail).
is_full(List) -> count_blank(List) =:= 0.
count_blank([]) -> 0;
count_blank([Head|Tail]) -> is_blank(Head) + count_blank(Tail).
is_blank(b) -> 1;
is_blank(_) -> 0.
test() ->
test_is_blank(),
test_count_blank(),
test_is_full(),
test_find(),
test_judge().
test_is_blank() ->
assert(is_blank(b) =:= 1, "is_blank(b)"),
assert(is_blank(x) =:= 0, "is_blank(x)"),
assert(is_blank(o) =:= 0, "is_blank(o)").
test_count_blank() ->
assert(count_blank([]) =:= 0, "count_blank #1"),
assert(count_blank([x,x,x,x,x,x,x,x,x]) =:= 0, "count_blank #2"),
assert(count_blank([x,x,x,x,x,x,x,x,b]) =:= 1, "count_blank #3"),
assert(count_blank([b,b,b,b,b,b,b,b,b]) =:= 9, "count_blank #4").
test_is_full() ->
assert(is_full([x,x,x,x,o,o,o,o,x]) =:= true, "test_is_full #1"),
assert(is_full([x,x,x,x,o,o,o,o,b]) =:= false, "test_is_full #2"),
assert(is_full([b,b,b,b,b,b,b,b,b]) =:= false, "test_is_full #3").
test_find() ->
assert(find({x,x,x}, []) =:= false, "test_find #1"),
assert(find({x,x,x}, [{x,x,x}]) =:= true, "test_find #2"),
assert(find({x,x,x}, [{o,x,o},{x,x,x}] ) =:= true, "test_find #3"),
assert(find({o,o,o}, [{o,x,o},{o,o,o}] ) =:= true, "test_find #4").
test_judge() ->
assert(judge({x, x, x,
b, b, b,
b, b, b}) =:= x, "test_judge x"),
assert(judge({o, o, o,
x, b, b,
x, b, b}) =:= o, "test_judge o"),
assert(judge({o, x, x,
x, o, o,
x, o, x}) =:= cat, "test_judge cat"),
assert(judge({o, o, x,
x, b, b,
x, b, b}) =:= no_winner, "test_judge no_winner").
Code language: Erlang (erlang)
1> c(selfstudy2_3).
{ok,selfstudy2_3}
2> selfstudy2_3:test().
true
3> selfstudy2_3:judge({x, o, b, x, o, b, x, b, b}).
x
Code language: Erlang (erlang)
3日目
この本の内容だけでは作れなかったよ
• translate_service を監視し,死んだら再起動するようにせよ.
• Doctor プロセスが死んだとき,自分自身で再起動するようにしてみよ.
• Doctor モニタのモニタを作成し,どちらかのモニタが死んだら再起動するようにせよ.
• メッセージのログをファイルに記録する基本的な OTP サーバを作成せよ.
• ネットワーク越しに動作するように translate_service を改良してみよ.
ここに解答例があったわ