如何使用Prolog删除List中的偶数

时间:2013-12-18 20:48:09

标签: prolog

我需要删除第一个列表中的所有偶数,然后将其余的数字保存到第二个列表中。 我的第一个非工作方法是:

remove_even([],[]).
remove_even([H1|T1],[H2|T2]):- 
    H1 mod 2 =:= 0,
    remove_even(T1,_).
remove_even([H1|T1],[H2|T2]):-
    remove_even(T1,T2).

使用SWI-Prolog 7.1.37运行的示例查询:

?- remove_even([1,2,3,4],NewList).
NewList = [_G252, _G255|_G256].     % BAD! expected: NewList = [1,3]

我的代码出了什么问题?

更新:接下来尝试,它不起作用,因为remove_even一旦检查它是否有效,然后返回false并转到另一个规则,其中发生了递归。 ..

remove_even([],[]).
remove_even([El|T],[T]):- El mod 2 =:= 0.
remove_even([H1|T1],[H2|T2]):-
    remove_even(T1,T1).

UPDATE2:在逻辑中丢失了:

remove_even([],[]).
remove_even([El|T],T):-El mod 2 =:= 0. % removing the head if head is even
remove_even([H|T1], [H|T2]) :-         % case where head is odd
    H mod 2 =\= 0,                     % rules that ensure head is odd
    remove_even(T1, T1). 

    % just copying T1 from source list to destination, 
    % will look up T1 values in next recursive iteration when it becomes a H. 

UPDATE3:遵循建议,但仍然无效。

remove_even([],[]).
remove_even([El|T], NewT)   :- El mod 2 =:= 0, remove_even(T, NewT).
remove_even([H|T1], [H|T2]) :- H  mod 2 =\= 0, remove_even(T1, T1).

UPDATE4:没有单身错误

remove_even([],[]).
remove_even([El|T], NewT)  :- El mod 2 =:= 0, remove_even(T, NewT).
remove_even([El|T1], NewT) :- El mod 2 =\= 0, remove_even(T1,[NewT|T1]). 

UPDATE5:让它运转,但几乎不知道如何(某些魔法;)也许)。 gtrace是好的,但让Prolog绘制某种决策树或一些易于理解的图形表示是有用的。

remove_even([],[]).
remove_even([El|T], NewT)   :- El mod 2 =:= 0, remove_even(T, NewT).
remove_even([H|T1], [H|T2]) :- H  mod 2 =\= 0, remove_even(T1, T2).

如何在考虑递归调用的情况下解释逻辑上的最后一句? 我的尝试:[H|T2]是一个列表,其中偶数元素已从[H|T1] IFF 中移除。两个列表的头部都是奇数并且目标列表的尾部T2是尾部{{ 1}}删除所有偶数元素。正确的吗?

5 个答案:

答案 0 :(得分:4)

你有单例变量警告的事实是一个有道理的事情的线索。您的子句标题意味着您关心特定变量,但子句逻辑不会实例化或以其他方式使用它。

分析您提供的规则(条款)

remove_even([],[]).

好的规则。 删除了偶数的空列表是空列表。

remove_even([H1|T1],[H2|T2]):- 
    H1 mod 2 =:= 0,
    remove_even(T1,_).

此规则说明, [H2|T2]是列表[H1|T1],如果H1是偶数,则会删除偶数,如果我从T1中移除均值并丢弃它们。这听起来不对。它也没有说明你如何获得H2。注意:如果逻辑没有规定,您可能不希望将此子句中的结果列表拆分为头尾。

remove_even([H1|T1],[H2|T2]):-
    remove_even(T1,T2).

此规则表示 [H2|T2][H1|T1],如果T2T1并删除偶数,则会删除偶数。这听起来部分正确,但该规则并未指明如何处理H1H2

更新:在您的更新中,新的第二个子句:

remove_even([El|T],[T]):- El mod 2 =:= 0.

这更接近。一个问题是,T已经是一个列表,因此您不想要[T],只需要T。然后它变成:

remove_even([E1|T], T) :- E1 mod 2 =:= 0.

其中说:如果[E1|T]甚至,则删除了偶数元素的列表T是列表E1。这是一个正确的陈述,但不是完整的逻辑。它没有对T作出任何规定。如果列表T具有偶数元素,该怎么办?有关此特定条款的更正版本,请参阅@ Sergey的答案。

您更新的第三条有一些新问题:

remove_even([H1|T1],[H2|T2]):-
    remove_even(T1,T1).

有三个单例变量。该规则说明,如果[H1|T1]本身删除了偶数元素,则删除偶数元素的 [H2|T2]会产生T1(即T1没有偶数元素)。这听起来并不合乎逻辑。所以你需要重新考虑这条规则。我假设你打算头是奇数的情况(因为第2条涉及偶数头)。在这种情况下,您只是将头部复制到结果列表。您的子句标题应如下所示:

remove_even([H|T1], [H|T2]) :-   % case where head is odd
     % put rules here that ensure head is odd, and define how T1 and T2 are related

所以你最终会有3个子句:(1)从空列表中删除偶数元素,(2)从头部为偶数的列表中删除偶数元素,以及(3)从头部列表中删除偶数元素很奇怪。一切听起来都很完整。只需遵循逻辑。

UPDATE4回复:

新的第3条通过引入一些问题来消除单身:

remove_even([El|T1], NewT):-         
    El mod 2 =\= 0,                     
    remove_even(T1, [NewT|T1]). 

