如何防止在级联运算符中创建中间对象?

时间:2008-09-22 13:27:25

标签: c# .net operators

我在我的应用程序中使用自定义Matrix类,我经常添加多个矩阵:

Matrix result = a + b + c + d; // a, b, c and d are also Matrices

然而,这为每个加法操作创建了一个中间矩阵。由于这是简单的添加,因此可以通过一次添加所有4个矩阵的元素来避免中间对象并创建结果。我怎么能做到这一点?

注意:我知道我可以定义多个功能,例如Add3Matrices(a, b, c)Add4Matrices(a, b, c, d)等,但我希望保持result = a + b + c + d的优雅。

11 个答案:

答案 0 :(得分:8)

您可以使用延迟评估将自己限制为单个小型中间件。像

这样的东西
public class LazyMatrix
{
    public static implicit operator Matrix(LazyMatrix l)
    {
        Matrix m = new Matrix();
        foreach (Matrix x in l.Pending)
        {
            for (int i = 0; i < 2; ++i)
                for (int j = 0; j < 2; ++j)
                    m.Contents[i, j] += x.Contents[i, j];
        }

        return m;
    }

    public List<Matrix> Pending = new List<Matrix>();
}

public class Matrix
{
    public int[,] Contents = { { 0, 0 }, { 0, 0 } };

    public static LazyMatrix operator+(Matrix a, Matrix b)
    {
        LazyMatrix l = new LazyMatrix();
        l.Pending.Add(a);
        l.Pending.Add(b);
        return l;
    }

    public static LazyMatrix operator+(Matrix a, LazyMatrix b)
    {
        b.Pending.Add(a);
        return b;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Matrix a = new Matrix();
        Matrix b = new Matrix();
        Matrix c = new Matrix();
        Matrix d = new Matrix();

        a.Contents[0, 0] = 1;
        b.Contents[1, 0] = 4;
        c.Contents[0, 1] = 9;
        d.Contents[1, 1] = 16;

        Matrix m = a + b + c + d;

        for (int i = 0; i < 2; ++i)
        {
            for (int j = 0; j < 2; ++j)
            {
                System.Console.Write(m.Contents[i, j]);
                System.Console.Write("  ");
            }
            System.Console.WriteLine();
        }

        System.Console.ReadLine();
    }
}

答案 1 :(得分:3)

在C ++中,可以使用Template Metaprogramshere,使用模板来完成此操作。但是,模板编程并非易事。我不知道C#中是否有类似的技术,很可能不是。

这种技术,在c ++中完全符合你的要求。缺点是,如果某些东西不太正确,那么编译器错误消息往往会运行到几个页面,几乎不可能破译。

如果没有这些技术,我怀疑你只能使用Add3Matrices等功能。

但是对于C#,这个链接可能正是你所需要的:Efficient Matrix Programming in C#虽然它似乎与C ++模板表达式略有不同。

答案 2 :(得分:3)

至少可以避免痛苦的事情

Matrix Add3Matrices(a,b,c) //and so on 

将是

Matrix AddMatrices(Matrix[] matrices)

答案 3 :(得分:3)

您无法避免创建中间对象。

但是,您可以使用here所述的表达式模板来最小化它们,并对模板进行奇怪的延迟评估。

在最简单的层次上,表达式模板可以是一个对象,它存储对多个矩阵的引用,并在赋值时调用Add3Matrices()之类的适当函数。在最高级别,表达式模板将根据请求以惰性方式计算最小信息量。

答案 4 :(得分:2)

这不是最干净的解决方案,但如果你知道评估订单,你可以这样做:

result = MatrixAdditionCollector() << a + b + c + d

(或者同名的不同名字)。然后,MatrixCollector实现+ as + =,即以未定义大小的0矩阵开始,在评估第一个+时获取大小并将所有内容添加到一起(或复制第一个矩阵)。这会将中间对象的数量减少到1(如果以良好的方式实现赋值,则甚至为0,因为MatrixCollector可能会立即/包含结果。)
我不完全确定这是否是丑陋的地狱或者其中一个更糟糕的黑客。一个明显的优点是,它发生的事情很明显。

答案 5 :(得分:2)

我认为你可以明确地实现所需的附加行为:

Matrix result = a;
result += b;
result += c;
result += d;

但正如Doug在本文评论中指出的那样,编译器会将此代码视为我写的:

Matrix result = a;
result = result + b;
result = result + c;
result = result + d;

所以临时工作仍然存在。

我只是删除了这个答案,但似乎其他人可能有同样的误解,所以请考虑这个例子。

答案 6 :(得分:2)

我可能会建议一个行为与StringBuilder非常相似的MatrixAdder。您将矩阵添加到MatrixAdder,然后调用ToMatrix()方法,该方法将在延迟实现中为您添加。这样可以获得您想要的结果,可以扩展到任何类型的LazyEvaluation,但也不会引入任何可能会混淆其他代码维护者的聪明实现。

答案 7 :(得分:1)

Bjarne Stroustrup有一篇名为Abstraction, libraries, and efficiency in C++的短文,他提到了用来实现你所寻找的技巧。具体来说,他提到了图书馆Blitz++,这是一个科学计算库,也有一些有效的矩阵运算,以及一些其他有趣的库。另外,我建议在该主题上阅读a conversation with Bjarne Stroustrup on artima.com

答案 8 :(得分:0)

使用运营商是不可能的。

答案 9 :(得分:0)

我的第一个解决方案是这样的(如果可能的话,添加到Matrix类中):

static Matrix AddMatrices(Matrix[] lMatrices) // or List<Matrix> lMatrices
{
    // Check consistency of matrices

    Matrix m = new Matrix(n, p);

    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            foreach (Maxtrix mat in lMatrices)
                m[i, j] += mat[i, j];

    return m;
}

我在Matrix类中有它,因为你可以依赖私有方法和属性,这些方法和属性对于你的函数有用,以防矩阵的实现发生变化(非空节点的链表而不是一个大的双数组,例如)。

当然,你会失去result = a + b + c + d的优雅。但是你会有result = Matrix.AddMatrices(new Matrix[] { a, b, c, d });

的内容

答案 10 :(得分:0)

有几种方法可以实现延迟评估来实现这一目标。但重要的是要记住,并不总是你的编译器能够获得所有这些代码的最佳代码。

我已经在GCC中实现了很好的实现,甚至超过了传统几个的性能对于不可读的代码,因为它们引导编译器观察到数据段之间没有别名(有些难以理解,数组无处可去)。但其中一些在MSVC完全失败,反之亦然。不幸的是,这些发布时间过长(不要认为数千行代码适合这里)。

一个非常复杂的库,具有很强的嵌入式知识,是用于科学计算的Blitz ++库。