dblack / wobblini.net wrote:
> Hi --
>
> On Wed, 6 Dec 2006, Martin DeMello wrote:
>
> > While golfing with the hex_ip thread, I realised that map takes no
> > argument, and join takes no block, leaving a vacuum for a combined
> > method:
>
> Your point about map re-raised a question that was in my head
> recently.
>
> There was an RCR a while back, rejected by Matz, that asked for:
>
>    enum.map(:m)
>
> to be the same as:
>
>    enum.map {|e| e.m }
>
> It looks like Ruby >= 1.9 has this:
>
>    enum.map(&:m)
>
> which strikes me as the same thing, functionally, but visually noisier
> and semantically more obscure.
>
> I'm just wondering what the rationale is for rejecting the simple
> version and introducing the less simple one.
>
>
> David

Good question.  One guess is that they wanted a more generic approach,
instead of manually handling specific methods.  But that's a guess.

This may have been implemented before, but here's something I've been
toying with:

module Enumerable
   # Returns the numeric total of the elements of +enum+.
   # Raises an error if any of the elements are non-numeric.
   #
   def sum
      total = 0
      each{ |val| total += val }
      total
   end

   # Returns a new array containing the results of running
   # +block+ once for every element in the +enum+.  Any symbols
   # passed as arguments are assumed to be methods, and will be
   # called on every element *before* being yielded to the block.
   # Non-symbols are assumed to be arguments to those methods.
   #
   # Examples:
   #
   # array = ['foo', 'bar']
   #
   # array.map(:capitalize)      => ['Foo', 'Bar']
   # array.map(:+, 'x')          => ['foox', 'barx']
   # array.map(:+, 'y', :upcase) => ['FOOY', 'BARY']
   #
   def map(*args)
      array     = [] unless block_given?
      hash      = {}
      key       = nil

      args.each{ |arg|
         if arg.is_a?(Symbol)
            key = arg
            hash[key] = []
         else
            hash[key] << arg
         end
      }

      each{ |obj|
         hash.each{ |sym, args|
            if args.empty?
               obj = obj.send(sym)
            else
               obj = obj.send(sym, *args)
            end
         }

         if block_given?
            yield obj
         else
            array << obj
         end
      }

      return array unless block_given?
   end
end

class Array
   # Returns a new array containing the results of running
   # +block+ once for every element in the +enum+.  Any symbols
   # passed as arguments are assumed to be methods, and will be
   # called on every element *before* being yielded to the block.
   # Non-symbols are assumed to be arguments to those methods.
   #
   # Examples:
   #
   # array = ['foo', 'bar']
   #
   # array.map(:capitalize)      => ['Foo', 'Bar']
   # array.map(:+, 'x')          => ['foox', 'barx']
   # array.map(:+, 'y', +upcase) => ['FOOY', 'BARY']
   #--
   # The Array class actually has its own implementation of
   # the +map+ method, hence the duplication.
   #
   def map(*args)
      array     = [] unless block_given?
      hash      = {}
      key       = nil

      args.each{ |arg|
         if arg.is_a?(Symbol)
            key = arg
            hash[key] = []
         else
            hash[key] << arg
         end
      }

      each{ |obj|
         hash.each{ |sym, args|
            if args.empty?
               obj = obj.send(sym)
            else
               obj = obj.send(sym, *args)
            end
         }
         if block_given?
            yield obj
         else
            array << obj
         end
      }

      return array unless block_given?
   end
end