Prolog 使谓词可逆

Prolog 使谓词可逆,prolog,Prolog,我不熟悉prolog;我来自结构化编程背景,这一点很明显:) 我正在建立一个prolog查询,它涉及到一个数字的反转;例如,reverse_num(123,X)导致X=321。我提出了以下定义,但它只有在我提供一个数字作为第一个参数时才起作用 reverse_num(Num, Revnum) :- number_chars(Num, Atoms), reverse(Revatoms, Atoms), number_chars(Reversed, Revatoms), Revers

我不熟悉prolog;我来自结构化编程背景,这一点很明显:)

我正在建立一个prolog查询,它涉及到一个数字的反转;例如,
reverse_num(123,X)
导致
X=321
。我提出了以下定义,但它只有在我提供一个数字作为第一个参数时才起作用

reverse_num(Num, Revnum) :-
  number_chars(Num, Atoms),
  reverse(Revatoms, Atoms),
  number_chars(Reversed, Revatoms),
  Reversed = Revnum.
如果我这样做,
number\u chars/2
谓词不喜欢未经验证的变量:
reverse\u num(X,123)
(我希望
X
321

我是否试图让reverse_num做一些不应该做的事情(是否应该理解为只使用数字作为第一个参数,变量作为第二个参数)


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

谓词具有以下特征:

number_chars(?Number, ?CharList) 
但是,尽管签名没有完全指定,但至少必须实例化
Number
CharList
。这就是错误发生的原因

如果你打电话:

reverse_num(Num,123)
您将调用
number\u chars/2
,此时两者都未统计,因此谓词将出错

解决这个问题的一个不太好的方法是询问
Num
RevNum
是否为
number/2
s。您可以通过编写两个版本来实现这一点。它还将过滤其他调用,如
reverse_num(f(a),b)
等:

reverse_num(Num,Revnum) :-
    \+ number(Num),
    \+ number(Revnum),
    throw(error(instantiation_error, _)).

reverse_num(Num, Revnum) :-
    ground(Num),
    number(Num),
    !,
    number_chars(Num, Atoms),
    reverse(Revatoms, Atoms),
    number_chars(Revnum, Revatoms).

reverse_num(Num, Revnum) :-
    ground(Revnum),
    number(Revnum),
    reverse_num(Revnum,Num).
或者,如果您使用了两轮(例如,
reverse_num(X,Y)。
)实例化错误,而不是@false所说的
false

reverse_num(Num,Revnum) :-
    \+ number(Num),
    \+ number(Revnum),
    !,
    throw(error(instantiation_error, _)).

reverse_num(Num, Revnum) :-
    number(Num),
    !,
    number_chars(Num, Atoms),
    reverse(Revatoms, Atoms),
    number_chars(Revnum, Revatoms).

reverse_num(Num, Revnum) :-
    reverse_num(Revnum,Num).
剪切(
)在行为上不是必需的,但会稍微提高性能。我并不真正喜欢这个实现,但是Prolog不能总是完全使谓词可逆,因为(a)可逆性是一个不可判定的属性,因为Prolog是图灵完备的;(b)Prolog的一个特点是体原子是从左到右计算的。否则,对某些程序进行评估将需要很长时间。有一些逻辑引擎可以按任意顺序执行此操作,因此将成功完成此任务

如果
谓词/2
是可交换的,则可以概括为以下模式:

predicate(X,Y) :-
    predicate1(X,A),
    predicate2(A,B),
    % ...
    predicaten(C,Y).
predicate(X,Y) :-
    predicate(Y,X).

但是你不能简单地将最后一个子句添加到理论中,因为它可以无限循环。

很高兴看到有人也担心定义在绑定参数集中没有限制的灵活规则

如果使用支持协同路由
when/2
内置谓词的Prolog系统(例如SICStus Prolog、SWI Prolog或YAP),请尝试:

这就产生了:

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

?- reverse_num( X, 123 ).
X = 321 .
(感谢提供这些答案的人:)

关系命名 在跳转到编码之前,让我们后退一步。毕竟,Prolog中的思想是定义关系。你的名字
reverse\u num/2
更像是暗示了一些行为,
num\u reversed/2
可能是一个更好的名字

确定关系 你的定义没那么糟糕,让我把它改写成1:

你看到图案了吗?所有编号
N*10^I
的结果相同

现在,让我们再问一些问题:

?- num_reversed(Num, 321).
ERROR: number_chars/2: Arguments are not sufficiently instantiated
嗯,我们期待什么?实际上,我们想把所有的
123*10^I
都打印出来。这是无限多的解决方案。所以,如果正确回答上述问题,将需要打印无限多个解决方案。如果我们直接打印,那将花费我们整个宇宙的一生,甚至更多

正是由于这个原因,Prolog产生了一个实例化错误。通过这一点,Prolog基本上陈述了:

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

所以Prolog给出的答案一点也不差!事实上,产生一个干净的错误比错误地失败要好得多。一般来说,Prolog的错误通常是一个非常有用的提示,提示您可能存在哪些语义问题。看看怎么做

共程 正如其他答案所建议的,协同路由,使用
when/2
可以解决这个问题。然而,协同路由本身存在许多语义问题。并非没有理由,由于与包容检查相关的许多问题,像这样的系统不提供它。与之兼容的实现将出人意料地低效

但是为了说明问题,我们可以通过如下方式查询它,使我们的定义更加通用

 ?- when(nonvar(Num), num_reversed(Num, Reversed)).
 when(nonvar(Num), num_reversed(Num, Reversed)).
现在我们返回的答案正好是我们输入的查询。这也称为挣扎。因此,有一种方法可以用一种紧凑的方式表示无限多个解!然而,这需要付出相当高的代价:您不再知道解决方案是否存在。想想:

?- when(nonvar(Num), num_reversed(Num, -1)).
when(nonvar(Num), num_reversed(Num, -1)).
其他人也建议等待
非AR(反向)
,这只有在我们给出无限多答案的情况下才是正确的,但是,正如我们所看到的,这需要太多的时间

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

然而,这种发展带来的一个更有希望的结果是制约因素。在那里,机制的定义更加清晰。出于实际目的,程序员将只使用现有库,如CLPFD、CLPQ或CHR。实现自己的库本身就是一个非常重要的项目。事实上,甚至可以使用
library(clpfd)
提供
num\u reversed/2
的实现,也就是说,将关系限制到整数大小写

模式相关条件句 传统上,许多这样的问题都是通过测试i来解决的
 ?- when(nonvar(Num), num_reversed(Num, Reversed)).
 when(nonvar(Num), num_reversed(Num, Reversed)).
?- when(nonvar(Num), num_reversed(Num, -1)).
when(nonvar(Num), num_reversed(Num, -1)).
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
      )
   ).
