Skip to content

Commit

Permalink
Added Tuplewise operator
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasdmentia committed Nov 15, 2019
1 parent 5ad0a65 commit 0ec9b4f
Show file tree
Hide file tree
Showing 9 changed files with 538 additions and 31 deletions.
13 changes: 13 additions & 0 deletions MoreLinq.Test/MoreLinq.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@
<Compile Include="SampleData.cs" />
<Compile Include="Scope.cs" />
<Compile Include="SequenceReader.cs" />
<Compile Include="TuplewiseTest.g.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>TuplewiseTest.g.tt</DependentUpon>
</Compile>
<Compile Include="WatchableEnumerator.cs" />
</ItemGroup>

Expand All @@ -78,7 +83,15 @@
</ItemGroup>

<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>

<ItemGroup>
<None Update="TuplewiseTest.g.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>TuplewiseTest.g.cs</LastGenOutput>
</None>
</ItemGroup>

</Project>
86 changes: 86 additions & 0 deletions MoreLinq.Test/TuplewiseTest.g.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#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 class TuplewiseTest
{
private void TuplewiseNWide<TSource, TResult, TFunc>(Func<IEnumerable<TSource>, TFunc, IEnumerable<TResult>> tuplewise, TFunc resultSelector, IEnumerable<TSource> source, params TResult[] fullResult)
{
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)));
}

private void TuplewiseNWideInt<TFunc>(Func<IEnumerable<int>, TFunc, IEnumerable<int>> tuplewise, TFunc resultSelector)
{
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()
);
}

private void TuplewiseNWideString<TFunc>(Func<IEnumerable<char>, TFunc, IEnumerable<string>> tuplewise, TFunc resultSelector)
{
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()
);
}

[Test]
public void TuplewiseIsLazy()
{
new BreakingSequence<object>().Tuplewise(BreakingFunc.Of<object, object, int>());
new BreakingSequence<object>().Tuplewise(BreakingFunc.Of<object, object, object, int>());
new BreakingSequence<object>().Tuplewise(BreakingFunc.Of<object, object, object, object, int>());
}

[Test]
public void TuplewiseIntegers()
{
TuplewiseNWideInt<Func<int, int, int>>((source, func) => MoreEnumerable.Tuplewise(source, func), (a, b ) => a + b );
TuplewiseNWideInt<Func<int, int, int, int>>((source, func) => MoreEnumerable.Tuplewise(source, func), (a, b, c ) => a + b + c );
TuplewiseNWideInt<Func<int, int, int, int, int>>((source, func) => MoreEnumerable.Tuplewise(source, func), (a, b, c, d) => a + b + c + d);
}

[Test]
public void TuplewiseStrings()
{
TuplewiseNWideString<Func<char, char, string>>((source, func) => MoreEnumerable.Tuplewise(source, func), (a, b ) => string.Join(string.Empty, a, b ));
TuplewiseNWideString<Func<char, char, char, string>>((source, func) => MoreEnumerable.Tuplewise(source, func), (a, b, c ) => string.Join(string.Empty, a, b, c ));
TuplewiseNWideString<Func<char, char, char, char, string>>((source, func) => MoreEnumerable.Tuplewise(source, func), (a, b, c, d) => string.Join(string.Empty, a, b, c, d));
}
}
}
103 changes: 103 additions & 0 deletions MoreLinq.Test/TuplewiseTest.g.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<#@ 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;

[TestFixture]
public class TuplewiseTest
{
private void TuplewiseNWide<TSource, TResult, TFunc>(Func<IEnumerable<TSource>, TFunc, IEnumerable<TResult>> tuplewise, TFunc resultSelector, IEnumerable<TSource> source, params TResult[] fullResult)
{
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)));
}

private void TuplewiseNWideInt<TFunc>(Func<IEnumerable<int>, TFunc, IEnumerable<int>> tuplewise, TFunc resultSelector)
{
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()
);
}

private void TuplewiseNWideString<TFunc>(Func<IEnumerable<char>, TFunc, IEnumerable<string>> tuplewise, TFunc resultSelector)
{
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()
);
}

