I'm working with a few iterators where I have to do something along these lines (enum is an enumerator)
enums_with_zero << enum.rewind if enum.peek == 0
which normally works fine, but this is after there's already been #next called on the enum a few times. The issue with this is that the enum could be at the end and with a few values passed for enum, I have hit the issue where enum.peek raises StopIteration because the enum is completed. Is there a way I could put in a guard to check if enum.peek or enum.next will cause StopIteration before I call it. Something that would have the behavior of this for example?
class Enumerator
def has_next?
begin
peek && true
rescue StopIteration
false
end
end
end
You can rescue the StopIteration explicitly, but there's also the idea that the loop method internally rescues a StopIteration exception by simply exiting the loop. (Inside loop, raise StopIteration has the same effect as break.)
This code simply exits the loop when you try to peek past the end:
a = %w(a b c d e).to_enum
loop do
print a.peek
a.next
end
The code outputs abcde. (It also transparently raises and rescues StopIteration.)
So, if you want to simply ignore the StopIteration exception when you try to peek past the end, just use loop.
Of course, once you peek past the end, you'll get dumped out of the loop. If you don't want that, you can use while and rescue to customize behavior. For example, if you want to avoid exiting if you peek past the end, and exit when you iterate past the end using next, you could do something like this:
a = %w(a b c d e).to_enum
while true
begin
print a.peek
rescue StopIteration
print "\nTried to peek past the end of the enum.\nWe're gonna overlook that.\n"
end
x = a.next rescue $!
break if x.class == StopIteration
end
p 'All done!'
The last two lines in the loop do the same thing as this, which you could use instead:
begin
a.next
rescue StopIteration
break
end
A point to make is that handling StopIteration is Ruby's intended way of dealing with getting to the end of an iterator. Quoting from Matz's book The Ruby Programming Language:
External iterators are quite simple to use: just call
nexteach time you want another element. When there are no more elements left,nextwill raise aStopIterationexception. This may seem unusual—an exception is raised for an expected termination condition rather than an unexpected and exceptional event. (StopIterationis a descendant ofStandardErrorandIndexError; note that it is one of the only exception classes that does not have the word “error” in its name.) Ruby follows Python in this external iteration technique. By treating loop termination as an exception, it makes your looping logic extremely simple; there is no need to check the return value ofnextfor a special end-of-iteration value, and there is no need to call some kind ofnext?predicate before callingnext.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With