1

如何创建一个接受另一个谓词并返回它的派生版本的谓词?

例如,成对谓词可以相当机械地扩展以应用于列表:

all_whatever(_, []).
all_whatever(X, [Y|T]) :- 
    whatever(X, Y), 
    all_whatever(X, T).

的定义是什么

pairwise_listwise(whatever, all_whatever).

如果不可能/常见/笨拙/违反原则,那么替代模式是什么?

4

1 回答 1

3

有两种不同的方法可以实现您想要的。最简单且可能首选的方法是定义一个元谓词,它接受任何二元谓词并将其应用于列表的所有元素,如下所示:

listwise(_,_,[]).
listwise(P,Y,[X|Xs]) :-
    call(P,Y,X),
    listwise(P,Y,Xs).

然后,您可以将其称为listwise(whatever, Y1, Xs1)应用于whateverY1每个元素Xs1

多亏了call/N元谓词,这才成为可能。请注意,此元谓词也可以将部分构建的目标作为第一个参数,因此替代公式可以是:

listwise(_,[]).
listwise(P,[X|Xs]) :-
    call(P,X),
    listwise(P,Xs).

然后将其称为listwise(whatever(Y1),Xs1)。这个版本的谓词实际上被称为maplist/2代替listwise,至少在 SWI-Prolog(在模块中library(apply))和 SICStus Prolog(在模块中library(lists))。

实现你想要的(实际上更接近你所要求的)的第二种方法是all_whatever/2使用术语扩展来实际定义一个新的谓词。术语扩展是一种在加载术语时重写术语的机制(参见例如 SWI-Prolog 中的更多详细信息:https ://www.swi-prolog.org/pldoc/doc_for?object=term_expansion/2 )。我在这里展示的是 SWI-Prolog 版本,它是通过为term_expansion/2谓词定义一个子句。这种机制在不同系统中的工作方式不同,或者完全缺失。

term_expansion(pairwise_listwise(PairPred,ListPred), ExpandedTerm) :-
    TerminalCall =.. [ListPred,_,[]],
    RecursiveCall =.. [ListPred,Y,[X|Xs]],
    SingleCall =.. [PairPred,Y,X],
    FinalCall =.. [ListPred,Y,Xs],
    ExpandedTerm = [TerminalCall, (RecursiveCall :- (SingleCall, FinalCall))].

在这个子句中,ExpandedTerm是一个列表,定义了我们要定义的两个子句,其中的所有术语都是使用谓词名称构建的=..。然后可以如下定义新谓词:

pairwise_listwise(whatever, all_whatever).

加载此代码时,该子句将被扩展并替换为定义新 predicate 的两个子句all_whatever。现在可以调用例如all_whatever(Y1,Xs1)

我更喜欢第一种方法(概念上更简单,并且适用于 Prolog 版本),但我认为了解术语扩展机制的存在也很有用。

于 2021-07-21T12:55:08.577 回答