如何以编程方式将循环转换为单个表达式?



我一直在读一本关于线性代数数学和编程微积分的书,有一件事让我印象深刻,那就是代码的冗长。 首先,我知道这样是什么,性能比使用 for 循环要好得多,但是,这使得代码非常难以阅读。

有没有办法使用 for 循环来创建详细函数,这样代码更易于维护?

这是我所说的一个非常简单的例子:

public int Sum(int[] a, int[] b, int size)
{
var sum = 0;
for(var index = 0; index < size; index++)
{
sum += a[index] + b[index];
}
return sum;
}

现在假设size是 3。 是否可以从方法Sum生成如下所示的单个表达式:

public int Sum3(int[] a, int[] b)
{
return a[0] + b [0] + a[1] + b[1] + a[2] + b[2];
}

因此,实际上,Sum的方法不是执行求和,而是用于创建可以求和的delegate。 它最终看起来像Func<int[], int[], int> Sum3.

目标是使用这种方法进行点积、行列式和矩阵乘法等操作。

更新:

我不希望在一行代码中对两个数组求和。这只是一个例子。我希望能够在不实际循环的情况下进行任何矢量或矩阵操作。

(这只是部分答案;它不涉及读取Summetbod的IL。

解析原始函数(在本例中Sum(后,您可以按如下方式构造相应的Sum3函数:

// using static System.Linq.Expressions.Expression;
var a = Parameter(typeof(int[]));
var b = Parameter(typeof(int[]));
var expr1 = Lambda(
Add(
Add(
Add(
Add(
Add(
ArrayIndex(a, Constant(0)),
ArrayIndex(b, Constant(0))
),
ArrayIndex(a,Constant(1))
),
ArrayIndex(b, Constant(1))
),
ArrayIndex(a, Constant(2))
),
ArrayIndex(b, Constant(2))
),
a,
b
);
var fn = expr1.Compile();
fn.DynamicInvoke(new[] { 1, 2, 3 }, new[] { 4, 5, 6 });

当然,您可能希望根据 IL 中的内容自定义逻辑。

另请注意,这很可能性能较低,因为编译表达式树所需的时间通常至少比简单的循环大一个数量级。除非你可以缓存各种size值的编译委托,并且用法的数量证明了这一点。


注意:这些代码大部分来自我编写的 ExpressionTreeToString 库,它可以生成创建给定表达式树所需的工厂方法调用。

// using ExpressionTreeToString;
Expression<Func<int[], int[], int>> expr = (a, b) => a[0] + b[0] + a[1] + b[1] + a[2] + b[2];
Console.WriteLine(expr.ToString("Factory methods"));

也许这种方式适用于您:

int[] arr1 = { 1, 2, 3, 4, 5 };
int[] arr2 = { 1, 2, 3, 4, 5 };
int index = 3;
var overallSum = arr1.Take(index).Sum() + arr2.Take(index).Sum();

如果您真的不想在最终运行时中迭代数据,我认为在编译时不可能完成此操作,除非您始终知道数组的确切长度(或者您为sum进行Int32.MaxValue覆盖(。您必须在运行时使用 C# 代码编写一些脚本,然后执行它。

相关内容

  • 没有找到相关文章

最新更新