Prolog 写経記 その 52 fac/2

(ほぼ) 毎日淡々と Prolog を写経します.元ネタはこちら.

Prologユーティリティライブラリ

Prologユーティリティライブラリ

今日からは「4 章 算術演算」に突入です.
今日は fac/2 を写経します.

解説

fac(N, Fact)FactN の階乗 N! (=1*2*3*…(N-1)*N) に具体化するか,あるいは FactN! と等しいかどうかを調べる.

ふむ.
再帰の例としてお馴染みの奴ですね.

モード

fac(+, ?).

ふむ.

定義

では,こいつの定義を写経しませう.

fac(0, 1) :-
	!.
fac(N, Fact) :-
	integer(N),
	N > 0,
	M is N - 1,
	fac(M, F),
	Fact is N * F, !.

最初の節で,0 の階乗は 1 であるという事実を定義しています.
次の節で 1 より大きな整数の階乗を求めています.
integer(N)N > 0 はガード条件ですね.
そして N - 1 の階乗を再帰的に求めてその結果に N を乗じることで N の階乗を求めています.

注記

引数 N は正の整数でなくてはならない.負の整数であれば fac は失敗する.N の値の増加に比べて Fact の値の増加は非常に速いため,たとえ fac が失敗してもオーバーフローか否かを調べること.

失敗しても? オーバーフローすると失敗するの? 失敗した理由か何かを調べられるの?
日本語的には「成功してもオーバーフローか否かを調べること」の方がピンときますが...

では使用例を写経しませう.

2 ?- fac(15, F).

F = 1.30767e+012 

Yes
3 ?- fac(9.1, X).

No
4 ?- fac(7, 5040).

Yes

最初の例は...
成功しているけれどオーバーフローの例になっているような気のせいが.精度足りてないよね.
まぁ,この程度じゃ甘いという事でしょうか.
そんなわけで (どんなわけで?),もっと Nを増やしてみるテスト.

9 ?- fac(200, F).
ERROR: Arithmetic: evaluation error: `float_overflow'

float としてオーバーフローすると失敗します.
この場合にオーバーフローか否かってどうやって調べるの?
やっぱり注記は「成功してもオーバーフローか否かを調べること」だと思うなぁ.


そんなわけで (どんなわけで?),それも述語に組み込んでみるテスト.

fac(0, 1) :-
	!.
fac(N, Fact) :-
	integer(N),
	N > 0,
	M is N - 1,
	fac(M, F),
	Fact is N * F, !,
	integer(Fact).

お試し.

13 ?- fac(10, F).

F = 3628800 

Yes
14 ?- fac(50, F).

No

悪くない感じ.