Prolog 中的功能模式
Functional patterns in Prolog
如何创建一个带有另一个谓词和 returns 它的派生版本的谓词?
例如,成对谓词可以相当机械地扩展以应用于列表:
all_whatever(_, []).
all_whatever(X, [Y|T]) :-
whatever(X, Y),
all_whatever(X, T).
的定义是什么
pairwise_listwise(whatever, all_whatever).
如果不是 possible/common/clunky/violates 原则,替代模式是什么?
有两种不同的方法可以实现您想要的。最简单且可能是首选的方法是定义一个元谓词,它接受任何二元谓词并将其应用于列表的所有元素,如下所示:
listwise(_,_,[]).
listwise(P,Y,[X|Xs]) :-
call(P,Y,X),
listwise(P,Y,Xs).
然后您可以将其称为 listwise(whatever, Y1, Xs1)
以将 whatever
应用于 Y1
以及 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).
加载此代码时,该子句将被扩展并替换为定义新谓词的两个子句 all_whatever
。现在可以调用 all_whatever(Y1,Xs1)
.
我更喜欢第一种方法(概念上更简单并且适用于 Prolog 版本)但我认为了解术语扩展机制的存在也很有用。
如何创建一个带有另一个谓词和 returns 它的派生版本的谓词?
例如,成对谓词可以相当机械地扩展以应用于列表:
all_whatever(_, []).
all_whatever(X, [Y|T]) :-
whatever(X, Y),
all_whatever(X, T).
的定义是什么
pairwise_listwise(whatever, all_whatever).
如果不是 possible/common/clunky/violates 原则,替代模式是什么?
有两种不同的方法可以实现您想要的。最简单且可能是首选的方法是定义一个元谓词,它接受任何二元谓词并将其应用于列表的所有元素,如下所示:
listwise(_,_,[]).
listwise(P,Y,[X|Xs]) :-
call(P,Y,X),
listwise(P,Y,Xs).
然后您可以将其称为 listwise(whatever, Y1, Xs1)
以将 whatever
应用于 Y1
以及 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).
加载此代码时,该子句将被扩展并替换为定义新谓词的两个子句 all_whatever
。现在可以调用 all_whatever(Y1,Xs1)
.
我更喜欢第一种方法(概念上更简单并且适用于 Prolog 版本)但我认为了解术语扩展机制的存在也很有用。