本文介绍了Prolog设计模式以扩展模块谓词的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下,我们下面有familytree模块(简单示例):

Imagine we have the familytree module below (simple example) :

:- module(familytree, [
        father/2,
        mother/2,
        %[...]
    ]).

father(X,Y) :- male(X),parent(X,Y).
father(unknown, _) :- male(unknown).

mother(X,Y) :- female(X),parent(X,Y).
mother(unknown, _) :- female(unknown).

sister(X,Y) :- female(X),parent(Z,X),parent(Z,Y), X \= Y.

%[... other relation predicates ... ]

我想将此模块谓词与不同的"dbs"一起使用,例如:

I want to use this module predicates with different "dbs", for examples with :

:- module(familytree_xyz, []).

male(james).
male(fred).
male(mike).

female(betty).
female(sandra).

parent(james, fred).
parent(betty, fred).

或:

:- module(familytree_simpson, []).

male(homer).
male(bart).

female(marge).
female(lisa).

parent(homer, bart).
%[...]


我需要:


I need :

  • 运行时而不是在编译时选择数据库.
  • 同时使用一个或多个数据库.
  • 扩展db,例如.与其他Simpson家族成员一起扩展"familytree_simpson"数据库模块创建一个"familytree_simpson_extended"数据库模块(请参见上面的示例)
  • 符合 swi-prolog .
  • to choose db on runtime, not on compilation.
  • to use one or more dbs in same time.
  • to extend db, for eg. create a "familytree_simpson_extended" db module with other Simpson family members extending "familytree_simpson" db module (see above example)
  • to be swi-prolog compliant.

目前,我尝试使用term_expansion/2discontiguous/1multifile/1dynamic/1thread_local/1指令,但是:

For now, I tried to play with term_expansion/2, discontiguous/1, multifile/1, dynamic/1 and thread_local/1 directives, but :

  • term_expansion/2似乎仅在编译时可用,
  • discontiguous/1multifile/1,未改编,
  • 前言中的
  • 动态数据库被视为"邪恶"实践,但是许多软件包和库都使用它(例如,penginesbroadcast模块,http lib).
  • thread_local/1的文档不多,并且似乎在Prolog源代码(swi-prolog)中不经常使用.
  • term_expansion/2 seems only usable on compile time,
  • discontiguous/1, multifile/1, not adapted,
  • dynamic dbs in prolog are seen as an "Evil" practice, however lot of packages and libraries use its (pengines, broadcast module,http lib, for examples).
  • thread_local/1 is not very documented and seems not often used in prolog source code (swi-prolog).

在使用动态谓词时,我将以前的代码更新如下:

With playing with dynamic predicate, I update previous code as follow :

%familytree.pl
:- module(familytree, [
        familytree_cleanup_db/0,
        familytree_use_db/1,
        %[... previous declarations ...]
    ]).

dynamic male/1, female/1, parent/2.

familytree_cleanup_db :-
    retractall(male/1),
    retractall(female/1),
    retractall(parent/2).

familytree_use_db(ModuleName) :-
    assert(male(X) :- ModuleName:male(X)),
    assert(female(X) :- ModuleName:female(X)),
    assert(parent(X,Y) :- ModuleName:parent(X,Y)).

%[... previous predicates ...]

然后:

%main.pl
% use familytree tool predicates
:- use_module(familytree).

%load all familytree dbs at compile time.
:- use_module(familytree_xyz).
:- use_module(familytree_simpson).
:- use_module(familytree_simpson_extended).

main_xyz:-
    familytree_cleanup_db,
    familytree_use_db(familytree_xyz),
    process.

main_simpson_all :-
    familytree_cleanup_db,
    familytree_use_db(familytree_simpson),
    familytree_use_db(familytree_simpson_extended),
    process.

process :-
    findall(X, father(X,_), Xs),
    write(Xs).

也可以按以下方式与其他数据库一起使用:

And it's ok to use with different db as follow :

?- main_simpson_all.
[homer,homer,abraham]
true.
?- main_xyz.
[james]
true.


因此,对于帖子的篇幅,我们深表歉意.问题:

  1. 此动态谓词解决方案应考虑哪些标准,利弊?这是一个好的解决方案吗?

  1. What are the criteria, pros/cons to consider with this dynamic predicates solution ? is it a good solution ?

采用干净/健壮的代码来实现序言的最佳实践/特定设计模式是什么?**

What are the best practice / specific design pattern for prolog to do that in a clean / robust code ?**

使用thread_local/1代替dynamic/1并封装对新线程的调用以避免清理数据库的情况如何?

What's about using thread_local/1 instead dynamic/1 and encapsulate call to new thread to avoid cleanup db?

推荐答案

扩展我的评论,Logtalk解决方案非常简单.首先,使用家庭关系谓词定义一个根对象:

Expanding my comment, the Logtalk solution is straightforward. First, define a root object with the family relations predicate:

