【问题标题】:Prolog number of nodes at a given depth in a treeProlog 树中给定深度的节点数
【发布时间】:2021-06-27 09:56:47
【问题描述】:

我是 Prolog 的新手。我正在尝试编写一个谓词,它返回树中给定深度的 二叉树 的节点数。例如下面的树

有 1 个深度为 0 的节点(根节点)、2 个深度为 1 的节点、3 个深度为 2 的节点和 3 个深度为 3 的节点。

在我的程序中,二叉树表示为[value, left-child, right-child] 形式的列表,其中左右子节点本身可以​​以相同的方式表示。例如,这里是上述树的表示:

[8, [3, [1, [], []], 
        [6, [4, [], []], 
            [7, [], []]]], 
    [10, [], 
         [14, 
            [13, [], []], 
            []]]]

以下是我目前的想法:

nbNodes([_,_,_],1,0).
nbNodes([_,LC,RC],N2,D2) :- 
    nbNodes(LC,NL,D), nbNodes(RC,NR,D), N2 is NL+NR, D2 is D+1.

基于我对 Prolog 的有限理解,这意味着:

  1. 每棵树只有一个深度为 0 的节点。
  2. 一棵树中深度为 D2 的节点数是三者的左右子节点中深度为 D2-1 的节点数之和。

    这两个假设似乎都是正确的。

但是,当我测试我的代码时,返回的数字(如果有)是错误的。例如,以下代码输出“false”而不是“3”:

nbNodes([8, [3, [1, [], []], [6, [4, [], []], [7, [], []]]], [10, [], [14, [13, [], []], []]]], N, 2).

我做错了什么? 非常感谢。

【问题讨论】:

标签: recursion prolog binary-tree


【解决方案1】:

怎么样

nbNodes( [],0,_).
nbNodes( [_,_,_],1,0).
nbNodes( [_,LC,RC],N2,D2):-
    D2 > 0,
    D is D2-1,
    nbNodes( LC,NL,D), 
    nbNodes( RC,NR,D), 
    N2 is NL+NR.

结果如下:

?- T = [8,[3,[1,[],[]],[6,[4,[],[]],[7,[],[]]]],[10,[],[14,[13,[],[]],[]]]],
   nbNodes(T,N,0).
T = [8, [3, [1, [], []], [6, [4, []|...], [7|...]]], [10, [], [14, [13|...], []]]],
N = 1 ;
false.
    
?- T = [8,[3,[1,[],[]],[6,[4,[],[]],[7,[],[]]]],[10,[],[14,[13,[],[]],[]]]],
   nbNodes(T,N,1).
T = [8, [3, [1, [], []], [6, [4, []|...], [7|...]]], [10, [], [14, [13|...], []]]],
N = 2 ;
false.
    
?- T = [8,[3,[1,[],[]],[6,[4,[],[]],[7,[],[]]]],[10,[],[14,[13,[],[]],[]]]],
   nbNodes(T,N,2).
T = [8, [3, [1, [], []], [6, [4, []|...], [7|...]]], [10, [], [14, [13|...], []]]],
N = 3 ;
false.
    
?- T = [8,[3,[1,[],[]],[6,[4,[],[]],[7,[],[]]]],[10,[],[14,[13,[],[]],[]]]],
   nbNodes(T,N,3).
T = [8, [3, [1, [], []], [6, [4, []|...], [7|...]]], [10, [], [14, [13|...], []]]],
N = 3 ;
false.

?- T = [8,[3,[1,[],[]],[6,[4,[],[]],[7,[],[]]]],[10,[],[14,[13,[],[]],[]]]], 
   nbNodes(T,N,4).
T = [8, [3, [1, [], []], [6, [4, []|...], [7|...]]], [10, [], [14, [13|...], []]]],
N = 0.

两个主要变化是

  1. 我为 empty 二叉树添加了一个子句。
  2. 我从D2 计算D之前我再次调用谓词并且不从D 计算D2 在递归调用之后

我猜你在问有什么区别,因为你被告知 Prolog 是声明性的,因此我们只需要指定情况(我们的知识库)而不是如何从我们的知识中推断出某些东西。

好吧,你被骗了。 Prolog 不是纯粹的声明式,因为它有一个顺序,它在其中搜索答案。

如果您想通过使用子句来证明目标G

G :- L1,L2,L3.

Prolog 试图证明 L1,然后是 L2,然后是 L3——尽管从逻辑的角度来看,这个子句只说明了

如果 L1 和 L2 和 L3 那么 G,

逻辑上等价于

如果 L3 和 L1 和 L2 那么 G

如果 Prolog 检查了所有可能的顺序,可能会有一个证明,但它没有。

此外,对于许多谓词,需要实例化某些参数才能使谓词起作用。这样的谓词之一是is/2。它的右侧需要完全实例化,is/2 才能工作。

您的谓词需要实例化它的第三个参数。你打电话的时候 nbNodes(LC,NL,D)nbNodes(RC,NR,D)D 未实例化。 即使您更改了子句的顺序并将D2 is D+1 放在这些子句之前,这也不起作用,因为is/2 需要完全实例化它的右侧,而D 那时还没有实例化。

尝试询问?- X is 2。和 ?- 2 is X. 看看区别。

为了更全面地了解,您需要深入了解 Prolog 对解析原则的(部分)实现。如果您想拥有一种更具声明性的 Prolog,请查看约束逻辑编程。

【讨论】:

  • 非常感谢您的回答。它确实解决了我的问题。与我的代码的主要区别似乎是 D 用 D2 表示,而不是 D2 用 D 表示。我不知道这对程序的运行有什么影响。如果您能详细说明这一点,我将不胜感激!
  • 你只需要了解Prolog的回溯+统一解析原理的部分实现——Prolog超简单:-)
  • 您添加的解释非常有用。您提到 Prolog 不是纯粹的声明性的。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多