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

Ada中如何正确约束二维数组类型为方阵?

Ada通用邻接矩阵包的方阵约束实现方案

我正在开发一个用于定义图的通用邻接矩阵包,首先实现了基于二维数组的静态版本:

初始代码实现

包规范

with Ada.Containers.Vectors;

generic
    type Weight_Type is private;
    with function "=" (Left, Right : Weight_Type) return Boolean;
    No_Edge_Value : Weight_Type;
package Generic_Adjacency_Matrices is
    type Static_Adjacency_Matrix
        is array (Positive range <>, Positive range <>)
        of Weight_Type;

    function Create (Vertices : Positive) return Static_Adjacency_Matrix;
end Generic_Adjacency_Matrices;

包体

package body Generic_Adjacency_Matrices is
    function Create (Vertices : Positive) return Static_Adjacency_Matrix is
        AM : Static_Adjacency_Matrix (1 .. Vertices, 1 .. Vertices);
    begin
        for I in AM'Range(1) loop
            for J in AM'Range(2) loop
                AM(I, J) := No_Edge_Value;
            end loop;
        end loop;

        return AM;
    end Create;
end Generic_Adjacency_Matrices;

需求:确保矩阵为方阵

我希望强制约束Static_Adjacency_Matrix必须是方阵(两个维度的范围完全一致)。根据Ada手册的说明:

Static_Predicate的作用类似约束,除未初始化变量或无效值外,子类型的所有对象始终满足断言;而Dynamic_Predicate仅在指定时机检查,其他情况下可能失效(比如修改记录子组件时不会触发检查)。

因为矩阵的维度在声明时就已确定,我认为使用Static_Predicate是更合理的选择,于是尝试修改类型声明:

with Ada.Containers.Vectors;

generic
    type Weight_Type is private;
    with function "=" (Left, Right : Weight_Type) return Boolean;
    No_Edge_Value : Weight_Type;
package Generic_Adjacency_Matrices is
    type Static_Adjacency_Matrix
        is array (Positive range <>, Positive range <>)
        of Weight_Type
        with Static_Predicate =>
            Static_Adjacency_Matrix'First(1)
            = Static_Adjacency_Matrix'First(2)
            and Static_Adjacency_Matrix'Last(1)
            = Static_Adjacency_Matrix'Last(2);

    function Create (Vertices : Positive) return Static_Adjacency_Matrix;
end Generic_Adjacency_Matrices;

但这种写法是非法的:Static_Predicate无法使用'Range、'First和'Last属性。

虽然Dynamic_Predicate支持这种写法,但我想寻找更合适的替代方案。比如将类型声明为私有,强制通过Create函数生成方阵,但我不喜欢这种方式——类型本身没有私有必要,还需要额外定义访问过程。

可行的替代方案

方案1:使用一维数组模拟方阵

将方阵存储为一维数组,通过索引转换函数实现二维访问,从类型层面保证存储的是方阵(数组长度必须是整数的平方):

包规范修改

with Ada.Containers.Vectors;

generic
    type Weight_Type is private;
    with function "=" (Left, Right : Weight_Type) return Boolean;
    No_Edge_Value : Weight_Type;
package Generic_Adjacency_Matrices is
    type Static_Adjacency_Matrix is array (Positive range <>) of Weight_Type;

    function Create (Vertices : Positive) return Static_Adjacency_Matrix;

    -- 二维访问接口
    function Get (Matrix : Static_Adjacency_Matrix; I, J : Positive) return Weight_Type;
    procedure Set (Matrix : in out Static_Adjacency_Matrix; I, J : Positive; Value : Weight_Type);

    -- 获取矩阵的顶点数
    function Vertex_Count (Matrix : Static_Adjacency_Matrix) return Positive;
end Generic_Adjacency_Matrices;

核心实现示例