<# const int max = 4;
const string alphabet = "abcdefghijklmnopqrstuvwxyz";
#>
[Test]
public void TuplewiseIsLazy()
{
<# for (int i = 2; i <= max; ++i) {
var funcTypeArgs = string.Join(" ", Enumerable.Repeat("object,", i).Concat(Enumerable.Repeat(" ", max - i)));
#>
new BreakingSequence<object>().Tuplewise(BreakingFunc.Of<<#= funcTypeArgs #> int>());
<# } #>
}

[Test]
public void TuplewiseIntegers()
{
<# for (int 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<Func<<#= funcTypeArgs #> int>>((source, func) => MoreEnumerable.Tuplewise(source, func), (<#= functorArgs #>) => <#= functorBody #>);
<# } #>
}

[Test]
public void TuplewiseStrings()
{
<# for (int 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<Func<<#= funcTypeArgs #> string>>((source, func) => MoreEnumerable.Tuplewise(source, func), (<#= functorArgs #>) => string.Join(string.Empty, <#= functorArgs #>));
<# } #>
}
}
}
78 changes: 72 additions & 6 deletions MoreLinq/Extensions.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3893,13 +3893,12 @@ public static partial class PairwiseExtension
/// only returned as the predecessor of the second element.
/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TResult">The type of the element of the returned sequence.</typeparam>
/// <typeparam name="TResult">The type of the elements of the returned sequence.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="resultSelector">A transform function to apply to
/// each pair of sequence.</param>
/// <returns>
/// Returns the resulting sequence.
/// </returns>
/// <param name="resultSelector">A transform function to apply to each pair of <paramref name="source"/>.</param>
/// <returns>Returns the resulting sequence.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null</exception>
/// <exception cref="ArgumentNullException"><paramref name="resultSelector"/> is null</exception>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>
Expand Down Expand Up @@ -6670,6 +6669,73 @@ public static TResult TrySingle<T, TCardinality, TResult>(this IEnumerable<T> so

}

/// <summary><c>Tuplewise</c> extension.</summary>

[GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")]
public static partial class TuplewiseExtension
{
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TResult">The type of the elements of the returned sequence.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="resultSelector">A transform function to apply to each pair of <paramref name="source"/>.</param>
/// <returns>Returns the resulting sequence.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null</exception>
/// <exception cref="ArgumentNullException"><paramref name="resultSelector"/> is null</exception>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>

public static IEnumerable<TResult> Tuplewise<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TResult> resultSelector)
=> MoreEnumerable.Tuplewise(source, resultSelector);

/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TResult">The type of the elements of the returned sequence.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="resultSelector">A transform function to apply to each triplet of <paramref name="source"/>.</param>
/// <returns>Returns the resulting sequence.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null</exception>
/// <exception cref="ArgumentNullException"><paramref name="resultSelector"/> is null</exception>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>

public static IEnumerable<TResult> Tuplewise<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource, TResult> resultSelector)
=> MoreEnumerable.Tuplewise(source, resultSelector);

/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TResult">The type of the elements of the returned sequence.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="resultSelector">A transform function to apply to each 4-tuple of <paramref name="source"/>.</param>
/// <returns>Returns the resulting sequence.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null</exception>
/// <exception cref="ArgumentNullException"><paramref name="resultSelector"/> is null</exception>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>

public static IEnumerable<TResult> Tuplewise<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource, TSource, TResult> resultSelector)
=> MoreEnumerable.Tuplewise(source, resultSelector);

}

/// <summary><c>Window</c> extension.</summary>

[GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")]
Expand Down
10 changes: 10 additions & 0 deletions MoreLinq/MoreLinq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
- Transpose
- TraverseBreadthFirst
- TraverseDepthFirst
- Triplewise
- TrySingle
- Unfold
- Window
Expand Down Expand Up @@ -172,6 +173,10 @@
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>ToDelimitedString.g.cs</LastGenOutput>
</None>
<None Update="Tuplewise.g.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Tuplewise.g.cs</LastGenOutput>
</None>
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
Expand Down Expand Up @@ -236,6 +241,11 @@
<AutoGen>True</AutoGen>
<DependentUpon>ToDelimitedString.g.tt</DependentUpon>
</Compile>
<Compile Update="Tuplewise.g.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Tuplewise.g.tt</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
31 changes: 6 additions & 25 deletions MoreLinq/Pairwise.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,12 @@ static partial class MoreEnumerable
/// only returned as the predecessor of the second element.
/// </summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
/// <typeparam name="TResult">The type of the element of the returned sequence.</typeparam>
/// <typeparam name="TResult">The type of the elements of the returned sequence.</typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="resultSelector">A transform function to apply to
/// each pair of sequence.</param>
/// <returns>
/// Returns the resulting sequence.
/// </returns>
/// <param name="resultSelector">A transform function to apply to each pair of <paramref name="source"/>.</param>
/// <returns>Returns the resulting sequence.</returns>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null</exception>
/// <exception cref="ArgumentNullException"><paramref name="resultSelector"/> is null</exception>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>
Expand All @@ -49,24 +48,6 @@ static partial class MoreEnumerable
/// </example>

public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TResult> resultSelector)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
{
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);
}
}
Loading

0 comments on commit 0ec9b4f

Please sign in to comment.