reverse_num(X, Y) :-
    when((nonvar(Xs);nonvar(Ys)), reverse(Xs, Ys)),
    when((nonvar(X) ;nonvar(Xs)), atomic_chars(X, Xs)),
    when((nonvar(Y) ;nonvar(Ys)), atomic_chars(Y, Ys)).
% generalized... thanks Pasaba Por Aqui
:- meta_predicate when_2(0).
when_2(P) :-
    strip_module(P,_,Q),
    Q =.. [_,A0,A1],
    when((ground(A0);ground(A1)), P).

reverse_num(X, Y) :-
    maplist(when_2, [reverse(Xs, Ys), atomic_chars(X, Xs), atomic_chars(Y, Ys)]).
reverse_num(X, Y) :-
    when_2(reverse(Xs, Ys)),
    when_2(atomic_chars(X, Xs)),
    when_2(atomic_chars(Y, Ys)).
reverse_num(X, Y) -:-
    reverse(Xs, Ys),
    atomic_chars(X, Xs),
    atomic_chars(Y, Ys).
:- op(900, xfy, ++).
A ++ B ++ C :- when_2(A), B ++ C.
A ++ B :- when_2(A), when_2(B).

reverse_num(X, Y) :- 
    reverse(Xs, Ys) ++ atomic_chars(X, Xs) ++ atomic_chars(Y, Ys).
reverse_number(X,Y) :- number(X) , ! , rev(X,Y) .
reverse_number(X,Y) :- number(Y) , ! , rev(Y,X) .

rev(N,R) :-
  N < 0 ,
  ! ,
  A is abs(N) ,
  rev(A,T) ,
  R is - T
  .
rev(N,R) :-
  number_chars(N,Ns) ,
  reverse(Ns,Rs) ,
  number_chars(R,Rs)
  .