I saw this code for a method same as each, except it receives a block to run some test against every item:
def every?(&predicate)
predicate = lambda { |item| item } if predicate.nil?
each do |item|
return false if !predicate.call(item)
end
true
end
Why is there a & in the parameter, and what does it do? What are the uses of it?
Sometimes in parameter lists you'll see something like
def foo(&block)
logic_with block
end
This just means that argument is expecting a block - and in your example.
&predicate just means passing a block as a parameter, which we're assigning to a local variable predicate
You can get a good idea of this from the fact that if predicate is nil the first line of the method assigns a new lamda to the predicate variable.
For further reading here's a good posts on blocks, procs and lambdas: http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/
EDITED per sawa's explanation below.
My take was you wanted the simple explanation that if you see & in this context it means a block is expected.
If you want to know specifically what the & operator itself actually does there's a good blog post here: http://ablogaboutcode.com/2012/01/04/the-ampersand-operator-in-ruby/
As sawa mentions it's very similar to calling to_proc on the incoming block. From the post I linked to, in more detail:
if object is a block, it converts the block into a simple proc.
if object is a Proc, it converts the object into a block while preserving the lambda? status of the object.
if object is not a Proc, it first calls #to_proc on the object and then converts it into a block.
The two operators * and & swap Ruby objects and non-objects.
The operator * prepended to a list of comma-separated objects (which is not an object) converts it into an array (which is an object).
*("foo", "bar", "baz") # => ["foo", "bar", "baz"]
The operator * prepended to an object converts it into an array by applying to_a, and then into a list of comma-separated objects.
*["foo", "bar", "baz"] # => ("foo", "bar", "baz")
*nil # => *[] # => ()
The operator & prepended to a block (which is not an object) converts it into a proc (which is an object).
&{|e| puts e} # => ->(e){puts e}
The operator & prepended to an object converts it into a proc by applying to_proc, and then into a block.
&->(e){puts e} # => {|e| puts e}
&:foo # => &->(e){e.foo} # => {|e| e.foo}
When you have a & in an argument position, the & is appended to a block, so the third case above applies. The block becomes a proc.
In the context of a method definition, putting an ampersand in front of the last parameter indicates that a method may take a block and gives us a name to refer to this block within the method body.
I often refer to this post when I get confused.
Related
Consider the following:
(1..10).inject{|memo, n| memo + n}
Question:
How does n know that it is supposed to store all the values from 1..10? I'm confused how Ruby is able to understand that n can automatically be associated with (1..10) right away, and memo is just memo.
I know Ruby code blocks aren't the same as the C or Java code blocks--Ruby code blocks work a bit differently. I'm confused as to how variables that are in between the upright pipes '|' will automatically be assigned to parts of an object. For example:
hash1 = {"a" => 111, "b" => 222}
hash2 = {"b" => 333, "c" => 444}
hash1.merge(hash2) {|key, old, new| old}
How do '|key, old, new|' automatically assign themselves in such a way such that when I type 'old' in the code block, it is automatically aware that 'old' refers to the older hash value? I never assigned 'old' to anything, just declared it. Can someone explain how this works?
The parameters for the block are determined by the method definition. The definition for reduce/inject is overloaded (docs) and defined in C, but if you wanted to define it, you could do it like so (note, this doesn't cover all the overloaded cases for the actual reduce definition):
module Enumerable
def my_reduce(memo=nil, &blk)
# if a starting memo is not given, it defaults to the first element
# in the list and that element is skipped for iteration
elements = memo ? self : self[1..-1]
memo ||= self[0]
elements.each { |element| memo = blk.call(memo, element) }
memo
end
end
This method definition determines what values to use for memo and element and calls the blk variable (a block passed to the method) with them in a specific order.
Note, however, that blocks are not like regular methods, because they don't check the number of arguments. For example: (note, this example shows the usage of yield which is another way to pass a block parameter)
def foo
yield 1
end
# The b and c variables here will be nil
foo { |a, b, c| [a,b,c].compact.sum } # => 1
You can also use deconstruction to define variables at the time you run the block, for example if you wanted to reduce over a hash you could do something like this:
# this just copies the hash
{a: 1}.reduce({}) { |memo, (key, val)| memo[key] = val; memo }
How this works is, calling reduce on a hash implicitly calls to_a, which converts it to a list of tuples (e.g. {a: 1}.to_a = [[:a, 1]]). reduce passes each tuple as the second argument to the block. In the place where the block is called, the tuple is deconstructed into separate key and value variables.
A code block is just a function with no name. Like any other function, it can be called multiple times with different arguments. If you have a method
def add(a, b)
a + b
end
How does add know that sometimes a is 5 and sometimes a is 7?
Enumerable#inject simply calls the function once for each element, passing the element as an argument.
It looks a bit like this:
module Enumerable
def inject(memo)
each do |el|
memo = yield memo, el
end
memo
end
end
And memo is just memo
what do you mean, "just memo"? memo and n take whatever values inject passes. And it is implemented to pass accumulator/memo as first argument and current collection element as second argument.
How do '|key, old, new|' automatically assign themselves
They don't "assign themselves". merge assigns them. Or rather, passes those values (key, old value, new value) in that order as block parameters.
If you instead write
hash1.merge(hash2) {|foo, bar, baz| bar}
It'll still work exactly as before. Parameter names mean nothing [here]. It's actual values that matter.
Just to simplify some of the other good answers here:
If you are struggling understanding blocks, an easy way to think of them is as a primitive and temporary method that you are creating and executing in place, and the values between the pipe characters |memo| is simply the argument signature.
There is no special special concept behind the arguments, they are simply there for the method you are invoking to pass a variable to, like calling any other method with an argument. Similar to a method, the arguments are "local" variables within the scope of the block (there are some nuances to this depending on the syntax you use to call the block, but I digress, that is another matter).
The method you pass the block to simply invokes this "temporary method" and passes the arguments to it that it is designed to do. Just like calling a method normally, with some slight differences, such as there are no "required" arguments. If you do not define any arguments to receive, it will happily just not pass them instead of raising an ArgumentError. Likewise, if you define too many arguments for the block to receive, they will simply be nil within the block, no errors for not being defined.
I am new to ruby and working through a tutorial but am not sure what this line of code means:
[movie, version_number].any?(&:nil?)
From my research, Array.any? returns true if any of the elements of the array are not false or nil. And &:nil? means to call to_proc() on the symbol :nil? i.e. :nil?.to_proc so the statement is equivalent to
[movie, version_number].any?(:nil?.to_proc)
which is equivalent to
[movie, version_number].any?{|item| item.nil?}
Further, any? Passes each element of the collection (in this case, Array) to the {|item| item.nil?} block.
When you put them together, does the line of code mean, call nil? on each element in the Array before calling .any? on the array, i.e. is it equivalent to:
[movie.nil?, version_number.nil?].any?
Or, in plain English, are any of movie or version_number equivalent to nil?
From Symbol#to_proc documentation:
Returns a Proc object which respond to the given method by sym.
(1..3).collect(&:to_s) #=> ["1", "2", "3"]
So in your case this is effectively the same as writing:
[movie, version_number].any?{|item| item.nil? }
any? expects a block[1] to be passed, which will be evaluated for each item, and will return true if the block evaluates to true for any of the members.
The to_proc method on Symbol is basically a convenience shortcut, when you just want to call a single method on the item passed to the block. As in the example above, this leads to shorter code than explicitly defining the block.
[1] Refer this article on blocks, procs and lambdas in ruby
This question already has answers here:
What does map(&:name) mean in Ruby?
(17 answers)
Closed 6 years ago.
Why does:
[1,2,3,4,5].map(&:to_s) #=> ["1", "2", "3", "4", "5"]
work but:
[1,2,3,4,5].map(&:*(2))
throws an unexpected syntax error?
& is called the to_proc operator. It calls the to_proc method on the expression that follows it and then passes the resulting Proc to the method as a block.
In the case of &:to_s, :to_s is a Symbol, so it the operator calls Symbol#to_proc. The docs are a little garbled, but suffice it to say that these two expressions are more-or-less equivalent:
my_proc = :to_s.to_proc
my_proc = Proc.new {|obj| obj.to_s }
So the answer to the question "Why doesn't &:*(2) work?" is that the expression that follows the & operator, :*(2), isn't a valid Ruby expression. It makes about as much sense to the Ruby parser as "hello"(2).
There is, by the way, a way to do what you're trying to do:
[1,2,3,4,5].map(&2.method(:*))
# => [2, 4, 6, 8, 10]
In the above code, 2.method(:*) returns a reference to the * method of the object 2 as a Method object. Method objects behave a lot like Proc objects, and they respond to to_proc. However, the above isn't exactly equivalent—it does 2 * n rather than n * 2 (a distinction that doesn't matter if n is also a Numeric)—and it's not any more succinct or readable than {|n| n * 2 }, and so rarely worth the trouble.
Ampersand and object (&:method)
The & operator can also be used to pass an object as a block to a method, as in the following example:
arr = [ 1, 2, 3, 4, 5 ]
arr.map { |n| n.to_s }
arr.map &:to_s
Both the examples above have the same result. In both, the map method takes the arr array and a block, then it runs the block on each element of the array. The code inside the block runs to_s on each element, converting it from integers to strings. Then, the map method returns a new array containing the converted items.
The first example is common and widely used. The second example may look a bit cryptic at first glance. Let's see what's happening:
In Ruby, items prefixed with colon (:) are symbols. If you are not familiar with the Symbol class/data type, I suggest you Google it and read a couple of articles before continuing. All method names in Ruby are internally stored as symbols. By prefixing a method name with a colon, we are not converting the method into a symbol, neither are we calling the method, we are just passing the name of the method around (referencing the method). In the example above, we are passing :to_s, which is a reference to the to_s method, to the ampersand (&) operator, which will create a proc (by calling to_proc under the hood). The proc takes a value as an argument, calls to_s on it and returns the value converted into a string.
Although the :to_s symbol is always the same, when running the map loop, it will refer to the to_s method of the class corresponding to each array item. If we passed an array such as [ 21, 4.453, :foobar, ] to the map method, the to_s method of the Fixnum class would be applied (called) on the first item, the to_s method of the Float class would be applied to the second item and the to_s method of the Symbol class would be applied to the third item. This makes sense because we are not passing the actual to_s method to the ampersand operator, just its name.
Below is an example of creating a proc that takes an argument, calls a method on it and returns the result of the method.
p = :upcase.to_proc
p.call("foo bar")
Output:
=> "FOO BAR"
Let's review what is going on in arr.map &:to_s
At each iteration of map, one item of the array (an integer) is passed to &:to_s
The :to_s symbol (which is a reference to the to_s method) is passed to the & operator, which creates a proc that will take an argument (an array item), call to_s on the argument and return the value converted into string;
The map method returns a new array containing the strings "1", "2", "3", "4" and "5".
Some block methods such as inject can optionally take a symbol instead of a block:
%w[a b c].inject(&:+)
# => "abc"
%w[a b c].inject(:+)
# => "abc"
%w[a b c].inject("", :+)
# => "abc"
while other block methods such as map cannot:
%w[a b c].map(&:upcase)
# => ["A", "B", "C"]
%w[a b c].map(:upcase)
# => ArgumentError: wrong number of arguments (1 for 0)
Why can't the latter take a symbol?
For inject, a block (or a substitute) is obligatory. If it weren't passed a block, then there has to be at least one argument, the last argument has to be a symbol, and the block would be constructed out of it. Whatever the arity, there is no ambiguity; the last argument is used to construct a block when the block is lacking.
For map, a block is optional. When there is no block given, then the return value would be an Enumerator instance. Hence, from the information whether a block was passed or not, it cannot be decided whether the last argument should be used to construct a block.
In the particular case of map, it does not take an argument, so there is a sense in saying that an extra argument should be taken as a block, but it makes things complicated to judge whether that last argument is to be taken as a block depending on the arity. And it also loses the future possibility of changing the arity of the method.
Not sure but i know this operator & to_proc, inject() method accept 2 args first accum second proc, but map() accept only one args proc or block. In inject() first args(accum) can be first item in enum.
It's just a special handling for this special case in some methods and lack of such handling in the others.
Let's say we have this code:
def something(*someargs)
return *someargs.join(",")
end
Now, I found you can reference *someargs just like any other variable anywhere in the method definition. But I tried this...returning *someargs as a string, separated with a comma. Yet, when I call this method:
a = something(4, 5)
p a.class # => Array
p a #> ["4,5"]
why does something(4,5) still returns an array? If I do something like this:
[4, 5].join(",")
the result will be a string not in an array. So my question would be, how do I make the "something" method return an actual string which contains all the arguments as a string. And it's weird because if I do *someargs.class, the result is "Array", yet it doesn't behave like a typical array...
Try below :
def something(*someargs)
return someargs.join(",")
end
a = something(4, 5)
p a.class # => String
p a # => "4,5"
One example to explain your case:
a = *"12,11"
p a # => ["12,11"]
So when you did return *someargs.join(","), someargs.join(",") created the string as "4,5".But now you are using splat operator(*) again on the evaluated string "4,5" with the assignment operation like a = *"4,5". So finally you are getting ["4,5"].
Read more scenarios with splat operators here - Splat Operator in Ruby
Hope that helps.
An object prepended with a splat *... is not an object. You cannot reference such thing, nor can you pass it as an argument to a method because there is no such thing. However, if you have a method that can take multiple arguments, for example puts, then you can do something like:
puts *["foo", "bar"]
In this case, there is no such thing as *["foo", "bar"]. The splat operator is expanding it into multiple arguments. It is equivalent to:
puts "foo", "bar"
Regarding why someargs remains to be an array after someargs.join(","). That is because join is not a destructive method. It does not do anything to the receiver. Furthermore, an object cannot change its class by a destructive method. The only way to change the reference of someargs from an array to a string is to reassign it.