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,