偶数和奇数列表

时间:2014-01-17 09:28:35

标签: list prolog

我正在尝试实现一个将列表分成两个列表的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 10,其中没有一个是真的。如果我有一个包含三个元素的列表并将条件更改为X is 3它完全没问题,但是这个部门似乎搞得搞乱了,所以任何想法都可以吗?

4 个答案:

答案 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).