LoginSignup
0
0

Prologで動的に述語を定義する

Last updated at Posted at 2024-05-01

概要

SWI-PrologのドキュメントでManaging (dynamic) predicatesの項目を読んでいて、ある程度使い方がわかったのでまとめる

使い方

節や事実の定義

動的に述語(節や事実)を定義するには以下の述語を利用する。

  • asserta : 前回定義した述語の前に定義する
  • assertz : 前回定義した述語の後ろに定義にる

以下はファイルから読み込んだデータを元に事実を定義する例

dynamic_fact.txtは以下のようにしておく

first
second
third
read_file_lines(FilePath, Lines) :-
    open(FilePath, read, Stream), % ファイルを読み込みモードで開く
    read_lines(Stream, Lines), % ファイルから行を読み込む
    close(Stream). % ファイルを閉じる

read_lines(Stream, []) :-
    at_end_of_stream(Stream), !. % ファイルの終端に達した場合は空リストを返す

read_lines(Stream, [Line|Rest]) :-
    \+ at_end_of_stream(Stream), % ファイルの終端に達していない場合
    read_line_to_codes(Stream, LineCodes), % 行を読み込む
    atom_codes(Line, LineCodes), % 文字コードをアトムに変換
    read_lines(Stream, Rest). % 次の行を読み込む

test_dynamic :-
  read_file_lines('./dynamic_fact.txt', Lines),
  forall(member(Line, Lines), (
    asserta(myfact1(Line)), % 事実 myfact1() を定義
    assertz(myfact2(Line))  % 事実 myfact2() を定義
  )),
  % 事実の先頭から検索するので結果が異なる
  myfact1(X1), writeln(X1), % third
  myfact2(X2), writeln(X2)  % first
  .

test_dynamic. % test_dynamicの実行  

節や事実の削除

節や事実を削除するには以下の述語を利用する

  • retract

「節や事実の定義」の項目で定義したコードを以下ように書き換えると事実が削除されて結果が変わる

test_dynamic :-
  read_file_lines('./dynamic_fact.txt', Lines),
  forall(member(Line, Lines), (
    asserta(myfact1(Line)),
  )),
  retract(myfact1(third)),  % myfact1(third) を削除
  myfact1(X1), writeln(X1)  % second (thirdを削除したため結果が変わる)
  .

節や事実をまとめて削除

節や事実をまとめて削除するには以下の述語を利用する

  • retractall

allと書いてあるが、必ずしもすべての節や事実を削除するのではない点に注意。
特定の事実や節以降のデータを消す場合は1,2番目の例のようにする。
全ての事実や節を削除したい場合は3番目の例のようにする。
また abolishとは述語自体を削除するわけではないので、削除後に参照すると3番目の例の結果のようにfalse (見つけられなかった)になる。

test_retractall :-
    % 1. 特定の事実を指定して削除 (assertz)
  assertz(myfact1(first)),
  assertz(myfact1(second)),
  assertz(myfact1(third)),
  retractall(myfact1(second)),
  myfact1(X), writeln(X),     % first (second, thirdを削除したため)
  % 2. 特定の事実を指定して削除 (asserta)
  asserta(myfact2(first)),
  asserta(myfact2(second)),
  asserta(myfact2(third)),
  retractall(myfact2(second)),
  myfact2(Y), writeln(Y),!,   % third (second, firstを削除したため)
  % 3. 全ての事実を削除
  asserta(myfact3(first)),
  asserta(myfact3(second)),
  asserta(myfact3(third)),
  retractall(myfact3(_)),
  myfact3(_).                 % false (述語を削除したわけではないのでエラーにはならない)

述語の削除

特定の述語を削除するには以下の述語を利用する

  • abolish

以下は事実として定義した述語を削除するコード
削除しているので、読み取りを行おうとするとエラーになる

test_abolish :-
  assertz(myfact1(first)),
  assertz(myfact1(second)),
  myfact1(X), writeln(X),
  abolish(myfact1/1), % 述語なので引数の数も指定する
  myfact1(_).         % Error

まとめ

予めわかっているような定数はasserta, assertzを利用せずに、直に定義してしまえば良いので、用途としては、動的に生成したデータやファイルに書かれているデータを事実や節として定義したい場合に利用するのではないかと思います。

また、今回は解説していませんが、フラグやトライ木を実現する述語も存在するようです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0