Skip to content

Instantly share code, notes, and snippets.

@ufcpp
Created October 28, 2016 03:33
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ufcpp/2b3e1a5821169f6b21ded175ad05c752 to your computer and use it in GitHub Desktop.
Save ufcpp/2b3e1a5821169f6b21ded175ad05c752 to your computer and use it in GitHub Desktop.
index付きforeach
namespace A
{
// IReadOnlyList 専用、アロケーション発生しない実装
public static partial class TupleEnumerable
{
public struct IndexedListEnumerable<T> : IEnumerable<(T item, int index)>
{
private IReadOnlyList<T> _list;
public IndexedListEnumerable(IReadOnlyList<T> list) { _list = list; }
public IndexedListEnumerator<T> GetEnumerator() => new IndexedListEnumerator<T>(_list);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IEnumerator<(T item, int index)> IEnumerable<(T item, int index)>.GetEnumerator() => GetEnumerator();
}
public struct IndexedListEnumerator<T> : IEnumerator<(T item, int index)>
{
public (T item, int index) Current => (_list[_i], _i);
int _i;
IReadOnlyList<T> _list;
internal IndexedListEnumerator(IReadOnlyList<T> list)
{
_i = -1;
_list = list;
}
public bool MoveNext()
{
_i++;
return _i < _list.Count;
}
object IEnumerator.Current => Current;
public void Dispose() { }
public void Reset() { throw new NotImplementedException(); }
}
public static IndexedListEnumerable<T> Indexed<T>(this IReadOnlyList<T> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return new IndexedListEnumerable<T>(source);
}
}
}
namespace B
{
// 汎用実装。GetEnumeratorでbox化避けれない。
public static partial class TupleEnumerable
{
public struct IndexedEnumerable<T> : IEnumerable<(T item, int index)>
{
private IEnumerable<T> _e;
public IndexedEnumerable(IEnumerable<T> e) { _e = e; }
public IndexedEnumerator<T> GetEnumerator() => new IndexedEnumerator<T>(_e.GetEnumerator());
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IEnumerator<(T item, int index)> IEnumerable<(T item, int index)>.GetEnumerator() => GetEnumerator();
}
public struct IndexedEnumerator<T> : IEnumerator<(T item, int index)>
{
public (T item, int index) Current => (_e.Current, _i);
int _i;
IEnumerator<T> _e;
internal IndexedEnumerator(IEnumerator<T> e)
{
_i = -1;
_e = e;
}
public bool MoveNext()
{
_i++;
return _e.MoveNext();
}
object IEnumerator.Current => Current;
public void Dispose() { }
public void Reset() { throw new NotImplementedException(); }
}
public static IndexedEnumerable<T> Indexed<T>(this IEnumerable<T> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
return new IndexedEnumerable<T>(source);
}
}
}
namespace C
{
// イテレーターを使った実装。シンプルだけど、どうしてもヒープ使っちゃう。
public static partial class TupleEnumerable
{
public static IEnumerable<(T item, int index)> Indexed<T>(this IEnumerable<T> source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
IEnumerable<(T item, int index)> impl()
{
var i = 0;
foreach (var item in source)
{
yield return (item, i);
++i;
}
}
return impl();
}
}
}
// 動作確認用
using System;
using System.Collections;
using System.Collections.Generic;
// ここの using 切り替えで、どの Indexed が呼ばれるかを切り替え
// 手元の環境だと以下の通り(GetTotalMemory の前後差分0が理想)
// A: 0
// B: 237568
// C: 720896
using A;
class Program
{
static void Main()
{
var items = new List<int> { 2, 3, 5, 7, 11, 13, 17 };
items.Indexed();
var begin = GC.GetTotalMemory(false);
for (int i = 0; i < 10000; i++)
{
var sum = 0;
foreach (var (item, index) in items.Indexed())
{
sum += item * index;
}
}
var end = GC.GetTotalMemory(false);
Console.WriteLine(end - begin);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment