Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shorthand for functions to map

In a map, I can call a method on the passed-in value using the convenient &: notation:

nums = (0..10).to_a
strs = nums.map(&:to_s)

Is there something similar for calling a function with the value passed in as the first argument?

nums = (0..10).to_a
nums.each(puts) # error!
like image 405
Vlad the Impala Avatar asked Feb 24 '12 20:02

Vlad the Impala


People also ask

What are the functions of a map?

Maps present information about the world in a simple, visual way. They teach about the world by showing sizes and shapes of countries, locations of features, and distances between places. Maps can show distributions of things over Earth, such as settlement patterns.

How do you create a map for a function?

map insert() in C++ STL The map::insert() is a built-in function in C++ STL which is used to insert elements with a particular key in the map container. Parameters: The function accepts a pair that consists of a key and element which is to be inserted into the map container.

What does () => mean in JavaScript?

It's a new feature that introduced in ES6 and is called arrow function. The left part denotes the input of a function and the right part the output of that function.

What is map () in JavaScript?

Definition and Usage. map() creates a new array from calling a function for every array element. map() calls a function once for each element in an array. map() does not execute the function for empty elements. map() does not change the original array.


1 Answers

Disclaimer: This post is purely educational. nums.each {|n| puts n} is really the only reasonable thing to write in a real project.

Understanding nums.map(&:to_s)

The existing short form works very simply. & calls to_proc on the symbol, and to_proc on a symbol is defined like this.

class Symbol
  def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
  end
end

Since this proc will start acting like a regular block that is passed into map, the *args in this case is really each element we're iterating through. We take the first of args (since * turns arguments into an array), and send self to it, self being the actual symbol, such as :to_s. Remaining arguments are passed in. So it's like saying nums.map{ |*args| args.shift.__send__(:to_s, *args) }.

Changing it to enable nums.each(&:puts)

We could easily re-implement to_proc to act differently. Here's a quick example.

class Symbol
  def to_proc
    Proc.new { |*args| __send__(self, *args) }
  end
end

(1..10).each(&:print) # => 12345678910

Here instead of sending symbol name as a message to the element, we are just calling symbol as a method on the current context, and simply passing the iterated element as an argument to it.

So it's more like saying (1..10).each{|*args| __send__(:print, *args)}.

Understanding nums.each(&method(:puts))

That said, as nash pointed out you could call nums.each(&method(:puts)). What happens there is that you get an object that represents method puts using ruby's method method. So then & calls .to_proc on the method object, turning it into proc, which itself starts playing the role of the block passed into each (or map). The arguments passed into that proc (each element you're iterating) then become the arguments to that method. Neat.

Implementing xargs

In order to avoid overwriting standard behavior, we could hypothetically implement our own xargs feature, like in shell scripting. (Do not do this at home, too-clever is bad.)

class Symbol
  def to_xargs
    Proc.new{ |*args| __send__(self, *args) }
  end
end

def xargs(sym)
  sym.to_xargs
end

(1..10).each(&xargs(:print)) # prints 12345678910
like image 153
Max Chernyak Avatar answered Oct 02 '22 12:10

Max Chernyak