From fb9441a6a49433de2c95869e37f831d6a763fe87 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Fri, 25 Nov 2022 14:40:09 -0600 Subject: [PATCH 1/2] Simplify EquiZip implementation --- MoreLinq/EquiZip.cs | 75 +++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/MoreLinq/EquiZip.cs b/MoreLinq/EquiZip.cs index 61212f122..1d40e0445 100644 --- a/MoreLinq/EquiZip.cs +++ b/MoreLinq/EquiZip.cs @@ -68,7 +68,7 @@ public static IEnumerable EquiZip( 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)); + return EquiZipImpl(first, second, Enumerable.Repeat(default(object?), int.MaxValue), Enumerable.Repeat(default(object?), int.MaxValue), (a, b, _, _) => resultSelector(a, b), 2); } /// @@ -121,7 +121,7 @@ public static IEnumerable EquiZip( 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)); + return EquiZipImpl(first, second, third, Enumerable.Repeat(default(object?), int.MaxValue), (a, b, c, _) => resultSelector(a, b, c), 3); } /// @@ -178,46 +178,49 @@ public static IEnumerable EquiZip( if (fourth == null) throw new ArgumentNullException(nameof(fourth)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return EquiZipImpl(first, second, third, fourth, resultSelector); + return EquiZipImpl(first, second, third, fourth, resultSelector, 4); } static IEnumerable EquiZipImpl( - IEnumerable s1, - IEnumerable s2, - IEnumerable? s3, - IEnumerable? s4, - Func resultSelector) + IEnumerable s1, + IEnumerable s2, + IEnumerable s3, + IEnumerable s4, + Func resultSelector, + int expectedTerminations) { - const int zero = 0, one = 1; - - var limit = 1 + (s3 != null ? one : zero) - + (s4 != null ? one : zero); + using var e1 = s1.GetEnumerator(); + using var e2 = s2.GetEnumerator(); + using var e3 = s3.GetEnumerator(); + using var e4 = s4.GetEnumerator(); - return ZipImpl(s1, s2, s3, s4, resultSelector, limit, enumerators => + while (true) { - var i = enumerators.Index().First(x => x.Value == null).Key; - return new InvalidOperationException(OrdinalNumbers[i] + " sequence too short."); - }); - } + if (!e1.MoveNext()) + { + if (e2.MoveNext() + || expectedTerminations >= 3 && e3.MoveNext() + || expectedTerminations >= 4 && e4.MoveNext()) + { + throw new InvalidOperationException("First sequence too short."); + } - static readonly string[] OrdinalNumbers = - { - "First", - "Second", - "Third", - "Fourth", - // "Fifth", - // "Sixth", - // "Seventh", - // "Eighth", - // "Ninth", - // "Tenth", - // "Eleventh", - // "Twelfth", - // "Thirteenth", - // "Fourteenth", - // "Fifteenth", - // "Sixteenth", - }; + yield break; + } + + if (!e2.MoveNext()) + throw new InvalidOperationException("Second sequence too short."); + if (!e3.MoveNext()) + throw new InvalidOperationException("Third sequence too short."); + if (!e4.MoveNext()) + throw new InvalidOperationException("Fourth sequence too short."); + + yield return resultSelector( + e1.Current, + e2.Current, + e3.Current, + e4.Current); + } + } } } From 5ff2ad4ea88cc2052eded807d87a421b9a301ba6 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Fri, 25 Nov 2022 15:37:47 -0600 Subject: [PATCH 2/2] Add missing unit test --- MoreLinq.Test/EquiZipTest.cs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/MoreLinq.Test/EquiZipTest.cs b/MoreLinq.Test/EquiZipTest.cs index 477a86384..1655ca36c 100644 --- a/MoreLinq.Test/EquiZipTest.cs +++ b/MoreLinq.Test/EquiZipTest.cs @@ -17,7 +17,10 @@ namespace MoreLinq.Test { + using System; + using System.Collections.Generic; using NUnit.Framework; + using NUnit.Framework.Interfaces; using Tuple = System.ValueTuple; [TestFixture] @@ -76,6 +79,29 @@ public void ZipIsLazy() bs.EquiZip(bs, BreakingFunc.Of()); } + public static readonly IEnumerable TestData = + from e in new[] + { + new { A = 1, B = "First", }, + new { A = 2, B = "Second", }, + new { A = 3, B = "Third", }, + new { A = 4, B = "Fourth", }, + } + select new TestCaseData(e.A, e.B); + + [Test, TestCaseSource(nameof(TestData))] + public void ShortestSequenceIsIdentifiedProperly(int shortSequence, string sequenceName) + { + using var seq1 = Enumerable.Range(1, shortSequence == 1 ? 2 : 3).AsTestingSequence(); + using var seq2 = Enumerable.Range(1, shortSequence == 2 ? 2 : 3).AsTestingSequence(); + using var seq3 = Enumerable.Range(1, shortSequence == 3 ? 2 : 3).AsTestingSequence(); + using var seq4 = Enumerable.Range(1, shortSequence == 4 ? 2 : 3).AsTestingSequence(); + + var ex = Assert.Throws(() => + seq1.EquiZip(seq2, seq3, seq4, (_, _, _, _) => 0).Consume()); + Assert.That(ex.Message, Is.EqualTo(sequenceName + " sequence too short.")); + } + [Test] public void MoveNextIsNotCalledUnnecessarily() {