我正在开发一个简单的Web服务,它将用户提供的事实添加到我的Prolog数据库中(使用assert
)。我认为将这些动态事实("数据")与我对这些事实进行操作的服务规则("代码")分开是更好的,因此将它们分成两个不同的模块。主要原因是我想要定期将动态事实保存到磁盘,同时能够开发没有问题且独立于用户数据的代码。
我一直在使用assert(my_store:fact(...))
将用户数据添加到my_store
模块,然后在代码模块中我开始编写规则,如
:- module (my_code, [a_rule/1, ...]).
a_rule(Term) :-
my_store:fact(...), ...
一切似乎都没问题但是这种方法my_store
在代码模块中是硬编码的,这有点令人担忧。例如,如果在一段时间后我决定更改数据模块名称,或者,我可能需要两个单独的数据模块,其中一个频繁持久,另一个只是偶尔执行持久性?
任何人都可以建议代码和数据组织中的最佳做法是什么?也许代码和数据的分离是反对" Prolog方式"?是否有任何好书深入讨论这些问题?
干杯, 亚切克
答案 0 :(得分:3)
这是一个很好的问题,涉及几个非常重要的主题。
我希望以下评论可以帮助您理清大多数问题,如果您通过单独解决特定问题的新问题跟进您最感兴趣的问题,可能会更多。
首先,在接受用户代码作为输入时,请确保只允许将安全代码添加到您的程序中。在SWI-Prolog中,有safe_goal/1
,它可以帮助您确保一些安全属性。它并不完美,但总比没有好。
再次在SWI-Prolog中,有library(persistency)。请仔细研究文档以了解它是如何工作的,以及如何使用它来将动态数据存储在磁盘上。
关于模块名称,我有两条评论:
use_module/1
与从其他地方获取的参数一起使用。这使您可以更灵活地指定要从中获取定义的模块,而无需更改和重新编译代码。关于代码和数据的分离:在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.
请注意,使用此解决方案同时拥有任意数量的数据存储是微不足道的。如果数据存储需要特定版本的代码,那么您只需扩展代码对象,然后创建专用数据存储作为该专用对象的扩展。