so the "[*]" operator would descend into nested arrays.
In total ignorance of whether the parser would allow it, I'd like to say that I think that'd be a nice addition to ruby2 if there's no other clean shorthand out there already.
> where I use map to apply a single method to elements inside arrays.
I've done something like this before:
module Enumerable def map_send(*methods) map {|obj| methods.inject(obj) {|obj, meth| obj.send(meth) }} end end a = %w{ foo bar baz} a.map_send(:capitalize, :reverse) #=> ["ooF", "raB", "zaB"]
> .... I think something > like this would be very nice:
> people[*].email_addr
That's an interesting-looking operator there. I think it's very unclear what it does. I'd prefer the mouse-poo version people.map { @.email_addr } or people.map { it.email_addr }
I don't think Enumerable wants a special operator like this; method names convey meaning.
At Thu, 27 Oct 2005 15:05:41 +0900, Ron M wrote in [ruby-talk:162876]:
> That sure beats C, but wouldn't it be nice if there > were a shorthand for doing so. I think something > like this would be very nice:
> people[*].email_addr
What about:
module Mappable class Mapper def initialize(obj) @obj = obj end def method_missing(meth, *args, &block) @obj.map {|i| i.send(meth, *args, &block)} end end def self.included(klass) super aref = klass.instance_method(:[]) klass.module_eval do define_method(:[]) do |*idx| if idx.empty? Mapper.new(self) else aref[*idx] end end end end end
> At Thu, 27 Oct 2005 15:05:41 +0900, > Ron M wrote in [ruby-talk:162876]: >> That sure beats C, but wouldn't it be nice if there >> were a shorthand for doing so. I think something >> like this would be very nice:
>> people[*].email_addr
> What about:
> module Mappable > class Mapper > def initialize(obj) > @obj = obj > end > def method_missing(meth, *args, &block) > @obj.map {|i| i.send(meth, *args, &block)} > end > end > def self.included(klass) > super > aref = klass.instance_method(:[]) > klass.module_eval do > define_method(:[]) do |*idx| > if idx.empty? > Mapper.new(self) > else > aref[*idx] > end > end > end > end > end
> class Array > include Mappable > end
> a = %w[a b c] > p a[].upcase
I don't like it because now [] does two completely different things: access and mapping. I'd prefer something like this:
module Enumerable # replacement for map def mapx(*a,&b) raise "Can only have one" if !a.empty? && b if a.empty? map(&b) else map {|x| a.inject(x) {|v,m| v.send(m)}} end end end
>> %w{a b c}.mapx :upcase => ["A", "B", "C"] >> %w{a b c}.mapx {|a| a + "x"}
=> ["ax", "bx", "cx"]
>> %w{abc bcd cde}.mapx :upcase, :reverse
=> ["CBA", "DCB", "EDC"]
I just chose mapx to get it working fast, ideally the original map method of Enumerable classes would be changed.
> module Mappable > class Mapper > def initialize(obj) > @obj = obj > end > def method_missing(meth, *args, &block) > @obj.map {|i| i.send(meth, *args, &block)} > end > end > def self.included(klass) > super > aref = klass.instance_method(:[]) > klass.module_eval do > define_method(:[]) do |*idx| > if idx.empty? > Mapper.new(self) > else > aref[*idx] > end > end > end > end > end
> class Array > include Mappable > end
> a = %w[a b c] > p a[].upcase
> -- > Nobu Nakada
As always, there's something magical about your code! Unfortunately, it makes pp blow up for one ;)
For a similar purpose, I use this - not as slick but simple and understandable. Doesn't handle the nesting though.
module Enumerable def where(&block) self.select{|x| x.instance_eval(&block) } end def project(&block) self.map{|x| x.instance_eval(&block) } end end
as in
depts.where{ name == "Apps" }.project{ people.project{ name } }
On Fri, 28 Oct 2005, nobuyoshi nakada wrote: > Hi,
> At Thu, 27 Oct 2005 21:07:03 +0900, > Trans wrote in [ruby-talk:162907]: >> A shorthand for:
>> people.every.email_addr
>> But I like your [*]. Hmm... I maybe able to adjust above to use [:*]
> I feel your "every" much cleaner than [*].
I agree, visually, but I find both of the semantically opaque compared to people.each {|person| ... } I know that people.every could return some kind of generator or enumerator, which could then be fed "email_addr" symbolically... but it seems to conceal rather than reveal what's going on.
David A. Black wrote: > I know that people.every could return > some kind of generator or enumerator, which could then be fed > "email_addr" symbolically...
module Enumerable def every enum, obj = self, Object.new obj.define_method :method_missing do |name, *args| enum.map { |element| element.send(name, *args) } end return obj end end
By the way, you need the Object#define_method if you want it to work:
class Object def define_method(*args, &block) singleton_class = class << self; self; end singleton_class.module_eval do define_method(*args, &block) end end end
> By the way, you need the Object#define_method if you want it to work:
I definitely *don't* want it to work :-) I dislike using dot syntax for non-dot semantics. I've never liked things like:
hash.where.the.key.equals(10)
even though they can usually be made to work.
I continue to struggle to understand what people find so horrible about ary.each {|item| .... }, ary.map {|item| ... }, and so forth. I'll have some more coffee and maybe I'll start to see the light....
David A. Black wrote: > I continue to struggle to understand what people find so horrible > about ary.each {|item| .... }, ary.map {|item| ... }, and so forth. > I'll have some more coffee and maybe I'll start to see the light....
There's nothing really wrong with them, but they are verbose in simple (and common) cases:
result = array.map {|element| element.transform }
The only issue is that you're saying "element" twice when you would only say it once if you were describing the operation to a person.
Groovy's (optional) implicit parameter is the counterpoint:
// Groovy: result = array.map { it.transform() }
Tangentially, I wonder if the proposed Ruby 2 block syntax makes this slightly more possible.
# Pseudo-Ruby 2.0: result = array.map -> { it.transform }
Actually, probably less possible - IIRC the new block style is meant to be more like the method semantics, and so stricter on parameters.
So, anyway, I don't mind the idea of an implicit parameter, but I realise it's not going to happen before Ruby 3.
> I strongly prefer the latter. You've gone out of your way to make it > long and wordy :-)
> addresses = contacts.map {|c| c.email_addr }
That's of course a matter of taste, but I don't like single-letter variables. Abbreviations of long words can do, but the variable name should reflect the object it is referencing. Furthermore, I often use `collect' instead of `map' simply because that's what I'm doing: collecting email-addresses from a list of contacts.
>> But I agree that the dot syntax is bad, I was just proving that it >> could easily be done. This would be better:
>> addresses = contacts.every(:email_addr)
> There was an RCR once for enum.map(:method) {|x| ... } but it was > rejected.
I don't see why a method such as `map' should work that way - it isn't implied by the method name.
>> And maybe even have a `with_every' method:
>> contacts.with_every(:email_addr) do |email_addr| >> puts " - " + email_addr >> end
> "with_every" feels like the wrong word, though. It suggests that > they're all being used at the same time. Maybe:
> contacts.each_send(:email_addr)
> or something.
Maybe. `with_each' would also do. But still I think it is inconsistent to have `each' yield each object in a collection and `each_send` yield the result of calling a method on those objects. I'd rather we have `every' handle the latter.
# calls `method' on each object in `collection' # and returns the return values of those calls # in an array collection.every :method
# calls `method' on each object in `collection' # and yields the return values collection.with_every :method do |result|; end
They could of course both be contained in a single method, which both returned an array and yielded the return values.
I'm not suggesting that we put this in the Ruby Core, but I think it's a great library method.
On Fri, 28 Oct 2005, Dave Burt wrote: > David A. Black wrote: >> I continue to struggle to understand what people find so horrible >> about ary.each {|item| .... }, ary.map {|item| ... }, and so forth. >> I'll have some more coffee and maybe I'll start to see the light....
> There's nothing really wrong with them, but they are verbose in simple (and > common) cases:
> result = array.map {|element| element.transform }
> The only issue is that you're saying "element" twice when you would only say > it once if you were describing the operation to a person.
I probably would say "element" at least twice in describing it to a person, I think. "Go through the array one element at a time, call the "transform" method on the current element, and save the result to an accumulator array" or something like that.
> Groovy's (optional) implicit parameter is the counterpoint:
> // Groovy: > result = array.map { it.transform() }
<shudder/> :-)
> Tangentially, I wonder if the proposed Ruby 2 block syntax makes this > slightly more possible.
David A. Black wrote: > I definitely *don't* want it to work :-) I dislike using dot syntax > for non-dot semantics. I've never liked things like:
> hash.where.the.key.equals(10)
> even though they can usually be made to work.
I sort-of agree. It's definitely a semantic we're not used to --using a method call to give us a "reoriented" version of the same thing --kind of like having Roles. I suspect this will become a more common paradigm over time and me may grow acustomed to it, but I'm with you in that I'd rather have a distinguishing indication of when that's occuring. Perhaps:
people:every.email_addr
And maybe that's what Matz already has in mind. And besides, it would be nice to have it as a language feature becasue current implementations are not very efficient:
def every Functor.new do |op,*args| self.collect{ |a| a.send(op,*args) } end end
or at best
def every @__every_functor__ ||= Functor.new do |op,*args| self.collect{ |a| a.send(op,*args) } end end
> I continue to struggle to understand what people find so horrible > about ary.each {|item| .... }, ary.map {|item| ... }, and so forth. > I'll have some more coffee and maybe I'll start to see the light....
Not horrible, but looking at a language like R with it's elemewise operations, one kind of wishes we had shorthands techinques as nice.
BTW, I have another version of #every which is even more R-like which DeMello helped write. But I'm searching for a another name for it. Here it is:
module Enumerable
# Returns an elementwise Functor designed to make R-like # elementwise operations possible. # # [1,2].ew + 3 #=> [4,5] # [1,2].ew + [4,5] #=> [5,7] # [1,2].ew + [[4,5],3] #=> [[5,7],[4,5]] # #-- # Special thanks to Martin DeMello for helping to develop this. #++ def elementwise Functor.new do |op,*args| a = args.collect do |arg| if arg.kind_of?(Enumerable) ln = ( arg.length > self.length ? self.length : arg.length ) self[0...ln].zip(arg[0...ln]).collect{ |a,b| a.send(op,b) } #self[0...ln].zip(arg[0...1n]).collect{ |a,b| b ? a.send(op,b) : nil } else self.collect{ |a| a.send(op,arg) } end end a.flatten! if args.length == 1 a end end
> p ary[0].upcase # "A" > p ary.upcase # ["A", "B", "C", "D", "E"] > p ary.gsub(/[bd]/, 'X') # ["a", "X", "c", "X", "e"]
I don't like that one, because it provides no visual distinction between methods that act on the array as a whole and methods that map over it. Would be a nice place to introduce the -> operator, though :)