是“无限”的。迭代器设计不好?

发布于 2024-08-28 04:41:55 字数 1431 浏览 8 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

南街女流氓 2024-09-04 04:41:56

我认为它是完全合法 - 迭代器只是一个“东西”流。为什么流必须是有界的?

许多其他语言(例如 Scala)都内置了无界流的概念,并且可以对其进行迭代。例如,使用 scalaz

scala> val fibs = (0, 1).iterate[Stream](t2 => t2._2 -> (t2._1 + t2._2)).map(_._1).iterator
fibs: Iterator[Int] = non-empty iterator

scala> fibs.take(10).mkString(", ") //first 10 fibonnacci numbers
res0: String = 0, 1, 1, 2, 3, 5, 8, 13, 21, 34

编辑: 就最小惊喜原则而言,我认为这完全取决于上下文。例如,我希望这个方法返回什么?

public Iterator<Integer> fibonacciSequence();

I think it is entirely legitimate - an Iterator is just a stream of "stuff". Why should the stream necessarily be bounded?

Plenty of other languages (e.g. Scala) have the concept of unbounded streams built in to them and these can be iterated over. For example, using scalaz

scala> val fibs = (0, 1).iterate[Stream](t2 => t2._2 -> (t2._1 + t2._2)).map(_._1).iterator
fibs: Iterator[Int] = non-empty iterator

scala> fibs.take(10).mkString(", ") //first 10 fibonnacci numbers
res0: String = 0, 1, 1, 2, 3, 5, 8, 13, 21, 34

EDIT: In terms of the principle of least surprise, I think it depends entirely on the context. For example, what would I expect this method to return?

public Iterator<Integer> fibonacciSequence();
隔岸观火 2024-09-04 04:41:56

Iterator全部要点是它是惰性的,即它只提供您所要求的对象数量。如果用户请求无限Iterator的所有对象,这是他们的问题,而不是你的问题。

The whole point of an Iterator is that it is lazy, i.e. that it gives you only as many objects as you ask for. If a user asks for all objects of an infinite Iterator, it's their problem, not yours.

橘亓 2024-09-04 04:41:56

虽然我也认为它是合法的,但我想补充一点,这样一个 Iterator (或更准确地说:一个 Iterable 生成这样一个 Iterator ) 无法与增强型 for 循环(又名 for-each-loop)配合使用:

for (Object o : myIterable) {
   ...
}

由于增强型 for 循环内的代码无法直接访问迭代器,因此无法调用 remove() 在迭代器上。

因此,要结束循环,必须执行以下操作之一:

  • 访问 Iterator 的内部 List 并直接删除对象,这可能会引发 ConcurrentModificationException
  • 使用 break 退出循环
  • “使用”Exception 退出循环
  • 使用 return 退出循环

除最后一个之外的所有内容这些替代方案并不完全是留下增强型 for 循环的最佳方式。

当然,“普通”for 循环(或任何其他带有显式迭代器变量的循环)都可以正常工作:

for (Iterator it = getIterator(); it.hasNext();) {
  Object o = it.next()
  ...
}

While I too think that it's legitimate, I'd like to add that such an Iterator (or more precisely: an Iterable producing such an Iterator) would not play well with the enhanced for-loop (a.k.a for-each-loop):

for (Object o : myIterable) {
   ...
}

Since the code inside an enhanced for loop has no direct access to the iterator, it couldn't call remove() on the iterator.

So to end the looping it would have to do one of the following:

  • Get access to the internal List of the Iterator and remove objects directly, possibly provoking a ConcurrentModificationException
  • use break to exit the loop
  • "use" an Exception to exit the loop
  • use return to leave the loop

All but the last of those alternatives aren't exactly the best way to leave an enhanced for-loop.

The "normal" for-loop (or any other loop together with an explicit Iterator variable) would work just fine, of course:

for (Iterator it = getIterator(); it.hasNext();) {
  Object o = it.next()
  ...
}
夜吻♂芭芘 2024-09-04 04:41:56

这可能是语义上的,但迭代器应该努力返回下一个项目,而不考虑它何时结束。结束是集合的副作用,尽管它可能看起来是常见的副作用。

不过,无限与包含 10 万亿项的集合有什么区别?打电话的人要么想要全部,要么不想要。让调用者决定返回多少项或何时结束。

我不会说调用者不能使用 for-each 构造。他可以,只要他想要所有的物品。

集合文档中的某些内容(例如“可能是无限集合”)是合适的,但不是“无限迭代器”。

This may be semantics, but the iterator should diligently return the next item without regard to when it will end. Ending is a side effect for a collection, though it may seem like a common side effect.

Still, what's the difference between infinite, and a collection with 10 trillion items? The caller either wants them all, or he doesn't. Let the caller decide how many items to return or when to end.

I wouldn't say the caller couldn't use the for-each construct. He could, as long as he wants all the items.

Something in the documentation for the collection like "may be an infinite collection" would be appropriate, but not "infinite iterator".

羁拥 2024-09-04 04:41:56

这是完全合法的使用 - 只要有适当的记录

使用 CyclicIterator 名称是一个好主意,因为它推断如果没有正确定义循环退出情况,迭代器上的循环很可能是无限的。

It is a perfectly legitimate use - as long as it is properly documented.

Using the name CyclicIterator is a good idea, as it infers that looping on the iterator might well possibly be infinite if the loop exit case is not properly defined.

十雾 2024-09-04 04:41:56

当您创建无限数据时,无限迭代器非常有用,例如线性循环序列,如 斐波那契序列

所以使用这样的完全没问题。

An infinite iterator is very useful when you create infinite data, e.g. linear recurrent sequences like the Fibonacci sequence.

So it is totally fine to use such.

萌梦深 2024-09-04 04:41:56

是的。您只需要一个不同的标准来确定何时停止迭代。

无限列表的概念在 Haskell 等惰性求值语言中非常常见,并导致完全不同的程序设计。

Yes. You just need a different criteria for when to stop iterating.

The concept of infinite lists is very common in lazy evaluation languages like Haskell and leads to completely different program designs.

写下不归期 2024-09-04 04:41:56

实际上,无论 JavaDoc API 怎么说,Iterator 来自 Iterable而不是 Collection。后者扩展了前者,这就是为什么它也可以生成 Iterator ,但 Iterable 不成为 Collection 是完全合理的。事实上,甚至有一些 Java 类实现了 Iterable,但没有实现 Collection

现在,至于期望......当然,如果没有明确表明情况确实如此,则不得提供无限迭代器。但有时,期望可能具有欺骗性。例如,人们可能期望 InputStream 总是结束,但这实际上并不是它的要求。同样,返回从此类流读取的行的迭代器可能永远不会结束。

Actually, an Iterator comes from an Iterable, not a Collection, whatever the JavaDoc API says. The latter extends the former, and that's why it can generate an Iterator as well, but it's perfectly reasonably for an Iterable not to be a Collection. In fact, there are even a few Java classes which implement Iterable but not Collection.

Now, as for expectations... of course an infinite iterator must not be made available without a clear indication that this happens to be the case. But, sometimes, expectations can be deceiving. For instance, one might expect an InputStream to always come to an end, but that really isn't a requirement of it. Likewise, an Iterator returning lines read from such a stream might never end.

胡渣熟男 2024-09-04 04:41:56

考虑一下您是否有生成器函数。正如您所注意到的,迭代器可能是生成器的合理接口,也可能不是。

另一方面,Python 的生成器终止。

Consider whether you have a generator function. An iterator may be a reasonable interface to a generator, or it might not, as you've noticed.

On the other hand, Python has generators which do terminate.

圈圈圆圆圈圈 2024-09-04 04:41:56

