ruby - Pass a function that expects a function param to a function - ruby

def do_whatever # func A receives func B
params = { test: 1 }
proc = Proc.new{ puts "an important exec #{params[:test]}"; return "important response" } # func C
yield(proc)
end
do_whatever do
begin # func B
resp = yield # executes func C
puts resp
rescue => e
puts e
end
end
Hi, I want a function (e.g. func A) to be passed a block of function (e.g. func B) and executes it. That block of function (e.g. function B) also receives a block of function (e.g. func C) that is initialized in that function. On above code, I expect to see the output:
an important exec 1
important response
but instead I got an error: no block given (yield)

This should do the trick:
def do_whatever # func A receives func B
params = { test: 1 }
proc = Proc.new{ puts "an important exec #{params[:test]}"; next "important response" } # func C
yield(proc)
end
do_whatever do |func_c|
begin # func B
resp = func_c.call
puts resp
rescue => e
puts e
end
end
When you call yield inside do_whatever that is passed as the argument to the block of do_whatever, and returns the proc as an argument to block. Since return does not work in Procs, you will need to use next instead.

Related

Is it possible to get the value that will be returned from a function if it's not stored in a variable?

I'm debugging a method that looks like this
def method
if something
"a"
elsif somethingelse
"b"
#elseif ...
else
"z"
end
# the current return value is ???
end
The way ruby works gives the impression that every expression returns a value and it is saved somewhere. And the last one is going to be returned at the end of the method (if no return is found).
In the case above, the if condition returns a string (which is stored somewhere?) and at the end of the method, since nothing else changed, that string is returned.
Is it possible to access that value? (a little like _ in the console, that returns the result from the last command)
[based on Aleksei Matiushkin's comment]
In Ruby, control expressions have a return value.
Your method merely returns the result of the if expression. And you can inspect it via tap:
def foo(b)
if b
123
else
456
end.tap { |result| p result: result }
end
foo(true)
# prints {:result=>123}
#=> 123
foo(false)
# prints {:result=>456}
#=> 456
If you have more complicated situation without a single last expression, e.g.:
class C
def foo(b)
return 123 if b
456
end
end
You could use refinements to replace the method:
module M
refine C do
def foo(*)
super.tap { |result| p result: result }
end
end
end
Usage:
using M
c = C.new
c.foo(true)
# prints {:result=>123}
#=> 123
c.foo(false)
# prints {:result=>456}
#=> 456
Yet another approach is to use TracePoint to hook into the execution:
def foo(b) # 1
return 123 if b # 2
456 # 3
end # 4
TracePoint.trace(:return) do |tp|
puts "method `#{tp.method_id}' returned #{tp.return_value.inspect} on line ##{tp.lineno}"
end
foo(true)
foo(false)
Output:
method `foo' returned 123 on line #2
method `foo' returned 456 on line #4
Note that this prints the return value of all method calls. You might want to limit it to tp.method_id == :foo

Conditionally assign return value of functions in one line

Imagine I have the following two functions:
def myfunc
puts "hello from myfunc"
return "returned myfunc"
end
def myfunc2
puts "hello from myfunc2"
return "returned myfunc2"
end
And I want to conditionally assign the return value of these functions to a variable, while at the same time ensuring that both functions are called like so:
x = nil
temp = myfunc
x = temp unless x
temp = myfunc2
x = temp unless x
How can I reduce each two line assignment statement segment to one line?
Note, the following won't work because the second function won't get called:
x = nil
x ||= myfunc
x ||= myfunc2
x = [myfunc1, myfunc2].reduce { |f1, f2| f1 || f2 }
Unfortunately, the short notation would not work because of the necessity to imply short-circuit on or:
x = [myfunc1, myfunc2].reduce(:||) # does not work
Not sure why you want to do this but this is valid ruby code and both methods will get called
x ||= a = myfunc; b = myfunc2; a || b
Both methods are called but on first run of this line, x will always be assigned to return of myfunc so I don't understand the purpose of this code.
Or maybe you want a random assignment of a or b ?
x ||= a = myfunc; b = myfunc2; [a,b].sample
You could use an Hash:
h = {f1: myfunc, f2: myfunc2}
x = nil
x ||= h[:f1]
x ||= h[:f2]

How to count how many times an iterator will call yield?

I have a method, foo, that yields objects. I want to count the number of objects it yields.
I have
def total_foo
count = 0
foo { |f| count += 1}
count
end
but there's probably a better way. Any ideas for this new Rubyist?
Here's the definition for foo (it's a helper method in Rails):
def foo(resource=#resource)
resource.thingies.each do |thingy|
bar(thingy) { |b| yield b } # bar also yields objects
end
end
Any method that calls yield can be used to build an Enumerator object, on which you can call count, by means of the Object#to_enum method. Remember that when you call count the iterator is actually executed so it should be free of side effects! Following a runnable example that mimics your scenario:
#resources = [[1,2], [3,4]]
def foo(resources = #resources)
resources.each do |thingy|
thingy.each { |b| yield b }
end
end
foo { |i| puts i }
# Output:
# 1
# 2
# 3
# 4
to_enum(:foo).count
# => 4
You can pass an argument to foo:
to_enum(:foo, [[5,6]]).count
# => 2
Alternatively you can define foo to return an Enumerator when it's called without a block, this is the way stdlib's iterators work:
def foo(resources = #resources)
return to_enum(__method__, resources) unless block_given?
resources.each do |thingy|
thingy.each { |b| yield b }
end
end
foo.count
# => 4
foo([[1,2]]).count
# => 2
foo([[1,2]]) { |i| puts i }
# Output:
# 1
# 2
You can pass a block to to_enum that is called when you call size on the Enumerator to return a value:
def foo(resources = #resources)
unless block_given?
return to_enum(__method__, resources) do
resources.map(&:size).reduce(:+) # thanks to #Ajedi32
end
end
resources.each do |thingy|
thingy.each { |b| yield b }
end
end
foo.size
# => 4
foo([]).size
# => 0
In this case using size is sligthly faster than count, your mileage may vary.
Assuming you otherwise only care about the side-effect of foo, you could have foo itself count the iterations:
def foo(resource=#resource)
count = 0
resource.thingies.each do |thingy|
bar(thingy) do |b|
count += 1
yield b
end # bar also yields objects
end
count
end
And then:
count = foo { |f| whatever... }
You can also ignore the return value if you choose, so just:
foo { |f| whatever... }
In cases you don't care what the count is.
There may be better ways to handle all of this depending upon the bigger context.

Ruby yield called from within Integer#times not returning the evaluated block

Got a very simple question why when I define a block as so:
def test
yield
end
a=test{7} => a=7
yet when I define a block like this
def test(n)
n.times{ yield }
end
a=test(4){7} => 4
why does the return value become n not yield?
It is returning the value from Integer#times (which happens to be same number on which you called the method - as can be seen on the Rubinius source or on RubySpec) instead of the block return value.
Since Integer#times calls the block multiple times, you have basically two alternatives to that:
Combine the results of all the calls on an array and return that.
def test(n)
result = []
n.times { |current| result << yield(current) }
result
end
# Or, leveranging Enumerator#map:
def test(n)
n.times.map { |current| yield(current) }
end
# Or, still shorter, by forwarding the block:
def test(n, &block)
n.times.map(&block)
end
test(4) { |n| n * 2 } # => [0, 2, 4, 6]
Store the last value returned from the block on a variable, and return it:
def test(n)
result = nil
n.times { |current| result = yield(current) }
result
end
test(4) { |n| n * 2 } # => 6
If you look at the source, times method will return the number of times it ran, not the result of the block it runs.

Ruby function as value of hash

I was wondering if or how it is possible to map a function to a value of a hash.
For example:
----Start Class------------
def foo(var)
return var + 2
end
hash_var = { func => foo() }
----End Class--------------
so that I could later call
Class::hash_var["func"][10]
or
Class::hash_var["func"](10)
and that would return 12?
You could use method method.
def foo(var)
return var + 2
end
hash_var = { :func => method(:foo) }
hash_var[:func].call(10)
Functions/methods are one of the few things in Ruby that are not objects, so you can't use them as keys or values in hashes. The closest thing to a function that is an object would be a proc. So you are best off using these...
The other answers pretty much listed all possible ways of how to put a proc into a hash as value, but I'll summarize it nonetheless ;)
hash = {}
hash['variant1'] = Proc.new {|var| var + 2}
hash['variant2'] = proc {|var| var + 2}
hash['variant3'] = lambda {|var| var + 2}
def func(var)
var + 2
end
hash['variant4'] = method(:func) # the *method* method returns a proc
# describing the method's body
there are also different ways to evaluate procs:
hash['variant1'].call(2) # => 4
hash['variant1'][2] # => 4
hash['variant1'].(2) # => 4
You can set the value to a Proc and call it.
hash_var = {
'func' => proc {|x| x+2}
}
hash_var['func'].call(10) #=> 12
Try using lambdas
hash_var = { :func => lambda { |var| var + 2 }}
hash_var['func'].call(5) #=> 7
Another option would be:
def foo(name)
puts "Hi #{name}"
end
def bar(numb)
puts numb + 1
end
hash_var = { func: :foo, func2: :bar }
send hash_var[:func], "Grid"
# => Hi Grid
send hash_bar[:func2], 3
# => 4
Here is some information about the #send method What does send() do in Ruby?

Resources