ArgumentError calling a Proc - ruby

s = Proc.new {|x|x*2}
puts s.call(5)
-> 10
def foo(&a)
a.call(5)
end
puts "test foo:"
foo(s)
When I try to call the proc above, I get:
foo: wrong number of arguments (1 for 0) (ArgumentError)
My expectation was that I can pass a proc to a method if the method is defined with this type of signature:
def foo(&a)
and then I can execute the proc insiide foo like this:
a.call(5)

If you want to pass an actual proc to foo, just define it with def foo(a). Putting the & in front of a means "this isn't actually an argument. Instead take the block passed to this method, create a proc out of it, and store that proc in the variable a". In other words with your current definition you can call foo like this:
foo do |x|
puts x*2
end
You can also use & when calling a method to turn a proc into a block:
foo(&s)

Related

Don't pass block when calling super

How can I set a block to nil for the super call?
class A
def foo
if block_given?
result = yield
# do stuff with the yield result
end
# some more code
end
end
class B < A
def foo
block_result = yield
# process block results and store it
# ...
super
end
end
B.new.foo { puts "block called" }
# => block called
# => block called
I don't want to yield the block twice. Is it somehow possible that block_given? in class A returns false?
Background is that I don't own the A class and I can't change it's foo method but I want to avoid calling my block twice. I also don't want to pass a dummy / empty block to super, because the behaviour of A's foo method changes when a block is given.
It's actually not that obvious. From the documentation we know that:
calling super passes all arguments
calling super() passes no arguments
What the documentation doesn't say is that this only applies to positional and keyword arguments. super() still passes a given block!
You have to explicitly unset the block by calling:
super(&nil)
How does this work?
You probably know that you can define a method with an explicit block argument:
def foo(&block)
# ...
end
and that you can pass it as a block to another method: (that other method might be super)
def foo(&block)
super(&block)
end
Now, if you pass a block when calling the method, the corresponding block variable will be an instance of Proc:
def foo(&block)
p block_given: block_given?, block: block
end
foo {}
#=> {:block_given=>true, :block=>#<Proc:0x00007ff4990d0030>}
If you call it without passing block, the block variable will just be nil:
foo
#=> {:block_given=>false, :block=>nil}
So if no block is given, block is nil and
super(&block)
becomes:
super(&nil)

Ruby pass arguments to a method

I would like to add a method to a class C
class C
end
I defined a proc:
impl = proc{|x,y| puts "x=#{x} - y=#{y}"}
I added the proc to the class as a method foo:
C.send(:define_method, :foo, lambda do |args = impl.parameters.map { |arg| arg[1] }|
puts "foo is called"
impl.call(args)
end
)
When I call foo as C.new.foo(1,2), I get an error:
ArgumentError: wrong number of arguments (2 for 0..1)
To avoid this, I need to call foo like C.new.foo([1,2]). Someone can tell me how to avoid this problem?
Answering the question stated:
C.send(:define_method, :foo, lambda do |*args|
args = impl.parameters.map(&:last) if args.empty?
puts "foo is called, args are: #{args.inspect}"
impl.call(*args)
end)
C.new.foo(1,2)
#⇒ foo is called, args are: [1, 2]
# x=1 - y=2
Since you want to pass an arbitrary amount of parameters to the method, the lambda should receive splat argument.
Also, defaulting to impl.parameters.map(&:last) makes not much sense, since the defaults will be symbols [:x, :y].

How can I pass a method as an argument in Ruby?

