Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why return an enumerator?

I''m curious about why ruby returns an Enumerator instead of an Array for something that seems like Array is an obvious choice. For example:

'foo'.class
# => String

Most people think of a String as an array of chars.

'foo'.chars.class
# => Enumerator

So why does String#chars return an Enumerable instead of an Array? I'm assuming somebody put a lot of thought into this and decided that Enumerator is more appropriate but I don't understand why.

like image 558
pguardiario Avatar asked May 09 '12 12:05

pguardiario


3 Answers

If you want an Array, call #to_a. The difference between Enumerable and Array is that one is lazy and the other eager. It's the good old memory (lazy) vs. cpu (eager) optimization. Apparently they chose lazy, also because

str = "foobar"
chrs = str.chars
chrs.to_a # => ["f", "o", "o", "b", "a", "r"]
str.sub!('r', 'z')
chrs.to_a # => ["f", "o", "o", "b", "a", "z"]
like image 161
Reactormonk Avatar answered Sep 20 '22 22:09

Reactormonk


  1. Abstraction - the fact that something may be an Array is an implementation detail you don't care about for many use cases. For those where you do, you can always call .to_a on the Enumerable to get one.

  2. Efficiency - Enumerators are lazy, in that Ruby doesn't have to build the entire list of elements all at once, but can do so one at a time as needed. So only the number you need is actually computed. Of course, this leads to more overhead per item, so it's a trade-off.

  3. Extensibility - the reason chars returns an Enumerable is because it is itself implemented as an enumerator; if you pass a block to it, that block will be executed once per character. That means there's no need for e.g. .chars.each do ... end; you can just do .chars do ... end. This makes it easy to construct operation chains on the characters of the string.

like image 25
Mark Reed Avatar answered Sep 20 '22 22:09

Mark Reed


This completely in accordance with the spirit of 1.9: to return enumerators whenever possible. String#bytes, String#lines, String#codepoints, but also methods like Array#permutation all return an enumerator.

In ruby 1.8 String#to_a resulted in an array of lines, but the method is gone in 1.9.

like image 35
steenslag Avatar answered Sep 21 '22 22:09

steenslag