I am trying to use lambdas as blocks for an array each:
(1..10).each ->(i) {p i}
But I get this error:
ArgumentError: wrong number of arguments (given 1, expected 0)
Why does it happen? The lambda has exactly one argument.
Range#each doesn't take a lambda, it takes a block. The lambda is being passed as an argument, which Range#each is not expecting. Try this instead:
(1..10).each { |i| p i }
You can convert a lambda if you are so inclined:
lam = ->(i) { p i }
(1..10).each(&lam)
Related
I noticed if you type:
object &, you get the object back.
For example:
1.class # => Integer
1 &.class # => Integer
'hello'.then { |x| x.equal?(x &.itself) } # => true
[1, 2, 3] &.map(&:next) # => [2, 3, 4]
I am unable to find a documentation for the syntax for object &.method
How does this syntax work?
There are 2 seperate operators here:
Safe navigation operator &. - It is safe navigation operator which was introduced in Ruby 2.3.0. It basically returns nil if the callee is nil instead of raising excecption undefined method called for Nil class. eg:
a = 1
a.next
# => 2
a&.next
# => 2
a = nil
a.next
# => NoMethodError (undefined method `next' for nil:NilClass)
a&.next
# => nil ## No exception, returns nil
You can read about it more here and documentation
Unary & : This operator is a little more complex. It is almost equivalent to calling #to_proc but not quite that. But for this discussion let us think like that. So, if you have a Proc, calling with & in front of it will call #to_proc on the Proc and convert it into a block
multiply_by_2 = Proc.new { |x| x * 2 }
# => #<Proc:0x00007fb4771cf560>
# &multiply_by_2 almost equivalent to { |x| x * 2 } but is not correct syntax
[1, 2].map(&multiply_by_2)
# => [2, 4]
# equivalent to [1, 2].map { |x| x * 2 }
But what happens if we give a symbol like :abc to & operator instead of a proc. It will try to call #to_proc on the symbol and ruby has defined Symbol#to_proc which roughly translates to something like this:
def to_proc
# this will return some block like { |x| x.send(:abc) }
lambda { |x| x.send(self) }
end
So &:abc roughly translates to this block { |x| x.abc } using the below transformation
&:abc =====> :abc.to_proc =====> { |x| x.send(:abc) } ====> { |x| x.abc }
So, instead of doing [1, 2, 3].map { |x| x.next }, you could do [1, 2, 3].map(&:next) as &:next is roughly equivalent to the block { |x| x.next }.
See unary & (which is the main source of what I have written here) for more reading.
It's ruby syntax, & calls to_proc on the object and passes the result as a block to the method.
An explanation from the pickaxe book, programming Ruby 1.9 and 2.0
Blocks Can Be Objects
Blocks are like anonymous methods, but there’s
more to them than that. You can also convert a block into an object,
store it in variables, pass it around, and then invoke its code later.
Remember we said that you can think of blocks as being like an
implicit parameter that’s passed to a method? Well, you can also make
that parameter explicit. If the last parameter in a method definition
is prefixed with an ampersand (such as &action ), Ruby looks for a
code block whenever that method is called. That code block is
converted to an object of class Proc and assigned to the parameter.
You can then treat the parameter as any other variable. Here’s an
example where we create a Proc object in one instance method and store
it in an instance variable. We then invoke the proc from a second
instance method.
class ProcExample
def pass_in_block(&action)
#stored_proc = action
end
def use_proc(parameter)
#stored_proc.call(parameter)
end
end
Use it like so
eg = ProcExample.new
eg.pass_in_block { |param| puts "The parameter is #{param}" }
eg.use_proc(99)
produces:
The parameter is 99
When I try to call a method with select and num.even? as follows,
def selection(array)
puts "This is inside the method"
return yield(array)
end
collection = [1,2,3,4,5]
selection(collection.select) { |num| num.even? }
I get a no defined Method error:
undefined method `even?' for #<Enumerator: [1, 2, 3, 4, 5]:select>
I'm looking for a return of even numbers in the array. I can get the select even? combo work in other examples of an array.
Array#select returns an Enumerator instance if no block was given to it
then you call selection method passing the result of call to collection.select as an argument and { |num| num.even? } as block
inside your selection function you yield the argument (an Enumerator instance) to the block
in the block you call even? on the block argument, resulting in the error message you receive.
I am unsure what’s wrong with collection.select(&:even?), but if you want to re-implement it yourself, here you go:
def selection(array)
# convention: return enumerator unless block is given
return enum_for(:selection) unless block_given?
enumerator = array.each
result = []
loop do
(value = enumerator.next) rescue return result
result.push(value) if yield value
end
end
selection([1,2,3,4,5]) { |num| num.even? }
#⇒ [2, 4]
You are seeing this error because you are passing an enumerator object in to your "selection" method ... that is, the result of "collection.select" is an Enumerator and enumerators do not implement an "even" method.
I believe that you are trying to implement your own version of "select". The following is one way to achieve your stated intent: "I'm looking for a return of even numbers in the array."
def selection(array)
results = []
for item in array do
results << item if yield item
end
results
end
collection = [1,2,3,4,5]
puts selection(collection) { |num| num.even? }
# => [2,4]
https://mixandgo.com/learn/mastering-ruby-blocks-in-less-than-5-minutes is a nice reference
yield(array) is passing the whole array in one go to the block given to the method, so it is trying to call even? on the array.
As shown in the ruby example below, I can't call a lambda with wrong number of arguments as Proc created from a Method because it is strict about the number of arguments:
# method with no args
def a; end
instance_eval(&method(:a))
# ArgumentError: wrong number of arguments (1 for 0)
method(:a).to_proc.call(1, 2, 3)
# ArgumentError: wrong number of arguments (3 for 0)
method(:a).to_proc.lambda?
# => true
How do I get a Proc that is not a lambda from either a Proc that is or from a Method?
There is no way to do this.
Besides the argument passing, I wonder what you would expect from a return in the method. It can only behave in lambda way...
If you really have to do this, you will need to build your own block, e.g.
Proc.new{ a }
For a more generic way, you'll have to check the arity of the method and pass only the required parameters.
Try wrapping it in a non-lambda Proc, like so:
l = lambda {|a,b| puts "a: #{a}, b: #{b}" }
p = proc {|a,b| l.call(a,b) }
l.lambda?
#=> true
l.arity
#=> 2
l.call("hai")
#=> ArgumentError: wrong number of arguments (1 for 2)
l.call("hai", "bai", "weee", "womp", "woo")
#=> ArgumentError: wrong number of arguments (5 for 2)
p.lambda?
#=> false
p.arity
#=> 2
p.call("hai")
#=> a: hai, b:
p.call("hai", "bai", "weee", "womp", "woo")
#=> a: hai, b: bai
Convert Lambda to Proc
Here's a work-around that wraps a lambda or a method call in a Proc and uses splat to handle any number of arguments:
def lambda_to_proc(lambda)
Proc.new do |*args|
diff = lambda.arity - args.size
diff = 0 if diff.negative?
args = args.concat(Array.new(diff, nil)).take(lambda.arity)
lambda.call(*args)
end
end
This would always work no matter the number of arguments passed; Extra arguments will be dropped and nil will replace the missing arguments.
Example:
# lambda with two args
some_lambda = -> (a,b) { [a, b] }
# method with no args
def some_method; "hello!"; end
lambda_to_proc(some_lambda).call(5)
# => [5, nil]
lambda_to_proc(method(:some_method)).call(1,2,3)
# => "hello!"
Note: There is no direct way to convert a lambda or a method call to a proc. This is just a workaround and obviously slower than the real deal (because of wrapping one call in another).
What do you call the -> operator as in the following?
->(...) do
...
end
Aren't the following snippets equivalent?
succ = ->(x) {x + 1}
succ = lambda {|x| x + 1}
In Ruby Programming Language ("Methods, Procs, Lambdas, and Closures"), a lambda defined using -> is called lambda literal.
succ = ->(x){ x+1 }
succ.call(2)
The code is equivalent to the following one.
succ = lambda { |x| x + 1 }
succ.call(2)
Informally, I have heard it being called stabby lambda or stabby literal.
=> == Hash Rocket
Separates keys from values in a hash map literal.
-> == Dash Rocket
Used to define a lambda literal in Ruby 1.9.X (without args) and Ruby 2.X (with args). The examples you give (->(x) { x * 2 } & lambda { |x| x * 2 }) are in fact equivalent.
->(x) { ... } is the same as lambda { |x| ... }. It creates a lambda. See Kernel#lambda A lambda is a type of proc, one that ensures the number of parameters passed to it is correct. See also Proc::new and Kernel#proc.
How can I pass a variable number of args to a yield.
I don't want to pass an array (as the following code does), I'd actually like to pass them as a programmatic number of args to the block.
def each_with_attributes(attributes, &block)
results[:matches].each_with_index do |match, index|
yield self[index], attributes.collect { |attribute| (match[:attributes][attribute] || match[:attributes]["##{attribute}"]) }
end
end
Use the splat-operator * to turn the array into arguments.
block.call(*array)
or
yield *array
Use the asterisk to expand an array into its individual components in an argument list:
def print_num_args(*a)
puts a.size
end
array = [1, 2, 3]
print_num_args(array);
print_num_args(*array);
Will print:
1
3
In the first case the array is passed, in the second case each element is passed separately. Note that the function being called needs to handle variable arguments such as the print_num_args does, if it expects a fixed size argument list and received more or less than expected you will get an exception.
Asterisk will expand an array to individual arguments in ruby:
def test(a, b)
puts "#{a} + #{b} = #{a + b}"
end
args = [1, 2]
test *args