You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dafny框架系统问题:原地翻转二叉树验证失败求助

Dafny验证原地翻转二叉树的modify clause问题

Dafny的框架系统存在诸多易踩坑点。我正尝试验证原地翻转二叉树问题,分别以对象方法和独立函数两种方式实现。但独立函数的递归调用始终被提示违反上下文的modify clause,我已尽可能将相关内容添加到modify clause中仍无法解决,我认为归纳法应足够支撑验证,特此寻求解决方法。

相关代码

/**
 * 原地翻转二叉树问题
 * Definition for a binary tree node.
 */

class TreeNode {
    var val: int;
    var left: TreeNode?;
    var right: TreeNode?;
    ghost var repr: set<TreeNode>;

    constructor(val: int, left: TreeNode?, right: TreeNode?)
        requires left != null ==> left.Valid()
        requires right != null ==> right.Valid()
        requires left != null && right != null ==> left.repr !! right.repr
        ensures this.val == val
        ensures this.left == left
        ensures this.right == right
        ensures left != null ==> this !in left.repr
        ensures right != null ==> this !in right.repr
        ensures Valid()
    {
        this.val := val;
        this.left := left;
        this.right := right;
        var leftRepr := if left != null then {left}+left.repr else {};
        var rightRepr := if right != null then {right}+right.repr else {};
        this.repr := {this} + leftRepr + rightRepr;
    }

    predicate Valid()
        reads this, repr
        decreases repr
    {
        this in repr &&
        (this.left != null ==>
        (this.left in repr
        && this !in this.left.repr
        && this.left.repr < repr
        && this.left.Valid()
        ))
        && (this.right != null ==>
        (this.right in repr
        && this !in this.right.repr
        && this.right.repr < repr
        && this.right.Valid())) &&
        (this.left != null && this.right != null ==> this.left.repr !! this.right.repr)
    }

}

method  invertBinaryTree(root: TreeNode?) returns (newRoot: TreeNode?)
    modifies {root} + (if root != null && root.left != null then {root.left} else {}) + (if root != null && root.right != null then {root.right} else {})
    requires root != null ==> root.Valid()
    ensures root != null ==> newRoot == root && newRoot.right == old(root.left) && root.left == old(root.right)
    ensures root == null ==> newRoot == null
    ensures root != null ==> newRoot != null && newRoot.repr == root.repr && newRoot.Valid()
    decreases if root == null then {} else root.repr
{
    if root != null {
        assert root in root.repr;
        assert root.Valid();
        var leftChild := null;
        if root.left != null {
            assert root.left != null;
            assert root.left.repr < root.repr;
            assert root.left.Valid();
            leftChild := invertBinaryTree(root.left);
        }
        var rightChild := root.right;
        if root.right != null  {
            assert root.right.Valid();
            rightChild := invertBinaryTree(root.right);
        }

        root.right := leftChild;
        root.left := rightChild;
        return root;
    }else{
        return null;
    }
}

问题分析与修复

核心问题

当前方法的modifies子句仅声明了修改当前节点、直接左子节点和直接右子节点,但递归调用时,子树的所有节点都会被修改(递归会逐层翻转子树的左右节点)。Dafny的框架检查要求modifies必须覆盖所有可能被修改的对象集合,因此当前的modifies范围不足以覆盖递归过程中修改的子树节点,导致违反错误。

修复步骤

  1. 修改modifies子句:利用TreeNode的repr幽灵变量(该变量已包含当前节点及其所有子节点),将modifies范围改为:

    modifies if root != null then root.repr else {}
    

    这样就能覆盖递归过程中所有会被修改的节点。

  2. 简化冗余断言:原代码中的部分断言可被Dafny自动推导,无需手动添加,简化后代码更简洁。

修复后的完整方法代码

method  invertBinaryTree(root: TreeNode?) returns (newRoot: TreeNode?)
    modifies if root != null then root.repr else {}
    requires root != null ==> root.Valid()
    ensures root != null ==> newRoot == root 
        && newRoot.right == old(root.left) 
        && root.left == old(root.right)
    ensures root == null ==> newRoot == null
    ensures root != null ==> newRoot != null 
        && newRoot.repr == root.repr 
        && newRoot.Valid()
    decreases if root == null then {} else root.repr
{
    if root != null {
        var leftChild := invertBinaryTree(root.left);
        var rightChild := invertBinaryTree(root.right);

        root.right := leftChild;
        root.left := rightChild;
        return root;
    }else{
        return null;
    }
}

验证逻辑说明

  • modifies root.repr直接覆盖了当前树的所有节点,满足递归时修改子树节点的框架要求。
  • decreases root.repr通过集合大小的递减保证递归终止,符合Dafny的终止检查规则。
  • ensures条件中的newRoot.repr == root.repr确保翻转操作不会改变树的节点集合,只是调整了节点间的引用关系,这也和Valid()谓词的要求一致。

内容的提问来源于stack exchange,提问作者Hath995

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 15:00:53