I am passing a method as an argument to a called function:
def my_function(args1)
puts args1
end
def my_calling_method
self.my_function(def do_this
return 2*3
end)
end
When I call my_calling_method which makes a call to my_function, I am getting args1 as nil instead of def do_this return 2*3 end.
Am I doing anything wrong? Can we pass method as an argument in Ruby?
Alright, I tried implemented a Proc for my requirement now but I am having a hard time to pass it to the calling method.
my_Proc = Proc.new do
return 2*3
end
def my_calling_method
self.my_function
end
def my_function my_Proc
my_Proc.call
end
The reference material I used passes a Proc as an argument to the method like I do, but I am getting error, zero arguments passed to my_function as I am not passing any argument through my_calling_method.
Defining a new method will not return a value. (Much like writing down a phone number does not result in a conversation.)
irb:001>def something
irb:002> # code here
irb:003>end
=> nil
When you run that in IRB, you get nil, right? So, if you define that method as part of a method call:
some_method( def something; stuff; end )
You are getting back nil from the method definition and hence nil is what gets passed into some_method.
Without knowing exactly what it is you are trying to accomplish, I will tell you that you can pass methods, or what are called "blocks", into your method call.
def my_function(&block)
puts block.call
end
my_function {2*3}
#=> 6
my_function {t = Time.now; t + 8640}
#=> 2013-08-09 14:03:29 -0500
my_function do
name = "Charlie"
name.downcase.reverse.capitalize
end
#=> Eilrahc
In fact, this is what you are doing (more or less) with the method .each
array.each {|ele| foo}
I recommend reading up on Ruby's block, Procs, and Lambdas for passing methods in as arguments.
Nothing wrong. A method definition returns nil. The value of def do_this; return 2*3 end is nil. That is what you get.

Why can a method definition without a block parameter accept a block?

Why can a method definition without a block parameter accept a block? This is the demo code:
def fun
yield
end
fun {puts 'hello ruby'}
Because that's how ruby works. Any method can be passed a block. It is responsibility of that method to check if block_given? and yield to it if needed.
This is implicit block passing. When you declare a block parameter, then something different happens: the block is converted to a Proc object, so that it can be called like a function and passed around as a parameter. You can't do that with implicit blocks (AFAIK).
def foo &block
block.call 3
bar block
end
# this method expects proc as a regular parameter (not a block), so you can pass
# a block in addition to it (if you so desire)
def bar block
block.call 4
end
foo do |x|
puts "this is #{x}"
end
# >> this is 3
# >> this is 4

Difference between block and &block in Ruby

Why sometimes I should use block and other times &block inside functions that accept blocks?
block is just a local variable, &block is a reference to the block passed to the method.
def foo(block = nil)
p block
end
foo # => nil
foo("test") # => test
foo { puts "this block will not be called" } # => nil
def foo(&block)
p block
end
foo # => nil
foo("test") # => ArgumentError: wrong number of arguments (1 for 0)
foo { puts "This block won't get called, but you'll se it referenced as a proc." }
# => #<Proc:0x0000000100124ea8#untitled:20>
You can also use &block when calling methods to pass a proc as a block to a method, so that you can use procs just as you use blocks.
my_proc = proc {|i| i.upcase }
p ["foo", "bar", "baz"].map(&my_proc)
# => ["FOO", "BAR", "BAZ"]
p ["foo", "bar", "baz"].map(my_proc)
# => ArgumentError: wrong number of arguments (1 for 0)
The variable name block doesn't mean anything special. You can use &strawberries if you like, the ampersand is the key here.
You might find this article helpful.
In an argument list, &whatever takes the block that was passed to the method and wraps it in a Proc object. The Proc is stored in a variable called whatever (where that can be whatever name you typed after the ampersand, of course — usually it's "block"). After a method call, the &whatever syntax turns a Proc into a block. So if you define a method like so:
def thing(&block)
thing2 &block
end
You're defining a method that takes a block and then calls another method with that block.
If you don't set the & before block, Ruby won't recognize it's relationship to the "block" you pass to the function. Here some examples.
def f(x, block); end
f(3) { 2+2 } # gives an error, because "block" is a
# regular second argument (which is missing)
def g(x, &block); end
g(3) { 2+2 } # legal
def h(x); end
h(3) { 2+2 } # legal
For later use in a function:
def x(&block) # x is a 0 param function
y(block) # y is a 1 param function (taking one "Proc")
z(&block) # z is a 0 param function (like x) with the block x received
end
So, if you call z(&block) it's (nearly!!) the same as calling z { yield }: You just pass the block to the next function.

Resources