From: "David A. Black" Date: 2008-11-03T07:03:12+09:00 Subject: Re: ruby1.9: lazy versions of Enumerator#select and friends? Hi -- On Mon, 3 Nov 2008, Brian Candler wrote: > David A. Black wrote: >> It did have something like this, but not any more. I'm not sure they >> were identical to what you've written, but the basic idea of an >> enumerator that bundled itself with a block did exist in 1.9 for a >> while. > > I'd be interested to learn exactly what the dropped feature was. > > Perhaps we're talking at cross purposes, but I'm not sure I'm suggesting > "an enumerator that bundled itself with a block". Rather, I'm suggesting > that certain methods in Enumerable which used to return an array, > instead return an Enumerator, so that they can be chained horizontally. This is the feature that disappeared -- definitely not identical to what you're doing, but kind of germane: a = [1,2,3,4].enum_for(:map, &lambda {|x| x * 10}) a.select {|x| x > 20 } # [30,40] > By this I mean: > > a.meth1 { ... }.meth2 { ... }.meth3 { ... } > > currently runs "vertically" (meth1 enumerates all of 'a' and creates an > array; meth2 enumerates this array and creates another array; then meth3 > enumerates this array and creates a final array) > > By running "horizontally" I mean that the first element of 'a' is > processed all the way across; then the second element of 'a'; and so on. > No intermediate arrays are created. Right; that was clear from what you said. We're talking in the same problem domain (chainable, lazy enumerators), in somewhat different terms. >> I'd strongly go for #1. #2 would require not only massive code >> overhaul, but change of habits, and lots of to_a is unsightly (and you >> wouldn't necessarily want to have to hard-code the class of what you >> want back, anyway [i.e., the 'a' in 'to_a']). > > I don't understand the last sentence - a whole bunch of Enumerable > methods like #map, #select etc are already hard-coded to return an > Array, so you currently have no choice. True, but that doesn't take newly-written enumerable classes, and there are a few overrides here and there (like Hash#select returning a hash, in 1.9). I wonder what role what I have dubbed the "un-overriding" of enumerable methods would play. Here's an example: >> hash = { 1 => 2 } => {1=>2} >> hash.select { true } => {1=>2} >> hash.each.select { true } => [[1, 2]] Since Enumerator doesn't override select (which of course it can't, in a general way), calling select on an enumerator for the hash has a different effect from calling select on the hash. I haven't puzzled through how this would play out with your construct, but it seems like it might crop up (though maybe no more than with enumerators in general). > With what I propose you can use any method you like to build the result. > If you don't want an array, then don't use to_a. You could use 'inject' > or 'each' or 'each_with_object' to gather the resulting elements in > whatever way you like: e.g. > > a.meth1 { ... }.meth2 { ...}.each { |x,y| h[x] = y } > > Or even, as the original example I posted shows, you can just use the > results as they arrive and then discard them: > > a.meth1 { ... }.meth2 { ...}.each { |x| puts x } > > Now that Enumerators exist, the fact that Enumerable methods are > hard-coded always to create an Array seems very limiting. There are so > many other things you might want to create instead. I'm still not sure I'd like to have to do a kind of enumerator-resolving last call to every map, select, inject, etc. It's different with each, because it exists only for its block side-effects in the first place. The ones that return result sets that you care about would all have to be "capped" with to_a or something, unless the last method in the chain somehow knew not to return an enumerator. David -- Rails training from David A. Black and Ruby Power and Light: Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL Advancing with Rails January 19-22 Fort Lauderdale, FL * * Co-taught with Patrick Ewing! See http://www.rubypal.com for details and updates!