Last active
August 26, 2020 10:40
-
-
Save WhiteBlackGoose/538d932cbeec7e7bfffd45eee007c951 to your computer and use it in GitHub Desktop.
Simple code for generating loops in linq expressions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Threading.Tasks; | |
public static class NestedLoopBuilder | |
{ | |
public static Expression CreateLoop(ParameterExpression var, Expression until, Expression onIter) | |
{ | |
var label = Expression.Label(); | |
var loop = Expression.Loop( | |
Expression.Block( | |
Expression.IfThenElse( | |
Expression.LessThan(var, until), | |
Expression.Block( | |
onIter | |
), | |
Expression.Break(label) | |
), | |
Expression.PostIncrementAssign(var) | |
), | |
label); | |
return Expression.Block( | |
Expression.Assign(var, Expression.Constant(0)), | |
loop | |
); | |
} | |
public static Expression CompileNestedLoops(Expression[] shapes, Func<ParameterExpression[], Expression> onIter) | |
{ | |
var acts = new List<Expression>(); | |
var locals = new ParameterExpression[shapes.Length]; | |
for (int i = 0; i < shapes.Length; i++) | |
locals[i] = Expression.Parameter(typeof(int), "x_" + i); | |
var localShapes = new ParameterExpression[shapes.Length]; | |
for (int i = 0; i < shapes.Length; i++) | |
localShapes[i] = Expression.Parameter(typeof(int), "shape_" + i); | |
var localShapesAssigned = new Expression[shapes.Length]; | |
for (int i = 0; i < shapes.Length; i++) | |
localShapesAssigned[i] = Expression.Assign(localShapes[i], shapes[i]); | |
Expression currExpr = onIter(locals); | |
for (int i = shapes.Length - 1; i >= 0; i--) | |
{ | |
currExpr = CreateLoop(locals[i], localShapes[i], currExpr); | |
} | |
acts.AddRange(localShapesAssigned); | |
acts.Add(currExpr); | |
var localVariables = new List<ParameterExpression>(); | |
localVariables.AddRange(locals); | |
localVariables.AddRange(localShapes); | |
return Expression.Block(localVariables, Expression.Block(acts)); | |
} | |
public static Expression CompileNestedLoops(Expression[] shapes, Func<ParameterExpression[], Expression> onIter, | |
bool parallel, ParameterExpression[] localsToPass) | |
{ | |
if (!parallel) | |
return CompileNestedLoops(shapes, onIter); | |
else | |
{ | |
var x = Expression.Parameter(typeof(int), "outerLoopVar"); | |
Func<ParameterExpression[], Expression> newOnIter = exprs => | |
{ | |
var arr = new ParameterExpression[exprs.Length + 1]; | |
arr[0] = x; | |
for (int i = 0; i < exprs.Length; i++) | |
arr[i + 1] = exprs[i]; | |
return onIter(arr); | |
}; | |
var shape0 = shapes[0]; | |
var others = new ArraySegment<Expression>(shapes, 1, shapes.Length - 1).ToArray(); | |
var compiledLoops = CompileNestedLoops(others, newOnIter); | |
var newArgs = localsToPass.ToList().Append(x).ToArray(); | |
var del = Expression.Lambda(compiledLoops, newArgs); | |
var actions = new List<Expression>(); | |
var localDelCall = Expression.Invoke(del, newArgs); | |
var finalLambda = Expression.Lambda( | |
localDelCall, | |
x | |
); | |
var enu = typeof(Parallel) | |
.GetMethods() | |
.Where(mi => mi.Name == nameof(Parallel.For)) | |
.Where(mi => mi.GetParameters().Length == 3) | |
.Where(mi => mi.GetParameters()[2].ParameterType == typeof(Action<int>)).ToArray(); | |
if (enu.Count() != 1) | |
throw new InvalidEnumArgumentException(); | |
var mi = enu.FirstOrDefault(); | |
var call = Expression.Call(null, mi, Expression.Constant(0), shape0, finalLambda); | |
return call; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment