List 奇偶表

List 奇偶表,list,prolog,List,Prolog,我正在尝试实现一个Prolog程序,它将一个列表拆分为两个列表。第一个包含偶数位置的数字,第二个包含奇数位置的数字 例如:偶数(1,2,4,7,5],偶数,奇数)。将导致偶数=[2,7]。和奇数=[1,4,5]。 我已经找到了比谷歌搜索问题更优雅的解决方案,但是我想在我的代码中找到问题所在(可能是运算符误用),因为我真的认为我对Prolog运算符的理解很差(特别是在算术比较方面)。此外,谷歌搜索只会让事情变得更糟,每个网站都有完全不同的解释 我的代码: even_odd([], [], []).

我正在尝试实现一个Prolog程序,它将一个列表拆分为两个列表。第一个包含偶数位置的数字,第二个包含奇数位置的数字

例如:
偶数(1,2,4,7,5],偶数,奇数)。
将导致
偶数=[2,7]。
奇数=[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是1
0
,它们都不是真的。如果我有一个包含三个元素的列表,并将条件更改为
X为3
,这是完全正确的,但除法似乎把它搞砸了,所以有什么想法吗?

算术求值运算符(is)/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,...

会更有意义。

虽然Capelical对代码失败的原因有正确的想法,但他误解了您关于需要偶数/奇数位置的问题

问题是,您正在从列表的末尾计算这些值,因此
偶数([1,2,4,7],偶数,奇数)。
将导致
偶数=[1,4]。
奇数=[2,7]。
这不是您想要的。
一个解决方案是在处理之前撤销列表,然后撤销结果,或者采取不同的方法

在这里,我们首先将第一个元素添加到奇数列表中,然后交替添加到偶数和奇数列表中,直到到达最后一个元素。


有几个问题

首先是@capelical指出的一个,即
is/2
谓词在左侧实例化一个变量,在右侧实例化一个表达式的值。原始代码的表达式在左侧,值在右侧,并且将始终失败。因此:

X mod 2 is 1
应改为:

1 is X mod 2
或者,正如@capelical所说,
xmod2=:=1

其次,您的
append
尝试将单个元素追加到列表中<代码>追加仅对列表进行操作。因此:

append(Odd1, H, Odd)
应该是

even_odd([H|T],Even,Odd):- 0 is H mod 2,...
append(Odd1, [H], Odd)
第三,
append
和递归
偶数
调用的顺序将导致问题:

append(Odd1, [H], Odd),
even_odd(T,Even,Odd1).
当调用
append
时,
Odd1
是未实例化的,就像
Odd
一样。这将为
追加
生成无限多个解决方案,然后可接受的答案才能被基本情况绑定,从而导致堆栈溢出。解决此问题的简单方法是交换它们:

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)

或者干脆把它放在条款的开头。最后的条款,以及所有的修正,看起来像:

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
最后,还有另一个问题:列表的奇偶性是从列表的末尾而不是开始确定的。这是由于在当前列表片段上使用
长度
作为确定奇偶校验的方法的逻辑

要解决这个问题,在给定的代码结构中(同样,避开@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
这是一个可行的解决方案,但根本不是最优的,因为它没有利用代码中列表位置的“知识”(冗余地引入了一个计数或索引,以了解我们在列表中的位置)。

您可以使用它

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

TO希望数字处于偶数/奇数位置,而不是偶数/奇数值。是的,谢谢,也许我在阅读问题时有盲点。好吧,我知道我的解决方案在尝试了一段时间后没有多大意义,但问题主要是评估失败的原因。这就回答了我的问题,为什么支票是假的。现在看来一切都好了!:)这是一个很好的解决方案,虽然不是OP所要求的:我在谷歌上找到了比我更优雅的解决方案,但是我想在我的代码中找到问题所在…@mbrach我同意,但他们的解决方案中有一个无法轻易纠正的缺陷,除了添加更多的乱七八糟的东西,比如前后颠倒。这就是我添加这个示例的原因:在我看来,他们似乎在尝试用Prolog编写过程代码。我认为在序言中教授正确的思维方式是至关重要的。我刚刚想到了一个有趣的挑战。对任意数量的列表执行此操作,而不是两个列表。例如
divvy([1,5,4,8,6,4,4,2,0,4],3,X)?X=[[1,8,4,4],[5,6,2],[4,4,0]。
感谢您的努力。虽然我的问题主要是比较失败的原因,但这是一个很好的解决方案。不过我还是找到了一个更优雅的!我只是好奇为什么我的解决方案不能通过一个确定的点!;)总共+1。所有可以通过模式匹配表达的东西都应该通过模式匹配来表达。将列表与其奇偶位置关联的情况可以通过模式匹配清楚地表示出来,因此这种解决方案是非常可取的。请注意,您可以在所有方向上使用此版本,例如:
?-偶数(列表[a,b,c],[d,e,f])。
,这要么根本不可能,要么导致moster其他版本的端接问题,甚至在应用建议的更正后也不起作用
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).