package body Generic_Adjacency_Matrices is
    function Create (Vertices : Positive) return Static_Adjacency_Matrix is
        Size : constant Positive := Vertices * Vertices;
        AM : Static_Adjacency_Matrix (1 .. Size);
    begin
        for Index in AM'Range loop
            AM(Index) := No_Edge_Value;
        end loop;
        return AM;
    end Create;

    function Vertex_Count (Matrix : Static_Adjacency_Matrix) return Positive is
        use Ada.Numerics.Elementary_Functions;
        Count : constant Float := Sqrt(Float(Matrix'Length));
    begin
        if Count /= Float(Integer(Count)) then
            raise Constraint_Error with "Not a valid square matrix";
        end if;
        return Positive(Integer(Count));
    end Vertex_Count;

    function Get (Matrix : Static_Adjacency_Matrix; I, J : Positive) return Weight_Type is
        Count : constant Positive := Vertex_Count(Matrix);
    begin
        if I not in 1 .. Count or J not in 1 .. Count then
            raise Constraint_Error with "Index out of bounds";
        end if;
        return Matrix((I - 1) * Count + J);
    end Get;

    procedure Set (Matrix : in out Static_Adjacency_Matrix; I, J : Positive; Value : Weight_Type) is
        Count : constant Positive := Vertex_Count(Matrix);
    begin
        if I not in 1 .. Count or J not in 1 .. Count then
            raise Constraint_Error with "Index out of bounds";
        end if;
        Matrix((I - 1) * Count + J) := Value;
    end Set;
end Generic_Adjacency_Matrices;

这种方式从存储结构上保证了方阵特性,同时通过封装的访问函数提供二维数组的使用体验。

方案2:使用Dynamic_Predicate做动态检查

虽然Dynamic_Predicate是运行时检查,但由于矩阵的维度一旦声明就不会改变(数组的范围是静态约束的),因此只要在创建和赋值时检查一次,就能长期保证方阵特性:

with Ada.Containers.Vectors;

generic
    type Weight_Type is private;
    with function "=" (Left, Right : Weight_Type) return Boolean;
    No_Edge_Value : Weight_Type;
package Generic_Adjacency_Matrices is
    type Static_Adjacency_Matrix
        is array (Positive range <>, Positive range <>)
        of Weight_Type
        with Dynamic_Predicate =>
            Static_Adjacency_Matrix'First(1) = Static_Adjacency_Matrix'First(2)
            and Static_Adjacency_Matrix'Last(1) = Static_Adjacency_Matrix'Last(2);

    function Create (Vertices : Positive) return Static_Adjacency_Matrix;
end Generic_Adjacency_Matrices;

当用户尝试手动创建非方阵的实例时,会触发运行时Constraint_Error;而通过Create函数生成的实例天然是方阵,不会有问题。这种方案最简单,不需要额外封装,适合大部分场景。

方案3:私有类型+数组视图(折中方案)

如果既想强制方阵约束,又想保留二维数组的直接访问体验,可以将类型声明为私有,同时提供数组视图的访问接口:

包规范

with Ada.Containers.Vectors;

generic
    type Weight_Type is private;
    with function "=" (Left, Right : Weight_Type) return Boolean;
    No_Edge_Value : Weight_Type;
package Generic_Adjacency_Matrices is
    type Static_Adjacency_Matrix is private;

    function Create (Vertices : Positive) return Static_Adjacency_Matrix;

    -- 提供常量视图支持下标访问
    type Constant_Matrix_View is array (Positive range <>, Positive range <>) of Weight_Type
        with Constant_Indexing => Get;

    function Get (Matrix : Static_Adjacency_Matrix; I, J : Positive) return Weight_Type;
    function View (Matrix : Static_Adjacency_Matrix) return Constant_Matrix_View;

    -- 可变版本的访问接口
    procedure Set (Matrix : in out Static_Adjacency_Matrix; I, J : Positive; Value : Weight_Type);

private
    type Static_Adjacency_Matrix is array (Positive range <>, Positive range <>) of Weight_Type;
end Generic_Adjacency_Matrices;

包体核心实现

package body Generic_Adjacency_Matrices is
    function Create (Vertices : Positive) return Static_Adjacency_Matrix is
        AM : Static_Adjacency_Matrix (1 .. Vertices, 1 .. Vertices);
    begin
        for I in AM'Range(1) loop
            for J in AM'Range(2) loop
                AM(I, J) := No_Edge_Value;
            end loop;
        end loop;
        return AM;
    end Create;

    function Get (Matrix : Static_Adjacency_Matrix; I, J : Positive) return Weight_Type is
    begin
        return Matrix(I, J);
    end Get;

    function View (Matrix : Static_Adjacency_Matrix) return Constant_Matrix_View is
    begin
        return Matrix;
    end View;

    procedure Set (Matrix : in out Static_Adjacency_Matrix; I, J : Positive; Value : Weight_Type) is
    begin
        Matrix(I, J) := Value;
    end Set;
end Generic_Adjacency_Matrices;

这种方案既阻止了用户直接创建非方阵实例,又通过View函数和Constant_Indexing特性,让用户可以像使用普通二维数组一样访问矩阵元素。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 04:44:53