让我们说我有一个序列。
的IEnumerable< INT>序列= GetSequenceFromExpensiveSource();
//序列现在包含:0,1,2,3,...,999999,1000000
获取顺序并不便宜,动态生成的,我想遍历一次而已。
我想获得0 - 999999(即一切,但最后一个元素)
我承认,我可以做这样的事情:
sequence.Take(sequence.Count() - 1);
但导致这两个枚举过大的顺序。
有一个LINQ构造,让我做的:
sequence.TakeAllButTheLastElement();
解决方案
我不知道LINQ的解决方案 - 但你可以使用发电机(收益率回报)容易code算法自己。
公共静态的IEnumerable< T> TakeAllButLast< T>(这IEnumerable的< T>源){
VAR IT = source.GetEnumerator();
布尔hasRemainingItems = FALSE;
布尔isFirst = TRUE;
牛逼项=默认(T);
做 {
hasRemainingItems = it.MoveNext();
如果(hasRemainingItems){
如果产量退货项目(isFirst!);
项目= it.Current;
isFirst = FALSE;
}
}而(hasRemainingItems);
}
静态无效的主要(字串[] args){
变种SEQ = Enumerable.Range(1,10);
Console.WriteLine(的string.join(,,Seq.Select(X => x.ToString())的ToArray())。);
Console.WriteLine(的string.join(,,Seq.TakeAllButLast()选择(X => x.ToString())的ToArray())。);
}
或作为广义解丢弃的最后n项(使用队列就像在评论建议):
公共静态的IEnumerable< T> SkipLastN< T>(这IEnumerable的< T>源,INT N){
VAR IT = source.GetEnumerator();
布尔hasRemainingItems = FALSE;
VAR缓存=新的队列< T>(N + 1);
做 {
如果(hasRemainingItems = it.MoveNext()){
cache.Enqueue(it.Current);
如果(cache.Count将N)
收益回报cache.Dequeue();
}
}而(hasRemainingItems);
}
静态无效的主要(字串[] args){
变种SEQ = Enumerable.Range(1,4);
Console.WriteLine(的string.join(,,Seq.Select(X => x.ToString())的ToArray())。);
Console.WriteLine(的string.join(,,Seq.SkipLastN(3)。选择(X => x.ToString())的ToArray())。);
}
Let's say I have a sequence.
IEnumerable<int> sequence = GetSequenceFromExpensiveSource();
// sequence now contains: 0,1,2,3,...,999999,1000000
Getting the sequence is not cheap and is dynamically generated, and I want to iterate through it once only.
I want to get 0 - 999999 (i.e. everything but the last element)
I recognize that I could do something like:
sequence.Take(sequence.Count() - 1);
but that results in two enumerations over the big sequence.
Is there a LINQ construct that lets me do:
sequence.TakeAllButTheLastElement();
解决方案
I don't know a Linq solution - But you can easily code the algorithm by yourself using generators (yield return).
public static IEnumerable<T> TakeAllButLast<T>(this IEnumerable<T> source) {
var it = source.GetEnumerator();
bool hasRemainingItems = false;
bool isFirst = true;
T item = default(T);
do {
hasRemainingItems = it.MoveNext();
if (hasRemainingItems) {
if (!isFirst) yield return item;
item = it.Current;
isFirst = false;
}
} while (hasRemainingItems);
}
static void Main(string[] args) {
var Seq = Enumerable.Range(1, 10);
Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
Console.WriteLine(string.Join(", ", Seq.TakeAllButLast().Select(x => x.ToString()).ToArray()));
}
Or as a generalized solution discarding the last n items (using a queue like suggested in the comments):
public static IEnumerable<T> SkipLastN<T>(this IEnumerable<T> source, int n) {
var it = source.GetEnumerator();
bool hasRemainingItems = false;
var cache = new Queue<T>(n + 1);
do {
if (hasRemainingItems = it.MoveNext()) {
cache.Enqueue(it.Current);
if (cache.Count > n)
yield return cache.Dequeue();
}
} while (hasRemainingItems);
}
static void Main(string[] args) {
var Seq = Enumerable.Range(1, 4);
Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
Console.WriteLine(string.Join(", ", Seq.SkipLastN(3).Select(x => x.ToString()).ToArray()));
}