no. 7| 05.20.2014 |

Enumerable#cycle

Typically, I get to my technical and cultural blog assignments mid week. Last week, however, things got away from me a bit and I put the blogging exercises off until last. It turns out to have been a poor choice. Knowing about "Enumerable" would have really helped as I was working on the creative challenge: Make an Accountability Group Creator.

Enumerable is a "mixin" that "provides collection classes with several traversal and searching methods, and with the ability to sort". When I was working on my group maker, I wrote a whole big chunk of nested, looping code to rearrange group members. If I'd know about Eneumerable, I would have saved myself a lot of grief.

Enumerable adds on top of the built in array methods (like .rotate) to provide a whole additional toolkit full of fun stuff. One of the options for this blog assignment was to pick one of a selection of Enumerable methods, learn about it, and then write about it. So, ok everybody... meet Enumerable#cycle.

.cycle is pretty slick — and kinda dangerous. Fun! Get ready to kill process y'all.

When you call cycle on an array or hash, it returns one element at a time, in order of index number. When it gets to the end, it starts over at the beginning. You can specify an optional argument to control how many times cycle does its thing, and you can also (optionally) provide a block of code that cycle will pass the contents of each element into. Like this:

array_of_some_stuff = [1, 2, 3, 4, 5]
array_of_some_stuff.cycle(2) { |x| print x }

You'll get: 1234512345

So here's the dangerous part: If you don't specify a limiting number in that parameter spot, or include some logic in your block that causes a break at some point, .cycle will repeat forever.

[1, 2, 3].cycle

Equals equals badness. Imagine the sound of a chainsaw. The above example gets you jammed up code, freaking out Ruby processes, and probably a hot laptop. If you want to experiment with .cycle, consider having Activity Monitor at the ready. If irb stops responding, and control-c doesn't help, you can jump into Activity Monitor, click the CPU tab, decend sort by %CPU to find ruby, then force quit it. (I had to do this, even after force quitting Terminal. A few times actually.)

Now some neat stuff you can do with .cycle — Let's make an array:

moods = ['blah', 'jacked', 'pumped', 'grumpin', 'whatever']

Now, let's say you're a moody, but predictable kind of person. Always changing your mind about how you feel, but orderly about it. You always go from "pumped" to "grumpin". (And so on and so forth.)

You can actually call .cycle on an array object, and then "store" that in another object:

in_the_moment = moods.cycle

So now in_the_moment contains this ordered, forever repeating loop of elements from moods. (Don't worry — this scheme isn't running a constant infinite loop in the background. The contained moods.cycle sits quietly until it's needed.)

Now watch what happens when you use the .next method on in_the_moment:

9.times { print "#{in_the_moment.next} " }

You get: blah jacked pumped grumpin whatever blah jacked pumped grumpin

That. Is. Neat. An easy way to keep track of and return ordered, repeating items. Days of the week anyone?

As I was researching .cycle, I found clever examples of stuff it could be used for, but some of them didn't work for me, and caused ruby to drop into never never land. I suspect this has to do with the way .cycle's implementation has changed with different ruby versions. Which is another reason why .cycle should be used with caution: if you don't have all the details right, it could bite you.

#DBC, #Ruby, #Enumerable, #.cycle, #InfiniteLoops