Matrix 在Prolog中将事实提取为矩阵

Matrix 在Prolog中将事实提取为矩阵,matrix,prolog,prolog-findall,Matrix,Prolog,Prolog Findall,假设我们有以下情况: edge(a, 1, 10). edge(b, 2, 20). edge(c, 3, 30). edge(d, 4, 40). 我想提取这些事实的矩阵表示(M),这样 M = [[a,b,c,d],[1,2,3,4],[10,20,30,40]] 这里有一个简单的解决方案: edgeMatrix(M) :- findall(A, edge(A, _, _), As), findall(B, edge(_, B, _), Bs), findall(C, edg

假设我们有以下情况:

edge(a, 1, 10).
edge(b, 2, 20).
edge(c, 3, 30).
edge(d, 4, 40).
我想提取这些事实的矩阵表示(
M
),这样

M = [[a,b,c,d],[1,2,3,4],[10,20,30,40]]
这里有一个简单的解决方案:

edgeMatrix(M) :-
  findall(A, edge(A, _, _), As),
  findall(B, edge(_, B, _), Bs),
  findall(C, edge(_, _, C), Cs),
  M = [As, Bs, Cs].
然而,这种方法存在一些问题,即:

  • 我们遍历数据库n次,其中n是参数的数量;及
  • 这不能很好地推广到任意n
  • 所以问题是:在Prolog中实现这一点最惯用的方法是什么

    edgeMatrix(M) :-
        findall([A,B,C],edge(A,B,C),Trans),
        transpose(Trans,M).
    
    现在,您可以简单地从模块中导入矩阵,或者像中一样自己实现一个矩阵(是的,我知道这很懒,但是重新发明轮子有什么意义?)

    如果我在
    swipl
    中运行此命令,我会得到:

    ?- edgeMatrix(M).
    M = [[a, b, c, d], [1, 2, 3, 4], [10, 20, 30, 40]].
    
    看起来和你想要的一模一样


    当然,您可以说,计算
    转置/2
    仍然有一些计算开销,但是收集阶段只进行一次(如果这些不仅仅是事实,还包括从句的答案),这也可能很昂贵,此外,我认为一个模块可能会非常有效地实现子句。

    我认为您不会找到一个既全面又高效的解决方案。对于N=3,这里有一个简单的解决方案:

    edges(Edges) :-
        Goal = edge(_A, _B, _C),
        findall(Goal, Goal, Edges).
    
    edges_abcs_([], [], [], []).
    edges_abcs_([edge(A,B,C)|Edges], [A|As], [B|Bs], [C|Cs]) :-
        edges_abcs_(Edges, As, Bs, Cs).
    
    edges_abcs([As, Bs, Cs]) :-
        edges(Edges),
        edges_abcs_(Edges, As, Bs, Cs).
    
    在添加100000个额外的
    edge/3
    事实后,执行如下操作:

    ?- time(edges_abcs(M)).
    % 200,021 inferences, 0.063 CPU in 0.065 seconds (97% CPU, 3176913 Lips)
    M = [[a, b, c, d, 1, 2, 3, 4|...], [1, 2, 3, 4, 1, 2, 3|...], [10, 20, 30, 40, 1, 2|...]].
    
    为便于比较,以下是问题实施的衡量标准:

    ?- time(edgeMatrix_orig(M)).
    % 300,043 inferences, 0.061 CPU in 0.061 seconds (100% CPU, 4896149 Lips)
    M = [[a, b, c, d, 1, 2, 3, 4|...], [1, 2, 3, 4, 1, 2, 3|...], [10, 20, 30, 40, 1, 2|...]].
    
    下面是基于Willem的
    transpose/2
    的更通用的解决方案:

    ?- time(edgeMatrix_transpose(M)).
    % 700,051 inferences, 0.098 CPU in 0.098 seconds (100% CPU, 7142196 Lips)
    M = [[a, b, c, d, 1, 2, 3, 4|...], [1, 2, 3, 4, 1, 2, 3|...], [10, 20, 30, 40, 1, 2|...]].
    
    因此,我的解决方案在推理数量方面似乎是最好的:100000个推理用于
    findall/3
    ,100000个推理用于遍历列表。这个问题的答案对每个
    findall/3
    都有100000个推论,但仅此而已。但是,它稍微快一点,因为它的内存效率更高:分配的所有内容最终都会出现在最终解决方案中,而我的程序分配了一个100000个
    edge/3
    术语的列表,这些术语必须被垃圾收集。(在SWI Prolog中,如果打开探查器和/或GC跟踪,则可以看到垃圾收集。)

    如果我真的需要它尽可能快,并且可以推广到许多不同的N值,我会写一个宏,它可以扩展到问题的解


    编辑:如果取消了“惯用”要求,我将求助于将
    edge
    数据库作为列表存储在SWI Prolog全局变量中。在这种情况下,我的单遍实现将在没有
    findall/3
    开销的情况下工作,并且不会产生中间垃圾。

    这是非常巧妙的。。。我喜欢;-)虽然由于
    transpose/2
    @hugoseroneforreira,它可能会有我提到的相同的性能问题,但您可以查看
    transpose/2
    的定义,但我认为它与列表列表作为矩阵表示一样高效。总之,在纯Prolog中没有直接的、通用的方法来处理矩阵(据我所知)。你可以避免使用列表作为开始,但是你的问题没有给出足够的细节。