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).
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 using array.each you can specify the function in two forms:
Curly Braces:
a = [1,2,3]
a.each { |x| puts x * x }
Output:
1
4
9
=> [1, 2, 3]
'do' Syntax:
a = [1,2,3]
a.each do |x|
puts (x * x)
end
Output:
1
4
9
=> [1, 2, 3]
Question:
How can I replicate the 'do' syntax style with my own custom function? The closest to the curly brace style I can get is:
What I've tried:
def PutWith2Arg(proc)
puts proc.call(2)
end
PutWith2Arg(Proc.new { |x| x + 100 })
Output:
102
=> nil
The do |foo| … end and { |foo| … } syntaxes are equivalent. These are 'blocks' in Ruby, and any method can get them. To call them you need to either:
def my_method # No need to declare that the method will get a block
yield(42) if block_given? # Pass 42 to the block, if supplied
end
my_method do |n|
puts "#{n} times 2 equals #{n*2}"
end
#=> "42 times 2 equals 84"
my_method{ |n| puts "#{n} times 2 equals #{n*2}" }
#=> "42 times 2 equals 84"
my_method # does nothing because no block was passed
or, for more sophisticated uses:
def my_method( &blk ) # convert the passed block to a Proc named blk
blk.call( 42 ) if blk
end
# Same results when you call my_method, with or without a block
The latter style is useful when you need to pass the block on to another method. If you have a Proc or Lambda referenced by a variable, you can pass it to a method as the block for that method using the & syntax:
def my_method( &blk ) # convert the passed block to a Proc named blk
[1,2,3].each( &blk ) # invoke 'each' using the same block passed to me
end
my_method{ |x| p x=>x**2 }
#=> {1=>1}
#=> {2=>4}
#=> {3=>9}
For more details, this webpage is fairly instructive.
I am trying to understand Ruby in more depth and was reading:
http://www.zenspider.com/Languages/Ruby/QuickRef.html#25
However, I dont understand what the following means in that definition:
parameters := ( [param]* [, hashlist] [*array] [&aProc] )
I know "param" is any number of parameters specified, and then i get lost what the remainder means?
For example, I have:
def doIt(param1, param2, param3)
end
and in this case [param]* is equal to param1, param2, param3...so where does hashlist come in? and *array and &aProc?
Could someone please clarify this for me
If the last argument of a method is a non-empty hash literal, you can pass it like this
def foo(x, y, the_hash)
p the_hash['key2']
end
foo(0, 0, :key1 => 'val1', 'key2' => 42) # 42
instead of the normal way:
foo(0, 0, { :key1 => 'val1', 'key2' => 42 }) # 42
Usually, the hash is defaulted to {} (def foo(x, y, the_hash = {})) so passing an empty hash fits to this scheme.
Additionally, you can specify one "catch-all" (splat) argument which will become an array of all arguments not already assigned to normal arguments:
def foo(p1, *rest)
p rest
end
foo(0) # "[]"
foo(0, 23, 42) # "[23, 42]"
Or, e.g.
def foo(p1, *rest, p2)
p rest
end
foo(0, 100) # "[]"
foo(0, 100, 23, 42) # "[100, 23]"
You cannot have splat arguments before arguments with default value. Therefore, the hash argument syntax and the splat argument are rarely used in combination.
Finally, in your method definition you can have as last argument an identifier prefixed with & which will point to the block at the method invocation (its Proc object) or be nil if there is none. This is normally not needed if you just want to invoke the block -- you can use yield for that.
def foo(&p)
p.call
end
foo { puts "hello" } # hello
vs.
def foo
yield
end
foo { puts "hello" } # hello
Given that map() is defined by Enumerable, how can Hash#map yield two variables to its block? Does Hash override Enumerable#map()?
Here's a little example, for fun:
ruby-1.9.2-p180 :001 > {"herp" => "derp"}.map{|k,v| k+v}
=> ["herpderp"]
It doesn't override map
Hash.new.method(:map).owner # => Enumerable
It yields two variables which get collected into an array
class Nums
include Enumerable
def each
yield 1
yield 1, 2
yield 3, 4, 5
end
end
Nums.new.to_a # => [1, [1, 2], [3, 4, 5]]
Given that map() is defined by Enumerable, how can Hash#map yield two variables to its block?
It doesn't. It yields a single object to its block, which is a two-element array consisting of the key and the value.
It's just destructuring bind:
def without_destructuring(a, b) end
without_destructuring([1, 2])
# ArgumentError: wrong number of arguments (1 for 2)
def with_destructuring((a, b)) end # Note the extra parentheses
with_destructuring([1, 2])
def with_nested_destructuring((a, (b, c))) p a; p b; p c end
with_nested_destructuring([1, [2, 3]])
# 1
# 2
# 3
# Note the similarity to
a, (b, c) = [1, [2, 3]]
Theoretically, you would have to call map like this:
hsh.map {|(k, v)| ... }
And, in fact, for inject, you actually need to do that:
hsh.inject {|acc, (k, v)| ... }
However, Ruby is more lenient with argument checking for blocks than it is for methods. In particular:
If you yield more than one object, but the block only takes a single argument, all the objects are collected into an array.
If you yield a single object, but the block takes multiple arguments, Ruby performs destructuring bind. (This is the case here.)
If you yield more objects than the block takes arguments, the extra objects get ignored.
If you the block takes more arguments than you are yielding, the extra arguments are bound to nil.
Basically, the same semantics as parallel assignment.
In fact, before Ruby 1.9, block arguments actually did have assignment semantics. This allowed you to do crazy things like this:
class << (a = Object.new); attr_accessor :b end
def wtf; yield 1, 2 end
wtf {|#a, a.b| } # WTF? The block body is empty!
p #a
# 1
p a.b
# 2
This crazy stuff works (in 1.8 and older), because block argument passing is treated the same as assignment. IOW, even though the above block is empty and doesn't do anything, the fact that block arguments are passed as if they had been assigned, means that #a is set and the a.b= setter method is called. Crazy, huh? That's why it was removed in 1.9.
If you want to startle your co-workers, stop defining your setters like this:
attr_writer :foo
and instead define them like this:
define_method(:foo=) {|#foo|}
Just make sure someone else ends up maintaining it :-)
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