ヒープ領域への述語の追加と削除

「ヒープ領域への述語の追加と削除」の編集履歴(バックアップ)一覧に戻る

ヒープ領域への述語の追加と削除 - (2014/05/07 (水) 12:45:41) の編集履歴(バックアップ)


  • assertとretract について
  assertやretractはヒープ領域内に新たに述語を追加したり、反対に削除したりする組み込み述語です。

  • assert/1
  ヒープ領域に述語を追加します。類似の述語としてasserta,assertzがあり、
  それぞれassertaは同一名の述語があった場合に、その述語の先頭に、
  assertzはその述語の最後尾に追加されます。
  ※assertについても、基本的に同一述語名の最後尾に追加が行われますが、何番目に追加が行われるかは保障されていません。
   そのため、確実に最後尾に述語を追加したい場合は、assertzを使用した方が確実です。

  • 例 assert
?-a(1).
no
?-assert(a(1)).
yes
?-a(1). 
yes

  • 解説
  1回目のa(1).ではまだ何の述語の定義もされていないため、失敗して終了しています。
  その後、assert(a(1))の実行により,ヒープ領域にa(1)という述語が定義されたため、
  2回目のa(1).が成功するようになっています。


  • listingについて
  listingとはヒープ領域に登録(consultやassert)されたプログラムを出力する組み込み述語です。
  上述のassertの例では、assertによって、a(1)という述語が定義されたらしい、ということはわかりますが、
  具体的になにが行われているのかわかりません。 
  そこで以降の例ではlistingを使用して、ヒープ領域の状態を確認しながら、動きを見てみましょう。


  • 例 listing
?-listing.
yes
?-assert(a(1)).
yes
?-listing.
a(1).
yes

  • 解説
  1回目のlisting.ではヒープ領域にまだなにも登録が行われていないため、
  何も表示されずに、終了しています。
  2回目のlistingでは直前に行われたassert(a(1)).によって、a(1)という述語が
  ヒープ領域に登録されたため、a(1).という内容が出力されています。


  • asserta/1
  ヒープ領域へ述語を追加しますが、assertと違って、同一述語名の先頭に登録されます。
  そのため、追加した述語を、既に登録済みの述語より優先的に処理したい場合などに、使用します。


  • 例 asserta
事前に以下のコマンドを実行しておきます。
?-assert(a(1)).
?-assert(a(2)).
?-assert(a(3)).

ヒープ領域を確認
?-listing.
a(1).
a(2).
a(3).
yes

assertaの実行
?-asserta(a(0)).
yes

ヒープ領域の確認
?-listing.
a(0).
a(1).
a(2).
a(3).
yes

  • 解説
  事前に実行したassertでは述語は実行した順番でaという述語が登録されていますが、
  その後実行された、asserta(a(0))はa(1)という述語より前に登録されていることがわかります。



  • assertの利用方法の一例
  assertにはフラグのような利用方法があります。
  以下にその一例を示します。

  • 例 aという述語で定義された内容を表示する。ただし同一の述語が存在する場合は1度だけしか表示しないものとする。
a(1).
a(1).
a(2).
a(3).

test:-a(X),test2(X),write(X),nl,fail.
test:-!.
	
test2(X):-b(X),!,fail.
test2(X):-assert(b(X)).

  • 解説
  上記の例ではまず、出力対象のaという述語を定義しています。
  そして、testという述語はaという述語を順番に呼び出し、
  aの述語の中の数字が出力されたことがあるかのチェック用の述語test2を呼び出し,
  その数字が出力されたことがなければ出力します。
  チェック用の述語のtest2では、
  1つ目の定義で出力済みか否かのフラグ用の述語b(X)の存在チェックを行います。
  b(X)が存在する(出力されたことがある)ならfailさせて、且つ、
  カットオペレータによってバックトラックも行わずにtest述語のwrite以降の処理を実行せず、次のaの述語を探して処理に移ります。
  2つ目の定義は述語b(X)が存在しなかった(出力されたことがなかった)場合の処理を記述されており、
  assertによって、現在の入力引数でbという述語を定義して(出力済みのフラグを立てて)、
  test述語のwrite以降の処理を実行します。

  • 実行してみましょう
?-test.
1
2
3
yes

  上述のプログラムで、a(1)という述語は2つ登録されていますが、1は1度だけしか表示されないことが確認出来ました。



  • retract/1
  引数で指定された述語とユニファイされた述語をヒープ領域から削除します。

  • 例 retract
事前に以下のコマンド実行しておきます。
?-assert(a(1)).
?-assert(a(2)).
?-assert(a(3)).

ヒープ領域を確認
?-listing.
a(1).
a(2).
a(3).
yes

retractの実行
?-retract(a(2)).
yes

ヒープ領域の確認
?-listing.
a(1).
a(3).

  • 解説
  retract(a(2))で指定された、a(2)の述語が削除されています。
  なお、retractは指定した述語とユニファイ可能な述語が登録されていなければ、失敗します。


  • retractとabolishとs_new
