我正在尝试编写一个称为“范围”的谓词,以创建指定范围内的整数列表。
示例:
range(4,9, L).
L = [4,5,6,7,8,9]
我写的谓词似乎工作正常,但是完成后,我可以按“ Next”,然后返回:
L = [4,5,6,7,8,9,_1200 | _1202]
这是我的代码:
range(Low, Low, [Low]). %base case, if low and high are equal
range(Low, High, [H|T]):-
Low > High,
!; %if low is greater than high cut
H is Low, %set the head of the list
Num is Low + 1, %increment low by 1
range(Num, High, T). %recursive call
如果给出的范围无效,我也希望它返回一个空列表。例如:
范围(10,4,L)。
应返回一个空列表。相反,我得到了:
L = [_1164 | _1166]
我该如何调整它,以便它返回一个空列表,而不是头和尾的地址(我假设是)。
这是我追踪时的样子:
答案 0 :(得分:2)
例如,您可以简化控制流程
range(Low, Low, [Low]). %base case, if low and high are equal
range(Low, High, [Low|T]):- %set the head of the list
Low < High,
Num is Low + 1, %increment low by 1
range(Num, High, T). %build the tail
现在第二个子句将仅涵盖预期的情况。
关于您的解决方案,当Low>High
条件成功,剪切成功以及[H|T]
中的变量也没有任何绑定时(保持未实例化),而该子句也将成功。解决方法将导致规则失败:
range(Low, Low, [Low]). %base case, if low and high are equal
range(Low, High, [H|T]):- %set the head of the list
Low > High,
!, fail; % force failure
H is Low,
Num is Low + 1, %increment low by 1
range(Num, High, T). %recursive call
但这将为计算出的每个元素留下一个无用的选择点。
编辑
在这里,您可能会得到一个空列表:
range(Low, High, R):-
( Low > High
-> R = []
; R = [Low|T],
Num is Low + 1,
range(Num, High, T)
).
请注意,我删除了第一个子句(基本递归,现在已由第一个分支覆盖),并使用了易于阅读的'if-then-else'构造。对于我们的代码段,它等效于此:
range(Low, High, R):-
( Low > High,
!, R = []
; R = [Low|T],
Num is Low + 1,
range(Num, High, T)
).
答案 1 :(得分:1)
您在剪切(;
)之后写了分号(ems)(!
)而不是逗号(,
)。分号在某种程度上等效于“逻辑或”,因此您编写了以下内容:
range(A, A, [A]).
range(A, B, [C|E]) :-
( A>B, !
; C is A,
D is A+1,
range(D, B, E)
).
对于range(1, 2, L)
,基本上有两种方法可以得出解决方案(这不是标准符号,仅在此处用于提供一些关于Prolog如何获得结果的见解:
range(1, 2, [C|E])
C is 1,
D is 2,
range(2, 2, E) :-
range(2, 2, [2]). % we found a solution
range(2, 2, [C|E])
C is 2,
D is 3,
range(3, 2, E) :-
range(3, 2, [C|E])
3 > 2. % we found a solution
剪切不会不会停止执行该子句,剪切意味着Prolog将不再考虑该 specific 调用的下一个子句(因此,如果执行递归,剪切不会影响递归本身)。由于没有其他子句,因此此处的削减没有影响。
我们可以将代码简化为:
range(A, A, [A]).
range(A, B, [A|T]) :-
A < B,
A1 is A+1,
range(A1, B, T).
或者我们可以使用内置的between/3
[swi-doc],并在其上使用findall/3
[swi-doc]:
range(A, B, L) :-
findall(X, between(A, B, X), L).