编写一个谓词,将整数列表作为输入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]`, 1a。如果 `A` 是偶数, 为`E=[H|T]`分配新的列表节点, 设置其数据字段“H=A”, 并继续分离输入列表的其余部分“B” 进入 `T` 和 `O` ; 或者 1b。如果`A`是奇数, 为 `O=[H|T]` 分配新的列表节点, 设置其数据字段“H=A”, 并继续分离输入列表的其余部分“B” 进入 `E` 和 `T` ; 或者 2.如果`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)
.
?- list_evens_odds([1, 2, 3, 4, 5, 6], Es, Os)。 Es = [2, 4, 6], os = [1, 3, 5] ; 错误的。 ?- Zs = [A,B,C], list_evens_odds(Zs, Es, Os)。 Zs = [A, B, C], Es = [A, B, C], 奥斯= [], 一个 mod 2#=0, B mod 2#=0, C mod 2#=0 ; Zs = [A, B, C], Es = [A, B], 奥斯= [C], 一个 mod 2#=0, B mod 2#=0, C mod 2#=1 ; Zs = [A, B, C], Es = [A, C], 奥斯= [B], 一个 mod 2#=0, B mod 2#=1, C mod 2#=0 ... ?- Es = [E], Os = [O], list_evens_odds(Zs, Es, Os)。 Es = [E], 奥斯 = [O], Zs = [E, O], E mod 2#=0, O mod 2#=1 ; Es = [E], 奥斯 = [O], Zs = [O, E], E mod 2#=0, O mod 2#=1 ; 错误的。
接下来可能是最烦人的:在这里,我们询问是否存在一个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-predicate tpartition/4
和 reified test 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.
您可以在此处找到内置函数的简要说明。