:- object(familytree).

    :- public([
        father/2, mother/2,
        sister/2, brother/2
    ]).

    :- public([
        parent/2,
        male/1, female/1
    ]).

    father(Father, Child) :-
        ::male(Father),
        ::parent(Father, Child).

    mother(Mother, Child) :-
        ::female(Mother),
        ::parent(Mother, Child).

    sister(Sister, Child) :-
        ::female(Sister),
        ::parent(Parent, Sister),
        ::parent(Parent, Child),
        Sister \== Child.

    brother(Brother, Child) :-
        ::male(Brother),
        ::parent(Parent, Brother),
        ::parent(Parent, Child),
        Brother \== Child.

:- end_object.

请注意,对male/1female/1parent/2的定义的查找始于 self ,即在对象数据库中,该数据库将接收有关家庭关系.从您的示例代码派生的示例将是:

Note that the lookup of the definitions of the male/1, female/1, and parent/2 starts in self, i.e. in the object, the database, that will receive the queries about the family relations. An example, derived from your sample code would be:

:- object(simpsons,
    extends(familytree)).

    male(homer).
    male(bart).

    female(marge).
    female(lisa).

    parent(homer, bart).
    parent(homer, lisa).
    parent(marge, bart).
    parent(marge, lisa).

:- end_object.

查询示例可以是:

?- simpsons::parent(homer, Child).
Child = bart ;
Child = lisa.

您可以根据需要创建任意数量的家庭数据库,同时加载它们,并随意定义它们的专业化.例如:

You can them as many family databases as you want, load them at the same time, and define specializations of them at will. For example:

:- object(simpsons_extended,
    extends(simpsons)).

    male(Male) :-
        ^^male(Male).
    male(abe).
    male(herb).

    female(Male) :-
        ^^female(Male).
    female(gaby).
    female(mona).

    parent(Parent, Child) :-
        ^^parent(Parent, Child).
    parent(abe, homer).
    parent(abe, herb).
    parent(gaby, herb).
    parent(mona, homer).

:- end_object.

此解决方案可以满足您的所有要求. SWI-Prolog是受支持的Prolog编译器之一.您可以使用Logtalk的安装程序进行安装.另外,对于SWI-Prolog,您只需输入:

This solution fulfills all your requirements. SWI-Prolog is one of the supported Prolog compilers. You can install Logtalk using on of its installers. Alternatively, for SWI-Prolog, you can simply type:

?- pack_install(logtalk).

更新

在对此解决方案的评论中,您询问了有关将数据库注入到家族树对象逻辑中的问题.这很容易,但是还需要使用其他方法.首先将familytree定义为:

In your comment to this solution, you asked about injecting a database into the family tree object logic. That's easy but it also requires a different approach. First define familytree as:

:- object(familytree).

    :- public([
        father/2, mother/2,
        sister/2, brother/2
    ]).

    :- public([
        parent/2,
        male/1, female/1
    ]).
    :- multifile([
        parent/2,
        male/1, female/1
    ]).

    father(Father, Child) :-
        male(Father),
        parent(Father, Child).

    mother(Mother, Child) :-
        female(Mother),
        parent(Mother, Child).

    sister(Sister, Child) :-
        female(Sister),
        parent(Parent, Sister),
        parent(Parent, Child),
        Sister \== Child.

    brother(Brother, Child) :-
        male(Brother),
        parent(Parent, Brother),
        parent(Parent, Child),
        Brother \== Child.

:- end_object.

请注意,这是替代方法,我们将male/1female/1parent/2称为本地谓词,但它们也被声明为多文件谓词.现在我们需要在familytree对象中注入"族数据库:

Note that is this alternative, we call male/1, female/1, and parent/2 as local predicates but they are also declared as multifile predicates. Now we need to "inject" a family database in the familytree object:

:- category(simpsons).

    :- multifile([
        familytree::male/1,
        familytree::female/1,
        familytree::parent/2
    ]).

    familytree::male(homer).
    familytree::male(bart).

    familytree::female(marge).
    familytree::female(lisa).

    familytree::parent(homer, bart).
    familytree::parent(homer, lisa).
    familytree::parent(homer, maggie).
    familytree::parent(marge, bart).
    familytree::parent(marge, lisa).
    familytree::parent(marge, maggie).

:- end_category.

用法示例(假设familytree.lgtsimpsons.lgt文件):

Usage example (assuming familytree.lgt and simpsons.lgt files):

?- {familytree, simpsons}.
...
yes

几个示例查询:

?- familytree::parent(homer, Child).
Child = bart ;
Child = lisa ;
Child = maggie.

?- familytree::male(Male).
Male = homer ;
Male = bart.

?- familytree::father(Father, Child).
Father = homer,
Child = bart ;
Father = homer,
Child = lisa ;
Father = homer,
Child = maggie ;
false.

这篇关于Prolog设计模式以扩展模块谓词的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-16 07:10