object - 扩展模块谓词的 Prolog 设计模式

标签 object design-patterns module prolog

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

:- 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”一起使用,例如:
:- 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).
%[...]

我需要 :
  • 上选择 db运行时间 ,而不是编译。
  • 同时使用一个或多个数据库。
  • 扩展数据库,例如。与扩展“familytree_simpson”数据库模块的其他辛普森家族成员一起创建“familytree_simpson_extended”数据库模块(参见上面的示例)
  • 成为 sw-prolog 合规。

  • 目前,我尝试使用 term_expansion/2 , discontiguous/1 , multifile/1 , dynamic/1thread_local/1指令,但是:
  • term_expansion/2似乎只在编译时可用,
  • discontiguous/1 , multifile/1 ,未改编,
  • prolog 中的动态 dbs 被视为一种“邪恶”的做法,但是许多包和库都使用它(例如 penginesbroadcast 模块、http lib)。
  • thread_local/1没有很好的文档记录,并且似乎不经常在 prolog 源代码(swi-prolog)中使用。


  • 通过使用动态谓词,我将以前的代码更新如下:
    %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).
    

    可以使用不同的数据库,如下所示:
    ?- main_simpson_all.
    [homer,homer,abraham]
    true.
    ?- main_xyz.
    [james]
    true.
    

    所以,很抱歉帖子的长度。问题:
  • 使用这种动态谓词解决方案要考虑的标准和优缺点是什么?这是一个好的解决方案吗?
  • prolog 在干净/健壮的代码中做到这一点的最佳实践/特定设计模式是什么?**
  • 如何使用 thread_local/1而是 dynamic/1并封装对新线程的调用以避免清理数据库?
  • 最佳答案

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

    :- 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/1 的定义, female/1 , 和 parent/2从 self 开始,即在对象中,即数据库中,它将接收有关家庭关系的查询。从您的示例代码派生的一个示例是:
    :- 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.
    

    您可以根据需要添加任意数量的家庭数据库,同时加载它们,并随意定义它们的特化。例如:
    :- 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,您可以简单地键入:
    ?- pack_install(logtalk).
    

    更新

    在您对此解决方案的评论中,您询问了将数据库注入(inject)到家谱对象逻辑中的问题。这很容易,但也需要不同的方法。先定义familytree作为:
    :- 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/1 , female/1 , 和 parent/2作为本地谓词,但它们也被声明为多文件谓词。现在我们需要在 familytree 中“注入(inject)”一个家庭数据库。目的:
    :- 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 文件):
    ?- {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.
    

    关于object - 扩展模块谓词的 Prolog 设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32697056/

    相关文章:

    java - 在循环内创建对象

    java - 如何获取给定值是整数还是字符串

    c# - 具有设计模式统计信息的示例项目

    c++ - 面向对象设计 - 将 C 应用程序转换为 C++

    javascript - 自动导入具有相同文件后缀的模块

    javascript - React 组件和模块导出

    python - 如何在 python 中交换 2个对象的身份?

    javascript - 初始化 jQuery 函数并传递带有字符串的变量

    python - 在 python 中实现访问者设计模式时是否需要 accept()?

    macos - CPAN 安装几乎无法在 Mac 上运行,这正常吗?