控制' cut'在递归函数中从Prolog中的列表中删除元素

时间:2017-11-04 10:32:41

标签: prolog

我试图创建一个能够从列表中删除所有元素的函数。我看到一些解决方案已经mentioned。但我尝试了不同的方法。 我基本上是在复制新列表中的元素,如果它们不等于我想要删除的内容。

remove(X, [], A, A):-!.
remove(X1, [X1|T1], A, R):-
    remove(X1, T1, A, R).
remove(X2, [H2|T2], A2, R2):-
    remove(X2, T2, [H2 |A2], R2).

第一个答案是我期望的结果,但随后它不断提供更多可能性:

?- remove(x, [x, b,c,x,x], [], Result).
Result = [c, b] ;
Result = [x, c, b] ;
Result = [x, c, b] ;
Result = [x, x, c, b] ;
Result = [c, b, x] ;
Result = [x, c, b, x] ;
Result = [x, c, b, x] ;
Result = [x, x, c, b, x].
  1. [编辑后添加]为什么一直提供答案?
  2. 有没有办法让它停在第一个结果?
  3. 编辑:我也意识到这也是在逆转我的名单:(

2 个答案:

答案 0 :(得分:4)

问题在于条款:

remove(X2, [H2|T2], A2, R2):-
    remove(X2, T2, [H2 |A2], R2).

您需要明确说明X2H2不同。您可以使用dif/2

来实现
remove(X2, [H2|T2], A2, R2):-
        dif(X2,H2),
        remove(X2, T2, [H2 |A2], R2).

如果你没有声明X2H2是不同的,Prolog会尝试两种情况,这会导致保留或抛出元素H2。

答案 1 :(得分:3)

简短回答:如果你写remove(X2, [H2|T2], A2, R2),这并不意味着X2H2不同,所以你需要阻止Prolog采取这种做法科。 Prolog的一个基本方面是它自动执行回溯。因此,一个条款“触发”的事实并不妨碍它从其他条款中解除。

快速修复可以为第二个子句添加剪切:

remove(X, [], A, A) :- 
    !.
remove(X1, [X1|T1], A, R):-
    !,  % a cut
    remove(X1, T1, A, R).
remove(X2, [H2|T2], A2, R2):-
    remove(X2, T2, [H2 |A2], R2).

因此,如果X2H2 可以统一,Prolog将采用第二个条款,然后剪切将阻止Prolog采用第三个条款。

另一个重要方面是您的程序撤消列表。我们可以通过在这里删除累加器来解决这个问题(你也可以使用差异列表作为累加器,但这会引入太多的复杂性):

remove(_, [], []).
remove(X1, [X1|T1], R):-
    !,
    remove(X1, T1, R).
remove(X2, [H2|T2], [H2|R2]):-
    remove(X2, T2, R2).

现在你可以用它来删除可能相同的列表中的元素:注意Prolog执行统一,所以它也会删除自由变量,或部分未接地的可以统一。

但是:削减是危险的,通常会导致不纯的谓词。 Prolog的想法是您可以在多个方向中使用谓词。例如,您可以生成所有可能的列表,以便删除3后的结果为[1,4,2,5]。为此,我们可以添加约束dif/2

remove(_, [], []).
remove(X2, [H2|T2], [H2|R2]) :-
    dif(X2, H2),
    remove(X2, T2, R2).
remove(X1, [X1|T1], R):-
    remove(X1, T1, R).

我们也在这里删除了剪切。

现在我们可以使用不同的任务查询它:

  1. 从列表4中删除所有[1,4,2,4,4,5]

    ?- remove(4,[1,4,2,4,4,5],X).
    X = [1, 2, 5] ;
    false.
    
  2. 从列表中删除 元素A

    ?- remove(A,[1,4,2,4,4,5],X).
    X = [1, 4, 2, 4, 4, 5],
    dif(A, 5),
    dif(A, 4),
    dif(A, 4),
    dif(A, 2),
    dif(A, 4),
    dif(A, 1) ;
    A = 5,
    X = [1, 4, 2, 4, 4] ;
    A = 2,
    X = [1, 4, 4, 4, 5] ;
    A = 4,
    X = [1, 2, 5] ;
    A = 1,
    X = [4, 2, 4, 4, 5] ;
    false.
    
  3. 生成所有可能是删除输入的列表,结果为[1,4,2,5](无限量的答案):

    ?- remove(A,L,[1,4,2,5]).
    L = [1, 4, 2, 5],
    dif(A, 5),
    dif(A, 2),
    dif(A, 4),
    dif(A, 1) ;
    L = [1, 4, 2, 5, A],
    dif(A, 5),
    dif(A, 2),
    dif(A, 4),
    dif(A, 1) ;
    L = [1, 4, 2, 5, A, A],
    dif(A, 5),
    dif(A, 2),
    dif(A, 4),
    dif(A, 1) ;
    L = [1, 4, 2, 5, A, A, A],
    dif(A, 5),
    dif(A, 2),
    dif(A, 4),
    dif(A, 1) ;
    L = [1, 4, 2, 5, A, A, A, A],
    dif(A, 5),
    dif(A, 2),
    dif(A, 4),
    dif(A, 1)
    ...
    
  4. 从列表中删除了哪些元素?

    ?- remove(A,[1,4,3,2,5],[1,4,2,5]).
    A = 3 ;
    false.
    
    ?- remove(A,[1,3,4,3,2,5],[1,4,2,5]).
    A = 3 ;
    false.
    
  5. 我们可以从[1,4,2,5]中仅删除一种值来获取列表[1,9,4,3,2,5]吗? (不!)

    ?- remove(A,[1,9,4,3,2,5],[1,4,2,5]).
    false.