"무한"반복자가 잘못된 설계입니까?
Iterator
"무한한"구현 을 제공 하는 것이 일반적으로 나쁜 관행으로 간주됩니까? 즉, hasNext()
always (*) 호출이 true를 반환 하는 곳은 어디 입니까?
일반적으로 호출 코드가 비정상적으로 동작 할 수 있기 때문에 "예"라고 말하고 싶지만 아래 구현 hasNext()
에서는 호출자가 반복기가 초기화 된 List에서 모든 요소를 제거하지 않는 한 true를 반환합니다. 즉 , 종료 조건이 있습니다. 이것이 합법적 인 사용이라고 생각하십니까 Iterator
? 직관적이지 않다고 주장 할 수 있겠지만 계약을 위반하지 않는 것 같습니다.
public class CyclicIterator<T> implements Iterator<T> {
private final List<T> l;
private Iterator<T> it;
public CyclicIterator<T>(List<T> l) {
this.l = l;
this.it = l.iterator();
}
public boolean hasNext() {
return !l.isEmpty();
}
public T next() {
T ret;
if (!hasNext()) {
throw new NoSuchElementException();
} else if (it.hasNext()) {
ret = it.next();
} else {
it = l.iterator();
ret = it.next();
}
return ret;
}
public void remove() {
it.remove();
}
}
(Pedantic) 편집
어떤 사람들은 Iterator
피보나치 수열과 같은 무한 수열에서 값을 생성하기 위해를 사용 하는 방법에 대해 언급했습니다 . 그러나 Java Iterator
문서에는 Iterator가 다음과 같이 명시되어 있습니다.
컬렉션에 대한 반복기입니다.
이제 피보나치 시퀀스가 무한 컬렉션이라고 주장 할 수 있지만 Java에서는 컬렉션을 java.util.Collection
인터페이스 와 동일시 size()
하여 컬렉션을 제한해야 함을 암시하는 것과 같은 메서드를 제공 합니다. 따라서 Iterator
무제한 시퀀스에서 값 생성기 로 사용하는 것이 합법적 입니까?
나는 그것이 전적으로 합법적 이라고 생각합니다 - Iterator
는 단지 "물건"의 흐름 일뿐입니다. 스트림이 반드시 제한되어야하는 이유는 무엇입니까?
다른 많은 언어 (예 : 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();
요점 의는 Iterator
단지 당신이 요청으로 많은 개체로 당신을 준다 즉, 게으른 것입니다. 사용자가 무한의 모든 객체를 요청하면 Iterator
문제가 아니라 귀하의 문제입니다.
나도 그것이 합법적이라고 생각하지만, 그러한 Iterator
(또는 더 정확하게는 Iterable
그러한 생성 Iterator
)이 향상된 for-loop (일명 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 theIterator
and remove objects directly, possibly provoking aConcurrentModificationException
- 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()
...
}
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".
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.
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.
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.
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.
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.
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.
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.
참고URL : https://stackoverflow.com/questions/2622591/is-an-infinite-iterator-bad-design
'Program Tip' 카테고리의 다른 글
npm 설치시 최대 호출 스택 크기 초과 (0) | 2020.11.14 |
---|---|
더 나은 Windows 명령 줄 셸 (0) | 2020.11.14 |
내부 클래스에서 외부 클래스의 "this"에 어떻게 액세스 할 수 있습니까? (0) | 2020.11.14 |
요소의 배경색 코드를 얻는 방법은 무엇입니까? (0) | 2020.11.14 |
문자열 비교 자바 스크립트 반환 가능성 % (0) | 2020.11.14 |