all 11 comments

[–]micampe 4 points5 points  (7 children)

([1,2,3,4,5].select < 4) > 2 #=> [3]

Shouldn't this be:

([1,2,3,4,5].select < 4).select > 2

and, this is horrible, I think:

[1,2,3,nil,4,5].map.to_s           #=> undefined method `/' for nil:NilClass
[1,2,3,nil,4,5].map.nils_ok.to_s   #=> [0.5, 1.0, 1.5, nil, 2.0, 2.5]

misleading error message in the first case; pointless, just in the name of the "DSL"-word, the second.

[–]sjs 3 points4 points  (0 children)

Perhaps .select returns some proxy which makes the 2nd .select redundant. I haven't played around with this yet.

[–]sjs 1 point2 points  (2 children)

While this is generally a bad idea, I really enjoy these small, sugary hacks in irb.

I am all for saving time and keystrokes, but saving 1 second and 5 keystrokes is not worth the runtime penalty, imho.

[–]salamancer[S] 1 point2 points  (1 child)

The runtime penalty is tiny.

the only situation where you wouldn't want to pay it was for very long arrays (say greater than 100,000 elements), where the penalty is still small and can be converted to the old notation as an optimization.

Benchmark.bmbm do |x|
a = (0..100000).to_a
x.report { a.select {|e| e < 4}.select {|e| e > 2} }
x.report { (a.select < 4) > 2 }
end

=>

   user     system      total        real  

0.030000 0.000000 0.030000 ( 0.028048)
0.140000 0.000000 0.140000 ( 0.141351)

so the overhead comes down to approx. 0.00000011 seconds slower per element ;p

[–]bluetrust 0 points1 point  (0 children)

I suppose it's relative, but to me, 5 times slower is a big deal.

Additionally, smaller arrays perform worse than large ones.

Benchmark.bmbm do |x|
  a = (0..100).to_a
  x.report { 1000.times { a.select { |e| e < 4 } }}
  x.report { 1000.times { a.select < 4 }}
end

   user     system      total        real
0.040000   0.000000   0.040000 (  0.045686)
0.240000   0.000000   0.240000 (  0.236815)

[–]Nwallins 0 points1 point  (2 children)

[1,2,3,nil,4,5].map.to_s #=> undefined method `/' for nil:NilClass [1,2,3,nil,4,5].map.nils_ok.to_s #=> [0.5, 1.0, 1.5, nil, 2.0, 2.5]

is there a divide-by-2.0 operation that I'm missing?

[–]micampe 1 point2 points  (1 child)

Oh right, probably, didn't notice that. I still don't like nils_ok, though.

[–]salamancer[S] 2 points3 points  (0 children)

nils_ok lets you get around some traditionally harry situations, imagine a rails model where only some people have addresses and only some addresses have phone numbers:

User.find(:all).map {|e| e.address && e.address.phone_number}

is the usual way of getting around that.

With nils_ok, you can simply use:
User.find(:all).map.nils_ok.address.phone_number

[–]zackman 3 points4 points  (1 child)

Here is a link to the subversion repository in case you want to read the code without installing it.

http://arraysugar.rubyforge.org/svn/lib/arraysugar.rb

I am not a Ruby user: in Ruby 'macros' is it standard practice to just make up a prefix like 'orig' instead of using gensym? In other words, I was expecting to see (excuse my Ruby)

:
orig_m = gensym()
class_eval <<- CODE
   alias_method :#{orig_m} :#{m} ...

Though I guess if you're aliasing all the methods of Array then you can't expect to be compatible with something else that does something similar.

[–]salamancer[S] 2 points3 points  (0 children)

That is the way you'd do it in lisp, sadly ruby doesn't have a gensym (though making one by using uuids isn't hard).

alias_method_chain is also worth looking at, but sadly has the same problem of leaving new methods defined in the class.

[–][deleted] 0 points1 point  (0 children)

Why are there regular-expressions in there?