/// <summary> /// Takes two expressions, func (S -> T -> S) and seed (S), and an enumerable with elements /// of type T, and aggregates (aka folds) the elements to produce a valid of type S. /// </summary> /// <param name="expression">The enumerable to aggregate.</param> /// <param name="func">The aggregator function.</param> /// <param name="seed">The initial state used when aggregating.</param> /// <returns>An expression representing the calculation of the aggregation.</returns> public static Expression Aggregate(this EnumerableExpression expression, LambdaExpression func, Expression seed) { ParameterExpression accVar = Expression.Variable(seed.Type, "acc"); return(Expression.Block( new[] { accVar }, Expression.Assign(accVar, seed), expression.AggregateRaw(e => Expression.Assign(accVar, func.Substitute(accVar, e))), accVar)); }
/// <summary> /// Takes an IEnumerable{T}, a lambda of T -> IEnumerable{U} and returns an IEnumerable{U}. /// This is also known as a flatmap. /// </summary> /// <param name="enumerable">The original enumerable to flatmap.</param> /// <param name="selector">The flatmapper function.</param> /// <returns>The flatmapped enumerable.</returns> public static EnumerableExpression SelectMany(this EnumerableExpression enumerable, LambdaExpression selector) { Type newType = selector.ReturnType.GetIEnumerableElementType(); EnumerableExpression ToEnumerableExpresion(Expression expr) { return(expr.Type.IsArray ? OfArray(expr, newType) : OfEnumerable(expr, newType)); } return(enumerable.SelectManyRaw(elem => ToEnumerableExpresion(selector.Substitute(elem)), newType)); }
/// <summary> /// Takes a lambda expression that maps the elements of the enumerable and returns an /// enumerable of the elements after they were mapped. /// </summary> /// <param name="expression">The enumerable expression.</param> /// <param name="selector">The mapping function.</param> /// <returns>The enumerable after mapping.</returns> public static EnumerableExpression Select(this EnumerableExpression expression, LambdaExpression selector) { Func <ParameterExpression, Expression> SelectorBody(Func <ParameterExpression, Expression> continuation) { Expression Inner(ParameterExpression curElem) { ParameterExpression newElemVar = Expression.Variable(selector.ReturnType, "newElem"); return(Expression.Block( new[] { newElemVar }, Expression.Assign(newElemVar, selector.Substitute(curElem)), continuation(newElemVar))); } return(Inner); } return(expression.SelectRaw(SelectorBody, selector.ReturnType)); }
/// <summary> /// Takes a selector and uses it to map the elements of an enumerable. /// expression : Expression{IQueryable{T}}. /// selector : ((Expression{U} -> Expression{void}) -> (Expression{T} -> Expression{void})). /// returns : Expression{IQueryable{U}}. /// /// The selector takes a continuation that handles new elements and returns a continuation /// that takes old elements, gets the new element, and passes it into that continuation. /// </summary> /// <param name="expression">The enumerable expression to map.</param> /// <param name="selector">The mapping function.</param> /// <param name="newElementType">The new type of the enumerable.</param> /// <returns>The new mapped enumerable.</returns> private static EnumerableExpression SelectRaw( this EnumerableExpression expression, Func <Func <ParameterExpression, Expression>, Func <ParameterExpression, Expression> > selector, Type newElementType) { return(expression switch { LinearEnumerableExpression linear => new LinearEnumerableExpression( linear.Initialize, linear.HasNext, c => linear.MoveNext(selector(c)), newElementType), NestedEnumerableExpression nested => new NestedEnumerableExpression( nested.BaseEnumerable, e => nested.GetNestedEnumerable(e).SelectRaw(selector, newElementType), newElementType), _ => throw new ArgumentException("Must be a linear or nested enumerable.", nameof(expression)), });
/// <summary> /// Takes an enumerable of element T, and an expression from T -> bool, and filters the /// enumerable to elements where the predicate is true. /// </summary> /// <param name="expression">The enumerable to filter.</param> /// <param name="predicate">The predicate to filter on.</param> /// <returns>The filtered enumerable.</returns> public static EnumerableExpression Where(this EnumerableExpression expression, LambdaExpression predicate) { return(expression.SelectRaw( k => cur => Expression.IfThen(predicate.Substitute(cur), k(cur)), expression.ElementType)); }