在Prolog中跨模块组织数据和代码

时间:2016-07-20 20:55:47

标签: module prolog

我正在开发一个简单的Web服务,它将用户提供的事实添加到我的Prolog数据库中(使用assert)。我认为将这些动态事实("数据")与我对这些事实进行操作的服务规则("代码")分开是更好的,因此将它们分成两个不同的模块。主要原因是我想要定期将动态事实保存到磁盘,同时能够开发没有问题且独立于用户数据的代码。 我一直在使用assert(my_store:fact(...))将用户数据添加到my_store模块,然后在代码模块中我开始编写规则,如

:- module (my_code, [a_rule/1, ...]).

a_rule(Term) :-
   my_store:fact(...), ...

一切似乎都没问题但是这种方法my_store在代码模块中是硬编码的,这有点令人担忧。例如,如果在一段时间后我决定更改数据模块名称,或者,我可能需要两个单独的数据模块,其中一个频繁持久,另一个只是偶尔执行持久性?

任何人都可以建议代码和数据组织中的最佳做法是什么?也许代码和数据的分离是反对" Prolog方式"?是否有任何好书深入讨论这些问题?

干杯,   亚切克

3 个答案:

答案 0 :(得分:3)

这是一个很好的问题,涉及几个非常重要的主题。

我希望以下评论可以帮助您理清大多数问题,如果您通过单独解决特定问题的新问题跟进您最感兴趣的问题,可能会更多。

  1. 首先,在接受用户代码作为输入时,请确保只允许将安全代码添加到您的程序中。在SWI-Prolog中,有safe_goal/1,它可以帮助您确保一些安全属性。它并不完美,但总比没有好。

  2. 再次在SWI-Prolog中,有library(persistency)。请仔细研究文档以了解它是如何工作的,以及如何使用它来将动态数据存储在磁盘上。

  3. 关于模块名称,我有两条评论:

    • 明确的模块目标资格非常少见。只需加载模块并使用其子句。
    • 请注意,您可以动态加载模块 。也就是说,没有什么可以阻止您将use_module/1与从其他地方获取的参数一起使用。这使您可以更灵活地指定要从中获取定义的模块,而无需更改和重新编译代码。
  4. 关于代码和数据的分离:在Prolog中,没有这样的分离。

    所有Prolog条款都是 Prolog条款。

    条款!条款所有方式下来!

答案 1 :(得分:2)

感谢@mat的建议让我更多地阅读和思考。现在,我可以为我的问题发布一个潜在的解决方案;不理想,不是使用persistency库,而是简单的第一次尝试。

如上所述,用户数据与assert(my_store:fact(...))一起存储。这意味着模块my_store是动态创建的,并且没有允许use_module使用的文件。但是,我可以使用import/1谓词来导入动态断言的事实,所以我的解决方案看起来像这样:

:- module(my_code, [a_rule/1, ...]).

:- initialization import_my_store.

import_my_store :-
   import(my_store:fact/1),
   import(my_store:another_fact/1),
   ...

a_rule(Term) :-
   fact(...), ...

请注意,我可以使用fact/1而无需明确指定my_store模块。我也可以轻松地将用户数据转储到文件中。

save_db(File) :-
   tell(File),
   my_store:listing,
   told.

缺点是初始化时import/1次调用会生成警告,例如:import/1: my_store:fact/1 is not exported (still imported into my_code)。但这不是一个大问题,因为它们仍导入my_code ,我可以使用用户事实而无需明确的模块规范。

期待听到任何评论。欢呼声,

答案 2 :(得分:2)

使用Logtalk的解决方案,它提供了模块的替代方案。首先使用您的代码定义一个对象:

:- object(my_code).

    :- public([a_rule/1, ...]).

    :- private([fact/1, another_fact/1, ...]).
    :- dynamic([fact/1, another_fact/1, ...]).

    a_rule(Term) :-
        ::fact(...), ...

    ...

:- end_object.

然后,根据需要动态创建任意数量的数据存储作为my_code对象的扩展(派生原型):

?- create_object(my_store, [extends(my_code)], [], []).

要查询数据存储,只需向其发送一条消息:

?- my_store::a_rule(Term).

如果需要,create_object/4内置谓词可以加载商店的持久性文件(这样你就可以从你离开的地方继续):

?- create_object(my_store, [extends(my_code)], [include('my_store.pl'))], []).

用户数据可以通过按预期声明保存在数据存储中:

?- my_store::assertz(fact(...)).

您需要一个谓词将数据存储转储为文件,作为my_code对象中的公共谓词。例如:

:- public(dump/0).
dump :-
    self(Self),
    atom_concat(Self, '.pl', File),
    tell(File),
    dump_dynamic_predicates,
    told.

dump_dynamic_predicates :-
    current_predicate(Functor/Arity),
    functor(Template, Functor, Arity),
    predicate_property(Template, (dynamic)),
    ::Template,
    write_canonical(Template), write('.\n'),
    fail.
dump_dynamic_predicates.

现在您可以通过输入以下内容来转储数据存储:

?- my_store::dump.

请注意,使用此解决方案同时拥有任意数量的数据存储是微不足道的。如果数据存储需要特定版本的代码,那么您只需扩展代码对象,然后创建专用数据存储作为该专用对象的扩展。