问题描述
要详细讨论下面的评论中的讨论,我的最后一个问题:我正在寻找有关构建技术或最佳做法的建议SWI-Prolog代码,以便能够使用和测试算法及其支持模块的替代性,可互换实现.
To elaborate on a discussion in the comments below my last question: I am looking for suggestions on techniques or best practices for structuring SWI-Prolog code in order to be able to use and test alternative, interchangeable implementations of algorithms and their supporting modules.
当前的情况可以使用下面的一个小例子来说明:用户提供一些输入数据(文件 data.pl
)并加载要应用算法的模块(文件 graph.pl
).算法模块本身使用来自另一个模块(文件 path.pl
)的帮助谓词,该模块又需要访问用户提供的数据:
The current situation can be illustrated using the following small, ficticous example: The user provides some input data (file data.pl
) and loads a module with an algorithm to be applied (file graph.pl
). The algorithm module itself uses helper predicates from another module (file path.pl
) which in turn requires access to the user-provided data:
文件" data.pl
"(输入数据集):
File 'data.pl
' (input data set):
:- use_module(graph).
edge(a,b).
edge(b,c).
edge(c,d).
文件' graph.pl
'(算法):
File 'graph.pl
' (algorithm):
:- module(graph, [reachable/2]).
:- use_module(path).
reachable(X,Y) :-
path(X,Y), !.
reachable(X,Y) :-
path(Y,X), !.
文件' path.pl
"(带有辅助谓词的模块,请注意,该文件正在访问 user
中的数据):
:- module(path, [path/2]).
path(X,X).
path(X,Y) :-
user:edge(X,Z),
path(Z,Y).
对于将算法应用于单个输入数据集和算法的单个实现的用例,这很好:
For the use case of applying the algorithm to a single input data set and the single implementation of the algorithm, this is perfectly fine:
?- [data].
true.
?- reachable(a,a).
true.
?- reachable(a,d).
true.
?- reachable(d,a).
true.
现在假设我有更多的数据集,并且 graph
和 path
模块的多个替代实现(具有相同的接口,即导出的谓词).为了(小的)示例,让我们假设我们要归档数据文件 data1.pl
, data2.pl
,辅助谓词模块 path1.pl
, path2.pl
和算法模块 graph1
, graph2.pl
.
Now suppose that I have a larger number of data sets, and multiple alternative implementations of the graph
and path
modules (with the same interface, i.e., exported predicates). For the sake of the (small) example, let us assume we files data files data1.pl
, data2.pl
, helper predicate modules path1.pl
, path2.pl
, and algorithm modules graph1
, graph2.pl
.
我想使用 SWI-Prolog单元测试,并且最好能够编写一个支持不同数据集和不同模块实现的测试套件,而无需在两者之间重新启动Prolog.也就是说,我希望能够测试笛卡尔积中的所有组合
I want to automate testing these using SWI-Prolog unit tests, and preferably be able to write a test suite that supports both the differing data sets and the different module implementations, without the need to restart Prolog in between. That is to say I want to be able test all combinations in the Cartesian product
没有复制粘贴/复制代码.
without copy-pasting/duplicating code.
我的问题是:我将如何在SWI-Prolog中进行此操作?为此目的,是否有关于如何将代码结构化为模块的最佳实践,设计模式等?我是否应该使用动态导入在其他算法模块之间进行切换,并在数据的单元测试中简单地使用 setup
和 cleanup
?
My question is: How would I go about this in SWI-Prolog? Are there best practices, design patterns or the like on how to structure code into modules for this purpose? Should I perhaps make use of dynamic importing for switching between alternative algorithm modules, and simply use setup
and cleanup
in unit tests for the data?
推荐答案
首先,您具有元谓词.这些应该允许您将数据和算法的构造块都作为参数传递.看一下此示例.在绝对确定这种方法还不够好之前,我不会尝试更复杂的事情.
First, you have meta-predicates. Those should allow you to pass as arguments both the data and the building blocks of your algorithms. Take a look at this example. I wouldn't try anything more complicated until absolutely certain that this approach is not good enough.
Then, have you looked carefully at dynamic modules and the export/import interface?
最后,您总是可以退回到使用assert,retract,ablish等来手动管理数据库.如果这样做,则可以完全避免使用模块系统.
Finally, you can always fall back to manually managing the database with assert, retract, abolish and so on. If you do that you could avoid the module system altogether.
但是先尝试使用元谓词.这些是Prolog中通用算法"的明显机制.
But try doing it with meta-predicates first. Those are the obvious mechanism for "generic algorithms" in Prolog.
一些代码.首先,您可以使用单元测试箱做什么?好吧,您可以执行以下操作.这是三个模块:
Some code. First, what can you do with unit test boxes? Well, you can do the following. Here are three modules:
$ cat foo.pl
:- module(foo, [x/1]).
x(foo).
$ cat bar.pl
:- module(bar, [x/1]).
x(bar).
$ cat baz.pl
:- module(baz, []).
:- begin_tests(foo).
:- use_module(foo).
test(x) :- x(foo).
:- end_tests(foo).
:- begin_tests(bar).
:- use_module(bar).
test(x) :- x(bar).
:- end_tests(bar).
最后一个模块 baz
尚未导出任何内容,但是它确实具有两个单独的单元测试框.加载模块并运行测试:
The last module, baz
, doesn't yet export anything, but it does have two separate unit test boxes. Loading the module and running the tests:
$ swipl
Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.10-59-g09a7d554d-DIRTY)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.
For online help and background, visit http://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).
?- use_module(baz).
true.
?- run_tests.
% PL-Unit: foo . done
% PL-Unit: bar . done
% All 2 tests passed
true.
显然,单位文本框确实具有作用域.
So apparently unit text boxes do let you have scopes.
为了阐明这一点,关键是您可以拥有不带假定接口的元调用(因此没有其他参数)的客户端代码(在本例中为对 x/1
的调用).然后,您可以通过将两个竞争模块导入同一文件中两个单独的单元测试框中来测试同一接口的不同实现.
To clarify, the point is that you can have client code without meta-calls (so no additional arguments) that assumes an interface (in the example, the call to x/1
). Then, you can test different implementations of the same interface by importing the two competing modules in two separate unit test boxes within the same file.
所有这些似乎都可以用一种更简洁的方式在Logtalk中实现.
All of that seems to be doable with Logtalk in a cleaner way anyway.
这篇关于将SWI-Prolog代码构造为模块,以对多种算法和数据集进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!