Mercury 入門 (3) リスト
同じ型の複数の値を順番に並べたものをリストとよびます.
Mercury では例えば整数1, 2, 3
の3つの値からなるリストは,
[1, 2, 3]
のように表し,その型は,
list(int)
のように表します.リストに要素を追加するには,
[1 | [2, 3] ]
のように書きます.上のリストは[1, 2, 3]
と同じです.
空のリストは,
[]
のように書きます.以下の5つはすべてリスト[1, 2, 3]
を表しています.
[1, 2, 3]
[1 | [2, 3]]
[1, 2 | [3]]
[1, 2, 3 | []]
[1 | [2 | [3 | []]]]
リストを操作する述語の書き方
ここでは,整数リストの各要素の値を倍にする,以下の述語double_list/2
を考えます.
:- pred double_list(list(int)::in, list(int)::out) is det.
double_list([], []).
double_list([X|Xs], [Y|Ys]) :-
X * 2 = Y,
double_list(Xs, Ys).
ここでは,
- 入力が空リストの場合は結果は空リスト.
- リストが空でない場合は,先頭要素を2倍した整数と残りの要素を2倍した整数リストを 連結したものである.
と述語を定義しています.
このような引数の形で分岐させる述語は,入門 (2)で扱った,fact/2
の定義に使うことはできません.例えば,
% 間違った(決定性の指定に反する)プログラム
fact(0, 1).
fact(N, M) :-
M = N * X,
fact(N - 1, X).
のように書くことはできません.整数の0
は最初の定義にも2番目の定義にも合致するため,
fact/2
は決定的(det
)な述語であるという宣言に反してしまうからです.
次の例は,整数リストのすべての要素を画面に表示する述語です.
:- pred write_list(list(int)::in, io::di, io::uo) is det.
write_list([], !IO).
write_list([X|Xs], !IO) :-
write_int(X, !IO),
nl(!IO),
write_list(Xs, !IO).
この述語はこれまでの知識の組み合わせで理解できると思います.
double_list/2
とwrite_list/3
を使ったプログラムを以下に示します.
実行結果
2
4
6
ここで作ったdouble_list/2
とwrite_list/3
は
入門 (2)で扱った,
if then else ゴールを使って書くこともできます.以下に例を示します.
実行結果
2
4
6
論理和
Goal1
またはGoal2
が真の時に成功するゴールは
Goal1 ; Goal2
と書けます. これを使うと述語の引数以外の場所で分岐を記述できます. 以下に例を示します.
実行結果
2
4
6
個人的には今回の記事の中で,論理和を使ったプログラムが一番好きです.