我想在 prolog
转置一个矩阵,所以例如[[1,2,3],[1,2,3],[1,2,3]]应该变成[[1,1,1],[2] ,2,2],[3,3,3]
这就是我所做的:
%transpose(M,T)
transpose([],[]).
transpose(M,[H|T]):-row(H,M,M1),transpose(M1,T).
%row(H, M, M1).
row([],[],[]).
row([H|Tt] , [[H|T1]|T] , [T1|Z]) :- row(Tt,T,Z).
其中 M
将在 T
中转置 . 行谓词(不确定谓词和函数之间的差异)得到M中每个子列表的头部,因此我可以得到新的转置矩阵的整个第一行 T
. 我分别测试了行,它给出了正确的结果 . 但是当我尝试 transpose
时,我只得到'false'并且没有任何反应,没有打印新矩阵 T
的信息 .
谁能帮我?问题在于我构建程序的方式吗?或者我期望的结果只是 true/false
确实如果是这样,如何获得新矩阵?谢谢 :)
3 回答
首先,关于 terminology 的注释:函数的一个特征是每个输入只与一个输出相关 . 另一方面,关系可以适用于多种这样的组合 . Prolog谓词在他们的论点之间引发了 relations ,并且在回溯中会出现多个答案 .
至于你的具体问题,@ lurker已经给出了一些可靠的建议 .
但是,正如您已经注意到的那样,Prolog中的跟踪很快变得非常复杂,因此我想通过在Prolog中解释 declarative debugging 的一些原则来补充潜伏者所写的内容 .
从本质上讲,我们的目标是使问题更简单而不是更难 . 我们从您为您发布的计划提及的案例开始:
从逻辑上讲,这种关系是 too specific :它无法在它应该持有的情况下持有 .
为了找到这个问题的原因,我们可以使用文本或图形跟踪器逐步完成精确的执行细节,并尝试跟踪所有细节 . 这非常困难,所以我建议你先尝试找一个仍然失败的简单案例 .
我将通过 generalizing 查询来完成此操作 . 有几种方法可以做到这一点 . 例如,我可以使用新的变量而不是具体的整数:
啊哈,所以我们发现了一个更加普遍的案例,但仍然失败了 . 只要这种情况失败,更具体的情况肯定也会失败,因为我们正在推理 pure 和 monotonic 逻辑程序 .
所以,新的问题是:为什么这个更通用的查询失败了?为了找到原因,我们可以简单地进一步概括查询 . 例如,我们可以将此概括为以下情况,而不是推理每个包含3个元素的列表,其中两个列表被推广为"more than one element":
我们看到这个更通用的查询 still fails . 我们甚至可以进一步概括:
这仍然失败!请注意,我们也可以把它放得太远 . 例如:
在这里,我们遇到了一个完全不同的问题,与程序的终止属性有关 . 但是,在我们的案例中,我们希望找到意外失败的原因,因此我们回到上面的情况:
在这一点上,没有更多的概括,所以我们现在可以在这个简化的查询上调用跟踪器 .
但是,我们也可以继续朝不同的方向发展:并非每个测试用例都需要概括 . 我们还可以提出一个完全不同的情况,例如只有两个(而不是三个)元素的列表:
啊哈,所以这也失败了,尽管它应该成功!
甚至更简单的情况呢?
并进一步:
甚至进一步:
这些查询中的每一个都应该成功,但都会失败 .
为什么?
要找到原因,请考虑program-slicing的基础逻辑程序 . 例如,让我们采取一个我们发现应该成功的最简单的查询,但不是:
?- transpose([[_]], _).
我使用以下定义来原始程序的 generalize away 约束(=目标)
例如,我们可以 generalize
row/3
如下:即使有了这个更为通用的
row/3
版本,我们也会得到:这意味着:要使此查询成功,您必须向代码添加其他子句(使其更通用)或 change the remaining fragment .
像潜伏者一样,我也把剩下的作为锻炼 .
Prolog只有谓词而不是函数 . 谓词定义了一个关系,并且通常不被认为是"doing something",就像它“定义一些东西一样 . 你的
row/3
实际上定义了它的3个参数之间的关系.3个参数的相关性在于第1和第3个列表的元素是第二个列表的参数的头部和尾部 . 我将显示3个不同的查询,说明:函数不会以这种方式运行 . 你也可以看到,知道这种关系,这个名字有点泛泛 .
row
并没有真正充分描述这种关系 . 也许它可以被称为heads_and_tails/3
,也许可以交换第一和第二个参数 .你的
transpose/2
谓词失败的原因是它会递归到一个空列表列表(如果是3行矩阵,则为[[], [], []]
),但你的transpose/2
基本案例基于[]
.让我们看一下,如评论中所建议的那样,将
transpose([], [])
的基本情况替换为:以下示例查询结果:
因此第二个查询的结果不正确 . 沿着此谓词的逻辑解决此问题的一种方法是将基本情况从
transpose([], [])
更改为:这会产生一个谓词,它会产生以下结果:
事实证明,之前的解决方案是不充分的,因为它只是对逻辑关系的描述 .
除了关系规范之外还需要的是事实的迭代,随后根据关系进行 .
在以下程序中,
foreach
用于生成事实 .测试swipl .
示例查询遵循该程序 .
测试:
三个测试查询表明该程序基本适合:
不幸的是,该计划未能通过坚定的测试: