对谓词进行抽象
Abstracting over predicates
我正在尝试的练习从以下事实开始
byCar(auckland,hamilton).
byCar(hamilton,raglan).
byCar(valmont,saarbruecken).
byCar(valmont,metz).
byTrain(metz,frankfurt).
byTrain(saarbruecken,frankfurt).
byTrain(metz,paris).
byTrain(saarbruecken,paris).
byPlane(frankfurt,bangkok).
byPlane(frankfurt,singapore).
byPlane(paris,losAngeles).
byPlane(bangkok,auckland).
byPlane(singapore,auckland).
byPlane(losAngeles,auckland).
...并要求 reader 定义谓词 travel/3
,例如,
travel(valmont, losAngeles, T)
...会找到像
这样的解决方案
T = go(byCar(valmont, metz),
go(byTrain(metz, paris),
go(byPlane(paris, losAngeles)))).
这是我想出的:
travel(X,Y,go(byCar(X,Y))):-byCar(X,Y).
travel(X,Y,go(byTrain(X,Y))):-byTrain(X,Y).
travel(X,Y,go(byPlane(X,Y))):-byPlane(X,Y).
travel(X,Z,go(byCar(X,Y),T)):-byCar(X,Y),travel(Y,Z,T).
travel(X,Z,go(byTrain(X,Y),T)):-byTrain(X,Y),travel(Y,Z,T).
travel(X,Z,go(byPlane(X,Y),T)):-byPlane(X,Y),travel(Y,Z,T).
似乎有效...
?- travel(valmont, losAngeles, X).
X = go(byCar(valmont, saarbruecken), go(byTrain(saarbruecken, paris), go(byPlane(paris, losAngeles)))) ;
X = go(byCar(valmont, metz), go(byTrain(metz, paris), go(byPlane(paris, losAngeles)))) ;
false.
...但是我的眼睛很疼;所有的重复都是对抽象的呼喊。
我试图通过定义
来消除重复
oneLeg(X,Y):-byCar(X,Y);byTrain(X,Y);byPlane(X,Y).
...并将 travel/3
重新定义为
travel(X,Y,go(oneLeg(X,Y))):-oneLeg(X,Y).
travel(X,Z,go(oneLeg(X,Y),T)):-oneLeg(X,Y),travel(Y,Z,T).
...但结果还不完全:
?- travel(valmont, losAngeles, X).
X = go(oneLeg(valmont, saarbruecken), go(oneLeg(saarbruecken, paris), go(oneLeg(paris, losAngeles)))) ;
X = go(oneLeg(valmont, metz), go(oneLeg(metz, paris), go(oneLeg(paris, losAngeles)))) ;
false.
如何强制将结果中 oneLeg
的实例替换为 byCar
、byTrain
或 byPlane
"justifies" oneLeg
个实例?
firstACommentOnNamingThingsasInJavaByMixingTheCasesWhichIsHardToRead
: you_may_find_even_long_names_very_readable_when_using_underscores
.
其次,Prolog 是一种极其动态 的语言,您可以使用 call/N
元谓词系列和其他更高级别的 - 轻松构造和调用任意闭包对谓词进行排序,例如 (=..)/2
.
在您的示例中,考虑首先更改谓词名称以适应通常的 Prolog 命名约定,使用 下划线 分隔单词:
by_car(auckland, hamilton).
by_car(hamilton, raglan).
by_car(valmont, saarbruecken).
by_car(valmont, metz).
by_train(metz, frankfurt).
by_train(saarbruecken, frankfurt).
by_train(metz, paris).
by_train(saarbruecken, paris).
by_plane(frankfurt, bangkok).
by_plane(frankfurt, singapore).
by_plane(paris, los_angeles).
by_plane(bangkok, auckland).
by_plane(singapore, auckland).
by_plane(losAngeles, auckland).
现在,一个合适的抽象可能是谓词 means/1
,我们可以这样定义它:
means(plane).
means(train).
means(car).
很容易使用它来动态调用合适的谓词:
by_means(From, To, Means) :-
means(Means),
atom_concat(by_, Means, Pred),
call(Pred, From, To).
一种使用方法如下所示:
route(To, To) --> [].
route(From, To) --> [Step],
{ by_means(From, Next, Means),
Step =.. [Means,From,Next] },
route(Next, To).
示例查询和回答:
?- phrase(route(valmont, los_angeles), Rs).
Rs = [car(valmont, saarbruecken), train(saarbruecken, paris), plane(paris, los_angeles)] ;
Rs = [car(valmont, metz), train(metz, paris), plane(paris, los_angeles)] ;
false.
这其中的关键在于系统的命名约定和手段与谓词的对应关系。在这种情况下,动态构建对应以同时说明多个概念。为了提高效率、灵活性,甚至可能提高安全性,您当然也可以通过静态 Prolog 事实对通信本身进行编码。例如:
means_predicate(plane, by_plane).
means_predicate(train, by_train).
means_predicate(car, by_car).
by_means(From, To, Means) :-
means_predicate(Means, Pred),
call(Pred, From, To).
如果我必须这样做,我可能会尝试将 byCar、byPlane 和 byTrain 转换为一个 table、from_to_means。我发现您可以像这样手动执行此操作:
forall(byCar(From, To), assertz(from_to_means(From, To, car)))
然后飞机和火车也一样。在 SWI-Prolog 中也有术语扩展,所以也许你可以在三个原始 tables
之上插入
term_expansion(byCar(From, To), from_to_means(From, To, car)).
飞机火车也一样
那么你只需要评价from_to_means(From, To, Means)
或者写from_to_means(From, To, train)
就可以只选择一种交通工具了。
我正在尝试的练习从以下事实开始
byCar(auckland,hamilton).
byCar(hamilton,raglan).
byCar(valmont,saarbruecken).
byCar(valmont,metz).
byTrain(metz,frankfurt).
byTrain(saarbruecken,frankfurt).
byTrain(metz,paris).
byTrain(saarbruecken,paris).
byPlane(frankfurt,bangkok).
byPlane(frankfurt,singapore).
byPlane(paris,losAngeles).
byPlane(bangkok,auckland).
byPlane(singapore,auckland).
byPlane(losAngeles,auckland).
...并要求 reader 定义谓词 travel/3
,例如,
travel(valmont, losAngeles, T)
...会找到像
这样的解决方案T = go(byCar(valmont, metz),
go(byTrain(metz, paris),
go(byPlane(paris, losAngeles)))).
这是我想出的:
travel(X,Y,go(byCar(X,Y))):-byCar(X,Y).
travel(X,Y,go(byTrain(X,Y))):-byTrain(X,Y).
travel(X,Y,go(byPlane(X,Y))):-byPlane(X,Y).
travel(X,Z,go(byCar(X,Y),T)):-byCar(X,Y),travel(Y,Z,T).
travel(X,Z,go(byTrain(X,Y),T)):-byTrain(X,Y),travel(Y,Z,T).
travel(X,Z,go(byPlane(X,Y),T)):-byPlane(X,Y),travel(Y,Z,T).
似乎有效...
?- travel(valmont, losAngeles, X).
X = go(byCar(valmont, saarbruecken), go(byTrain(saarbruecken, paris), go(byPlane(paris, losAngeles)))) ;
X = go(byCar(valmont, metz), go(byTrain(metz, paris), go(byPlane(paris, losAngeles)))) ;
false.
...但是我的眼睛很疼;所有的重复都是对抽象的呼喊。
我试图通过定义
来消除重复oneLeg(X,Y):-byCar(X,Y);byTrain(X,Y);byPlane(X,Y).
...并将 travel/3
重新定义为
travel(X,Y,go(oneLeg(X,Y))):-oneLeg(X,Y).
travel(X,Z,go(oneLeg(X,Y),T)):-oneLeg(X,Y),travel(Y,Z,T).
...但结果还不完全:
?- travel(valmont, losAngeles, X).
X = go(oneLeg(valmont, saarbruecken), go(oneLeg(saarbruecken, paris), go(oneLeg(paris, losAngeles)))) ;
X = go(oneLeg(valmont, metz), go(oneLeg(metz, paris), go(oneLeg(paris, losAngeles)))) ;
false.
如何强制将结果中 oneLeg
的实例替换为 byCar
、byTrain
或 byPlane
"justifies" oneLeg
个实例?
firstACommentOnNamingThingsasInJavaByMixingTheCasesWhichIsHardToRead
: you_may_find_even_long_names_very_readable_when_using_underscores
.
其次,Prolog 是一种极其动态 的语言,您可以使用 call/N
元谓词系列和其他更高级别的 - 轻松构造和调用任意闭包对谓词进行排序,例如 (=..)/2
.
在您的示例中,考虑首先更改谓词名称以适应通常的 Prolog 命名约定,使用 下划线 分隔单词:
by_car(auckland, hamilton). by_car(hamilton, raglan). by_car(valmont, saarbruecken). by_car(valmont, metz). by_train(metz, frankfurt). by_train(saarbruecken, frankfurt). by_train(metz, paris). by_train(saarbruecken, paris). by_plane(frankfurt, bangkok). by_plane(frankfurt, singapore). by_plane(paris, los_angeles). by_plane(bangkok, auckland). by_plane(singapore, auckland). by_plane(losAngeles, auckland).
现在,一个合适的抽象可能是谓词 means/1
,我们可以这样定义它:
means(plane). means(train). means(car).
很容易使用它来动态调用合适的谓词:
by_means(From, To, Means) :- means(Means), atom_concat(by_, Means, Pred), call(Pred, From, To).
一种使用方法如下所示:
route(To, To) --> []. route(From, To) --> [Step], { by_means(From, Next, Means), Step =.. [Means,From,Next] }, route(Next, To).
示例查询和回答:
?- phrase(route(valmont, los_angeles), Rs). Rs = [car(valmont, saarbruecken), train(saarbruecken, paris), plane(paris, los_angeles)] ; Rs = [car(valmont, metz), train(metz, paris), plane(paris, los_angeles)] ; false.
这其中的关键在于系统的命名约定和手段与谓词的对应关系。在这种情况下,动态构建对应以同时说明多个概念。为了提高效率、灵活性,甚至可能提高安全性,您当然也可以通过静态 Prolog 事实对通信本身进行编码。例如:
means_predicate(plane, by_plane). means_predicate(train, by_train). means_predicate(car, by_car). by_means(From, To, Means) :- means_predicate(Means, Pred), call(Pred, From, To).
如果我必须这样做,我可能会尝试将 byCar、byPlane 和 byTrain 转换为一个 table、from_to_means。我发现您可以像这样手动执行此操作:
forall(byCar(From, To), assertz(from_to_means(From, To, car)))
然后飞机和火车也一样。在 SWI-Prolog 中也有术语扩展,所以也许你可以在三个原始 tables
之上插入term_expansion(byCar(From, To), from_to_means(From, To, car)).
飞机火车也一样
那么你只需要评价from_to_means(From, To, Means)
或者写from_to_means(From, To, train)
就可以只选择一种交通工具了。