【发布时间】:2015-10-10 23:31:35
【问题描述】:
编写一个谓词,将整数列表L 作为输入,并生成两个列表:包含来自L 的偶数元素的列表和来自L 的奇数元素的列表。
?- separate_parity([1,2,3,4,5,6], Es, Os).
Es = [2,4,6], Os = [1,3,5] ? ;
no
【问题讨论】:
编写一个谓词,将整数列表L 作为输入,并生成两个列表:包含来自L 的偶数元素的列表和来自L 的奇数元素的列表。
?- separate_parity([1,2,3,4,5,6], Es, Os).
Es = [2,4,6], Os = [1,3,5] ? ;
no
【问题讨论】:
只需对列表使用结构递归。写下每个互斥情况的等价关系:
parity_partition([A|B], [A|X], Y):- 0 is A mod 2, parity_partition(B,X,Y).
parity_partition([A|B], X, [A|Y]):- 1 is A mod 2, parity_partition(B,X,Y).
parity_partition([],[],[]).
这意味着:关系parity_partition(L,E,O) 持有,
L=[A|B] 和A 是偶数,当E=[A|X]、O=Y 和关系parity_partition(B,X,Y) 成立。 L=[A|B] 和A 是奇数,当E=X、O=[A|Y] 和关系parity_partition(B,X,Y) 成立。 L=[],当E=[] 和O=[]。只要写下这些等式,我们就可以使用 Prolog 程序来解决这个问题。
在操作上,这意味着:将列表 L 分成偶数列表 E 和赔率列表 O,
实际的操作顺序可能有点不同,但概念上是一样的:
1.尽量统一L=[A|B], E=[A|X]。如果没有,请转到 2。 1a。检查A是否是偶数。 如果没有,请放弃所做的实例化 作为统一的一部分,然后转到 2。 1b。继续使用 B、X 和相同的 O:使用 B 作为 L,使用 X 作为 E,然后转到 1。 2.尝试统一L=[A|B],O=[A|Y]。如果没有,请转到 3。 2a.检查 A 是否为奇数。 如果没有,放弃所做的实例化 作为统一的一部分,然后转到 3。 2b。继续使用 B、Y 和相同的 E:使用 B 作为 L,使用 Y 作为 O,然后转到 1。 3. 用 [] 统一 L,E,O。【讨论】:
您可以为此使用clpfd。这样你就得到了一个纯粹的关系:
:- use_module(library(clpfd)).
list_evens_odds([], [], []).
list_evens_odds([E|Zs], [E|Es], Os) :-
0 #= E mod 2,
list_evens_odds(Zs, Es, Os).
list_evens_odds([E|Zs], Es, [E|Os]) :-
1 #= E mod 2,
list_evens_odds(Zs, Es, Os).
您不仅可以使用它来将列表拆分为偶数和赔率。但你可以走得更远。以下是与 SWI 的交互,但您在 SICStus 中与 asserta(clpfd:full_answer) 的交互类似。
下一个可能是最烦人的:在这里,我们询问是否存在一个既是偶数又是奇数的整数EO。当然,这样的整数是不可能存在的。但我们仍然得到两个答案!
?- EOs=[EO], list_evens_odds(Zs, EOs, EOs)。 EO = [EO], Zs = [EO,EO], EO mod 2#=1, EO mod 2#=0 ; EO = [EO], Zs = [EO,EO], EO mod 2#=0, EO mod 2#=1 ; 错误的。
这说明了答案和解决方案之间的区别。我们在这里得到两个答案,但两者都不包含解决方案。大多数情况下,答案包含一个或多个解决方案,但也可以不包含任何解决方案,就像在这种情况下一样。这样的答案有时被称为不一致。
不一致不一定被视为实现的错误。它们是一种工程上的权衡:与实际收益相比,确保一致性的成本可能非常高。并且: Prolog 不会产生不正确的答案:对于必须保持的条件已显示。即使那个条件被证明是错误的。
【讨论】:
这个答案是基于clpfd、meta-predicatetpartition/4和具体化测试zodd_t/2:
:- use_module(library(clpfd)).
将tpartition/4 与zodd_t/2 结合使用,我们可以简单地编写:
?- tpartition(zodd_truth,[1,2,3,4,5,6],Es,Os).
Es = [1,3,5], Os = [2,4,6]. % succeeds deterministically
【讨论】:
@Will Ness 的回答很好而且很详细。如果您的 Prolog 提供,我将添加使用“高阶”内置函数(即接收谓词作为参数的谓词)的可能性:
separate_parity(L, E, O) :-
partition(is_even, L, E, O).
is_even(N) :- N mod 2 =:= 0.
您可以找到here 对内置函数的简要说明。
【讨论】: