编译时向量数学维度一致性检查

本文关键字:一致性 检查 向量 编译 | 更新日期: 2023-09-27 18:33:04

我正在用 C# 创建一个线性代数库,我想强制将维度不一致错误强制到编译时。我已经实现了与此类似的解决方案,其中我使用的 trait 是一个唯一映射到整数的类。问题是对于我希望我的 Vector 具有的每个可能的大小,我需要创建一个具有唯一名称的类来表示它。

下面是该实现的示例:

public class Vector<T> where T: ISize, new()
{
    static readonly T size = new T();
    List<double> values;
    public Vector(List<double> values)
    {
        if (values.Count != size.Size)
            throw new IndexOutOfRangeException();
        this.values = new List<double>(values);
    }
    public double Get(int index)
    {
        return values[index];
    }
    public Vector<T> Add(Vector<T> other)
    {
        var vv = new List<double>();
        for (int ii = 0; ii < size.Size; ++ii)
            vv.Add(other.Get(ii) + this.values[ii]);
        return new Vector<T>(vv);
    }
}
public interface ISize
{
    int Size { get; }
}
public class S1 : ISize
{
    public int Size
    {
        get { return 1; }
    }
}
public class S2 : ISize
{
    public int Size
    {
        get { return 2; }
    }
}

下面是它的用法示例:

class Program
{
    static void Main(string[] args)
    {
        var v1 = new Vector<S2>(new List<double>() { 1, 2 });
        var v2 = new Vector<S2>(new List<double>() { 10, -4 });
        var z1 = new Vector<S1>(new List<double>() { 10 });
        // works
        var v3 = v1.Add(v2);
        // complie-time error
        var z2 = z1.Add(v1);
    }
}

这非常适合我的目的,除了我需要为每个可能的矢量大小创建不同的 ISize 实现。有什么方法可以实现 Vector 类来解决这个问题吗?

编译时向量数学维度一致性检查

为了获得编译时错误,您需要具有不同的类型。C# 没有一个概念,即允许您定义本身采用一种值参数的类型参数 - 这是执行此操作所需的。

因此,我认为你所要求的是不可能的。

我认为可能有一种方法可以使用匿名类型为矢量实例系列创建唯一类型,但这将是古怪的,我认为它不会提供您想要的类型安全性。

C++模板中有这样的概念(所以这不是不合理的),只是在 C# 中是不可能的。

您可以使用编译时类型检查创建单个 N 维Vector类,但它非常混乱。 我们在这里创建的是 LISP 样式的链接列表,但通过泛型类型参数,而不是纯粹通过字段进行对象引用。

public interface IVector
{
    double Value { get; }
    IVector Tail { get; }
}
public class Vector<T> : IVector
    where T : IVector
{
    internal Vector(double value, T tail)
    {
        Value = value;
        Tail = tail;
    }
    public double Value { get; private set; }
    public T Tail { get; private set; }
    public Vector<Vector<T>> Add(double value)
    {
        return new Vector<Vector<T>>(value, this);
    }
}
internal class EmptyVector : IVector
{
    public double Value
    {
        get { throw new NotImplementedException(); }
    }
    public IVector Tail
    {
        get { return null; }
    }
}
public static class Vector
{
    public static readonly Vector<IVector> Empty = new Vector<IVector>(
        0, new EmptyVector());
    public static IEnumerable<double> AllValues(this IVector vector)
    {
        IVector current = vector;
        while (current != Vector.Empty && current != null)
        {
            yield return current.Value;
            current = current.Tail;
        };
    }
}

这允许我们编写:

var v1 = Vector.Empty.Add(1).Add(2);
var v2 = Vector.Empty.Add(10).Add(-4);
var z1 = Vector.Empty.Add(10);

v1 = v2;//works, as they are the same type
z1 = v2;//fails, as they aren't the same type, since they're a different size

这允许您编写接受特定大小的向量的方法。 这不方便,它不可扩展,但它有效。 如果你想,比如说,一个3D矢量作为参数,你可以写:

public static void Foo(Vector<Vector<Vector<IVector>>> vector)
{
    var first = vector.Value;
    var second = vector.Tail.Value;
    var third = vector.Tail.Tail.Value;
}