/// <summary> /// Applies an accumulator function over the values yielded from /// a data-producer. The first value in the seqnence /// is used as the initial accumulator value, and the specified function is used /// to select the result value. If the sequence is empty then /// the default value for TSource is returned. /// </summary> /// <typeparam name="TSource">The type of data yielded by the data-source</typeparam> /// <param name="func">Accumulator function to be applied to each term in the sequence</param> /// <param name="source">The data-source for the values</param> public static IFuture <TSource> Aggregate <TSource> (this IDataProducer <TSource> source, DotNet20.Func <TSource, TSource, TSource> func) { source.ThrowIfNull("source"); func.ThrowIfNull("func"); Future <TSource> ret = new Future <TSource>(); bool first = true; TSource current = default(TSource); source.DataProduced += value => { if (first) { first = false; current = value; } else { current = func(current, value); } }; source.EndOfData += () => ret.Value = current; return(ret); }
/// <summary> /// Returns an iterator which steps through the range, applying the specified /// step delegate on each iteration. The method determines whether to begin /// at the start or end of the range based on whether the step delegate appears to go /// "up" or "down". The step delegate is applied to the start point. If the result is /// more than the start point, the returned iterator begins at the start point; otherwise /// it begins at the end point. /// </summary> /// <param name="step">Delegate to apply to the "current value" on each iteration</param> public RangeIterator <T> Step(DotNet20.Func <T, T> step) { step.ThrowIfNull("step"); bool ascending = (this.comparer.Compare(this.start, step(this.start)) < 0); return(ascending ? this.FromStart(step) : this.FromEnd(step)); }
/// <summary> /// Returns a future that indicates whether any suitable values are /// yielded by the data-producer. The future will return false /// for an empty sequence or one with no matching values, or true for a sequence with matching values. /// </summary> /// <param name="source">The data-producer to be monitored.</param> /// <param name="predicate">The condition that must be satisfied.</param> public static IFuture <bool> Any <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); Future <bool> ret = new Future <bool>(); Action <TSource> production = null; Action completion = () => ret.Value = false; production = value => { if (predicate(value)) { ret.Value = true; source.DataProduced -= production; source.EndOfData -= completion; } }; source.DataProduced += production; source.EndOfData += completion; return(ret); }
/// <summary> /// Returns a future to a single value from a data-source that matches the /// specified condition, or the default value if no matching values /// are encountered. An exception /// is thrown if multiple matching values are encountered. /// </summary> /// <param name="source">The source data-producer.</param> /// <param name="predicate">The condition to be satisfied.</param> /// <exception cref="InvalidOperationException">Multiple matching terms are encountered.</exception> public static IFuture <TSource> SingleOrDefault <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); Future <TSource> ret = new Future <TSource>(); TSource output = default(TSource); bool gotValue = false; source.DataProduced += value => { if (predicate(value)) { if (gotValue) { throw new InvalidOperationException("More than one element in source data"); } output = value; gotValue = true; } }; source.EndOfData += () => { ret.Value = output; }; return(ret); }
/// <summary> /// Returns a data-producer that will yield /// elements a sequence as long as a condition /// (involving the element's index in the sequence) /// is satsified; when the condition fails for an element, /// that element and all subsequent elements are ignored. /// </summary> /// <param name="source">The source data-producer</param> /// <param name="predicate">The condition to yield elements</param> public static IDataProducer <TSource> TakeWhile <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, int, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); DataProducer <TSource> ret = new DataProducer <TSource>(); Action completion = () => ret.End(); Action <TSource> production = null; int index = 0; production = value => { if (!predicate(value, index++)) { ret.End(); source.DataProduced -= production; source.EndOfData -= completion; } else { ret.Produce(value); } }; source.DataProduced += production; source.EndOfData += completion; return(ret); }
/// <summary> /// Returns a data-producer that will ignore the /// elements from the start of a sequence while a condition /// (involving the elements's index in the sequence) /// is satsified; when the condition fails for an element, /// that element and all subsequent elements are yielded. /// </summary> /// <param name="source">The source data-producer</param> /// <param name="predicate">The condition to skip elements</param> public static IDataProducer <TSource> SkipWhile <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, int, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); DataProducer <TSource> ret = new DataProducer <TSource>(); Action completion = () => ret.End(); bool skipping = true; int index = 0; source.DataProduced += value => { if (skipping) { skipping = predicate(value, index++); } // Note - not an else clause! if (!skipping) { ret.Produce(value); } }; source.EndOfData += completion; return(ret); }
/// <summary> /// Returns the last element in a sequence that satisfies a specified condition, as a future value. /// </summary> /// <typeparam name="TSource">The type of the elements of source.</typeparam> /// <param name="source">The sequence to an element from.</param> /// <param name="predicate">A function to test each element for a condition.</param> /// <returns>The last element in the specified sequence that passes the test in /// the specified predicate function, as a future value. /// The actual value can only be retrieved after the source has indicated the end /// of its data. /// </returns> public static IFuture <TSource> Last <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); Future <TSource> ret = new Future <TSource>(); bool gotData = false; TSource prev = default(TSource); source.DataProduced += value => { if (predicate(value)) { prev = value; gotData = true; } }; source.EndOfData += () => { if (!gotData) { throw new InvalidOperationException("Sequence is empty"); } ret.Value = prev; }; return(ret); }
/// <summary> /// Filters a data-producer based on a predicate on each value /// </summary> /// <param name="source">The data-producer to be filtered</param> /// <param name="predicate">The condition to be satisfied</param> /// <returns>A filtered data-producer; only matching values will raise the DataProduced event</returns> public static IDataProducer <TSource> Where <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { predicate.ThrowIfNull("predicate"); return(source.Where((x, index) => predicate(x))); }
/// <summary> /// Returns a future to the minumum of a sequence of values that are /// obtained by taking a transform of the input sequence, using the default comparer /// </summary> /// <remarks>Null values are removed from the minimum</remarks> public static IFuture <TResult> Min <TSource, TResult> (this IDataProducer <TSource> source, DotNet20.Func <TSource, TResult> selector) { source.ThrowIfNull("source"); selector.ThrowIfNull("selector"); return(source.Select(selector).Min()); }
/// <summary> /// Returns a projection on the data-producer, using a transformation /// (involving the elements's index in the sequence) to /// map each element into a new form. /// </summary> /// <typeparam name="TSource">The source type</typeparam> /// <typeparam name="TResult">The projected type</typeparam> /// <param name="source">The source data-producer</param> /// <param name="projection">The transformation to apply to each element.</param> public static IDataProducer <TResult> Select <TSource, TResult>(this IDataProducer <TSource> source, DotNet20.Func <TSource, int, TResult> projection) { source.ThrowIfNull("source"); projection.ThrowIfNull("projection"); DataProducer <TResult> ret = new DataProducer <TResult>(); int index = 0; source.DataProduced += value => ret.Produce(projection(value, index++)); source.EndOfData += () => ret.End(); return(ret); }
/// <summary> /// Creates an iterator over the given range with the given step function, /// with the specified direction. /// </summary> public RangeIterator(Range <T> range, DotNet20.Func <T, T> step, bool ascending) { step.ThrowIfNull("step"); if ((ascending && range.Comparer.Compare(range.Start, step(range.Start)) >= 0) || (!ascending && range.Comparer.Compare(range.End, step(range.End)) <= 0)) { throw new ArgumentException("step does nothing, or progresses the wrong way"); } this.ascending = ascending; this.range = range; this.step = step; }
/// <summary> /// Applies an accumulator function over the values yielded from /// a data-producer, performing a transformation on the final /// accululated value. The specified seed value /// is used as the initial accumulator value, and the specified function is used /// to select the result value /// </summary> /// <typeparam name="TSource">The type of data yielded by the data-source</typeparam> /// <typeparam name="TResult">The final result type (after the accumulator has been transformed)</typeparam> /// <typeparam name="TAccumulate">The type to be used for the accumulator</typeparam> /// <param name="func">Accumulator function to be applied to each term in the sequence</param> /// <param name="resultSelector">Transformation to apply to the final /// accumulated value to produce the result</param> /// <param name="seed">The initial value for the accumulator</param> /// <param name="source">The data-source for the values</param> public static IFuture <TResult> Aggregate <TSource, TAccumulate, TResult> (this IDataProducer <TSource> source, TAccumulate seed, DotNet20.Func <TAccumulate, TSource, TAccumulate> func, DotNet20.Func <TAccumulate, TResult> resultSelector) { source.ThrowIfNull("source"); func.ThrowIfNull("func"); resultSelector.ThrowIfNull("resultSelector"); Future <TResult> result = new Future <TResult>(); TAccumulate current = seed; source.DataProduced += value => current = func(current, value); source.EndOfData += () => result.Value = resultSelector(current); return(result); }
/// <summary> /// Returns the number of elements in the specified sequence satisfy a condition, /// as a future value. /// </summary> /// <typeparam name="TSource">The type of the elements of source.</typeparam> /// <param name="source">A sequence that contains elements to be tested and counted.</param> /// <param name="predicate">A function to test each element for a condition.</param> /// <returns>A number that represents how many elements in the sequence satisfy /// the condition in the predicate function, as a future value. /// The actual count can only be retrieved after the source has indicated the end /// of its data. /// </returns> public static IFuture <int> Count <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); Future <int> ret = new Future <int>(); int count = 0; source.DataProduced += t => { if (predicate(t)) { count++; } }; source.EndOfData += () => ret.Value = count; return(ret); }
/// <summary> /// Filters a data-producer based on a predicate on each value; the index /// in the sequence is used in the predicate /// </summary> /// <param name="source">The data-producer to be filtered</param> /// <param name="predicate">The condition to be satisfied</param> /// <returns>A filtered data-producer; only matching values will raise the DataProduced event</returns> public static IDataProducer <TSource> Where <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, int, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); DataProducer <TSource> ret = new DataProducer <TSource>(); int index = 0; source.DataProduced += value => { if (predicate(value, index++)) { ret.Produce(value); } }; source.EndOfData += () => ret.End(); return(ret); }
/// <summary> /// Returns the last value from a sequence that matches the given condition, or the default /// for that type if no matching value is produced. /// </summary> /// <param name="source">The source data-producer.</param> /// <param name="predicate">The condition to be satisfied.</param> public static IFuture <TSource> LastOrDefault <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); Future <TSource> ret = new Future <TSource>(); TSource prev = default(TSource); source.DataProduced += value => { if (predicate(value)) { prev = value; } }; source.EndOfData += () => { ret.Value = prev; }; return(ret); }
/// <summary> /// Returns a future to the first value from a sequence that matches the given condition, or the default /// for that type if no matching value is produced. /// </summary> /// <param name="source">The source data-producer.</param> /// <param name="predicate">The condition to be satisfied.</param> public static IFuture <TSource> FirstOrDefault <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); Future <TSource> ret = new Future <TSource>(); Action completion = () => ret.Value = default(TSource); Action <TSource> production = null; production = t => { if (predicate(t)) { ret.Value = t; source.EndOfData -= completion; source.DataProduced -= production; } }; source.DataProduced += production; source.EndOfData += completion; return(ret); }
/// <summary> /// Returns the first element in a sequence that satisfies a specified condition, as a future value. /// </summary> /// <typeparam name="TSource">The type of the elements of source.</typeparam> /// <param name="source">The sequence to an element from.</param> /// <param name="predicate">A function to test each element for a condition.</param> /// <returns>The first element in the specified sequence that passes the test in /// the specified predicate function, as a future value. /// The actual value can only be retrieved after the source has indicated the end /// of its data. /// </returns> public static IFuture <TSource> First <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { source.ThrowIfNull("source"); predicate.ThrowIfNull("predicate"); Future <TSource> ret = new Future <TSource>(); Action completion = () => { throw new InvalidOperationException("Sequence is empty"); }; Action <TSource> production = null; production = t => { if (predicate(t)) { ret.Value = t; source.EndOfData -= completion; source.DataProduced -= production; } }; source.DataProduced += production; source.EndOfData += completion; return(ret); }
/// <summary> /// Returns a projection on the data-producer, using a transformation to /// map each element into a new form. /// </summary> /// <typeparam name="TSource">The source type</typeparam> /// <typeparam name="TResult">The projected type</typeparam> /// <param name="source">The source data-producer</param> /// <param name="projection">The transformation to apply to each element.</param> public static IDataProducer <TResult> Select <TSource, TResult>(this IDataProducer <TSource> source, DotNet20.Func <TSource, TResult> projection) { projection.ThrowIfNull("projection"); return(source.Select((t, index) => projection(t))); }
/// <summary> /// Returns a data-producer that will ignore the /// elements from the start of a sequence while a condition /// is satsified; when the condition fails for an element, /// that element and all subsequent elements are yielded. /// </summary> /// <param name="source">The source data-producer</param> /// <param name="predicate">The condition to skip elements</param> public static IDataProducer <TSource> SkipWhile <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { predicate.ThrowIfNull("predicate"); return(source.SkipWhile((t, index) => predicate(t))); }
/// <summary> /// Returns a future that indicates whether all values /// yielded by the data-producer satisfy a given condition. /// The future will return true for an empty sequence or /// where all values satisfy the condition, else false /// (if any value value fails to satisfy the condition). /// </summary> /// <param name="source">The data-producer to be monitored.</param> /// <param name="predicate">The condition that must be satisfied by all terms.</param> public static IFuture <bool> All <TSource>(this IDataProducer <TSource> source, DotNet20.Func <TSource, bool> predicate) { predicate.ThrowIfNull("predicate"); return(FutureProxy <bool> .FromFuture(source.Any(value => !predicate(value)), value => !value)); }