阅读: [E1|T1]如果NewT为奇数,E1[NewT|T1],其中偶数元素已移除T1 NewT } 的。这里的一个大问题是您使用[NewT|T1](列表)作为另一个列表E1的头部,因此它现在是一个列表列表,它们不匹配任何内容。请参阅上面第3条的先前提示。此外,规则的一部分不再是NewTE1的一部分。如果remove_even([],[]). 是奇数,则当删除偶数元素时,它应该是另一个列表的一部分。

UPDATE5响应(为什么现在有效?):

所以最终的工作版本如下:

remove_even([El|T], NewT):- 
    El mod 2 =:= 0,
    remove_even(T, NewT).

和以前一样:如果删除空列表的偶数元素,则会得到一个空列表。

NewT

[E1|T]是列表E1,如果NewT为偶数,TT("尾列表&#34),则会删除偶数元素;从E1中删除偶数元素的原始列表。换句话说,我们删除了T(一个偶数元素,第一个列表的头部 - 我们不再需要它,因为它甚至是偶然的)并留下T,列表的其余部分我们想要"处理"并找到与remove_even([H|T1], [H|T2]):- H mod 2 =\= 0, remove_even(T1, T2). 类似的列表,但删除偶数元素。

[H|T1]

我们之前已经介绍了这一点,但为了完整性:[H|T2]如果H不均匀,T1偶数元素被删除T2删除的元素是{{1}}。您对此子句的描述如下: [H | T2]是从[H | T1]中移除偶数元素的列表IFF两个列表的头部是奇数并且目标列表的尾部T2是尾部T1,其中删除了所有偶数元素。这不是很准确。你说," ......两个名单的IFF头都是奇怪的......",而在条款中,我们说头是相同和奇数(它是相同的数字),而不仅仅是两个奇数。

我在回答之前已经进一步描述了所有案例。如果你从逻辑上考虑它,它是有道理的。 :)

答案 1 :(得分:2)

如果您的Prolog系统提供,那么这个答案适合您。 (如果没有,请继续阅读!)

使用 tfilter/3reified predicate zodd_t/2,只需写下:

?- tfilter(zodd_t,[1,2,3,4],Zs). % remove even integers = keep odd integers
Zs = [1,3].

由于我们这里仅使用单调代码,因此我们也会获得更一般查询的正确答案:

?- tfilter(zodd_t,[A,B,C],Zs).
Zs = [     ], A mod 2 #= 0, B mod 2 #= 0, C mod 2 #= 0 ;
Zs = [    C], A mod 2 #= 0, B mod 2 #= 0, C mod 2 #= 1 ;
Zs = [  B  ], A mod 2 #= 0, B mod 2 #= 1, C mod 2 #= 0 ;
Zs = [  B,C], A mod 2 #= 0, B mod 2 #= 1, C mod 2 #= 1 ;
Zs = [A    ], A mod 2 #= 1, B mod 2 #= 0, C mod 2 #= 0 ;
Zs = [A,  C], A mod 2 #= 1, B mod 2 #= 0, C mod 2 #= 1 ;
Zs = [A,B  ], A mod 2 #= 1, B mod 2 #= 1, C mod 2 #= 0 ;
Zs = [A,B,C], A mod 2 #= 1, B mod 2 #= 1, C mod 2 #= 1.

即使没有,我们也可以使用上面的代码,只需使用不同的reified predicateseveninteger_t/2zeven_t/2,和 oddinteger_t/2zodd_t/2

示例查询:

?- tfilter(oddinteger_t,[1,2,3,4,5,6,7],Xs).
Xs = [1,3,5,7].

编辑2015-06-06

让我们尝试以下变化!

而不是直接使用zodd_t/2,而是使用zeven_t/2not_t/3

truth_negated(true,false).
truth_negated(false,true).

not_t(Goal,Param,Truth1) :-       % meta-predicate, negate truth value
   call(Goal,Param,Truth0),
   truth_negated(Truth0,Truth1).

让我们看看它是否与头对头比较:

?- tfilter(      zodd_t  ,[1,2,3,4],Zs).
Zs = [1,3].

?- tfilter(not_t(zeven_t),[1,2,3,4],Zs).
Zs = [1,3].

答案 2 :(得分:1)

让我们来看看你的一个条款:

remove_even([El|T],[T]) :- El mod 2 =:= 0.

首先,在符号[El | T]中El是单个项目,T是列表。然后[T]将列在列表中,这可能不是你想要的。它应该只是“remove_even([El | T],T)”。

接下来,您的规则变体只是将T复制到答案中,而不是从尾部删除任何偶数。只删除第一个数字(如果它是偶数)。 remove_even也应该应用于T.

最后我们应该有这样的事情:

remove_even([El|T], NewT) :- 
    El mod 2 =:= 0,
    remove_even(T, NewT).

答案 3 :(得分:1)

内置的便捷库exclude

1 ?- [user].
|: even(N) :- N mod 2 =:= 0.
% user://1 compiled 0.02 sec, 2 clauses
true.

2 ?- exclude(even, [1,2,3,4], L).
L = [1, 3].

答案 4 :(得分:0)

我的意思是,也许我读错了,但你认为你没有想过要彻底解决这个问题吗?因此,取出第一个列表中的所有偶数元素并将它们存储在第二个列表中,我就可以通过跳过每个偶数元素来实现这一点,如下所示:

remove_evens([], Ys).
remove_evens([X], [X|Ys]).
remove_evens([X,_|Xs], [X|Ys]):-
  remove_evens(Xs, Ys), !.

反过来会给出这个结果:

| ?- remove_evens([1,2,3,4,5,6,7], List2).
List2 = [1,3,5,7|_]