Can I evaluate a block inside a Proc?
No you can't, because the Proc you've created is an independent yield
- that is, it's a yield
that has no block in its context. Although you can call procs with specified parameters and thereby pass the parameters into the proc, yield
doesn't work based on specified parameters; it executes the block found within the proc's closure. And the proc's closure is predefined; it is not modified just because you call it later with a block.
So it's equivalent of just typing 'yield' straight into irb
(not within any method definitions) which returns the LocalJumpError: no block given (yield)
error.
evaluate a proc object in different context
You must pass the Proc as an argument so it's executed in that context.
Practice.instance_eval(&p)
Can I pass a block to a Proc?
Procs can't accept blocks as implicit arguments (the format you're trying). A proc can receive other proc objects as arguments, either explicitly, or using & arguments. Example:
a = Proc.new do |&block|
block.call
end
a.call() {puts "hi"}
yield
is a bit of laguage level magic that only works in the context of a method. Evaluate a block in a certain scope and pass an argument
Use instance_exec
instead:
So this will make it go:instance_exec(arg...) {|var...| block } → obj
Executes the given block within the context of the receiver (obj). In order to set the context, the variable
self
is set to obj while the code is executing, giving the code access to obj’s instance variables. Arguments are passed as block parameters.
"Foo".instance_exec(3, &block)
and give you the 'foo 3'
that you desire.The problem with this:
"Foo".instance_eval { block.call(3) }
is that self
will be "Foo"
inside { block.call(3) }
but not inside block
, block
will retain whatever self
was when block
was defined; there's nothing in block.call(3)
that forces a context so self
doesn't change. For example, given this:class C
def m
proc { |n| "#{downcase} #{n}" }
end
end
c = C.new
"Foo".instance_eval { c.m.call(3) }
When the proc is called, self
will be c
because that's what self
was when the proc was defined (i.e. when m
was called). What self
is inside the instance_eval
block doesn't have any effect on self
inside the proc. Return statements inside procs, lambdas, and blocks
As one answer in the linked question shows:
Your first example was successful because you definedThe
return
keyword always returns from the method or lambda in the current context. In blocks, it will return from the method in which the closure was defined. It cannot be made to return from the calling method or lambda.
victor
in the same function you wanted to return from, so a return
was legal in that context. In your second example, victor
was defined in the top-level. The effect of that return
, then, would not be to return from batman_yield
(the calling method), but [if it were valid] to return from the top-level itself (where the Proc
was defined).Clarification: while you can access the return value of a block (i.e. "The value of the last expression evaluated in the block is passed back to the method as the value of the yield" - as per your comment), you can't use the return
keyword, for the reason stated above. Example:
def batman_yield
value = yield
return value
"Iron man will win!"
end
victor = Proc.new { return "Batman will win!" }
victor2 = Proc.new { "Batman will win!" }
#batman_yield(&victor) === This code throws an error.
puts batman_yield(&victor2) # This code works fine.
using proc instead of block inside hash iterator
What your doing here in your attempt is wrapping yield with a Proc, but in reality you can replace yield with the Proc:
def each_word(&proc)
rank=0
keys = self.sort_by{|k,v| v}.reverse.to_h.keys
keys.each do |key, abs, rel|
if proc
proc.call(rank+=1, key,self[key], self.frequency(key))
end
end
end
What &proc
does it take the block that is passed in (whether with do ... end
or { ... }
syntax) and converts it to a proc.To check whether a block was given, you would simply use if proc
(whereas if you were using yield, you'd use block_given?
). You could alternatively use proc&.call
which is the safe navigation operator.
By the way, you should probably change rank +=1
to rank + 1
, there's no no point reassigning the variable's value here since it's not going to change the value in the hash anyway (numbers are immutable).
Is it possible to see the ruby code in a proc?
Take a look at the sourcify gem:
proc { x + y }.to_source
# >> "proc { (x + y) }"
How do I convert a Proc to a block in a Ruby C extension?
From the pickaxe, p. 871 (1.9 edition)
So pass your
VALUE rb_iterate( VALUE (*method)(), VALUE args, VALUE (*block)(), VALUE arg2 )
Invokes method with argument args and block block. A
yield
from that
method will invoke block with the argument given to yield and a second
argument arg2.
Proc
objects as arg2
and define a (*block)()
function that just forwards the passed value to the Proc
's #call
method.Something like
for (i = 0; i < numProcs; i++)
{
rb_iterate( forwarder, receiver, block, procs[i] );
}
/*...*/
VALUE forwarder(VALUE receiver)
{
// the block passed to #instance_eval will be the same block passed to forwarder
return rb_obj_instance_eval(0, NULL, receiver);
}
VALUE block(VALUE proc)
{
return rb_funcall(proc, rb_intern("call"), 0);
}
I haven't tested this code, but it's consistent with the caveats in this article. ruby block and returning something from block
When you pass in the block with &, you're converting it to a proc. The important point is that a proc and a lambda are different (lambda is actually a subclass of proc), specifically in how they deal with return.
So your refactored code is actually the equivalent of:
p = Proc.new { return 10;}
def lab(block)
puts 'before'
puts block.call
puts 'after'
end
lab p
which also generates a LocalJumpError.Here's why: A proc's return returns from its lexical scope, but a lambda returns to its execution scope. So whereas the lambda returns to lab
, the proc passed into it returns to the outer scope in which it was declared. The local jump error means it has nowhere to go, because there's no enclosing function.
The Ruby Programming Language says it best:
You just have to keep track of what you're using where. As others have suggested, all you need to do is drop theProcs have block-like behavior and lambdas have method-like behavior
return
from your block, and things will work as intended.
Related Topics
Sinatra Not Persisting Session with Redirect on Chrome
Running "Bundle Install" Fails and Asks Me to Run "Bundle Install"
Issue Installing Gems on Windows 7 with Proxy
How to Display Image Pointed by Url in Rails
Error While Starting Puma Server with Workers
How to Convert JSON to Xml in Ruby
How to Create Line Breaks in Ruby
Typeerror: Can't Convert Net::Httpok into String
How to Validate Overlapping Times in Rails with Postgresql
Convert Durations in Ruby - Hh:Mm:Ss.Sss to Milliseconds and Vice Versa
How to Show Longer Traces in Rails Testcases
Prawn Doesn't Seem to Push Layout Down When Using Repeat(:All)