Ты почти у цели. Вы поняли, что счетчик должен уменьшаться каждый раз, когда ваш предикат записывает число, так почему бы не остановиться, если он станет равным нулю? Если вы измените первое правило ...
print(_A,_B,0).
print(A,B,C) :-
D is A+B,
E is C-1,
print(D,B,E),
write(D).
... ваш предикат уже дает ответы:
?- seq_step_len(4,3,10).
3431282522191613107
true ;
ERROR: Out of local stack
Обратите внимание, что перед первыми двумя аргументами нерекурсивного правила есть подчеркивание. Это позволяет избежать одиночных предупреждений при загрузке исходного файла. Однако последовательность начинается не с 4, а с 34, она не заканчивается с 31, а с 7, и между цифрами нет пробела. И тогда есть эта ошибка ERROR: Out of local stack
. Последнее вы можете легко избежать, добавив цель C>0
в ваше рекурсивное правило. Неправильное начальное / конечное число можно избежать, написав A
вместо D
. Для обращения к обратной последовательности вы можете написать A
до рекурсии. И чтобы добавить пробелы между числами, я бы предложил использовать format / 2 вместо write/1
. Тогда print2/3
может выглядеть примерно так:
print2(_A,_B,0).
print2(A,B,C) :-
C>0, % <- new goal
format('~d ', [A]), % <- format/2 instead of write/1
D is A+B,
E is C-1,
print2(D,B,E).
Это дает желаемые результаты:
?- print2(4,3,10).
4 7 10 13 16 19 22 25 28 31
true ;
false.
А пока вы занимаетесь этим, почему нет последовательности в списке? Тогда вы могли бы использовать его, например, как цель в другом предикате. И было бы неплохо иметь более описательное имя, которое делает более очевидным, какой аргумент к чему. Итак, давайте добавим аргумент для последовательности, тогда предикат может выглядеть примерно так:
seq_start_end_len([],_A,_B,0).
seq_start_end_len([A|As],A,B,C) :-
C>0,
D is A+B,
E is C-1,
seq_start_end_len(As,D,B,E).
Если вы используете SWI-Prolog, вам может потребоваться набрать w , чтобы просмотреть весь список:
?- seq_start_end_len(Seq,4,3,10).
Seq = [4, 7, 10, 13, 16, 19, 22, 25, 28|...] [write]
Seq = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31] ;
false.
Однако, если вы попытаетесь запросить этот предикат с любым из трех последних аргументов, являющихся переменной, вы столкнетесь с ошибкой:
?- seq_start_end_len(Seq,X,3,10).
ERROR: is/2: Arguments are not sufficiently instantiated
?- seq_start_end_len(Seq,4,X,10).
ERROR: is/2: Arguments are not sufficiently instantiated
?- seq_start_end_len(Seq,4,3,X).
Seq = [],
X = 0 ;
ERROR: >/2: Arguments are not sufficiently instantiated
Это связано с использованием is/2
и >/2
. Вы можете избежать этих ошибок, используя CLP (FD) :
:- use_module(library(clpfd)). % <- new
seq_start_end_len([],_A,_B,0).
seq_start_end_len([A|As],A,B,C) :-
C#>0, % <- change
D #= A+B, % <- change
E #= C-1, % <- change
seq_start_end_len(As,D,B,E).
Если вы попробуете один из вышеупомянутых запросов сейчас, вы получите много остаточных целей в качестве ответа:
?- seq_start_end_len(Seq,X,3,10).
Seq = ['$VAR'('X'), _G1690, _G1693, _G1696, _G1699, _G1702, _G1705, _G1708, _G1711, _G1714],
'$VAR'('X')+3#=_G1690,
_G1690+3#=_G1693,
_G1693+3#=_G1696,
_G1696+3#=_G1699,
_G1699+3#=_G1702,
_G1702+3#=_G1705,
_G1705+3#=_G1708,
_G1708+3#=_G1711,
_G1711+3#=_G1714,
_G1714+3#=_G1838 ;
false.
Чтобы получить действительные числа, вам нужно ограничить диапазон X
и пометить переменные в последовательности:
?- X in 1..4, seq_start_end_len(Seq,X,3,10), label(Seq).
X = 1,
Seq = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28] ;
X = 2,
Seq = [2, 5, 8, 11, 14, 17, 20, 23, 26, 29] ;
X = 3,
Seq = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30] ;
X = 4,
Seq = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31] ;
false.
?- X in 1..4, seq_start_end_len(Seq,4,X,10), label(Seq).
X = 1,
Seq = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13] ;
X = 2,
Seq = [4, 6, 8, 10, 12, 14, 16, 18, 20, 22] ;
X = 3,
Seq = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31] ;
X = 4,
Seq = [4, 8, 12, 16, 20, 24, 28, 32, 36, 40] ;
false.
?- X in 1..4, seq_start_end_len(Seq,4,3,X), label(Seq).
X = 1,
Seq = [4] ;
X = 2,
Seq = [4, 7] ;
X = 3,
Seq = [4, 7, 10] ;
X = 4,
Seq = [4, 7, 10, 13] ;
false.
В версии CLP (FD) вы также можете задавать более общие запросы, например Какие существуют последовательности длиной от 4 до 6 с числами в диапазоне от 1 до 10? :
?- Len in 4..6, seq_start_end_len(Seq,S,E,Len), Seq ins 1..10, label(Seq).
Len = 4,
Seq = [1, 1, 1, 1],
S = 1,
E = 0 ;
Len = 4,
Seq = [1, 2, 3, 4],
S = E, E = 1 ;
Len = 4,
Seq = [1, 3, 5, 7],
S = 1,
E = 2 ;
.
.
.
Len = 6,
Seq = [9, 9, 9, 9, 9, 9],
S = 9,
E = 0 ;
Len = 6,
Seq = [10, 9, 8, 7, 6, 5],
S = 10,
E = -1 ;
Len = 6,
Seq = [10, 10, 10, 10, 10, 10],
S = 10,
E = 0 ;
false.
И вы получаете все 80 возможностей. Обратите внимание, насколько хорошо имя отражает реляционную природу предиката CLP (FD).