-
Notifications
You must be signed in to change notification settings - Fork 416
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Flatten consecutive chaining of Concat & Prepend
- Loading branch information
Showing
5 changed files
with
197 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
#region License and Terms | ||
// MoreLINQ - Extensions to LINQ to Objects | ||
// Copyright (c) 2017 Atif Aziz. 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; | ||
using System.Collections.Generic; | ||
|
||
/// <summary> | ||
/// Prepend-Concat node is a single linked-list of the discriminated union | ||
/// of a prepend item, a concat item and the source. | ||
/// </summary> | ||
|
||
abstract class PcNode<T> : IEnumerable<T> | ||
{ | ||
public static PcNode<T> WithSource(IEnumerable<T> source) => new Source(source); | ||
|
||
public PcNode<T> Prepend(T item) => new Item(item, isPrepend: true , next: this); | ||
public PcNode<T> Concat(T item) => new Item(item, isPrepend: false, next: this); | ||
|
||
sealed class Item : PcNode<T> | ||
{ | ||
public T Value { get; } | ||
public bool IsPrepend { get; } | ||
public int ConcatCount { get; } | ||
public PcNode<T> Next { get; } | ||
|
||
public Item(T item, bool isPrepend, PcNode<T> next) | ||
{ | ||
if (next == null) throw new ArgumentNullException(nameof(next)); | ||
|
||
Value = item; | ||
IsPrepend = isPrepend; | ||
ConcatCount = next is Item nextItem | ||
? nextItem.ConcatCount + (isPrepend ? 0 : 1) | ||
: 1; | ||
Next = next; | ||
} | ||
} | ||
|
||
sealed class Source : PcNode<T> | ||
{ | ||
public IEnumerable<T> Value { get; } | ||
public Source(IEnumerable<T> source) => Value = source; | ||
} | ||
|
||
public IEnumerator<T> GetEnumerator() | ||
{ | ||
var i = 0; | ||
T[] concats = null; // Array for > 4 concatenations | ||
var concat1 = default(T); // Slots for up to 4 concatenations | ||
var concat2 = default(T); | ||
var concat3 = default(T); | ||
var concat4 = default(T); | ||
|
||
var current = this; | ||
for (; current is Item item; current = item.Next) | ||
{ | ||
if (item.IsPrepend) | ||
{ | ||
yield return item.Value; | ||
} | ||
else | ||
{ | ||
if (concats == null) | ||
{ | ||
if (i == 0 && item.ConcatCount > 4) | ||
{ | ||
concats = new T[item.ConcatCount]; | ||
} | ||
else | ||
{ | ||
switch (i++) | ||
{ | ||
case 0: concat1 = item.Value; break; | ||
case 1: concat2 = item.Value; break; | ||
case 2: concat3 = item.Value; break; | ||
case 3: concat4 = item.Value; break; | ||
default: throw new IndexOutOfRangeException(); | ||
} | ||
continue; | ||
} | ||
} | ||
|
||
concats[i++] = item.Value; | ||
} | ||
} | ||
|
||
var source = (Source) current; | ||
|
||
foreach (var item in source.Value) | ||
yield return item; | ||
|
||
if (concats == null) | ||
{ | ||
if (i == 4) { yield return concat4; i--; } | ||
if (i == 3) { yield return concat3; i--; } | ||
if (i == 2) { yield return concat2; i--; } | ||
if (i == 1) { yield return concat1; i--; } | ||
yield break; | ||
} | ||
|
||
for (i--; i >= 0; i--) | ||
yield return concats[i]; | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters