Ad
  • Custom User Avatar

    Here's the bench mark for both methods.

    require "benchmark"
    
    n = 1_000_000
    ARR = %w(today is a good day isn't it?)
    Benchmark.bmbm do |bm|
      bm.report { n.times { ARR.map(&:capitalize) } }
      bm.report { n.times { ARR.map { |i| i.capitalize } } }
    end
    
    Rehearsal ------------------------------------
       1.890000   0.000000   1.890000 (  1.893212)
       1.930000   0.010000   1.940000 (  1.928549)
    --------------------------- total: 3.830000sec
    
           user     system      total        real
       1.900000   0.000000   1.900000 (  1.910439)
       2.050000   0.000000   2.050000 (  2.050394)
    
  • Custom User Avatar

    @mortonfox, As I said it's a good solution. I think the comments section is a good place to discuss tradeoffs that may not be obvious to evey user, such as performance issues. That way someone seeing these for the first time can make the best informed decision in the future.

  • Custom User Avatar

    The main thing this is good for is being concise. I never believed it was performant since Ruby has special optimization for blocks.

  • Custom User Avatar

    Clever is the tag chosen by Codewars; this wasn't some kind of slight. It's a good solution, but performance is sub-optimal and the better performing solution is also easier to read.

    And to say "As for performance, don't use Ruby", that's just silly. It doesn't matter what language someone is developing in performance should be a consideration. This isn't a case of pre-mature optimization. It's an easy performance gain. I provided the benchmark for anyone that might come along in the future to be helpful as they may not be aware of the performance difference.

  • Custom User Avatar

    It's not 'clever', it's perfectly readable to any Ruby developer. You could argue that so many things in ruby are not readable to 'the average developer'.
    Passing just the method is more common in many other languages, be it Python, Scheme, Haskell, Dart, Smalltalk or even C.

    As for performance, don't use Ruby, or optimize inner loops. For most Enumerable#map operations the actual operation will cost way more than iterating in whatever way.

  • Custom User Avatar

    This is certainly a clever solution and deserving up the the up votes for that. I'm surprised that this is so heavily upvoted though for "Best Practices", in comparison to the more direct map enumeration version here http://www.codewars.com/kata/reviews/5356b77a2930ee4c010007e6/groups/5397a4c4c20318c06e000ea0

    While this technique is certainly clever, I don't believe it is as readable to the average developer and the performance is worse.

    The results posted below are from benchmarking this solution against the standard map enumerable. This to_proc version shown here is about 8% slower. So you lose speed and code legibility.

    Calculating -------------------------------------
                proc    47.361k i/100ms
                 std    52.963k i/100ms
    -------------------------------------------------
                proc    980.817k (± 7.3%) i/s -      4.878M
                 std      1.060M (± 7.3%) i/s -      5.296M
    
  • Custom User Avatar

    Learned something here. Great example. Time to mess around in the REPL some more...

  • Default User Avatar

    A little digging brought me to Stack Overflow (http://stackoverflow.com/questions/1961030/ruby-ampersand-colon-shortcut). It's a really cool functionality, and I'm glad I know it now!

  • Custom User Avatar

    Read up on the unary & operator. It's operating on the :capitalize symbol. Basically it's making the captialize method into a block.

  • Default User Avatar

    It's leaning on functionality of Symbol#to_proc to do it. There's a few different pieces that make it up, and http://www.jacopretorius.net/2012/01/symbolto_proc-in-ruby.html seems to be a good in depth writeup of it.

    In short though:

    • &foo in arguments passes foo as a block to the method as if you'd done { foo.call }. If foo isn't a proc, it calls to_proc on it first.
    • Symbol#to_proc is implemented and creates a proc that sends the symbol to the block argument. That is :capitalize.to_proc returns a proc (block) equivilent to { |arg| arg.capitalize }
    • Putting it together means [].map { |element| element.capitalize } can be written as [].map(&:capitalize) and it returns the same result.
  • Default User Avatar

    I don't understand it. Doesn't Array#map accept a block as an argument? What is the syntax for "&:capitalize"?