Comment on this RCR (edit wiki page) | RCRchive home

RCR 262: more enumerator functionality

submitted by Kristof Bastiaensen on Thu Aug 05 2004 04:41:25 PM -0700

Status: pending


Abstract

This RCR proposes two additions for "enumerator": to_enum with a block, and Enumerable#enum_if.

Problem

The enumerator library provides a way to create an enumerable from any method. This RCR adds even more convenient functionality.

Proposal

to_enum creates an enumerable object from any method. I propose that to_enum can take a block, which enables it to transform the block-output. for example:

powers = 4.to_enum(:times){ |i| i * i}
powers.to_a
=> [0, 1, 4, 9]

A new method Enumerable#enum_if will also be provided to enable powerful filtering possibilities. It will yield the current value if the given block doesn't return false. for example:

(0..4).enum_if { |i| i % 2 == 0 }.to_a
=> [0, 2, 4]

This method can be useful as a replacement for Enumerable#select, where it may be inefficient to create a large temporary array:

large_dataset.enum_if { |d| sometest(d) }.collect do |d|
  <some transformations>
end

Any enumerable created by enum_if will reflect any changes made to the original object:

data = ["a", 6, 9, "foo", -19, "fuga", -19, "bar"]
ints = data.enum_if { |i| i.is_a? Numeric }
ints.to_a
=>  [6, 9, -19, -19]

data += ["Bear", 20, 3]
ints.to_a
=> [6, 9, -19, -19, 20, 3]

Analysis

Currently filtering can be done using Enumerable#select, but this can be inefficient when it is used with large data, because a temporary array needs to be created. The enumerable created by enum_if will also reflect any changes made to the original object. Using to_enum with a block provides a powerful way of creating custum enumerables that can perform any transformation on the original data.

Implementation

A patch is available which was created by Nobu Nokada


Vote for this RCR

Strongly opposed [0]
Opposed [2]
Neutral [0]
In favor [1]
Strongly advocate [0]

Change the status of this RCR to:

accepted

rejected

withdrawn


I haven't used enumerator much, so take these comments in that context.

The block idea: this sounds like you want a kind of default map behavior. But isn't the idea to return something which mimics an iterator -- and then (as with an iterator) tell it what to do? This seems maybe a little too magic.

#enum_if: I don't really like the idea of having an alternative #select because of implementation issues. That brings the implementation too directly into the language -- it's too low-level a decision, I think.

-- David Black


How about provide something very similar to Enumerator but return a new Enumerating object instead of an Array? I.e. what if

  obj.to_enum.select{|x| x.isa? Numeric}

works like enum_if that is proposed here?

--matz.


"The block idea: this sounds like you want a kind of default map behavior."

Well, the idea is more to have a kind of transparent map behaviour. In this way you can transform one kind of iterator in another one. You may want to save the enumerable to avoid doing the same transformation on an object over and over again. Someone had also implemented something similar to have a "lazy" iterator. It would be lazy because the block is only executed when a call to the original objects :each method is made.

  "obj.to_enum.select{|x| x.isa? Numeric}"

That's nice! I am not sure if it will confuse people who expect Enumerator#select to return an Array (or break something), but I like the idea.

--Kristof Bastiaensen


Back to RCRchive.


RCR Submission page and RCRchive powered by Ruby, Apache, RuWiki (modified), and RubLog