この問題に使用する解決策はもう少し複雑です。
私のutil静的クラスにはMarkEnd
、T
-itemsの-items を変換する拡張メソッドが含まれていEndMarkedItem<T>
ます。各要素にはint
、0である追加のマークが付けられます。または(最後の3アイテムに特に関心がある場合)-3、-2、または-1、最後の3アイテム。
これは、それ自体が役立つ場合があります。たとえばforeach
、最後の2を除く各要素の後にカンマを付けた単純なループでリストを作成する場合、最後から2番目の項目の後に結合語が続く(「および」など)または「または」)、およびポイントが続く最後の要素。
最後のnアイテムなしでリスト全体を生成するために、拡張メソッドButLast
はEndMarkedItem<T>
sを単純に繰り返しながらEndMark == 0
ます。
を指定しない場合tailLength
、最後のアイテムのみがマーク(MarkEnd()
または)またはドロップ(ButLast()
)されます。
他のソリューションと同様に、これはバッファリングによって機能します。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Adhemar.Util.Linq {
public struct EndMarkedItem<T> {
public T Item { get; private set; }
public int EndMark { get; private set; }
public EndMarkedItem(T item, int endMark) : this() {
Item = item;
EndMark = endMark;
}
}
public static class TailEnumerables {
public static IEnumerable<T> ButLast<T>(this IEnumerable<T> ts) {
return ts.ButLast(1);
}
public static IEnumerable<T> ButLast<T>(this IEnumerable<T> ts, int tailLength) {
return ts.MarkEnd(tailLength).TakeWhile(te => te.EndMark == 0).Select(te => te.Item);
}
public static IEnumerable<EndMarkedItem<T>> MarkEnd<T>(this IEnumerable<T> ts) {
return ts.MarkEnd(1);
}
public static IEnumerable<EndMarkedItem<T>> MarkEnd<T>(this IEnumerable<T> ts, int tailLength) {
if (tailLength < 0) {
throw new ArgumentOutOfRangeException("tailLength");
}
else if (tailLength == 0) {
foreach (var t in ts) {
yield return new EndMarkedItem<T>(t, 0);
}
}
else {
var buffer = new T[tailLength];
var index = -buffer.Length;
foreach (var t in ts) {
if (index < 0) {
buffer[buffer.Length + index] = t;
index++;
}
else {
yield return new EndMarkedItem<T>(buffer[index], 0);
buffer[index] = t;
index++;
if (index == buffer.Length) {
index = 0;
}
}
}
if (index >= 0) {
for (var i = index; i < buffer.Length; i++) {
yield return new EndMarkedItem<T>(buffer[i], i - buffer.Length - index);
}
for (var j = 0; j < index; j++) {
yield return new EndMarkedItem<T>(buffer[j], j - index);
}
}
else {
for (var k = 0; k < buffer.Length + index; k++) {
yield return new EndMarkedItem<T>(buffer[k], k - buffer.Length - index);
}
}
}
}
}
}