如何在序言中编写数字分类器?

How to write number classifiers in prolog?

第一次玩 Prolog,虽然我认为我知道它基本上有什么用,但我发现很难用它完成任何事情。所以,我试图找到最简单的任务,但我什至没能完成。

我认为这是因为我不知道 prolog 数据类型(数字)应该如何工作或者它们有特殊的语法。

所以,我对偶数进行分类的第一次尝试是:

even(0).
even(X) :- even(X-2).

结果:查询的堆栈溢出:even(2)。

所以我想如果不是这样,那么也许是:

even(0).
even(X+2) :- even(X).

even(2) 的结果:false。

所以我的简单问题是:如何在序言中编写这么简单的东西?是不是因为我用了数字,所以一切都不起作用?

使用is/2强制算术求值。就其本身而言,Prolog 术语只是结构符号实体,X-2 是 arity 2 的复合术语,-(X,2):

3 ?- write_canonical( X-2 ).
-(_,2)
true.

但是is用于算术表达式:

4 ?- Z is 5-2.
Z = 3.

因此您的定义应该是

even(X):-  X=:=0 -> true 
        ;  X > 0 -> Y is X-2, even(Y).

这种定义的缺点是不能以生成的方式调用它,例如even(X) 一个接一个地生成所有偶数。

它只适用于检查给定的数字。为简单起见,它会忽略负数并始终失败。

为什么不按正常方式进行:

is_even(X) :-
    X /\ 0x1 =:= 0.

如果你想在回溯时枚举非负偶数当参数未绑定时,这完全是另一回事。这可能很容易说:

even(X) :-
    between(0, infinite, X),
    is_even(X).

您可以像这样使用第二个定义:

?- even(X).
X = 0 ;
X = 2 ;
X = 4 ;
X = 6 . % and so on

is_even/1even/1有一些区别:

  • is_even/1 适用于任何正整数或负整数
  • 令人惊讶的是,
  • is_even/1 也适用于计算结果为整数的表达式,例如 X = 3, ..., is_even(X + 1)。这是因为 =:= 接受两边的算术表达式。
  • even/1 使用 between/3,因此 X 的域和错误条件与 between/3 的第三个参数相同。
  • 因此,even/1 不适用于负整数或算术表达式。

等等,还有更多!

显然,between(0, infinite, X) 不是您可以在除 SWI 之外的几乎所有 Prolog 中执行的操作。因此,您可以使用另一个谓词来枚举正整数(列表长度):

even_f(X) :-
    length(_, X),
    is_even(X).

(为此感谢@false)