我正在尝试实现一个将列表分成两个列表的Prolog程序。第一个包含偶数位置的数字,第二个包含数字是奇数位置。
例如:even_odd([1,2,4,7,5],Even,Odd).
将产生Even=[2,7].
和Odd=[1,4,5].
我找到了比谷歌搜索问题更优雅的解决方案,但是我想在我的代码中找到问题所在(可能是操作员滥用),因为我真的认为我对Prolog运算符的理解非常糟糕(尤其是算术比较)。谷歌搜索它只会使情况变得更糟,每个网站都有完全不同的解释。
我的代码:
even_odd([], [], []).
even_odd([H|T], Even, Odd) :-
length([H|T], X),
X mod 2 is 0,
append(Even1, H, Even),
even_odd(T, Even1, Odd).
even_odd([H|T], Even, Odd) :-
length([H|T], X),
X mod 2 is 1,
append(Odd1, H, Odd),
even_odd(T,Even,Odd1).
我试过跟踪,我知道问题出在X mod 2 is 1
或0
,其中没有一个是真的。如果我有一个包含三个元素的列表并将条件更改为X is 3
它完全没问题,但是这个部门似乎搞得搞乱了,所以任何想法都可以吗?
答案 0 :(得分:5)
虽然CapelliC对你的代码失败的原因有正确的认识,但他误解了你关于想要偶数/奇数位置的问题。
问题是,您正在计算列表中 end 的那些,因此even_odd([1,2,4,7],Even,Odd).
会产生Even=[1,4].
和Odd=[2,7].
不是你想要的。
一个解决方案是在处理之前颠倒列表并在之后反转结果,或采取不同的方法。
这里,我们首先将第一个元素添加到奇数列表中,然后在添加到偶数列表和奇数列表之间交替,直到我们到达最终元素。
even_odd(List, Even, Odd) :-
% First position is odd
even_odd_odd(List, Even, Odd).
% We handle the odd position, the next is even
even_odd_odd([H|T], Even, [H|Odd]) :-
even_odd_even(T, Even, Odd).
even_odd_odd([], [], []).
% We handle the even position; the next is odd
even_odd_even([H|T], [H|Even], Odd) :-
even_odd_odd(T, Even, Odd).
even_odd_even([], [], []).
答案 1 :(得分:3)
算术计算运算符(是)/ 2对右表达式执行算术运算,并将其与左参数统一,然后将测试应用于错误的变量。应该是
even_odd([H|T],Even,Odd):- 0 is H mod 2,...
或者,有(=:=)/ 2在两侧执行算术运算。然后可能是
H mod 2 =:= 0
此外,处理列表的方式没有多大意义。您必须在偶数列表中输入偶数值。然后像
even_odd([H|T],[H|Even],Odd):- 0 is H mod 2,...
会更有意义。
答案 2 :(得分:2)
有一些问题。
首先是@CapelliC指出的一个,即is/2
谓词在左边实例化一个变量,右边是一个表达式的值。原始代码左侧有表达式,右侧有值,总是会失败。因此:
X mod 2 is 1
应改为:
1 is X mod 2
或者,正如@CapelliC所说,X mod 2 =:= 1
。
其次,您的append
尝试将单个元素附加到列表中。 append
仅适用于列表。所以这个:
append(Odd1, H, Odd)
应该是
append(Odd1, [H], Odd)
第三,append
的排序和递归even_odd
调用会导致问题:
append(Odd1, [H], Odd),
even_odd(T,Even,Odd1).
调用Odd1
时, append
未实例化,Odd
也是如此。在可接受的答案可以被基本情况约束之前,这会为append
生成无限数量的解决方案,从而导致堆栈溢出。对此的简单解决方案是交换它们:
even_odd(T, Even, Odd1),
append(Odd1, [H], Odd).
这将立即起作用,但产生的结果顺序相反:
| ?- even_odd([1,2,3,4,5,6], E, O).
E = [5,3,1]
O = [6,4,2] ? ;
最容易解决的问题是前置而不是附加。取代
append(Odd1, [H], Odd)
使用
Odd = [H|Odd1]
或者只是将其包含在条款的头部。最后的条款,包含所有修复,然后看起来像:
even_odd([], [], []).
even_odd([H|T], [H|Even1], Odd) :-
length([H|T], X),
0 is X mod 2,
even_odd(T, Even1, Odd).
%%Even = [H|Even1]. % equivalent to append([H], Even1, Even)
even_odd([H|T], Even, [H|Odd1]) :-
length([H|T], X),
1 is X mod 2,
even_odd(T, Even, Odd1).
%%Odd = [H|Odd1]. % equivalent to append([H], Odd1, Odd)
屈服
| ?- even_odd([1,2,3,4,5,6], E, O).
E = [1,3,5]
O = [2,4,6] ? ;
no
最后,还有另一个问题:列表的奇偶校验是从列表的末尾而不是从开头确定的。这是因为在当前列表片段上使用length
作为确定奇偶校验的方法的逻辑。
要解决这个问题,在给定的代码结构中(再次,侧面步入@ATS提供的更自然的解决方案),您真的需要从头开始计算/索引。详细地看一下,这将引导我们找到这样的解决方案:
even_odd(L, Even, Odd) :-
even_odd(L, 1, Even, Odd).
even_odd([], _, [], []).
even_odd([H|T], C, [H|Even1], Odd) :-
0 is C mod 2,
C1 is C + 1,
even_odd(T, C1, Even1, Odd).
even_odd([H|T], C, Even, [H|Odd1]) :-
1 is C mod 2,
C1 is C + 1,
even_odd(T, C1, Even, Odd1).
给出了:
| ?- even_odd([1,2,3,4,5,6], E, O).
E = [2,4,6]
O = [1,3,5] ? a
no
这是一个有效的解决方案,但并不是最优的,因为它没有利用"知识"代码中的列表位置(冗余地引入计数或索引以了解我们在列表中的位置)。
答案 3 :(得分:0)
您可以使用此
even_odd([], [], []).
even_odd([H|T], [H|Odd1], Even) :-
1 is H mod 2,
even_odd(T, Odd1, Even).
even_odd([H|T], Odd, [H|Even1]) :-
0 is H mod 2,
even_odd(T, Odd, Even1).