我是初学者; 我来自结构化编程背景,因为很明显:)
我正在构建一个涉及撤消数字的序言查询; 例如.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做一些不应该做的事情(应该理解它只能用数字作为第一个参数而变量作为第二个)?
或者是否有一种简单/直接的方式来处理变量作为第一个参数?
在开始编码之前,让我们退后一步。毕竟,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/1并ground/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 中,由于一些特殊的低效率,这种重写是必要的。