diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 32d7ac7b7..54c8de278 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -61,6 +61,11 @@ + + True + True + TuplewiseTest.g.tt + @@ -74,7 +79,15 @@ + + + + TextTemplatingFileGenerator + TuplewiseTest.g.cs + + + diff --git a/MoreLinq.Test/TuplewiseTest.cs b/MoreLinq.Test/TuplewiseTest.cs new file mode 100644 index 000000000..ac84ac78c --- /dev/null +++ b/MoreLinq.Test/TuplewiseTest.cs @@ -0,0 +1,69 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Phillip Palk. 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.Test +{ + using System; + using System.Collections.Generic; + using NUnit.Framework; + + [TestFixture] + public partial class TuplewiseTest + { + // NOTE: See the T4 template TuplewiseTest.g.tt for the actual tests + + static void TuplewiseNWide(Func, TFunc, IEnumerable> tuplewise, TFunc resultSelector, IEnumerable source, params TResult[] fullResult) + where TFunc : Delegate + { + var arity = resultSelector.GetType().GetGenericArguments().Length - 1; + + for (var i = 0; i < fullResult.Length; ++i) + { + using var ts = source.Take(i).AsTestingSequence(); + Assert.That(tuplewise(ts, resultSelector), Is.EqualTo(fullResult.Take(i - arity + 1))); + } + } + + static void TuplewiseNWideInt(Func, TFunc, IEnumerable> tuplewise, TFunc resultSelector) + where TFunc : Delegate + { + const int rangeLen = 100; + var arity = resultSelector.GetType().GetGenericArguments().Length - 1; + + TuplewiseNWide( + tuplewise, + resultSelector, + Enumerable.Range(1, rangeLen), + Enumerable.Range(1, rangeLen - (arity - 1)).Select(x => x * arity + Enumerable.Range(1, arity - 1).Sum()).ToArray() + ); + } + + static void TuplewiseNWideString(Func, TFunc, IEnumerable> tuplewise, TFunc resultSelector) + where TFunc : Delegate + { + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; + var arity = resultSelector.GetType().GetGenericArguments().Length - 1; + + TuplewiseNWide( + tuplewise, + resultSelector, + alphabet, + Enumerable.Range(0, alphabet.Length - (arity - 1)).Select(i => alphabet.Skip(i).Take(arity)).ToArray() + ); + } + } +} diff --git a/MoreLinq.Test/TuplewiseTest.g.cs b/MoreLinq.Test/TuplewiseTest.g.cs new file mode 100644 index 000000000..15f52e618 --- /dev/null +++ b/MoreLinq.Test/TuplewiseTest.g.cs @@ -0,0 +1,50 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Phillip Palk. 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.Test +{ + using System; + using System.Collections.Generic; + using NUnit.Framework; + + public partial class TuplewiseTest + { + [Test] + public void TuplewiseIsLazy() + { + new BreakingSequence().Tuplewise(BreakingFunc.Of()); + new BreakingSequence().Tuplewise(BreakingFunc.Of()); + new BreakingSequence().Tuplewise(BreakingFunc.Of()); + } + + [Test] + public void TuplewiseIntegers() + { + TuplewiseNWideInt>(MoreEnumerable.Tuplewise, (a, b ) => a + b ); + TuplewiseNWideInt>(MoreEnumerable.Tuplewise, (a, b, c ) => a + b + c ); + TuplewiseNWideInt>(MoreEnumerable.Tuplewise, (a, b, c, d) => a + b + c + d); + } + + [Test] + public void TuplewiseStrings() + { + TuplewiseNWideString>(MoreEnumerable.Tuplewise, (a, b ) => string.Join(string.Empty, a, b )); + TuplewiseNWideString>(MoreEnumerable.Tuplewise, (a, b, c ) => string.Join(string.Empty, a, b, c )); + TuplewiseNWideString>(MoreEnumerable.Tuplewise, (a, b, c, d) => string.Join(string.Empty, a, b, c, d)); + } + } +} diff --git a/MoreLinq.Test/TuplewiseTest.g.tt b/MoreLinq.Test/TuplewiseTest.g.tt new file mode 100644 index 000000000..68b9370d8 --- /dev/null +++ b/MoreLinq.Test/TuplewiseTest.g.tt @@ -0,0 +1,67 @@ +<#@ 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) 2019 Phillip Palk. 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.Test +{ + using System; + using System.Collections.Generic; + using NUnit.Framework; + + public partial class TuplewiseTest + { +<# const int max = 4; + const string alphabet = "abcdefghijklmnopqrstuvwxyz"; +#> + [Test] + public void TuplewiseIsLazy() + { +<# for (var i = 2; i <= max; ++i) { + var funcTypeArgs = string.Join(" ", Enumerable.Repeat("object,", i).Concat(Enumerable.Repeat(" ", max - i))); +#> + new BreakingSequence().Tuplewise(BreakingFunc.Of<<#= funcTypeArgs #> int>()); +<# } #> + } + + [Test] + public void TuplewiseIntegers() + { +<# for (var i = 2; i <= max; ++i) { + var funcTypeArgs = string.Join(" ", Enumerable.Repeat("int,", i).Concat(Enumerable.Repeat(" ", max - i))); + var functorArgs = string.Join(", ", Enumerable.Range(1, i).Select(j => alphabet.Substring(j - 1, 1))) + new string(' ', 3 * (max - i)); + var functorBody = string.Join(" + ", Enumerable.Range(1, i).Select(j => alphabet.Substring(j - 1, 1))) + new string(' ', 4 * (max - i)); +#> + TuplewiseNWideInt int>>(MoreEnumerable.Tuplewise, (<#= functorArgs #>) => <#= functorBody #>); +<# } #> + } + + [Test] + public void TuplewiseStrings() + { +<# for (var i = 2; i <= max; ++i) { + var funcTypeArgs = string.Join(" ", Enumerable.Repeat("char,", i).Concat(Enumerable.Repeat(" ", max - i))); + var functorArgs = string.Join(", ", Enumerable.Range(1, i).Select(j => alphabet.Substring(j - 1, 1))) + new string(' ', 3 * (max - i)); +#> + TuplewiseNWideString string>>(MoreEnumerable.Tuplewise, (<#= functorArgs #>) => string.Join(string.Empty, <#= functorArgs #>)); +<# } #> + } + } +} diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index a5ca2ac81..e9901b9aa 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -3978,13 +3978,12 @@ public static partial class PairwiseExtension /// only returned as the predecessor of the second element. /// /// The type of the elements of . - /// The type of the element of the returned sequence. + /// The type of the elements of the returned sequence. /// The source sequence. - /// A transform function to apply to - /// each pair of sequence. - /// - /// Returns the resulting sequence. - /// + /// A transform function to apply to each pair of . + /// Returns the resulting sequence. + /// is null + /// is null /// /// This operator uses deferred execution and streams its results. /// @@ -6749,6 +6748,73 @@ public static IEnumerable> Transpose(this IEnumerableTuplewise extension. + + [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] + public static partial class TuplewiseExtension + { + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence and its + /// predecessor, with the exception of the first element which is + /// only returned as the predecessor of the second element. + /// + /// The type of the elements of . + /// The type of the elements of the returned sequence. + /// The source sequence. + /// A transform function to apply to each pair of . + /// Returns the resulting sequence. + /// is null + /// is null + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Tuplewise(this IEnumerable source, Func resultSelector) + => MoreEnumerable.Tuplewise(source, resultSelector); + + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence and its + /// 2 predecessors, with the exception of the first and second elements which are + /// only returned as the predecessors of the third element. + /// + /// The type of the elements of . + /// The type of the elements of the returned sequence. + /// The source sequence. + /// A transform function to apply to each triplet of . + /// Returns the resulting sequence. + /// is null + /// is null + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Tuplewise(this IEnumerable source, Func resultSelector) + => MoreEnumerable.Tuplewise(source, resultSelector); + + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence and its + /// 3 predecessors, with the exception of the first 3 elements which are + /// only returned as the predecessors of the 4th element. + /// + /// The type of the elements of . + /// The type of the elements of the returned sequence. + /// The source sequence. + /// A transform function to apply to each 4-tuple of . + /// Returns the resulting sequence. + /// is null + /// is null + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Tuplewise(this IEnumerable source, Func resultSelector) + => MoreEnumerable.Tuplewise(source, resultSelector); + + } + /// Window extension. [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 50b11a3aa..b702cc942 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -106,6 +106,7 @@ - TraverseBreadthFirst - TraverseDepthFirst - TrySingle (EXPERIMENTAL) + - Tuplewise - Unfold - Window - WindowLeft @@ -175,6 +176,10 @@ TextTemplatingFileGenerator ToDelimitedString.g.cs + + TextTemplatingFileGenerator + Tuplewise.g.cs + @@ -243,6 +248,11 @@ True ToDelimitedString.g.tt + + True + True + Tuplewise.g.tt + diff --git a/MoreLinq/Pairwise.cs b/MoreLinq/Pairwise.cs index 9e262e85d..a134bdd9b 100644 --- a/MoreLinq/Pairwise.cs +++ b/MoreLinq/Pairwise.cs @@ -29,13 +29,12 @@ static partial class MoreEnumerable /// only returned as the predecessor of the second element. /// /// The type of the elements of . - /// The type of the element of the returned sequence. + /// The type of the elements of the returned sequence. /// The source sequence. - /// A transform function to apply to - /// each pair of sequence. - /// - /// Returns the resulting sequence. - /// + /// A transform function to apply to each pair of . + /// Returns the resulting sequence. + /// is null + /// is null /// /// This operator uses deferred execution and streams its results. /// @@ -49,24 +48,6 @@ static partial class MoreEnumerable /// public static IEnumerable Pairwise(this IEnumerable source, Func resultSelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - - return _(); IEnumerable _() - { - using var e = source.GetEnumerator(); - - if (!e.MoveNext()) - yield break; - - var previous = e.Current; - while (e.MoveNext()) - { - yield return resultSelector(previous, e.Current); - previous = e.Current; - } - } - } + => source.Tuplewise(resultSelector); } } diff --git a/MoreLinq/Tuplewise.g.cs b/MoreLinq/Tuplewise.g.cs new file mode 100644 index 000000000..38b23bff7 --- /dev/null +++ b/MoreLinq/Tuplewise.g.cs @@ -0,0 +1,152 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Phillip Palk. 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; + + partial class MoreEnumerable + { + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence and its + /// predecessor, with the exception of the first element which is + /// only returned as the predecessor of the second element. + /// + /// The type of the elements of . + /// The type of the elements of the returned sequence. + /// The source sequence. + /// A transform function to apply to each pair of . + /// Returns the resulting sequence. + /// is null + /// is null + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Tuplewise(this IEnumerable source, Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e = source.GetEnumerator(); + + if (!e.MoveNext()) + yield break; + var predecessor1 = e.Current; + + while (e.MoveNext()) + { + yield return resultSelector(predecessor1, e.Current); + (predecessor1) = (e.Current); + } + } + } + + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence and its + /// 2 predecessors, with the exception of the first and second elements which are + /// only returned as the predecessors of the third element. + /// + /// The type of the elements of . + /// The type of the elements of the returned sequence. + /// The source sequence. + /// A transform function to apply to each triplet of . + /// Returns the resulting sequence. + /// is null + /// is null + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Tuplewise(this IEnumerable source, Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e = source.GetEnumerator(); + + if (!e.MoveNext()) + yield break; + var predecessor1 = e.Current; + + if (!e.MoveNext()) + yield break; + var predecessor2 = e.Current; + + while (e.MoveNext()) + { + yield return resultSelector(predecessor1, predecessor2, e.Current); + (predecessor1, predecessor2) = (predecessor2, e.Current); + } + } + } + + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence and its + /// 3 predecessors, with the exception of the first 3 elements which are + /// only returned as the predecessors of the 4th element. + /// + /// The type of the elements of . + /// The type of the elements of the returned sequence. + /// The source sequence. + /// A transform function to apply to each 4-tuple of . + /// Returns the resulting sequence. + /// is null + /// is null + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Tuplewise(this IEnumerable source, Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e = source.GetEnumerator(); + + if (!e.MoveNext()) + yield break; + var predecessor1 = e.Current; + + if (!e.MoveNext()) + yield break; + var predecessor2 = e.Current; + + if (!e.MoveNext()) + yield break; + var predecessor3 = e.Current; + + while (e.MoveNext()) + { + yield return resultSelector(predecessor1, predecessor2, predecessor3, e.Current); + (predecessor1, predecessor2, predecessor3) = (predecessor2, predecessor3, e.Current); + } + } + } + + } +} diff --git a/MoreLinq/Tuplewise.g.tt b/MoreLinq/Tuplewise.g.tt new file mode 100644 index 000000000..721215978 --- /dev/null +++ b/MoreLinq/Tuplewise.g.tt @@ -0,0 +1,90 @@ +<#@ 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) 2019 Phillip Palk. 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; + + partial class MoreEnumerable + { +<# const int max = 4; + var overloads = + from i in Enumerable.Range(2, max - 1) + let istr = i.ToString(CultureInfo.InvariantCulture) + let im1str = (i - 1).ToString(CultureInfo.InvariantCulture) + select new + { + Ts = string.Join(", ", Enumerable.Repeat("TSource", i)), + Nth = i == 2 ? "second" : i == 3 ? "third" : istr + "th", + NTuple = i == 2 ? "pair" : i == 3 ? "triplet" : istr + "-tuple", + PIsAre = (i - 1) == 1 ? "is" : "are", + Predecessors = (i - 1) == 1 ? "predecessor" : "predecessors", + PPredecessors = (i - 1) == 1 ? "predecessor" : im1str + " predecessors", + PElements = (i - 1) == 1 ? "first element" : (i - 1) == 2 ? "first and second elements" : ("first " + im1str + " elements"), + PredecessorNames = Enumerable.Range(1, i - 1).Select(n => "predecessor" + n.ToString(CultureInfo.InvariantCulture)) + }; + + foreach (var e in overloads) { #> + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence and its + /// <#= e.PPredecessors #>, with the exception of the <#= e.PElements #> which <#= e.PIsAre #> + /// only returned as the <#= e.Predecessors #> of the <#= e.Nth #> element. + /// + /// The type of the elements of . + /// The type of the elements of the returned sequence. + /// The source sequence. + /// A transform function to apply to each <#= e.NTuple #> of . + /// Returns the resulting sequence. + /// is null + /// is null + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Tuplewise(this IEnumerable source, Func<<#= e.Ts #>, TResult> resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return _(); IEnumerable _() + { + using var e = source.GetEnumerator(); + +<# foreach (var predecessor in e.PredecessorNames) { #> + if (!e.MoveNext()) + yield break; + var <#= predecessor #> = e.Current; + +<# } #> + while (e.MoveNext()) + { + yield return resultSelector(<#= string.Join(", ", e.PredecessorNames.Concat(new[] { "e.Current" })) #>); + (<#= string.Join(", ", e.PredecessorNames) #>) = (<#= string.Join(", ", e.PredecessorNames.Skip(1).Concat(new[] { "e.Current" })) #>); + } + } + } + +<# } #> + } +} diff --git a/README.md b/README.md index e3ba59bb4..d1d836666 100644 --- a/README.md +++ b/README.md @@ -673,6 +673,12 @@ Traces the elements of a source sequence for diagnostics. This method has 3 overloads. +### Tuplewise + +Returns a sequence resulting from applying a function to each element in the +source sequence and its N-1 predecessors, with the exception of the first N-1 +elements which are only returned as the predecessors of the Nth element. + ### Unfold Returns a sequence generated by applying a state to the generator function,