ヒープ領域に登録された述語を指定して削除する組み込み述語には、
retractの他にabolishがあります。
retractとabolishの違いは、retractが指定した述語の先頭から探して、
見つかった最初の述語を削除するのに対して、
abolishは、abolish(X,Y)という形で記述し、Xで指定した述語名の
Yで指定したアリティ数に合う述語を全て削除するという違いがあります。
s_newはヒープ領域を全てクリアします。

  • 例 retract,abolish,s_new
事前に以下のコマンド実行しておきます。
?-assert(a(1)).
?-assert(a(2)).
?-assert(a(1)).
?-assert(a(1)).
?-assert(a(1,1)).
?-assert(b(1)).

ヒープ領域の確認
?-listing.
a(1).
a(2).
a(1).
a(1).
a(1,1).
b(1).
yes

retcatの実行
?-retract(a(1)).
yes

ヒープ領域を確認するとa(1)の述語が	
先頭の1つだけ削除されていることがわかります
?-listing.
a(2).
a(1).
a(1).
a(1,1).
b(1).
yes

abolishの実行(aという述語のアリティ1つのものを削除)
?-abolish(a,1).
yes

ヒープ領域を確認するとaという述語のアリティが1つのものが
全て削除されていることがわかります
?-listing.
a(1,1).
b(1).
yes

s_newの実行
?-s_new.

ヒープ領域を確認すると残っていた述語も全てクリアされているのがわかります
?-listing.
yes

  • 注意 retractとabolishの使い分け
  retractでは上記の例のa(1)のように、まったく同じ述語が複数個存在する場合にも、
  削除される述語は1つだけであるため、a(1)という述語を全て削除したい場合には注意が必要となり、
  そういった場合にまとめて削除してくれるabolishは便利です。
  ただし、abolishは指定した述語名で同一アリティ数のものを全て削除してしまうため、
  上の例の場合なら、a(1)という述語を削除するつもりで使用したつもりが、
  a(2)という述語を削除したくない場合も削除されてしまうという問題が
  あるため、やはり注意が必要となります。



変数の型チェック var/1 nonvar/1

  • varとnonvarについて
  varやnonvarは、入力引数が未代入の変数か否かを確認する、型チェック用の述語です


  • var/1
  varは引数に変数が指定された場合に、その変数に値が代入されていない場合に限り、成功します。
  引数には変数以外にアトムやリストなども指定出来ますが、その場合はfailします。

  • 例 var

?-var(X).
X  = X
yes

?-X='abc',var(X).
no

?-var(123).
no

?-var([a,b,c]).
no


  • 解説
  1回目のvarではXに何も代入していないため、成功しています。
  2回目のvarでは直前にXに値が代入されているため、失敗しています。


  • nonvar/1
  nonvarは引数に変数が指定され、その変数に値が代入されていない場合に限り、failします。
  引数には変数以外にアトムやリストなども指定出来ますが、その場合もやはり成功します。

  • 例 nonvar

?-nonvar(X).
no

?-X='abc',nonvar(X).
X = abc
yes	

?-nonvar(123).
yes

?-nonvar([a,b,c]).
yes


  • 解説
  1回目のnonvarではXに何も代入していないため、失敗しています。
  2回目のnonvarでは直前にXに値が代入されているため、成功しています。



数値チェック integer/1 float/1

  • integerとfloatについて
  integerとfloatは、入力引数が整数か、浮動小数点数かを確認する、型チェック用の述語です。

  • integer/1
  integerは入力引数が整数か、整数の代入された変数の場合のみ成功する述語です。
  引数にはアトムやリストなども指定出来ますが、その場合はfailします。

  • 例 integer
?-integer(123).
yes
?-X=123,integer(X).
X = 123
yes
?-integer(1.23).
no
?-integer([a,b,c]).
no
?-integer('123').
no
?-integer(X).
no

  • 解説
  1つ目の実行では引数が整数であるため、2つ目は整数が代入された変数であるため成功しています。
  3つ目も引数は数値ですか、浮動小数点数であるため、失敗しています。
  その他はそもそも引数がリストやアトム、未代入の変数であるため、それぞれ整数であるという条件に合致しないため失敗しています。


  • float
floatは入力引数が浮動小数点数か、浮動小数点数の代入された変数の場合のみ成功する述語です。
引数にはアトムやリストなども指定出来ますが、その場合はfailします。

  • 例 float
?-float(1.23).
yes
?-X=1.23,float(X).
X = 1.23
yes
?-float(123).
no
?-float([a,b,c]).
no
?-float(X).
no

  • 解説
  1つ目の実行では引数が浮動小数点数であるため、2つ目は浮動小数点数が代入された変数であるため成功しています。
  3つ目も引数は数値ですが、整数であるため、失敗しています。
  その他はそもそも引数がリストや未代入の変数であるため、それぞれ浮動小数点数であるという条件に合致しないため失敗しています。



まとめ

今回は以下の事を学習しました。
  • ヒープ領域への述語を追加・削除
  • 代入済み・未代入変数の型チェック
  • 数値の型チェック