diff --git a/MoreLinq/Enumerator.cs b/MoreLinq/Enumerator.cs new file mode 100644 index 000000000..5e3897754 --- /dev/null +++ b/MoreLinq/Enumerator.cs @@ -0,0 +1,66 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Pierre Lando. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System.Collections.Generic; + + static class Enumerator + { + /// + /// + /// Move the to the next position and put the + /// new current value into . + /// + /// If the has no more element it's disposed and + /// set to null, and is set to default. + /// + /// If the is null the method return immediately + /// and is set to null. + /// + /// The type of element that are enumerated. + /// The enumerator to iterate or dispose. + /// The new current value of or + /// default if has no more element. + /// + /// + /// Because and may both be modified + /// they are both passed by reference. + /// + /// A bool value indicating if the enumerator has moved to the next element. + + public static bool TryRead(ref IEnumerator? enumerator, out T? item) + { + if (enumerator is null) + { + item = default; + return false; + } + + if (enumerator.MoveNext()) + { + item = enumerator.Current; + return true; + } + + enumerator.Dispose(); + enumerator = null; + item = default; + return false; + } + } +} diff --git a/MoreLinq/EquiZip.cs b/MoreLinq/EquiZip.cs deleted file mode 100644 index 61212f122..000000000 --- a/MoreLinq/EquiZip.cs +++ /dev/null @@ -1,223 +0,0 @@ -#region License and Terms -// MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#endregion - -namespace MoreLinq -{ - using System; - using System.Collections.Generic; - using System.Linq; - - static partial class MoreEnumerable - { - /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. An exception is thrown - /// if the input sequences are of different lengths. - /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. - /// - /// Function to apply to each pair of elements. - /// - /// A sequence that contains elements of the two input sequences, - /// combined by . - /// - /// - /// The input sequences are of different lengths. - /// - /// - /// , , or is . - /// - /// - /// n + l); - /// ]]> - /// The zipped variable, when iterated over, will yield "1A", - /// "2B", "3C", "4D" in turn. - /// - /// - /// This operator uses deferred execution and streams its results. - /// - - public static IEnumerable EquiZip( - this IEnumerable first, - IEnumerable second, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException(nameof(first)); - if (second == null) throw new ArgumentNullException(nameof(second)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return EquiZipImpl(first, second, null, null, (a, b, _, _) => resultSelector(a, b)); - } - - /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. An exception is thrown - /// if the input sequences are of different lengths. - /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in third sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. - /// The third sequence. - /// - /// Function to apply to each triplet of elements. - /// - /// A sequence that contains elements of the three input sequences, - /// combined by . - /// - /// - /// The input sequences are of different lengths. - /// - /// - /// , , , or is . - /// - /// - /// n + l + c); - /// ]]> - /// The zipped variable, when iterated over, will yield "1Aa", - /// "2Bb", "3Cc", "4Dd" in turn. - /// - /// - /// This operator uses deferred execution and streams its results. - /// - - public static IEnumerable EquiZip( - this IEnumerable first, - IEnumerable second, IEnumerable third, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException(nameof(first)); - if (second == null) throw new ArgumentNullException(nameof(second)); - if (third == null) throw new ArgumentNullException(nameof(third)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return EquiZipImpl(first, second, third, null, (a, b, c, _) => resultSelector(a, b, c)); - } - - /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. An exception is thrown - /// if the input sequences are of different lengths. - /// - /// Type of elements in first sequence - /// Type of elements in second sequence - /// Type of elements in third sequence - /// Type of elements in fourth sequence - /// Type of elements in result sequence - /// The first sequence. - /// The second sequence. - /// The third sequence. - /// The fourth sequence. - /// - /// Function to apply to each quadruplet of elements. - /// - /// A sequence that contains elements of the four input sequences, - /// combined by . - /// - /// - /// The input sequences are of different lengths. - /// - /// - /// , , , , or is . - /// - /// - /// n + l + c + f); - /// ]]> - /// The zipped variable, when iterated over, will yield "1AaTrue", - /// "2BbFalse", "3CcTrue", "4DdFalse" in turn. - /// - /// - /// This operator uses deferred execution and streams its results. - /// - - public static IEnumerable EquiZip( - this IEnumerable first, - IEnumerable second, IEnumerable third, IEnumerable fourth, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException(nameof(first)); - if (second == null) throw new ArgumentNullException(nameof(second)); - if (third == null) throw new ArgumentNullException(nameof(third)); - if (fourth == null) throw new ArgumentNullException(nameof(fourth)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return EquiZipImpl(first, second, third, fourth, resultSelector); - } - - static IEnumerable EquiZipImpl( - IEnumerable s1, - IEnumerable s2, - IEnumerable? s3, - IEnumerable? s4, - Func resultSelector) - { - const int zero = 0, one = 1; - - var limit = 1 + (s3 != null ? one : zero) - + (s4 != null ? one : zero); - - return ZipImpl(s1, s2, s3, s4, resultSelector, limit, enumerators => - { - var i = enumerators.Index().First(x => x.Value == null).Key; - return new InvalidOperationException(OrdinalNumbers[i] + " sequence too short."); - }); - } - - static readonly string[] OrdinalNumbers = - { - "First", - "Second", - "Third", - "Fourth", - // "Fifth", - // "Sixth", - // "Seventh", - // "Eighth", - // "Ninth", - // "Tenth", - // "Eleventh", - // "Twelfth", - // "Thirteenth", - // "Fourteenth", - // "Fifteenth", - // "Sixteenth", - }; - } -} diff --git a/MoreLinq/EquiZip.g.cs b/MoreLinq/EquiZip.g.cs new file mode 100644 index 000000000..15b74bc84 --- /dev/null +++ b/MoreLinq/EquiZip.g.cs @@ -0,0 +1,230 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// + /// Applies a specified function to the corresponding elements of two sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence has the same length as the input sequences. + /// If the input sequences are of different lengths, an exception is thrown. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// + /// A function that specifies how to merge the elements from the two sequences. + /// + /// An IEnumerable that contains merged elements of two input sequences. + /// + /// , + /// or + /// is null. + /// + /// The input sequences are of different lengths. + /// + /// This operator uses deferred execution and streams its results. + + public static IEnumerable EquiZip( + this IEnumerable first, + IEnumerable second, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e1 = first.GetEnumerator(); + using var e2 = second.GetEnumerator(); + + for (;;) + { + if (e1.MoveNext()) + { + if (e2.MoveNext()) + yield return resultSelector(e1.Current, e2.Current); + else + break; + } + else + { + if (e2.MoveNext()) + break; + else + yield break; + } + } + + throw new InvalidOperationException("The input sequences are of different lengths."); + } + } + + /// + /// + /// Applies a specified function to the corresponding elements of three sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence has the same length as the input sequences. + /// If the input sequences are of different lengths, an exception is thrown. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. + /// + /// A function that specifies how to merge the elements from the three sequences. + /// + /// An IEnumerable that contains merged elements of three input sequences. + /// + /// , + /// , + /// or + /// is null. + /// + /// The input sequences are of different lengths. + /// + /// This operator uses deferred execution and streams its results. + + public static IEnumerable EquiZip( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (third == null) throw new ArgumentNullException(nameof(third)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e1 = first.GetEnumerator(); + using var e2 = second.GetEnumerator(); + using var e3 = third.GetEnumerator(); + + for (;;) + { + if (e1.MoveNext()) + { + if (e2.MoveNext() && e3.MoveNext()) + yield return resultSelector(e1.Current, e2.Current, e3.Current); + else + break; + } + else + { + if (e2.MoveNext() || e3.MoveNext()) + break; + else + yield break; + } + } + + throw new InvalidOperationException("The input sequences are of different lengths."); + } + } + + /// + /// + /// Applies a specified function to the corresponding elements of four sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence has the same length as the input sequences. + /// If the input sequences are of different lengths, an exception is thrown. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the fourth input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. + /// The fourth sequence to merge. + /// + /// A function that specifies how to merge the elements from the four sequences. + /// + /// An IEnumerable that contains merged elements of four input sequences. + /// + /// , + /// , + /// , + /// or + /// is null. + /// + /// The input sequences are of different lengths. + /// + /// This operator uses deferred execution and streams its results. + + public static IEnumerable EquiZip( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + IEnumerable fourth, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (third == null) throw new ArgumentNullException(nameof(third)); + if (fourth == null) throw new ArgumentNullException(nameof(fourth)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e1 = first.GetEnumerator(); + using var e2 = second.GetEnumerator(); + using var e3 = third.GetEnumerator(); + using var e4 = fourth.GetEnumerator(); + + for (;;) + { + if (e1.MoveNext()) + { + if (e2.MoveNext() && e3.MoveNext() && e4.MoveNext()) + yield return resultSelector(e1.Current, e2.Current, e3.Current, e4.Current); + else + break; + } + else + { + if (e2.MoveNext() || e3.MoveNext() || e4.MoveNext()) + break; + else + yield break; + } + } + + throw new InvalidOperationException("The input sequences are of different lengths."); + } + } + + } +} diff --git a/MoreLinq/EquiZip.g.tt b/MoreLinq/EquiZip.g.tt new file mode 100644 index 000000000..2746a2071 --- /dev/null +++ b/MoreLinq/EquiZip.g.tt @@ -0,0 +1,142 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Globalization" #> +<#@ import namespace="System.Linq" #> +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +<# + var ordinals = new[] + { + string.Empty, + "First", "Second", "Third", "Fourth", + "Fifth", "Sixth", "Seventh", "Eighth" + }; + + var cardinals = new[] + { + "zero", + "one", "two", "three", "four", + "five", "six", "seven", "eight" + }; + + var overloads = + from argCount in Enumerable.Range(2, 3) + from args in new[] + { + from argPosition in Enumerable.Range(1, argCount) + select new + { + IsFirst = argPosition == 1, + IsLast = argPosition == argCount, + Name = ordinals[argPosition].ToLower(), + ordinal = ordinals[argPosition].ToLower(), + Type = $"T{ordinals[argPosition]}", + // Objects associated with the argument + Enumerator = $"e{argPosition}", + Value = $"v{argPosition}" + } + } + select new + { + Arguments = args.ToList(), + cardinal = cardinals[argCount], + TParams = string.Join(", ", from arg in args select arg.Type) + }; +#> +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { +<# foreach (var o in overloads) + { +#> + /// + /// + /// Applies a specified function to the corresponding elements of <#= o.cardinal #> sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence has the same length as the input sequences. + /// If the input sequences are of different lengths, an exception is thrown. + /// +<# foreach (var arg in o.Arguments) { #> + /// The type of the elements of the <#= arg.ordinal #> input sequence. +<# } #> + /// The type of the elements of the result sequence. +<# foreach (var arg in o.Arguments) { #> + /// The <#= arg.ordinal #> sequence to merge. +<# } #> + /// + /// A function that specifies how to merge the elements from the <#= o.cardinal #> sequences. + /// + /// An IEnumerable that contains merged elements of <#= o.cardinal #> input sequences. + /// +<# foreach (var arg in o.Arguments) { #> + /// <#= arg.IsLast ? " or" : "," #> +<# } #> + /// is null. + /// + /// The input sequences are of different lengths. + /// + /// This operator uses deferred execution and streams its results. + + public static IEnumerable EquiZip<<#= o.TParams #>, TResult>( +<# foreach (var arg in o.Arguments) { #> + <#= arg.IsFirst ? "this " : "" #>IEnumerable<<#= arg.Type #>> <#= arg.Name #>, +<# } #> + Func<<#= o.TParams #>, TResult> resultSelector) + { +<# foreach (var arg in o.Arguments) { #> + if (<#= arg.Name #> == null) throw new ArgumentNullException(nameof(<#= arg.Name #>)); +<# } #> + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { +<# foreach (var arg in o.Arguments) { #> + using var <#= arg.Enumerator #> = <#= arg.Name #>.GetEnumerator(); +<# } #> + + for (;;) + { + if (<#= o.Arguments.First().Enumerator #>.MoveNext()) + { + if (<# foreach (var arg in o.Arguments.Skip(1)) { #><#= arg.Enumerator #>.MoveNext()<#= arg.IsLast ? "" : " && " #><# } #>) + yield return resultSelector(<# foreach (var arg in o.Arguments) { #><#= arg.Enumerator #>.Current<#= arg.IsLast ? "" : ", " #><# } #>); + else + break; + } + else + { + if (<# foreach (var arg in o.Arguments.Skip(1)) { #><#= arg.Enumerator #>.MoveNext()<#= arg.IsLast ? "" : " || " #><# } #>) + break; + else + yield break; + } + } + + throw new InvalidOperationException("The input sequences are of different lengths."); + } + } + +<# } #> + } +} diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index a5ca2ac81..7c09d1c24 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -1338,40 +1338,30 @@ public static bool EndsWith(this IEnumerable first, IEnumerable second, public static partial class EquiZipExtension { /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. An exception is thrown - /// if the input sequences are of different lengths. - /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. + /// + /// Applies a specified function to the corresponding elements of two sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence has the same length as the input sequences. + /// If the input sequences are of different lengths, an exception is thrown. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. /// - /// Function to apply to each pair of elements. + /// A function that specifies how to merge the elements from the two sequences. /// - /// A sequence that contains elements of the two input sequences, - /// combined by . - /// - /// - /// The input sequences are of different lengths. - /// + /// An IEnumerable that contains merged elements of two input sequences. /// - /// , , or is . - /// - /// - /// n + l); - /// ]]> - /// The zipped variable, when iterated over, will yield "1A", - /// "2B", "3C", "4D" in turn. - /// + /// , + /// or + /// is null. + /// + /// The input sequences are of different lengths. /// - /// This operator uses deferred execution and streams its results. - /// + /// This operator uses deferred execution and streams its results. public static IEnumerable EquiZip( this IEnumerable first, @@ -1380,98 +1370,79 @@ public static IEnumerable EquiZip( => MoreEnumerable.EquiZip(first, second, resultSelector); /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. An exception is thrown - /// if the input sequences are of different lengths. + /// + /// Applies a specified function to the corresponding elements of three sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence has the same length as the input sequences. + /// If the input sequences are of different lengths, an exception is thrown. /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in third sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. - /// The third sequence. + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. /// - /// Function to apply to each triplet of elements. + /// A function that specifies how to merge the elements from the three sequences. /// - /// A sequence that contains elements of the three input sequences, - /// combined by . - /// - /// - /// The input sequences are of different lengths. - /// + /// An IEnumerable that contains merged elements of three input sequences. /// - /// , , , or is . - /// - /// - /// n + l + c); - /// ]]> - /// The zipped variable, when iterated over, will yield "1Aa", - /// "2Bb", "3Cc", "4Dd" in turn. - /// + /// , + /// , + /// or + /// is null. + /// + /// The input sequences are of different lengths. /// - /// This operator uses deferred execution and streams its results. - /// + /// This operator uses deferred execution and streams its results. - public static IEnumerable EquiZip( - this IEnumerable first, - IEnumerable second, IEnumerable third, - Func resultSelector) + public static IEnumerable EquiZip( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + Func resultSelector) => MoreEnumerable.EquiZip(first, second, third, resultSelector); /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. An exception is thrown - /// if the input sequences are of different lengths. + /// + /// Applies a specified function to the corresponding elements of four sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence has the same length as the input sequences. + /// If the input sequences are of different lengths, an exception is thrown. /// - /// Type of elements in first sequence - /// Type of elements in second sequence - /// Type of elements in third sequence - /// Type of elements in fourth sequence - /// Type of elements in result sequence - /// The first sequence. - /// The second sequence. - /// The third sequence. - /// The fourth sequence. + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the fourth input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. + /// The fourth sequence to merge. /// - /// Function to apply to each quadruplet of elements. + /// A function that specifies how to merge the elements from the four sequences. /// - /// A sequence that contains elements of the four input sequences, - /// combined by . - /// - /// - /// The input sequences are of different lengths. - /// + /// An IEnumerable that contains merged elements of four input sequences. /// - /// , , , , or is . - /// - /// - /// n + l + c + f); - /// ]]> - /// The zipped variable, when iterated over, will yield "1AaTrue", - /// "2BbFalse", "3CcTrue", "4DdFalse" in turn. - /// + /// , + /// , + /// , + /// or + /// is null. + /// + /// The input sequences are of different lengths. /// - /// This operator uses deferred execution and streams its results. - /// + /// This operator uses deferred execution and streams its results. - public static IEnumerable EquiZip( - this IEnumerable first, - IEnumerable second, IEnumerable third, IEnumerable fourth, - Func resultSelector) + public static IEnumerable EquiZip( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + IEnumerable fourth, + Func resultSelector) => MoreEnumerable.EquiZip(first, second, third, fourth, resultSelector); } @@ -6872,140 +6843,117 @@ public static IEnumerable> WindowRight(this IEnumerable< public static partial class ZipLongestExtension { /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// will always be as long as the longest of input sequences where the - /// default value of each of the shorter sequence element types is used - /// for padding. - /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. + /// + /// Applies a specified function to the corresponding elements of two sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as long as the longest of the input sequences. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. /// - /// Function to apply to each pair of elements. + /// A function that specifies how to merge the elements from the two sequences. /// - /// A sequence that contains elements of the two input sequences, - /// combined by . - /// + /// An IEnumerable that contains merged elements of two input sequences. /// - /// , , or is . - /// - /// - /// n + l); - /// ]]> - /// The zipped variable, when iterated over, will yield "1A", - /// "2B", "3C", "0D" in turn. - /// + /// , + /// or + /// is null. /// - /// This operator uses deferred execution and streams its results. + /// + /// If the input sequences are of different lengths, the default values of the types + /// of the elements of the shortests sequences are used for padding. + /// + /// This operator uses deferred execution and streams its results. /// public static IEnumerable ZipLongest( this IEnumerable first, IEnumerable second, - Func resultSelector) + Func resultSelector) => MoreEnumerable.ZipLongest(first, second, resultSelector); /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// will always be as long as the longest of input sequences where the - /// default value of each of the shorter sequence element types is used - /// for padding. + /// + /// Applies a specified function to the corresponding elements of three sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as long as the longest of the input sequences. /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in third sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. - /// The third sequence. + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. /// - /// Function to apply to each triplet of elements. + /// A function that specifies how to merge the elements from the three sequences. /// - /// A sequence that contains elements of the three input sequences, - /// combined by . - /// + /// An IEnumerable that contains merged elements of three input sequences. /// - /// , , , or is . - /// - /// - /// n + l + c); - /// ]]> - /// The zipped variable, when iterated over, will yield "1Aa", - /// "2Bb", "3Cc", "0Dd", "0e" in turn. - /// + /// , + /// , + /// or + /// is null. /// - /// This operator uses deferred execution and streams its results. + /// + /// If the input sequences are of different lengths, the default values of the types + /// of the elements of the shortests sequences are used for padding. + /// + /// This operator uses deferred execution and streams its results. /// - public static IEnumerable ZipLongest( - this IEnumerable first, - IEnumerable second, - IEnumerable third, - Func resultSelector) + public static IEnumerable ZipLongest( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + Func resultSelector) => MoreEnumerable.ZipLongest(first, second, third, resultSelector); /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// will always be as long as the longest of input sequences where the - /// default value of each of the shorter sequence element types is used - /// for padding. - /// - /// Type of elements in first sequence - /// Type of elements in second sequence - /// Type of elements in third sequence - /// Type of elements in fourth sequence - /// Type of elements in result sequence - /// The first sequence. - /// The second sequence. - /// The third sequence. - /// The fourth sequence. + /// + /// Applies a specified function to the corresponding elements of four sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as long as the longest of the input sequences. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the fourth input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. + /// The fourth sequence to merge. /// - /// Function to apply to each quadruplet of elements. + /// A function that specifies how to merge the elements from the four sequences. /// - /// A sequence that contains elements of the four input sequences, - /// combined by . - /// + /// An IEnumerable that contains merged elements of four input sequences. /// - /// , , , , or is . - /// - /// - /// n + l + c + f); - /// ]]> - /// The zipped variable, when iterated over, will yield "1AaTrue", - /// "2BbFalse", "3CcTrue", "0DdFalse", "0eTrue", "0\0False" in turn. - /// + /// , + /// , + /// , + /// or + /// is null. /// - /// This operator uses deferred execution and streams its results. + /// + /// If the input sequences are of different lengths, the default values of the types + /// of the elements of the shortests sequences are used for padding. + /// + /// This operator uses deferred execution and streams its results. /// - public static IEnumerable ZipLongest( - this IEnumerable first, - IEnumerable second, - IEnumerable third, - IEnumerable fourth, - Func resultSelector) + public static IEnumerable ZipLongest( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + IEnumerable fourth, + Func resultSelector) => MoreEnumerable.ZipLongest(first, second, third, fourth, resultSelector); } @@ -7016,38 +6964,30 @@ public static IEnumerable ZipLongest( public static partial class ZipShortestExtension { /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// is as short as the shortest input sequence. + /// + /// Applies a specified function to the corresponding elements of two sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as short as the shortest of the input sequences. /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. /// - /// Function to apply to each pair of elements. + /// A function that specifies how to merge the elements from the two sequences. /// - /// A projection of tuples, where each tuple contains the N-th element - /// from each of the argument sequences. - /// + /// An IEnumerable that contains merged elements of two input sequences. /// - /// , , or is . - /// - /// n + l); - /// ]]> - /// The zipped variable, when iterated over, will yield "1A", "2B", "3C", in turn. - /// + /// , + /// or + /// is null. /// /// - /// If the input sequences are of different lengths, the result sequence - /// is terminated as soon as the shortest input sequence is exhausted - /// and remainder elements from the longer sequences are never consumed. - /// + /// If the input sequences are of different lengths, the resulting sequence is terminated + /// as soon as the shortest input sequence reaches its end. + /// The remaining elements of the other sequences are never consumed. /// /// This operator uses deferred execution and streams its results. /// @@ -7059,105 +6999,85 @@ public static IEnumerable ZipShortest( => MoreEnumerable.ZipShortest(first, second, resultSelector); /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// is as short as the shortest input sequence. + /// + /// Applies a specified function to the corresponding elements of three sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as short as the shortest of the input sequences. /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in third sequence. - /// Type of elements in result sequence. - /// First sequence - /// Second sequence - /// Third sequence + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. /// - /// Function to apply to each triplet of elements. + /// A function that specifies how to merge the elements from the three sequences. /// - /// A projection of tuples, where each tuple contains the N-th element - /// from each of the argument sequences. + /// An IEnumerable that contains merged elements of three input sequences. /// - /// , , , or is . - /// - /// - /// c + n + l); - /// ]]> - /// The zipped variable, when iterated over, will yield - /// "98A", "100B", "102C", in turn. - /// + /// , + /// , + /// or + /// is null. /// /// - /// If the input sequences are of different lengths, the result sequence - /// is terminated as soon as the shortest input sequence is exhausted - /// and remainder elements from the longer sequences are never consumed. - /// + /// If the input sequences are of different lengths, the resulting sequence is terminated + /// as soon as the shortest input sequence reaches its end. + /// The remaining elements of the other sequences are never consumed. /// /// This operator uses deferred execution and streams its results. /// - public static IEnumerable ZipShortest( - this IEnumerable first, - IEnumerable second, - IEnumerable third, - Func resultSelector) + public static IEnumerable ZipShortest( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + Func resultSelector) => MoreEnumerable.ZipShortest(first, second, third, resultSelector); /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// is as short as the shortest input sequence. + /// + /// Applies a specified function to the corresponding elements of four sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as short as the shortest of the input sequences. /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in third sequence. - /// Type of elements in fourth sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. - /// The third sequence. - /// The fourth sequence. + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the fourth input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. + /// The fourth sequence to merge. /// - /// Function to apply to each quadruplet of elements. + /// A function that specifies how to merge the elements from the four sequences. /// - /// A projection of tuples, where each tuple contains the N-th element - /// from each of the argument sequences. + /// An IEnumerable that contains merged elements of four input sequences. /// - /// , , , , or is . - /// - /// - /// n + l + c + f); - /// ]]> - /// The zipped variable, when iterated over, will yield - /// "1AaTrue", "2BbFalse" in turn. - /// + /// , + /// , + /// , + /// or + /// is null. /// /// - /// If the input sequences are of different lengths, the result sequence - /// is terminated as soon as the shortest input sequence is exhausted - /// and remainder elements from the longer sequences are never consumed. - /// + /// If the input sequences are of different lengths, the resulting sequence is terminated + /// as soon as the shortest input sequence reaches its end. + /// The remaining elements of the other sequences are never consumed. /// /// This operator uses deferred execution and streams its results. /// - public static IEnumerable ZipShortest( - this IEnumerable first, - IEnumerable second, - IEnumerable third, - IEnumerable fourth, - Func resultSelector) + public static IEnumerable ZipShortest( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + IEnumerable fourth, + Func resultSelector) => MoreEnumerable.ZipShortest(first, second, third, fourth, resultSelector); } diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index a34d230da..ae09a3c97 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -164,6 +164,10 @@ TextTemplatingFileGenerator Cartesian.g.cs + + TextTemplatingFileGenerator + EquiZip.g.cs + Aggregate.g.cs TextTemplatingFileGenerator @@ -176,6 +180,14 @@ TextTemplatingFileGenerator ToDelimitedString.g.cs + + TextTemplatingFileGenerator + ZipLongest.g.cs + + + TextTemplatingFileGenerator + ZipShortest.g.cs + @@ -221,6 +233,11 @@ True Cartesian.g.tt + + True + True + EquiZip.g.tt + True True @@ -244,6 +261,16 @@ True ToDelimitedString.g.tt + + True + True + ZipLongest.g.tt + + + True + True + ZipShortest.g.tt + diff --git a/MoreLinq/ZipImpl.cs b/MoreLinq/ZipImpl.cs deleted file mode 100644 index 1f008a1dc..000000000 --- a/MoreLinq/ZipImpl.cs +++ /dev/null @@ -1,97 +0,0 @@ -#region License and Terms -// MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2018 Leandro F. Vieira (leandromoh). All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#endregion - -namespace MoreLinq -{ - using System; - using System.Collections.Generic; - using System.Collections; - - static partial class MoreEnumerable - { - delegate TResult Folder(params T[] args); - - static IEnumerable ZipImpl( - IEnumerable s1, - IEnumerable s2, - IEnumerable? s3, - IEnumerable? s4, - Func resultSelector, - int limit, - Folder? errorSelector = null) - { - IEnumerator? e1 = null; - IEnumerator? e2 = null; - IEnumerator? e3 = null; - IEnumerator? e4 = null; - var terminations = 0; - - try - { - e1 = s1 .GetEnumerator(); - e2 = s2 .GetEnumerator(); - e3 = s3?.GetEnumerator(); - e4 = s4?.GetEnumerator(); - - while (true) - { - var n = 0; - var v1 = Read(ref e1, ++n); - var v2 = Read(ref e2, ++n); - var v3 = Read(ref e3, ++n); - var v4 = Read(ref e4, ++n); - - if (terminations <= limit) - yield return resultSelector(v1, v2, v3, v4); - else - yield break; - } - } - finally - { - e1?.Dispose(); - e2?.Dispose(); - e3?.Dispose(); - e4?.Dispose(); - } - - T Read(ref IEnumerator? e, int n) - { - if (e == null || terminations > limit) - return default!; - - T value; - if (e.MoveNext()) - { - value = e.Current; - } - else - { - e.Dispose(); - e = null; - terminations++; - value = default!; - } - - if (errorSelector != null && terminations > 0 && terminations < n) - throw errorSelector(e1, e2, e3, e4); - - return value; - } - } - } -} diff --git a/MoreLinq/ZipLongest.cs b/MoreLinq/ZipLongest.cs deleted file mode 100644 index 38e5fdefc..000000000 --- a/MoreLinq/ZipLongest.cs +++ /dev/null @@ -1,183 +0,0 @@ -#region License and Terms -// MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#endregion - -namespace MoreLinq -{ - using System; - using System.Collections.Generic; - - static partial class MoreEnumerable - { - /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// will always be as long as the longest of input sequences where the - /// default value of each of the shorter sequence element types is used - /// for padding. - /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. - /// - /// Function to apply to each pair of elements. - /// - /// A sequence that contains elements of the two input sequences, - /// combined by . - /// - /// - /// , , or is . - /// - /// - /// n + l); - /// ]]> - /// The zipped variable, when iterated over, will yield "1A", - /// "2B", "3C", "0D" in turn. - /// - /// - /// This operator uses deferred execution and streams its results. - /// - - public static IEnumerable ZipLongest( - this IEnumerable first, - IEnumerable second, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException(nameof(first)); - if (second == null) throw new ArgumentNullException(nameof(second)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return ZipImpl(first, second, null, null, (a, b, _, _) => resultSelector(a, b), 1); - } - - /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// will always be as long as the longest of input sequences where the - /// default value of each of the shorter sequence element types is used - /// for padding. - /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in third sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. - /// The third sequence. - /// - /// Function to apply to each triplet of elements. - /// - /// A sequence that contains elements of the three input sequences, - /// combined by . - /// - /// - /// , , , or is . - /// - /// - /// n + l + c); - /// ]]> - /// The zipped variable, when iterated over, will yield "1Aa", - /// "2Bb", "3Cc", "0Dd", "0e" in turn. - /// - /// - /// This operator uses deferred execution and streams its results. - /// - - public static IEnumerable ZipLongest( - this IEnumerable first, - IEnumerable second, - IEnumerable third, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException(nameof(first)); - if (second == null) throw new ArgumentNullException(nameof(second)); - if (third == null) throw new ArgumentNullException(nameof(third)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return ZipImpl(first, second, third, null, (a, b, c, _) => resultSelector(a, b, c), 2); - } - - /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// will always be as long as the longest of input sequences where the - /// default value of each of the shorter sequence element types is used - /// for padding. - /// - /// Type of elements in first sequence - /// Type of elements in second sequence - /// Type of elements in third sequence - /// Type of elements in fourth sequence - /// Type of elements in result sequence - /// The first sequence. - /// The second sequence. - /// The third sequence. - /// The fourth sequence. - /// - /// Function to apply to each quadruplet of elements. - /// - /// A sequence that contains elements of the four input sequences, - /// combined by . - /// - /// - /// , , , , or is . - /// - /// - /// n + l + c + f); - /// ]]> - /// The zipped variable, when iterated over, will yield "1AaTrue", - /// "2BbFalse", "3CcTrue", "0DdFalse", "0eTrue", "0\0False" in turn. - /// - /// - /// This operator uses deferred execution and streams its results. - /// - - public static IEnumerable ZipLongest( - this IEnumerable first, - IEnumerable second, - IEnumerable third, - IEnumerable fourth, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException(nameof(first)); - if (second == null) throw new ArgumentNullException(nameof(second)); - if (third == null) throw new ArgumentNullException(nameof(third)); - if (fourth == null) throw new ArgumentNullException(nameof(fourth)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return ZipImpl(first, second, third, fourth, resultSelector, 3); - } - } -} diff --git a/MoreLinq/ZipLongest.g.cs b/MoreLinq/ZipLongest.g.cs new file mode 100644 index 000000000..0ad199529 --- /dev/null +++ b/MoreLinq/ZipLongest.g.cs @@ -0,0 +1,242 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// + /// Applies a specified function to the corresponding elements of two sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as long as the longest of the input sequences. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// + /// A function that specifies how to merge the elements from the two sequences. + /// + /// An IEnumerable that contains merged elements of two input sequences. + /// + /// , + /// or + /// is null. + /// + /// + /// If the input sequences are of different lengths, the default values of the types + /// of the elements of the shortests sequences are used for padding. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable ZipLongest( + this IEnumerable first, + IEnumerable second, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + IEnumerator e1 = null; + IEnumerator e2 = null; + + try + { + e1 = first.GetEnumerator(); + e2 = second.GetEnumerator(); + + // | is used instead of || in purpose. All operands have to be evaluated. + while ( + Enumerator.TryRead(ref e1, out var v1) | + Enumerator.TryRead(ref e2, out var v2)) + { + yield return resultSelector(v1, v2); + } + } + finally + { + e1?.Dispose(); + e2?.Dispose(); + } + } + } + + /// + /// + /// Applies a specified function to the corresponding elements of three sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as long as the longest of the input sequences. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. + /// + /// A function that specifies how to merge the elements from the three sequences. + /// + /// An IEnumerable that contains merged elements of three input sequences. + /// + /// , + /// , + /// or + /// is null. + /// + /// + /// If the input sequences are of different lengths, the default values of the types + /// of the elements of the shortests sequences are used for padding. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable ZipLongest( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (third == null) throw new ArgumentNullException(nameof(third)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + IEnumerator e1 = null; + IEnumerator e2 = null; + IEnumerator e3 = null; + + try + { + e1 = first.GetEnumerator(); + e2 = second.GetEnumerator(); + e3 = third.GetEnumerator(); + + // | is used instead of || in purpose. All operands have to be evaluated. + while ( + Enumerator.TryRead(ref e1, out var v1) | + Enumerator.TryRead(ref e2, out var v2) | + Enumerator.TryRead(ref e3, out var v3)) + { + yield return resultSelector(v1, v2, v3); + } + } + finally + { + e1?.Dispose(); + e2?.Dispose(); + e3?.Dispose(); + } + } + } + + /// + /// + /// Applies a specified function to the corresponding elements of four sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as long as the longest of the input sequences. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the fourth input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. + /// The fourth sequence to merge. + /// + /// A function that specifies how to merge the elements from the four sequences. + /// + /// An IEnumerable that contains merged elements of four input sequences. + /// + /// , + /// , + /// , + /// or + /// is null. + /// + /// + /// If the input sequences are of different lengths, the default values of the types + /// of the elements of the shortests sequences are used for padding. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable ZipLongest( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + IEnumerable fourth, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (third == null) throw new ArgumentNullException(nameof(third)); + if (fourth == null) throw new ArgumentNullException(nameof(fourth)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + IEnumerator e1 = null; + IEnumerator e2 = null; + IEnumerator e3 = null; + IEnumerator e4 = null; + + try + { + e1 = first.GetEnumerator(); + e2 = second.GetEnumerator(); + e3 = third.GetEnumerator(); + e4 = fourth.GetEnumerator(); + + // | is used instead of || in purpose. All operands have to be evaluated. + while ( + Enumerator.TryRead(ref e1, out var v1) | + Enumerator.TryRead(ref e2, out var v2) | + Enumerator.TryRead(ref e3, out var v3) | + Enumerator.TryRead(ref e4, out var v4)) + { + yield return resultSelector(v1, v2, v3, v4); + } + } + finally + { + e1?.Dispose(); + e2?.Dispose(); + e3?.Dispose(); + e4?.Dispose(); + } + } + } + + } +} diff --git a/MoreLinq/ZipLongest.g.tt b/MoreLinq/ZipLongest.g.tt new file mode 100644 index 000000000..91330bb2b --- /dev/null +++ b/MoreLinq/ZipLongest.g.tt @@ -0,0 +1,146 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Globalization" #> +<#@ import namespace="System.Linq" #> +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +<# + var ordinals = new[] + { + string.Empty, + "First", "Second", "Third", "Fourth", + "Fifth", "Sixth", "Seventh", "Eighth" + }; + + var cardinals = new[] + { + "zero", + "one", "two", "three", "four", + "five", "six", "seven", "eight" + }; + + var overloads = + from argCount in Enumerable.Range(2, 3) + from args in new[] + { + from argPosition in Enumerable.Range(1, argCount) + select new + { + IsFirst = argPosition == 1, + IsLast = argPosition == argCount, + Name = ordinals[argPosition].ToLower(), + ordinal = ordinals[argPosition].ToLower(), + Type = $"T{ordinals[argPosition]}", + // Objects associated with the argument + Enumerator = $"e{argPosition}", + Value = $"v{argPosition}" + } + } + select new + { + Arguments = args.ToList(), + cardinal = cardinals[argCount], + TParams = string.Join(", ", from arg in args select arg.Type) + }; +#> +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { +<# foreach (var o in overloads) + { +#> + /// + /// + /// Applies a specified function to the corresponding elements of <#= o.cardinal #> sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as long as the longest of the input sequences. + /// +<# foreach (var arg in o.Arguments) { #> + /// The type of the elements of the <#= arg.ordinal #> input sequence. +<# } #> + /// The type of the elements of the result sequence. +<# foreach (var arg in o.Arguments) { #> + /// The <#= arg.ordinal #> sequence to merge. +<# } #> + /// + /// A function that specifies how to merge the elements from the <#= o.cardinal #> sequences. + /// + /// An IEnumerable that contains merged elements of <#= o.cardinal #> input sequences. + /// +<# foreach (var arg in o.Arguments) { #> + /// <#= arg.IsLast ? " or" : "," #> +<# } #> + /// is null. + /// + /// + /// If the input sequences are of different lengths, the default values of the types + /// of the elements of the shortests sequences are used for padding. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable ZipLongest<<#= o.TParams #>, TResult>( +<# foreach (var arg in o.Arguments) { #> + <#= arg.IsFirst ? "this " : "" #>IEnumerable<<#= arg.Type #>> <#= arg.Name #>, +<# } #> + Func<<#= o.TParams #>, TResult> resultSelector) + { +<# foreach (var arg in o.Arguments) { #> + if (<#= arg.Name #> == null) throw new ArgumentNullException(nameof(<#= arg.Name #>)); +<# } #> + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { +<# foreach (var arg in o.Arguments) { #> + IEnumerator<<#= arg.Type #>> <#= arg.Enumerator #> = null; +<# } #> + + try + { +<# foreach (var arg in o.Arguments) { #> + <#= arg.Enumerator #> = <#= arg.Name #>.GetEnumerator(); +<# } #> + + // | is used instead of || in purpose. All operands have to be evaluated. + while ( +<# foreach (var arg in o.Arguments) { #> + Enumerator.TryRead(ref <#= arg.Enumerator #>, out var <#= arg.Value #>)<#= arg.IsLast ? ")" : " |" #> +<# } #> + { + yield return resultSelector(<# foreach (var arg in o.Arguments) { #><#= arg.Value #><#= arg.IsLast ? "" : ", " #><# } #>); + } + } + finally + { +<# foreach (var arg in o.Arguments) { #> + <#= arg.Enumerator #>?.Dispose(); +<# } #> + } + } + } + +<# } #> + } +} diff --git a/MoreLinq/ZipShortest.cs b/MoreLinq/ZipShortest.cs deleted file mode 100644 index fbb28d046..000000000 --- a/MoreLinq/ZipShortest.cs +++ /dev/null @@ -1,201 +0,0 @@ -#region License and Terms -// MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#endregion - -namespace MoreLinq -{ - using System; - using System.Collections.Generic; - - static partial class MoreEnumerable - { - /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// is as short as the shortest input sequence. - /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. - /// - /// Function to apply to each pair of elements. - /// - /// A projection of tuples, where each tuple contains the N-th element - /// from each of the argument sequences. - /// - /// - /// , , or is . - /// - /// n + l); - /// ]]> - /// The zipped variable, when iterated over, will yield "1A", "2B", "3C", in turn. - /// - /// - /// - /// If the input sequences are of different lengths, the result sequence - /// is terminated as soon as the shortest input sequence is exhausted - /// and remainder elements from the longer sequences are never consumed. - /// - /// - /// This operator uses deferred execution and streams its results. - /// - - public static IEnumerable ZipShortest( - this IEnumerable first, - IEnumerable second, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException(nameof(first)); - if (second == null) throw new ArgumentNullException(nameof(second)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return ZipImpl(first, second, null, null, (a, b, _, _) => resultSelector(a, b)); - } - - /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// is as short as the shortest input sequence. - /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in third sequence. - /// Type of elements in result sequence. - /// First sequence - /// Second sequence - /// Third sequence - /// - /// Function to apply to each triplet of elements. - /// - /// A projection of tuples, where each tuple contains the N-th element - /// from each of the argument sequences. - /// - /// , , , or is . - /// - /// - /// c + n + l); - /// ]]> - /// The zipped variable, when iterated over, will yield - /// "98A", "100B", "102C", in turn. - /// - /// - /// - /// If the input sequences are of different lengths, the result sequence - /// is terminated as soon as the shortest input sequence is exhausted - /// and remainder elements from the longer sequences are never consumed. - /// - /// - /// This operator uses deferred execution and streams its results. - /// - - public static IEnumerable ZipShortest( - this IEnumerable first, - IEnumerable second, - IEnumerable third, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException(nameof(first)); - if (second == null) throw new ArgumentNullException(nameof(second)); - if (third == null) throw new ArgumentNullException(nameof(third)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return ZipImpl(first, second, third, null, (a, b, c, _) => resultSelector(a, b, c)); - } - - /// - /// Returns a projection of tuples, where each tuple contains the N-th - /// element from each of the argument sequences. The resulting sequence - /// is as short as the shortest input sequence. - /// - /// Type of elements in first sequence. - /// Type of elements in second sequence. - /// Type of elements in third sequence. - /// Type of elements in fourth sequence. - /// Type of elements in result sequence. - /// The first sequence. - /// The second sequence. - /// The third sequence. - /// The fourth sequence. - /// - /// Function to apply to each quadruplet of elements. - /// - /// A projection of tuples, where each tuple contains the N-th element - /// from each of the argument sequences. - /// - /// , , , , or is . - /// - /// - /// n + l + c + f); - /// ]]> - /// The zipped variable, when iterated over, will yield - /// "1AaTrue", "2BbFalse" in turn. - /// - /// - /// - /// If the input sequences are of different lengths, the result sequence - /// is terminated as soon as the shortest input sequence is exhausted - /// and remainder elements from the longer sequences are never consumed. - /// - /// - /// This operator uses deferred execution and streams its results. - /// - - public static IEnumerable ZipShortest( - this IEnumerable first, - IEnumerable second, - IEnumerable third, - IEnumerable fourth, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException(nameof(first)); - if (second == null) throw new ArgumentNullException(nameof(second)); - if (third == null) throw new ArgumentNullException(nameof(third)); - if (fourth == null) throw new ArgumentNullException(nameof(fourth)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return ZipImpl(first, second, third, fourth, resultSelector); - } - - static IEnumerable ZipImpl( - IEnumerable s1, - IEnumerable s2, - IEnumerable? s3, - IEnumerable? s4, - Func resultSelector) - { - return ZipImpl(s1, s2, s3, s4, resultSelector, 0); - } - } -} diff --git a/MoreLinq/ZipShortest.g.cs b/MoreLinq/ZipShortest.g.cs new file mode 100644 index 000000000..302511530 --- /dev/null +++ b/MoreLinq/ZipShortest.g.cs @@ -0,0 +1,194 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// + /// Applies a specified function to the corresponding elements of two sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as short as the shortest of the input sequences. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// + /// A function that specifies how to merge the elements from the two sequences. + /// + /// An IEnumerable that contains merged elements of two input sequences. + /// + /// , + /// or + /// is null. + /// + /// + /// If the input sequences are of different lengths, the resulting sequence is terminated + /// as soon as the shortest input sequence reaches its end. + /// The remaining elements of the other sequences are never consumed. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable ZipShortest( + this IEnumerable first, + IEnumerable second, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e1 = first.GetEnumerator(); + using var e2 = second.GetEnumerator(); + + while (e1.MoveNext() && e2.MoveNext()) + { + yield return resultSelector(e1.Current, e2.Current); + } + } + } + + /// + /// + /// Applies a specified function to the corresponding elements of three sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as short as the shortest of the input sequences. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. + /// + /// A function that specifies how to merge the elements from the three sequences. + /// + /// An IEnumerable that contains merged elements of three input sequences. + /// + /// , + /// , + /// or + /// is null. + /// + /// + /// If the input sequences are of different lengths, the resulting sequence is terminated + /// as soon as the shortest input sequence reaches its end. + /// The remaining elements of the other sequences are never consumed. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable ZipShortest( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (third == null) throw new ArgumentNullException(nameof(third)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e1 = first.GetEnumerator(); + using var e2 = second.GetEnumerator(); + using var e3 = third.GetEnumerator(); + + while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext()) + { + yield return resultSelector(e1.Current, e2.Current, e3.Current); + } + } + } + + /// + /// + /// Applies a specified function to the corresponding elements of four sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as short as the shortest of the input sequences. + /// + /// The type of the elements of the first input sequence. + /// The type of the elements of the second input sequence. + /// The type of the elements of the third input sequence. + /// The type of the elements of the fourth input sequence. + /// The type of the elements of the result sequence. + /// The first sequence to merge. + /// The second sequence to merge. + /// The third sequence to merge. + /// The fourth sequence to merge. + /// + /// A function that specifies how to merge the elements from the four sequences. + /// + /// An IEnumerable that contains merged elements of four input sequences. + /// + /// , + /// , + /// , + /// or + /// is null. + /// + /// + /// If the input sequences are of different lengths, the resulting sequence is terminated + /// as soon as the shortest input sequence reaches its end. + /// The remaining elements of the other sequences are never consumed. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable ZipShortest( + this IEnumerable first, + IEnumerable second, + IEnumerable third, + IEnumerable fourth, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (third == null) throw new ArgumentNullException(nameof(third)); + if (fourth == null) throw new ArgumentNullException(nameof(fourth)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e1 = first.GetEnumerator(); + using var e2 = second.GetEnumerator(); + using var e3 = third.GetEnumerator(); + using var e4 = fourth.GetEnumerator(); + + while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext() && e4.MoveNext()) + { + yield return resultSelector(e1.Current, e2.Current, e3.Current, e4.Current); + } + } + } + + } +} diff --git a/MoreLinq/ZipShortest.g.tt b/MoreLinq/ZipShortest.g.tt new file mode 100644 index 000000000..a05f482bf --- /dev/null +++ b/MoreLinq/ZipShortest.g.tt @@ -0,0 +1,130 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Globalization" #> +<#@ import namespace="System.Linq" #> +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +<# + var ordinals = new[] + { + string.Empty, + "First", "Second", "Third", "Fourth", + "Fifth", "Sixth", "Seventh", "Eighth" + }; + + var cardinals = new[] + { + "zero", + "one", "two", "three", "four", + "five", "six", "seven", "eight" + }; + + var overloads = + from argCount in Enumerable.Range(2, 3) + from args in new[] + { + from argPosition in Enumerable.Range(1, argCount) + select new + { + IsFirst = argPosition == 1, + IsLast = argPosition == argCount, + Name = ordinals[argPosition].ToLower(), + ordinal = ordinals[argPosition].ToLower(), + Type = $"T{ordinals[argPosition]}", + // Objects associated with the argument + Enumerator = $"e{argPosition}", + Value = $"v{argPosition}" + } + } + select new + { + Arguments = args.ToList(), + cardinal = cardinals[argCount], + TParams = string.Join(", ", from arg in args select arg.Type) + }; +#> +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { +<# foreach (var o in overloads) + { +#> + /// + /// + /// Applies a specified function to the corresponding elements of <#= o.cardinal #> sequences, + /// producing a sequence of the results. + /// + /// The resulting sequence is as short as the shortest of the input sequences. + /// +<# foreach (var arg in o.Arguments) { #> + /// The type of the elements of the <#= arg.ordinal #> input sequence. +<# } #> + /// The type of the elements of the result sequence. +<# foreach (var arg in o.Arguments) { #> + /// The <#= arg.ordinal #> sequence to merge. +<# } #> + /// + /// A function that specifies how to merge the elements from the <#= o.cardinal #> sequences. + /// + /// An IEnumerable that contains merged elements of <#= o.cardinal #> input sequences. + /// +<# foreach (var arg in o.Arguments) { #> + /// <#= arg.IsLast ? " or" : "," #> +<# } #> + /// is null. + /// + /// + /// If the input sequences are of different lengths, the resulting sequence is terminated + /// as soon as the shortest input sequence reaches its end. + /// The remaining elements of the other sequences are never consumed. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable ZipShortest<<#= o.TParams #>, TResult>( +<# foreach (var arg in o.Arguments) { #> + <#= arg.IsFirst ? "this " : "" #>IEnumerable<<#= arg.Type #>> <#= arg.Name #>, +<# } #> + Func<<#= o.TParams #>, TResult> resultSelector) + { +<# foreach (var arg in o.Arguments) { #> + if (<#= arg.Name #> == null) throw new ArgumentNullException(nameof(<#= arg.Name #>)); +<# } #> + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { +<# foreach (var arg in o.Arguments) { #> + using var <#= arg.Enumerator #> = <#= arg.Name #>.GetEnumerator(); +<# } #> + + while (<# foreach (var arg in o.Arguments) { #><#= arg.Enumerator #>.MoveNext()<#= arg.IsLast ? "" : " && " #><# } #>) + { + yield return resultSelector(<# foreach (var arg in o.Arguments) { #><#= arg.Enumerator #>.Current<#= arg.IsLast ? "" : ", " #><# } #>); + } + } + } + +<# } #> + } +} diff --git a/README.md b/README.md index ece3287fc..303809208 100644 --- a/README.md +++ b/README.md @@ -211,9 +211,10 @@ This method has 2 overloads. ### EquiZip -Returns a projection of tuples, where each tuple contains the N-th -element from each of the argument sequences. An exception is thrown -if the input sequences are of different lengths. +Applies a specified function to the corresponding elements of multiple sequences, +producing a sequence of the results. +The resulting sequence has the same length as the input sequences. +If the input sequences are of different lengths, an exception is thrown. This method has 3 overloads. @@ -702,19 +703,17 @@ Creates a right-aligned sliding window over the source sequence of a given size. ### ZipLongest -Returns a projection of tuples, where each tuple contains the N-th -element from each of the argument sequences. The resulting sequence -will always be as long as the longest of input sequences where the -default value of each of the shorter sequence element types is used -for padding. +Applies a specified function to the corresponding elements of multiple sequences, +producing a sequence of the results. +The resulting sequence is as long as the longest of the input sequences. This method has 3 overloads. ### ZipShortest -Returns a projection of tuples, where each tuple contains the N-th -element from each of the argument sequences. The resulting sequence -is as short as the shortest input sequence. +Applies a specified function to the corresponding elements of multiple sequences, +producing a sequence of the results. +The resulting sequence is as short as the shortest of the input sequences. This method has 3 overloads.