使谓词可逆

Jef*_*ler 5 prolog

我是初学者; 我来自结构化编程背景,因为很明显:)

我正在构建一个涉及撤消数字的序言查询; 例如.reverse_num(123,X)结果X = 321.我提出了以下定义,但它仅在我提供数字作为第一个参数时才有效.

reverse_num(Num, Revnum) :-
  number_chars(Num, Atoms),
  reverse(Revatoms, Atoms),
  number_chars(Reversed, Revatoms),
  Reversed = Revnum.
Run Code Online (Sandbox Code Playgroud)

number_chars/2如果我做的话,谓词不喜欢未经证实的变量:( reverse_num(X,123)我期待X的是321).

我是否努力使reverse_num做一些不应该做的事情(应该理解它只能用数字作为第一个参数而变量作为第二个)?

或者是否有一种简单/直接的方式来处理变量作为第一个参数?

fal*_*lse 6

关系命名

在开始编码之前,让我们退后一步。毕竟,Prolog 中的想法是定义关系。你的名字reverse_num/2更像是暗示了一些行动,num_reversed/2可能是一个更好的名字。

确定关系

你的定义还不错,让我把它改写为1

num_reversed(Num, Reversed) :-
   number_chars(Num, Chars),
   reverse(Chars, Revchars),
   number_chars(Reversed, Revchars).

?- num_reversed(123,X).
X = 321.

?- num_reversed(1230,X).
X = 321.

?- num_reversed(12300,X).
X = 321.
Run Code Online (Sandbox Code Playgroud)

你看到图案了吗?所有数字N*10^I都有相同的结果!

现在,让我们再问一些:

?- num_reversed(Num, 321).
ERROR: number_chars/2: Arguments are not sufficiently instantiated
Run Code Online (Sandbox Code Playgroud)

嗯,我们期待什么?实际上,我们希望全部123*10^I打印出来。那是无限多的解决方案。所以上面的查询,如果正确回答,将需要打印无限多个解决方案。如果我们直接打印它们,那将花费我们整个宇宙的生命,甚至更多!

正是因为这个原因,Prolog 反而产生了一个实例化错误。通过这个,Prolog 本质上是这样说的:

这个目标太笼统了,我无法做出很好的回答。也许有无数个解决方案,也许没有。我不知道。但至少我通过发出错误来表明这一点。要消除此错误,您需要更多地实例化参数。

所以 Prolog 给出的答案一点也不!事实上,产生一个干净的错误比错误地失败要好得多。一般来说,Prolog 的错误通常是对您可能遇到的语义问题的非常有用的提示。查看所有错误类如何。

协程

正如其他答案所建议的那样,协同、使用when/2可能会解决这个问题。但是,协程本身存在很多语义问题。并非没有原因,像XSB这样的系统不提供它,因为与包含检查相关的许多问题。与其兼容的实现将出乎意料地低效。

但就这一点而言,我们可以通过查询它来使我们的定义更加通用

 ?- when(nonvar(Num), num_reversed(Num, Reversed)).
 when(nonvar(Num), num_reversed(Num, Reversed)).
Run Code Online (Sandbox Code Playgroud)

现在,我们返回的正是我们输入的查询的答案。这也称为挣扎。因此,有在一个紧凑的方式来表示无限5月解决方案的方式!但是,这需要付出相当高的代价:您不再知道解决方案是否存在。考虑到:

?- when(nonvar(Num), num_reversed(Num, -1)).
when(nonvar(Num), num_reversed(Num, -1)).
Run Code Online (Sandbox Code Playgroud)

其他人也建议等待,nonvar(Reversed)这只有在我们产生无限多个答案时才是正确的 - 但是,正如我们所看到的 - 这需要太多时间。

在 80 年代初期,协同工作看起来是一条非常有前途的道路。然而,它从来没有真正流行过作为一种通用的编程方法。大多数时候,你会遇到太多的挣扎,这只是一种痛苦,甚至比实例化错误更难处理。

然而,这种发展的一个更有前途的后代受到了限制。在那里,机制的定义更加清晰。出于实际目的,程序员将只使用现有的库,如 CLPFD、CLPQ 或 CHR。实现自己的库本身就是一个非常重要的项目。事实上,甚至有可能提供一种num_reversed/2使用的实现,library(clpfd)即限制与整数情况的关系。

模式依赖条件

传统上,许多此类问题是通过显式测试实例来解决的。完全使用nonvar/1ground/1喜欢条件 in执行此操作是一种很好的风格when/2- 其他类型的测试谓词很容易导致错误,正如另一个答案所证明的那样。

num_reversed(Num, Reversed) :-
   (  nonvar(Num)
   -> original_num_reversed(Num, Reversed)
   ;  original_num_reversed(Reversed, Base),
      (  Base =:= 0
      -> Num is 0
      ;  length(_, I),
         Num is Base*10^I
      )
   ).
Run Code Online (Sandbox Code Playgroud)

对于使用基数为 2 的浮点数,上面的代码很快就会中断,而对于基数 10 则稍晚一些。实际上,对于经典的基数 2 浮点数,关系本身没有多大意义。

对于 的定义number_chars/2,ISO/IEC 13211-1:1995 有以下模板和模式子条款

8.16.7.2 模板和模式

number_chars(+number, ?character_list)
number_chars(-number, +character_list)

第一种情况是当第一个参数被实例化时(因此nonvar)。第二种情况,当第一个参数未实例化时。在这种情况下,必须实例化第二个参数。

但是请注意,由于非常相似的问题,number_chars/2不是关系。例如,Chs = ['0','0'], number_chars(0, Chs)成功,而number_chars(0, Chs), Chs = ['0','0']失败。

非常精美的印刷品

1 这种重写是必要的,因为在许多 Prolog 中,reverse/2只有在第一个参数已知时才会终止。在 SWI 中,由于一些特殊的低效率,这种重写是必要的。