递归谓词在达到基本案例后仍继续

时间:2018-12-09 06:38:16

标签: recursion prolog

我正在尝试编写一个称为“范围”的谓词,以创建指定范围内的整数列表。

示例:

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]

我该如何调整它,以便它返回一个空列表,而不是头和尾的地址(我假设是)。

这是我追踪时的样子:

Trace

2 个答案:

答案 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).