在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)
这两个谓词只能以一种方式工作,尽管它们都是明显的双射,这非常令人难过.
问题包括两部分(有两个要求:易于使用,易于维护):
也许有一些标志告诉"必要时反转条款的顺序"?
我目前正在使用SWI-Prolog 7.1.x,如果它是相关的.
有两种方法可以解决此问题:要么使用一个额外的参数来说明顺序应该是什么,然后实施一个基于谓词的顶层谓词,以选择要使用的谓词,要么编写一个使该谓词成为条件的顶层谓词。通过测试哪些参数是免费的而无需外部信息来进行选择。在后者中,您可能会使用var/1或nonvar/1逻辑外的预定义谓词。我在使定句式语法可逆时广泛使用了此方法,以便可以将它们用于解析和生成文本,这样就避免了编写程序和维护程序时的大量工作。
更新以响应其他要求。
为了避免代码重复,您应该尝试识别两个版本中的通用部分,并为每个部分定义谓词。然后,您可以在需要的地方调用这些谓词,而不必在很多地方使用它们的代码。但这在太大的示例中并没有真正的用处:也许您可以尝试使用差异列表并避免使用级联来简化谓词。