3

这是我正在处理的简化场景。循环结构有多种方法。

for (int i=0; i<I; i++) {
    // Do something
    for (int j=0; j<J; j++) {
        // Do something
        for (int k=0; k<K; k++) {
            // Do something
            Update(a,b,c);
        }
    }
}

在一种方法中,Update(a,b,c) 是

a[i] += b[j] * c[k]

在另一种方法中,它是

b[j] += a[i] * c[k]

而在另一种方法中,它是

c[k] += a[i] * b[j]

目前,我的代码到处都是重复的。C# 中是否有一种模式,这样我就不会重复代码?我很想使用委托,但似乎委托会降低性能(这在我的情况下至关重要)。

有没有办法为这种情况编写宏或内联委托函数?

4

5 回答 5

11

像这样的东西?

void DoUpdates(Action<int, int, int> update)
{
  for (int i=0; i<I; i++) {
    // Do something
    for (int j=0; j<J; j++) {
      // Do something
      for (int k=0; k<K; k++) {
        // Do something
        update(i, j, k);
      }
    }
  }
}

然后在调用者中

DoUpdates((int i, int j, int k) => { a[i] += b[j] * c[k]; });

那是你要找的吗?

于 2013-02-26T01:14:53.390 回答
4
void Update<T>(T[] x, T[] y, T[] z, int i, int j, int k)
{
    x[i] += y[j] * z[k];
}

用法:

Update(a, b, c, i, j, k);
Update(b, a, c, j, i, k);
Update(c, a, b, k, i, j);

我看到它a总是被i(等等 - bby jcby k)访问。您可以尝试使用这个事实来优化代码。

于 2013-02-26T01:05:27.477 回答
2

如果性能很关键,您可以避免在内部循环中调用方法,如下所示:

void Update(int[]x, int[]y, int[]z, int I, int J, int K)
{
    for (int i = 0; i < I; i++)
    {
        // Do something
        for (int j = 0; j < J; j++)
        {
            // Do something
            for (int k = 0; k < K; k++)
            {
                // Do something
                x[i] += y[j] * z[k];
            }
        }
    }
}

和调用代码:

Update(a, b, c, I, J, K);
Update(b, a, c, J, I, K);
Update(c, a, b, K, I, J);
于 2013-02-26T01:49:23.907 回答
0

您可能正在实现诸如大数乘法或向量的其他线性组合之类的东西。您需要您描述为内联委托的方法的原因很可能是因为在计算期间存储结果的位置不同,也因为嵌套的 for 循环是硬编码的。因此,我建议修改您的代码,如下所示:

public void Update(int destinationIndex, int[][] arrays, int[] indices) {
    var product=1;

    for(var i=indices.Length; i-->0; )
        if(destinationIndex!=i)
            product*=arrays[i][indices[i]];

    arrays[destinationIndex][indices[destinationIndex]]+=product;
}

public void PerformUpdate(
    int destinationIndex, int[] counts, int[][] arrays, Action<int, int>[] actions,
    List<int> indices=null, int level=0
    ) {
    if(level==counts.Length)
        Update(destinationIndex, arrays, (indices??new List<int>()).ToArray());
    else
        for(int count=counts[level], i=0; i<count; i++) {
            if(null!=actions&&level<actions.Length)
                actions[level](i, count); // do something according to nesting level

            (indices=indices??new List<int>()).Add(i);
            PerformUpdate(destinationIndex, counts, arrays, actions, indices, 1+level);
            indices.RemoveAt(indices.Count-1);
        }
}

此代码以递归方式实现。int[][] array只要您要定义 and 的计算,而不是定义 and 中的方法名称,就可以将operator *替换为operator +通用MutiplyScalar数组AddScalar

因此,我们不会使用委托Update来控制目的地。相反,我们只是使用destinationIndex来完成。下面是一个测试用例:

int[] a=new[] { 1, 2 }, b=new[] { 3, 4, 5 }, c=new[] { 6 };
Action<int, int> m=(index, count) => Debug.Print("index={0}; count={1}", index, count);
PerformUpdate(2, new[] { a.Length, b.Length, c.Length }, new[] { a, b, c }, new[] { m, m, m });

我们仍然有内联委托,它们Lambda Expressions在 c# 中调用。根据您提供的原始代码,Do something嵌套的 for 循环之间有 s 。但是,我们找不到太多非全球已知的信息Update;我们可以看到的最显着的区别是迭代索引和结束数字,它们i, Ij, Jk, K。因此,我们只是将这些作为参数传递给Action<int, int>for 做某事,并且它们对于每个级别的 for 循环都是可变的。

执行很大程度上取决于indices. 它存储当前for循环的迭代索引并传递给下一级递归调用。此外,如果您传递的arrays计数小于其Lengthin indices,它将被视为具有您传递给的计数长度的数组。不要传递负数,也不要传递更大的数。它可以是缺乏Action<int, int>,那只是意味着什么都不做而不是做某事

于 2013-02-26T05:38:11.060 回答
0

这可能会内联它。

interface IFunc<T>
{
    void Invoke(ref T a, ref T b, ref T c);
}

void DoUpdates<TFunc>(TFunc update)
    where TFunc : IFunc<int>
{
    for (int i = 0; i < I; i++)
        for (int j = 0; j < J; j++)
            for (int k = 0; k < K; k++)
                update.Invoke(ref i, ref j, ref k);
}
于 2013-02-26T05:47:52.993 回答