Mercury 勉強メモ

関数論理型言語 Mercury を勉強するブログです.

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).

ここでは,

  1. 入力が空リストの場合は結果は空リスト.
  2. リストが空でない場合は,先頭要素を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/2write_list/3を使ったプログラムを以下に示します.

実行結果

2
4
6

ここで作ったdouble_list/2write_list/3入門 (2)で扱った, if then else ゴールを使って書くこともできます.以下に例を示します.

実行結果

2
4
6

論理和

Goal1またはGoal2が真の時に成功するゴールは

Goal1 ; Goal2

と書けます. これを使うと述語の引数以外の場所で分岐を記述できます. 以下に例を示します.

実行結果

2
4
6

個人的には今回の記事の中で,論理和を使ったプログラムが一番好きです.