我需要删除第一个列表中的所有偶数,然后将其余的数字保存到第二个列表中。 我的第一个非工作方法是:
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}}删除所有偶数元素。正确的吗?
答案 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]
,如果T2
为T1
并删除偶数,则会删除偶数。这听起来部分正确,但该规则并未指明如何处理H1
和H2
。
更新:在您的更新中,新的第二个子句:
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条的先前提示。此外,规则的一部分不再是NewT
是E1
的一部分。如果remove_even([],[]).
是奇数,则当删除偶数元素时,它应该是另一个列表的一部分。
UPDATE5响应(为什么现在有效?):
所以最终的工作版本如下:
remove_even([El|T], NewT):-
El mod 2 =:= 0,
remove_even(T, NewT).
和以前一样:如果删除空列表的偶数元素,则会得到一个空列表。
NewT
[E1|T]
是列表E1
,如果NewT
为偶数,T
为T
("尾列表&#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系统提供clpfd,那么这个答案适合您。 (如果没有,请继续阅读!)
使用meta-predicate tfilter/3
和
reified 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.
即使没有clpfd,我们也可以使用上面的代码,只需使用不同的reified predicates:
eveninteger_t/2
→zeven_t/2
,和
oddinteger_t/2
→zodd_t/2
。
示例查询:
?- tfilter(oddinteger_t,[1,2,3,4,5,6,7],Xs). Xs = [1,3,5,7].
让我们尝试以下变化!
而不是直接使用zodd_t/2
,而是使用zeven_t/2
和not_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|_]