LaunchKit
All quizzes
Ruby 0 views

Toggling values with Array#cycle and next

Question

What's the return value of the following Ruby code?

toggler = %w(start stop).cycle

toggler.next
toggler.next

toggler.next # => ???

The correct answer is

Explanation

TL;DR

Array#cycle without a block returns an Enumerator that repeats the array's elements forever. Each next call advances it by one element, wrapping around at the end: "start", "stop", then "start" again. The third call returns "start".

Step by step

toggler = %w(start stop).cycle

toggler.next # => "start"
toggler.next # => "stop"
toggler.next # => "start"
toggler.peek # => "stop"

next performs external iteration: instead of handing a block to the enumerator, you pull values out one at a time, and the enumerator remembers its position between calls. peek shows the upcoming element without advancing, and rewind resets the position to the start.

Because the enumerator is infinite, methods that consume it all would never return. Lazy-friendly methods work fine:

%w(start stop).cycle.first(5) # => ["start", "stop", "start", "stop", "start"]

Edge cases

cycle(n) repeats the collection a finite number of times. Once a finite enumerator is exhausted, next raises StopIteration:

two = %w(a b).cycle(1)

two.next # => "a"
two.next # => "b"

begin
  two.next
rescue StopIteration => e
  e.class # => StopIteration
end

StopIteration is the one exception Kernel#loop rescues silently, which is why loop { work(enum.next) } exits cleanly when the enumerator runs out.

With a block, cycle iterates immediately instead of returning an enumerator: [1, 2].cycle { |x| ... } loops forever, and [1, 2].cycle(2) { |x| ... } runs the block four times and returns nil.

Share this quiz

Comments

No comments yet. Be the first.

Only used to confirm and publish your comment. Never shown publicly, never shared.

Markdown: **bold**, `code`, ```fenced blocks```, > quotes, [links](url). HTML and images are not rendered.