我可以想象无限迭代器的任意数量的用途。例如,程序迭代由另一台服务器或后台进程发送的状态消息会怎么样。虽然在现实生活中,服务器可能最终会停止,但从程序的角度来看,它可能会继续读取,直到被与迭代器到达末尾无关的东西停止为止。

这肯定是不寻常的,正如其他人所说,应该仔细记录。但我不会宣布这是不可接受的。

I can imagine any number of uses for an infinite iterator. Like, what about a program iterating through status messages being sent by another server or a background process. While in real life the server will presumably stop eventually, from the program's point of view it might just keep reading until it is stopped by something unrelated to the iterator reaching its end.

It would certainly be unusual and, as others have said, should be carefully documented. But I wouldn't declare it unacceptable.

蔚蓝源自深海 2024-09-04 04:41:56

如果您有一个需要迭代器的对象,并且您碰巧给了它一个无限迭代器,那么一切都应该很好。当然,该对象永远不应该要求迭代器停止。 是潜在的糟糕设计。

想想音乐播放器。您告诉它以“连续播放模式”开始播放曲目,它会播放曲目,直到您告诉它停止为止。也许您可以通过永远随机播放播放列表来实现这种连续播放模式......直到用户按下“停止”。在这种情况下,MusicPlayer 需要迭代播放列表,可能是Collection永远。在这种情况下,您需要一个您构建的 CyclicIterator 或一个随机选择下一个轨道且永不耗尽的 ShuffleIterator。到目前为止,一切都很好。

MusicPlayer.play(Iteratorplaylist) 不会关心播放列表是否结束。它既可以与轨道列表上的迭代器一起使用(播放到最后,然后停止),也可以与轨道列表上的 ShuffleIterator

一起使用(永远选择随机轨道)。一切都还好。

如果有人尝试编写 MusicPlayer.playContinuously() ,希望“永远”播放曲目(直到有人按下“停止”),但接受 Iterator 该怎么办? > 作为参数? 就是设计问题。显然,playContinuously() 需要一个无限的 Iterator,如果它接受一个普通的 Iterator 作为其参数,那么那就不好了设计。例如,它需要检查 hasNext() 并处理返回 false 的情况,即使它永远不应该发生。坏的。

因此,实现您想要的所有无限迭代器,只要它们的客户端不依赖这些迭代器的结尾即可。就好像您的客户端依赖于永远运行的迭代器一样,那么不要给它一个简单的迭代器

这应该是显而易见的,但似乎并非如此。

If you have an object that needs an iterator, and you happen to give it an infinite iterator, then all should be well. Of course, that object should never require the iterator to stop. And that is the potentially bad design.

Think of a music player. You tell it to start playing tracks in "continuous play mode" and it plays tracks until you tell it to stop. Maybe you implement this continuous play mode by shuffling a playlist forever... until the user presses "stop". In this case, MusicPlayer needs to iterator over a playlist, that might be Collection<Track>, forever. In this case, you'd want either a CyclicIterator<Track> as you've built or a ShuffleIterator<Track> that randomly picks the next track and never runs out. So far, so good.

MusicPlayer.play(Iterator<Track> playlist) wouldn't care whether the play list ends or not. It works both with an iterator over a list of tracks (play to the end, then stop) and with a ShuffleIterator<Track> over a list of tracks (pick a random track forever). All still good.

What if someone tried to write MusicPlayer.playContinuously()--which expects to play tracks "forever" (until someone presses "stop"), but accepts an Iterator<Track> as the parameter? That would be the design problem. Clearly, playContinuously() needs an infinite Iterator, and if it accepts a plain Iterator as its parameter, then that would be bad design. It would, for example, need to check hasNext() and handle the case where that returns false, even though it should never happen. Bad.

So implement all the infinite iterators you want, as long as their clients don't depend on those iterators ending. As if your client depends on the iterator going on forever, then don't give it a plain Iterator.

This should be obvious, but it doesn't seem to be.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文