IEnumerable.Skip(1).Take(1).Single() 的替代方案
我遇到了一个看似简单而尴尬的问题,但遇到了困难。我想要的只是 IEnumberable 中的下一个元素,而不使用 Skip(1).Take(1).Single()。这个例子说明了基本问题。
private char _nextChar;
private IEnumerable<char> getAlphabet()
{
yield return 'A';
yield return 'B';
yield return 'C';
}
public void sortAlphabet()
{
foreach (char alpha in getAlphabet())
{
switch (alpha)
{
case 'A': //When A pops up, I want to get the next element, ie 'B'
_nextChar = getAlphabet().Skip(1).Take(1).Single();
break;
case 'B': //When B pops up, I want 'C' etc
_nextChar = getAlphabet().Skip(1).Take(1).Single();
break;
}
}
}
除了丑陋之外,这个例子还有效。但是假设 IEnumerable 包含 200 万个元素,那么 LINQ 语句会使程序执行速度慢得难以忍受。我想要的很简单。我只想要 IEnumberable<> 中的下一个元素。如果有这样的功能,我所有的问题都会得到解决:
_nextChar = getAlphabet().moveNext() //or getNext()
如果解决方案保持示例的相同结构/布局/功能,那就更好了,但是,我很灵活。我的程序是一个文件解析器,在 200 万行文本中有一些键,例如“money=324”,其中“money”和“324”是 IEnumberable 中的相邻元素,当解析器遇到“money”时,我想要“ 324”。 (谁不呢?:D 抱歉双关语不好。)
I am having a difficult time with a seemingly easy and embarrassing problem. All I want is the next element in an IEnumberable without using Skip(1).Take(1).Single(). This example illustrates the basic problem.
private char _nextChar;
private IEnumerable<char> getAlphabet()
{
yield return 'A';
yield return 'B';
yield return 'C';
}
public void sortAlphabet()
{
foreach (char alpha in getAlphabet())
{
switch (alpha)
{
case 'A': //When A pops up, I want to get the next element, ie 'B'
_nextChar = getAlphabet().Skip(1).Take(1).Single();
break;
case 'B': //When B pops up, I want 'C' etc
_nextChar = getAlphabet().Skip(1).Take(1).Single();
break;
}
}
}
Other than being ugly, this example works. But let's say that the IEnumerable contained 2 million elements, then the LINQ statement makes the program execute unbearably slow. What I want is simple. I just want the next element in an IEnumberable<>. All my problems would be solved if there was a function like:
_nextChar = getAlphabet().moveNext() //or getNext()
It is much preferred if the solution keeps the same structure/layout/functionality of the example however, I am flexible. My program is a file parser, and among the 2 million lines of text are some keys like "money=324" where "money" and "324" are neighbor elements in the IEnumberable and when the parser comes across "money" I want "324". (who doesn't? :D Sorry for bad pun.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
有一个完全类似的函数。它只是属于
IEnumerator
,而不是IEnumerable
!不过,这里有个问题要问你。此代码是否有必要使用
IEnumerable
,而不是IList
?我问这个问题是因为,就好像这并不明显一样,如果您可以通过索引随机访问getAlphabet
返回的项目(并且如果有人想指出您可以请使用ElementAt
执行此操作现在就把这个想法从你的脑海中赶出去)。我的意思是,考虑一下这种情况下的代码会是什么样子:
这不是更容易吗?
There is a function exactly like that. It just belongs to
IEnumerator<T>
, notIEnumerable<T>
!Here's a question for you, though. Is it necessary that this code use an
IEnumerable<char>
, rather than anIList<char>
? I ask because, as if this weren't obvious, the code would be much simpler if you had random access to the items returned bygetAlphabet
by index (and if someone is tempted to point out that you can do this withElementAt
, please, just get that idea out of your head right now).I mean, consider what the code would look like in this case:
Isn't that much easier?
我认为你想要这个:
请注意,这会消耗下一个元素,如果我正确阅读你的问题,这正是你想要的。
I reckon you want this:
Note that this consumes the next element, just what you want if I read your question right.
正如另一个答案中指出的那样,有一个
MoveNext()
方法,您可以通过IEnumerator
访问所有可枚举的方法。 code> 调用IEnumerable.GetEnumerator()
返回的接口。但是,使用MoveNext()
和Current
可能会让人感觉有些“低级”。如果您希望使用
foreach
循环来处理您的getAlphabet()
集合,您可以编写一个扩展方法,该方法从任何可枚举的两个元素中返回元素:按如下方式使用它:
您将得到以下输出:(
请注意,虽然上述扩展方法每个返回两个项目对,但这些对重叠一个项目!您实际上也获得了每个项目如果您希望扩展方法本身返回最后一项,您可以通过调整所使用的缓冲方法来调整它。)
As was pointed out in another answer, there is a
MoveNext()
method, and you have access to it for all enumerables via theIEnumerator<T>
interface returned by a call toIEnumerable<T>.GetEnumerator()
. However, working withMoveNext()
andCurrent
can feel somewhat "low-level".If you'd prefer a
foreach
loop to process yourgetAlphabet()
collection, you could write an extension method that returns elements from any enumerable in pairs of two:You'd use it as follows:
And you'd get the following output:
(Note that while the above extension method returns pairs of two items each, the pairs overlap by one item! You essentially get each item as well as a look-ahead. If you wanted the extension method to return the last item by itself, you could adapt it by adapting the buffering method used.)
正如前面提到的,状态机非常适合这些情况。
它也符合我们思考问题的方式,因此可读性非常好。
我不确定您到底想用代码做什么,所以下面的示例在遇到下一个字符时返回它。状态机可以很好地适应复杂的任务,并且可以在纸上进行建模和手动检查。
As alluded to earlier, a state machine is ideally suited for these cases.
It also matches the way we think about the problem, so readability is excellent.
I'm not sure exactly what you want to do with the code, so the example below returns the next character when it encounters it. The state machine scales well to complex tasks, and can be modeled and hand-checked on paper.
您的代码每次都会返回
'B'
,因为您调用getAlphabet()
,它每次都会返回一个新的IEnumerable
。根据您想要执行的操作,我可能建议使用基于索引的迭代而不是枚举器。如果您使用
MoveNext
来获取下一个元素,则会弄乱循环,因此使用基于索引的检索会更干净地工作,并且开销要少得多。Your code will return
'B'
every time, because you callgetAlphabet()
, which returns a newIEnumerable
each time.Based on what you're trying to do, I'd probably suggest using index-based iteration instead of an enumerator. If you used
MoveNext
to get the next element, you'd mess up your loop, so using an index-based retrieval would work more cleanly with much less overhead.如果您使用的是 .NET 4.0,那么您想要实现的目标非常简单:
如果您使用低于 .NET 4.0 的任何内容,它仍然很容易 定义您自己的Zip 函数 和 Tuple 类。
If you're using .NET 4.0 then what you're trying to achieve is very simple:
If you using anything less than .NET 4.0, its still very easy to define your own Zip function and Tuple class.