Recursion 递归谓词在到达基本情况后继续

Recursion 递归谓词在到达基本情况后继续,recursion,prolog,Recursion,Prolog,我正在尝试编写一个名为“range”的谓词,它在指定的范围内创建一个int列表 例如: range(4,9, L). L = [4,5,6,7,8,9] 我编写的谓词似乎工作正常,但当它完成时,我能够点击“下一步”,然后返回: L=[4,5,6,7,8,9,_1200 | u 1202] 这是我的密码: range(Low, Low, [Low]). %base case, if low and high are equal range(Low, High, [H|T]):- Low

我正在尝试编写一个名为“range”的谓词,它在指定的范围内创建一个int列表

例如:

range(4,9, L).
L = [4,5,6,7,8,9]
我编写的谓词似乎工作正常,但当它完成时,我能够点击“下一步”,然后返回:

L=[4,5,6,7,8,9,_1200 | u 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=[U 1164 | U 1166]

我如何调整它,使其返回一个空列表,而不是(我假设是)头部和尾部的地址

以下是我在跟踪时看到的情况:


例如,您可以简化控制流程

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
但这将为每个计算的元素留下一个无用的choicepoint

编辑

这里有一种可能会为您的需求提供一个空列表:

range(Low, High, R):-
    (   Low > High
    ->  R = []
    ;   R = [Low|T],
        Num is Low + 1,
        range(Num, High, T)
    ).
注意,我删除了第一个子句(基本递归,现在由第一个分支覆盖),并使用了更易于阅读的“”构造。对于我们的代码片段,它相当于:

range(Low, High, R):-
    (   Low > High,
    !,  R = []
    ;   R = [Low|T],
        Num is Low + 1,
        range(Num, High, T)
    ).
您在剪切(
)后写了一个分号(
),而不是逗号(
)。分号在某种程度上相当于“逻辑or”,因此您可以这样写:

range(A, A, [A]).
range(A, B, [C|E]) :-
    (   A>B, !
    ;   C is A,
        D is A+1,
        range(D, B, E)
    ).
对于
范围(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 

剪切不停止子句的执行,一个割表示PROLO将不再考虑该特定调用的下一个子句(因此,如果我们执行递归,割对递归本身没有影响),因为没有其他子句,所以这里的切割没有影响。 我们可以将代码简化为:

range(A, A, [A]).
range(A, B, [A|T]) :-
    A < B,
    A1 is A+1,
    range(A1, B, T).

你在剪切处写了一个分号而不是逗号,但是剪切还是没用的。谢谢你,这很有帮助。我不完全理解剪切的作用,但我现在明白了。当我用无效参数运行它时,虽然我得到的是一个假而不是一个空列表,但有没有办法使它返回一个空列表?@Sauromayne:你的意思是
Start
大于
Stop
?最后一个
介于/3
之间的解决方案应该可以解决这个问题。感谢您告诉我如何简化它。因此,当我们强制失败时,它返回“false”的原因是什么如果我传递了无效的参数?有没有办法让它给出一个空的列表而不是假的好吧,那么我唯一的问题是,既然没有基本情况(至少我看到了),是什么阻止了它永远继续下去?假设它在Low为10,High为9时到达Low>High。然后它会做->R=[]哪一个只是将列表的尾部设置为空列表?是的,base子句是第一个分支。请注意,包含
的子句可以重写为在两个子句中“拆分”它们。
range(A, A, [A]).
range(A, B, [A|T]) :-
    A < B,
    A1 is A+1,
    range(A1, B, T).
range(A, B, L) :-
    findall(X, between(A, B, X), L).