Это так называемый отказоустойчивый цикл .
Более простая ситуация, с которой нужно разобраться:
repeat :- true.
repeat :- repeat.
forever_x :- repeat, write('x'), fail.
, которая навсегда печатает x
в приглашении.
Почему?Потому что соединения Пролога (,
, "и") целей похожи на вложенные циклы :
find(G):- this(X1), that(X2).
точно так же (в псевдокод )
def find(G):
foreach solution X1 to { this(X1) }:
foreach solution X2 to { that(X2) }:
yield G using the found values X1, X2.
Возврат происходит естественным образом в циклах. Если для некоторых X1
не существует X2
, который удовлетворяет that(X2)
, то G
не получается,и внешний цикл просто пропускает к следующему значению X1
, которое удовлетворяет this(X1)
.
. А расчеты Пролога (;
, "или") целей - это просто сопоставление циклов (просто помещая один цикл за другим).
Таким образом, определение repeat
действует так, как если бы оно было определено как
def repeat:
yield % yield an empty value that isn't used anywhere
repeat % call self, yielding again; and again; repeating endlessly
def forever_x:
foreach solution to { repeat }: % endless stream of empty solutions
foreach solution to { write('x') }: % there will be only one, empty solution
foreach solution to { fail }: % there will be no solutions, ever, so
yield % this will never happen
, а ваш n_times/1
, как если бы
% n_times(N) :- N =< 1.
% n_times(N) :- N > 1, N1 is N - 1, n_times(N1).
def n_times(n):
foreach solution to { n =< 1 }: % succeeds at most once
yield n
foreach solution to { n > 1 }: % at most once, mutually exclusively
foreach solution to { n1 = n - 1 }: % there will be only one solution
n_times(n1)
, поэтому, естественно, это удастся, то есть «доходность», n раз.