Prolog 序言:求和值​;二叉树中叶子的形状

Prolog 序言:求和值​;二叉树中叶子的形状,prolog,Prolog,我有一个二叉树,对于这个二叉树,我必须加上值​​包含在叶子中,给出了一个类似的谓词: sum_leafs ([Node, Left, Right], Sum). 我不明白我该怎么做,必须计算叶子的值,我应该计算节点的和,以防左边和右边都为零 [Node, nil, nil] 所以我有点像: sum_leafs([Node, Left, Right], Sum):- sum_leafs(Left, Sum_Left), sum_leafs(Right, Sum_Right),

我有一个二叉树,对于这个二叉树,我必须加上值​​包含在叶子中,给出了一个类似的谓词:

sum_leafs ([Node, Left, Right], Sum).
我不明白我该怎么做,必须计算叶子的值,我应该计算节点的和,以防左边和右边都为零

[Node, nil, nil]
所以我有点像:

sum_leafs([Node, Left, Right], Sum):-
    sum_leafs(Left, Sum_Left),
    sum_leafs(Right, Sum_Right),
    Sum is (Sum_Left + Sum_Right).
我的尝试:

sum_leafs(nil, 0).
sum_leafs([Node, nil, nil], Node).
sum_leafs([_, Left, Right], Sum):-
    sum_leafs(Left, Sum_Left),
    sum_leafs(Right, Sum_Right),
    Sum is (Sum_Left + Sum_Right).
我怎样才能做到这一点

谢谢你

我已经这样做了:

sum_leafs(nil, 0).
sum_leafs([Node, nil, nil], Node).
sum_leafs([_, Left, Right], Sum):-
    sum_leafs(Left, Sum_Left),
    sum_leafs(Right, Sum_Right),
    Sum is (Sum_Left + Sum_Right).

对于二叉树来说,列表是一种糟糕的表示形式。让我们开始改变这一点:

  • 空树:
    nil
  • 非空树:
    t(左、值、右)
使用此表示法,可以将对叶值求和的谓词编写为:

sum_leaves(Tree, Sum) :-
    sum_leaves(Tree, 0, Sum).

sum_leaves(nil, Sum, Sum).
sum_leaves(t(nil,Value,nil), Sum0, Sum) :-
    !,
    Sum is Sum0 + Value. 
sum_leaves(t(Left,_,Right), Sum0, Sum) :-
    sum_leaves(Left, Sum0, Sum1),
    sum_leaves(Right, Sum1, Sum).
但这第一个版本不是尾部递归的。我们可以改进它:

sum_leaves(Tree, Sum) :-
    sum_leaves(Tree, 0, Sum).

sum_leaves(nil, Sum, Sum).
sum_leaves(t(nil,Value,nil), Sum0, Sum) :-
    !,
    Sum is Sum0 + Value. 
sum_leaves(t(Left,_,Right), Sum0, Sum) :-
    sum_leaves(Left, LeftSum),
    Sum1 is Sum0 + LeftSum,
    sum_leaves(Right, Sum1, Sum).

两件事:命名有点混乱,因为“节点”不是真正的节点,而是节点的“值”。更重要的是:这段代码有一些不需要的“替代解决方案”。如果您点击了一个叶子(一个有两个
nil
子项的节点),那么第二个子句适用,但第三个子句也适用。第二个解决方案总是给出0,而不是预期的“叶上和”。为了避免这种情况,如果第二个子句与之匹配,请在正文中插入一个cut:
sum\u leafs([Value,nil,nil],Value]:-
或在第三个子句中添加一个保护:
(左\==nil;右\==nil)
。稍等保罗。因此,两个版本都在将值累加到
Sum0
中,以得到
Sum
。第一个版本不是尾部递归的,因为我们在左和右子代上有双重递归下降(但是编译器可能仍然能够沿着右子代在递归下降上构建一个循环)。但第二个版本真的比第一个版本“更”具有尾部递归性吗?在所有调用之后,
sum_leaves(Left,Sum0,Sum1)
就隐藏在对
sum_leaves(Left,LeftSum)
的中间调用后面,该调用最后只在
sum_leaves(Left,0,LeftSum)
处结束,在SWI Prolog中没有太多变化,我们可以使用
sum\u leaves/3
最后一句中的谓词
prolog\u current\u frame/1
检查TCO:
sum\u leaves(t(左,右),Sum0,sum):-prolog\u current\u frame(frame),writeln(frame),…
。通过这种修改,查询
?-sum_leaves(t(t(t(t(t(t(nil,1,nil)))产生3倍相同的帧编号(=堆栈没有增长),但查询
?-sum_leaves(t(t(t(nil,1,nil)),2,nil),3,nil),4,nil),S)。
产生3个不同的帧编号(=堆栈确实增长)。