我想在Prolog中创建一系列数字。因此,如果函数为print(4,3,10)
,则会打印 4 7 10 13 16 19 22 25 28 31 。第二个参数确定下一个数字,最后一个参数确定序列应停止的位置。
我有代码,但似乎无法运作。
print(A,B,C) :- C>=0.
print(A,B,C) :- D is A+B, E is C-1, print(D,B,E), write(D).
结果只显示true
。
这个问题有什么解决方案吗?感谢。
答案 0 :(得分:6)
你几乎就在那里。你已经意识到每次你的谓词写一个数字时都必须减少计数器,所以为什么不在它变为零时停止?如果您更改了第一条规则......
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
。要在数字I和#之间添加空格,建议使用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)谓词的关系性质。
答案 1 :(得分:3)
equal(X,X).
initial_step_size_sequence(Initial,Step,Size,Sequence):-
length([_H|List],Size),
maplist(equal(Step),List),
scanl(plus,List,Initial,Sequence).
使用更高阶谓词是一种方法。我已经命名谓词,以便变量清晰。请注意,您需要一个四位谓词,而不是您建议的三个谓词。
?- initial_step_size_sequence(4,3,10,S).
S = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31]
答案 2 :(得分:3)
DCG可能是在代码中嵌入@tas(+1)的两个好建议的最简单方法。
print(_,_,0) --> [].
print(A,B,C) --> {C>0, D is A+B, E is C-1}, [A], print(D,B,E).
测试:
?- phrase(print(4,3,10),S).
S = [4, 7, 10, 13, 16, 19, 22, 25, 28, 31] ;
false.
修改:
像foldl/4这样的高阶谓词也可以非常紧凑地解决这个问题(在@ user27815:+1之前提出这个想法):
initial_step_size_sequence(Initial,Step,Size,Sequence):-
length(Sequence,Size),
foldl({Step}/[A,B,C]>>(A=B,C is B+Step),Sequence,Initial,_).
请注意&#39;分配&#39;序列元素A=B
(自由变量,由长度/ 2创建),以及库(yall)的使用来内联谓词。