質問: 以下のサンプルコードでは、 大きな数のフィボナッチ数計算を、途中の計算結果を保存することで可能にしています。 ところが二度三度と繰り返すと、重複して計算結果が溜まってしまいます。 そこで abolish したいのですが、これを実行すると元の定義も消えてしまいます。 こういう場合の定番となっている処理方法はありますか? ``` :- dynamic('_n番目のフィボナッチ数は_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'/2). フィボナッチ数(_n番目,_フィボナッチ数) :- '_n番目のフィボナッチ数は_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'(_n番目,_フィボナッチ数). '_n番目のフィボナッチ数は_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'(0, 0) :- !. '_n番目のフィボナッチ数は_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'(1, 1) :- !. '_n番目のフィボナッチ数は_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'(_n番目,_フィボナッチ数) :- '_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'(_n番目,_フィボナッチ数). '_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'(_n番目,_フィボナッチ数) :- '_n-1番目のフィボナッチ数と'(_n番目,_フィボナッチ数_1), '_n-2番目のフィボナッチ数を'(_n番目,_フィボナッチ数_2), 加算したものになる(_フィボナッチ数_1,_フィボナッチ数_2,_フィボナッチ数), '_n番目のフィボナッチ数の計算結果を保存する'(_n番目,_フィボナッチ数). '_n-1番目のフィボナッチ数と'(_n番目,_フィボナッチ数_1) :- _n_1番目 is _n番目 - 1, '_n番目のフィボナッチ数は_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'(_n_1番目,_フィボナッチ数_1). '_n-2番目のフィボナッチ数を'(_n番目,_フィボナッチ数_2) :- _n_2番目 is _n番目 - 2, '_n番目のフィボナッチ数は_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'(_n_2番目,_フィボナッチ数_2). 加算したものになる(_フィボナッチ数_1,_フィボナッチ数_2,_フィボナッチ数) :- _フィボナッチ数 is _フィボナッチ数_1 + _フィボナッチ数_2. '_n番目のフィボナッチ数の計算結果を保存する'(_n番目,_フィボナッチ数) :- asserta(('_n番目のフィボナッチ数は_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'(_n番目,_フィボナッチ数) :- ! )). ```
回答: 最初から、計算結果を重複して保存しないようにするという手があります。 * 計算結果が保存されていた時は、保存されている値を返します。 * 保存されていなかった場合は、フィボナッチ数を計算し、保存します。 この考えでプログラムを書くと、次のようになります。 ``` :- dynamic('N番目のフィボナッチ数を計算するとFになった'/2). フィボナッチ数(N, F) :- 'N番目のフィボナッチ数はFだ'(N, F), !. 'N番目のフィボナッチ数はFだ'(0, 0) :- !. 'N番目のフィボナッチ数はFだ'(1, 1) :- !. 'N番目のフィボナッチ数はFだ'(N, F) :- 'N番目のフィボナッチ数を計算するとFになった'(N, F), !. 'N番目のフィボナッチ数はFだ'(N, F) :- 'N番目のフィボナッチ数Fを計算し、保存する'(N, F), !. 'N番目のフィボナッチ数Fを計算し、保存する'(N, F) :- 'N番目のフィボナッチ数Fを計算する'(N, F), 'N番目のフィボナッチ数Fを保存する'(N, F). 'N番目のフィボナッチ数Fを計算する'(N, F) :- N1 is N - 1, N2 is N - 2, 'N番目のフィボナッチ数はFだ'(N1, F1), 'N番目のフィボナッチ数はFだ'(N2, F2), F is F1 + F2. 'N番目のフィボナッチ数Fを保存する'(N, F) :- assert('N番目のフィボナッチ数を計算するとFになった'(N, F)). ``` 補足: もとのプログラムは、フィボナッチ数の定義と、保存された計算結果を同じ述語(`'_n番目のフィボナッチ数は_n-1番目のフィボナッチ数と_n-2番目のフィボナッチ数を加算したものになる'/2`)にしてしまった点に問題があります。このプログラムでは、`'N番目のフィボナッチ数はFだ'/2`とは別に、`'N番目のフィボナッチ数を計算するとFになった'/2`という計算結果を保存するためだけに用いる述語を用意することで、問題を解決しています。これなら、abolishしたときに元の定義まで消えてしまうことがありません。