如何在Prolog中创建双向谓词?

And*_*kin 9 prolog

在Prolog中,有一些谓词是双向的(甚至是"多向的"),因为人们可以用许多不同的方式将变量集分成输入和输出变量.例如,谓词number_string就像两个方向的双射:

number_string(N, "42"). % finds solution N = 42
number_string(42, S).   % finds solution S = "42"
Run Code Online (Sandbox Code Playgroud)

然而,这些奇妙的财产似乎并没有被条款的结合所保留.例如,考虑以下两个谓词,它们简单地将字符串翻译3x^2成抽象语法树的片段,如term(3,2):

parse_stuff(String, term(Coeff, Pow)) :- 
  string_concat(CoeffStr, MonomialStr, String),
  string_concat("x^", PowStr, MonomialStr),
  number_string(Coeff, CoeffStr),
  number_string(Pow, PowStr).

write_stuff(String, term(Coeff, Pow)) :- 
  number_string(Pow, PowStr),
  number_string(Coeff, CoeffStr),
  string_concat("x^", PowStr, MonomialStr),
  string_concat(CoeffStr, MonomialStr, String).
Run Code Online (Sandbox Code Playgroud)

除了右侧的定义子句的顺序相反之外,两个谓词都完全相同.右侧的所有条款本身都是双向的.

这是一个示例会话:

?- parse_stuff("3x^2", X).
X = term(3, 2).

?- parse_stuff(X, term(3, 2)).
ERROR: string_concat/3: Arguments are not sufficiently instantiated

?- write_stuff(X, term(3, 2)).
X = "3x^2".

?- write_stuff("3x^2", X).
ERROR: number_string/2: Arguments are not sufficiently instantiated
Run Code Online (Sandbox Code Playgroud)

这两个谓词只能以一种方式工作,尽管它们都是明显的双射,这非常令人难过.

问题包括两部分(有两个要求:易于使用,易于维护):

  1. 是否有任何可靠的方法在Prolog中构建双向谓词?
  2. 是否有可能避免代码重复(即维护两个版本:一个正常,一个反转)?

也许有一些标志告诉"必要时反转条款的顺序"?

我目前正在使用SWI-Prolog 7.1.x,如果它是相关的.


↓跳转到我自己的答案↓

mig*_*ilg 5

有两种方法可以解决此问题:要么使用一个额外的参数来说明顺序应该是什么,然后实施一个基于谓词的顶层谓词,以选择要使用的谓词,要么编写一个使该谓词成为条件的顶层谓词。通过测试哪些参数是免费的而无需外部信息来进行选择。在后者中,您可能会使用var/1nonvar/1逻辑外的预定义谓词。我在使定句式语法可逆时广泛使用了此方法,以便可以将它们用于解析和生成文本,这样就避免了编写程序和维护程序时的大量工作。

更新以响应其他要求。

为了避免代码重复,您应该尝试识别两个版本中的通用部分,并为每个部分定义谓词。然后,您可以在需要的地方调用这些谓词,而不必在很多地方使用它们的代码。但这在太大的示例中并没有真正的用处:也许您可以尝试使用差异列表并避免使用级联来简化谓词。

  • 在某些情况下,@ AndreyTyukin可以将谓词重构为更*关系*(即您所说的*双向*)。但是,并非总是有如此优雅的解决方案。 (2认同)