Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Tuplewise operator #711

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions MoreLinq.Test/MoreLinq.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@
<Compile Include="..\MoreLinq\Debug.cs" Link="Debug.cs" />
<Compile Include="..\MoreLinq\Disposable.cs" Link="Disposable.cs" />
<Compile Include="..\MoreLinq\Reactive\Subject.cs" Link="Subject.cs" />
<Compile Update="TuplewiseTest.g.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>TuplewiseTest.g.tt</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
Expand All @@ -74,7 +79,15 @@
</ItemGroup>

<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know what this is and do we need it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it's because I've added the T4 template to the test project. I don't believe it's 'needed' as far as building is concerned, but I think it's basically a tag used by the IDE to expose certain things for the project.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that's what the service in the following line was but seems 82a7f48d-3b50-4b1e-b82e-3ada8210c358 is for tests so this one must be for T4. I was just surprised it wasn't already there since we've been using T4 since a long time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we've been using T4 since a long time.

Not in MoreLinq.Test project, until now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that certainly explains it, @Orace!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means we'll need tt.sh/tt.cmd to cover the test project as well now.

<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>
69 changes: 69 additions & 0 deletions MoreLinq.Test/TuplewiseTest.cs
Original file line number Diff line number Diff line change
@@ -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<TSource, TResult, TFunc>(Func<IEnumerable<TSource>, TFunc, IEnumerable<TResult>> tuplewise, TFunc resultSelector, IEnumerable<TSource> 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<TFunc>(Func<IEnumerable<int>, TFunc, IEnumerable<int>> 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<TFunc>(Func<IEnumerable<char>, TFunc, IEnumerable<string>> 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()
);
}
}
}
50 changes: 50 additions & 0 deletions MoreLinq.Test/TuplewiseTest.g.cs
Original file line number Diff line number Diff line change
@@ -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<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>>(MoreEnumerable.Tuplewise, (a, b ) => a + b );
TuplewiseNWideInt<Func<int, int, int, int>>(MoreEnumerable.Tuplewise, (a, b, c ) => a + b + c );
TuplewiseNWideInt<Func<int, int, int, int, int>>(MoreEnumerable.Tuplewise, (a, b, c, d) => a + b + c + d);
}

[Test]
public void TuplewiseStrings()
{
TuplewiseNWideString<Func<char, char, string>>(MoreEnumerable.Tuplewise, (a, b ) => string.Join(string.Empty, a, b ));
TuplewiseNWideString<Func<char, char, char, string>>(MoreEnumerable.Tuplewise, (a, b, c ) => string.Join(string.Empty, a, b, c ));
TuplewiseNWideString<Func<char, char, char, char, string>>(MoreEnumerable.Tuplewise, (a, b, c, d) => string.Join(string.Empty, a, b, c, d));
}
}
}
67 changes: 67 additions & 0 deletions MoreLinq.Test/TuplewiseTest.g.tt
Original file line number Diff line number Diff line change
@@ -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<object>().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<Func<<#= funcTypeArgs #> 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<Func<<#= funcTypeArgs #> string>>(MoreEnumerable.Tuplewise, (<#= 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 @@ -3978,13 +3978,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 @@ -6749,6 +6748,73 @@ public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumera

}

/// <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 @@ -106,6 +106,7 @@
- TraverseBreadthFirst
- TraverseDepthFirst
- TrySingle (EXPERIMENTAL)
- Tuplewise
- Unfold
- Window
- WindowLeft
Expand Down Expand Up @@ -175,6 +176,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)' == 'net462'">
Expand Down Expand Up @@ -243,6 +248,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>

<Target Name="_CollectTextTemplates">
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