what happens when *args is passed to yield in ruby - ruby-on-rails

what happens when *args passed to yield in ruby, in capture_helper.rb of rails I saw a statement where *args is passed to yield statement, what actually happens when we do so.
buffer = with_output_buffer { value = yield(*args) }
where first parameter is builder object and second parameter is the block passed

With the * operator (splat operator) prefixing a variable (which must be an array or hash), the values of the array are extracted:
ary = [1, 2, 3]
def foo(a, b, c)
a + b + c
end
foo(ary)
# => ArgumentError: wrong number of arguments (given 1, expected 3)
foo(*ary)
# 6
It's just the same with yield, except that the values are passed to the block:
def bar
ary2 = [5, 6]
yield(*ary2)
end
bar do |x, y|
puts x + y
end
# 11

Splats have many uses in ruby. When you use a splat in a method call it turns an array into a list of arguments.
def test(*args)
args
end
Consider these examples:
a = [1,2,3]
test(a)
# => [[1, 2, 3]]
test(1,2,3)
# => [1, 2, 3]
test(*a)
# => [1, 2, 3]
In the first example the array is treated as the first argument so the result is an array in an array. While *[1,2,3] de-structures the array so that we get the desired result.
This makes it extremely useful for calling methods that take a variable number of arguments. yield works just like any other method in this regard, as using a splat will de-structure the array in args and call the passed block with a list of arguments.

Related

Class-level memoization vs Constants. What is the difference? Which one to use?

I am working on a rails app, where I have a class with a large constant. The reviewer asked me use a self method instead, stating that having a large constant will increase the application startup time. But I don't see the difference or the advantage.
Can you please help me understand the difference?
class Test
X = [1, 2, 3]
def self.x
#_x ||= [1, 2, 3]
end
end
Test::X
Test.x
When Ruby encounters a class keyword it evaluates the class body right away (same for module). You might want to try this in IRB:
class Foo
puts 'I got evaluated'
end
# I got evaluated
That's why your constant assignment is evaluated the moment you load the class file.
Methods on the other hand have to be called explicitly:
class Foo
def self.bar
puts 'I got evaluated'
end
end
Foo.bar
# I got evaluated
Conditionally assigning your array to an instance variable in a method defers the creation until you actually need (i.e. call) it.
Your example with debug output added:
class Test
X = (puts 'X assigned' ; [1, 2, 3])
def self.x
#_x ||= (puts '#_x assigned' ; [1, 2, 3])
end
end
# X assigned <-- constant from class body
Test::X
#=> [1, 2, 3]
Test.x
# #_x assigned <-- instance variable from method
#=> [1, 2, 3]
Test.x
#=> [1, 2, 3]

Ruby (Monkey Patching Array)

Back for more help on my coursework at Bloc. Decided to bring you guys in on a problem I'm having with Monkey Patching the Array Class. This assignment had 8 specs to be met and I'm stuck now with one left and I'm not sure what to do.
I'm only going to give you the RSpecs and written requirements for the part that I'm having trouble w/ since everything else seems to be passing. Also I'll include the starter scaffolding that they gave me to begin with so there's no confusion or useless additions to the code.
Here are the written requirements for the Array Class Monkey Patch:
Write a new new_map method that is called on an instance of the Array class. It should use the array it's called on as an implicit (self) argument, but otherwise behave identically. (INCOMPLETE)
Write a new_select! method that behaves like the select, but mutates the array on which it's called. It can use Ruby's built-in collection select method. (COMPLETE)
Here are the RSpecs that need to be met regarding the Array class:
Note: "returns an array with updated values" Is the only Spec not passing.
describe Array do
describe '#new_map' do
it "returns an array with updated values" do
array = [1,2,3,4]
expect( array.new_map(&:to_s) ).to eq( %w{1 2 3 4} )
expect( array.new_map{ |e| e + 2 } ).to eq( [3, 4, 5, 6] )
end
it "does not call #map" do
array = [1,2,3,4]
array.stub(:map) { '' }
expect( array.new_map(&:to_s) ).to eq( %w{1 2 3 4} )
end
it "does not change the original array" do
array = [1,2,3,4]
expect( array.new_map(&:to_s) ).to eq( %w{1 2 3 4} )
expect( array ).to eq([1,2,3,4])
end
end
describe '#new_select!' do
it "selects according to the block instructions" do
expect( [1,2,3,4].new_select!{ |e| e > 2 } ).to eq( [3,4] )
expect( [1,2,3,4].new_select!{ |e| e < 2 } ).to eq( [1] )
end
it "mutates the original collection" do
array = [1,2,3,4]
array.new_select!(&:even?)
expect(array).to eq([2,4])
end
end
end
Here is the scaffolding they started me with:
class Array
def new_map
end
def new_select!(&block)
end
end
Lastly, here is my code:
class Array
def new_map
new_array = []
self.each do |num|
new_array << num.to_s
end
new_array
end
def new_select!(&block)
self.select!(&block)
end
end
Ruby Array Class:
map { |item| block } → new_ary
A block is like a method, and you specify a block after a method call, for example:
[1, 2, 3].map() {|x| x*2} #<---block
^
|
method call(usually written without the trailing parentheses)
The block is implicitly sent to the method, and inside the method you can call the block with yield.
yield -> calls the block specified after a method call. In ruby, yield is equivalent to yield(), which is conceptually equivalent to calling the block like this: block().
yield(x) -> calls the block specified after a method call sending it the argument x, which is conceptually equivalent to calling the block like this: block(x).
So, here is how you can implement new_map():
class Array
def new_map
result = []
each do |item|
result << yield(item)
end
result
end
end
arr = [1, 2, 3].new_map {|x| x*2}
p arr
--output:--
[2, 4, 6]
This comment is a bit advanced, but you don't actually have to write self.each() to call the each() method inside new_map(). All methods are called by some object, i.e. the object to the left of the dot, which is called the receiver. For instance, when you write:
self.each {....}
self is the receiver of the method call each().
If you do not specify a receiver and just write:
each {....}
...then for the receiver ruby uses whatever object is assigned to the self variable at that moment. Inside new_map() above, ruby will assign the Array that calls the new_map() method to self, so each() will step through the items in that Array.
You have to be a little bit careful with the self variable because ruby constantly changes the value of the self variable without telling you. So, you have to know what ruby has assigned to the self variable at any particular point in your code--which comes with experience. Although, if you ever want to know what object ruby has assigned to self at some particular point in your code, you can simply write:
puts self
Experienced rubyists will crow and cluck their tongues if they see you write self.each {...} inside new_map(), but in my opinion code clarity trumps code trickiness, and because it makes more sense to beginners to write self there, go ahead and do it. When you get a little more experience and want to show off, then you can eliminate explicit receivers when they aren't required. It's sort of the same situation with explicit returns:
def some_method
...
return result
end
and implicit returns:
def some_method
...
result
end
Note that you could write new_map() like this:
class Array
def new_map(&my_block) #capture the block in a variable
result = []
each do |item|
result << my_block.call(item) #call the block
end
result
end
end
arr = [1, 2, 3].new_map {|x| x*2}
p arr
--output:--
[2, 4, 6]
Compare that to the example that uses yield(). When you use yield(), it's as if ruby creates a parameter variable named yield for you in order to capture the block. However, with yield you use a different syntax to call the block, namely (), or if their are no arguments for the block, you can eliminate the parentheses--just like you can when you call a method. On the other hand, when you create your own parameter variable to capture a block, e.g. def new_map(&my_block), you have to use a different syntax to call the block:
my_block.call(arg1, ...)
or:
myblock[arg1, ...]
Note that #2 is just like the syntax for calling a method--except that you substitute [] in place of ().
Once again, experienced rubyists will use yield to call the block instead of capturing the block in a parameter variable. However, there are situations where you will need to capture the block in a parameter variable, e.g. if you want to pass the block to yet another method.
Looking at the spec here:
it "returns an array with updated values" do
array = [1,2,3,4]
expect( array.new_map(&:to_s) ).to eq( %w{1 2 3 4} )
expect( array.new_map{ |e| e + 2 } ).to eq( [3, 4, 5, 6] )
end
It looks like they just want you to re-write Array.map so that it will work with any given block. In your implementation you are telling the method to work in a very specific way, namely to call .to_s on all of the array elements. But you don't want it to always stringify the array elements. You want it to do to each element whatever block is provided when the method is called. Try this:
class Array
def new_map
new_array = []
each do |num|
new_array << yield(num)
end
new_array
end
end
Notice in my example that the method definition doesn't specify any particular operation to be performed on each element of self. It simply loops over each element of the array, yields the element (num) to whatever block was passed when .new_map was called, and shovels the result into the new_array variable.
With that implementation of .new_map and given array = [1,2,3,4], you can call either array.new_map(&:to_s) (the block is where the arbitrary operation to be performed on each element of the array is specified) and get ["1","2","3","4"] or you can call array.new_map { |e| e + 2 } and get [3,4,5,6].
I would like to say a few words about new_select!.
select vs select!
You have:
class Array
def new_select!(&block)
self.select!(&block)
end
end
which you could instead write:
class Array
def new_select!
self.select! { |e| yield(e) }
end
end
This uses the method Array#select!. You said Array#select could be used, but made no mention of select!. If you cannot use select!, you must do something like this:
class Array
def new_select!(&block)
replace(select(&block))
end
end
Let's try it:
a = [1,2,3,4,5]
a.new_select! { |n| n.odd? }
#=> [1, 3, 5]
a #=> [1, 3, 5]
Explicit vs implicit receivers
Notice that I have written this without any explicit receivers for the methods Array#replace and Array#select. When there is no explicit receiver, Ruby assumes that it is self, which is the a. Therefore, Ruby evaluates replace(select(&block)) as though it were written with explicit receivers:
self.replace(self.select(&block))
It's up to you to decide if you want to include self.. Some Rubiests do, some don't. You'll notice that self. is not included in Ruby built-in methods implemented in Ruby. One other thing: self. is required in some situations to avoid ambiguity. For example, if taco= is the setter for an instance variable #taco, you must write self.taco = 7 to tell Ruby you are referring to the setter method. If you write taco = 7, Ruby will assume you want to create a local variable taco and set its value to 7.
Two forms of select!
Is your method new_select! a direct replacement for select!? That is, are the two methods functionally equivalent? If you look at the docs for Array#select!, you will see it has two forms, the one you have mimicked, and another that you have not implemented.
If select! is not given a block, it returns an enumerator. Now why would you want to do that? Suppose you wish to write:
a = [1,2,3,4,5]
a.select!.with_index { |n,i| i < 2 }
#=> [1, 2]
Has select! been given a block? No, so it must return an enumerator, which becomes the receiver of the method Enumerator#with_index.
Let's try it:
enum0 = a.select!
#=> #<Enumerator: [1, 2, 3, 4, 5]:select!>
Now:
enum1 = enum0.with_index
#=> #<Enumerator: #<Enumerator: [1, 2, 3, 4, 5]:select!>:with_index>
You can think of enum1 as a "compound enumerator". Look carefully at the description (above) of the object Ruby returns when you define enum1.
We can see the elements of the enumerator by converting it to an array:
enum1.to_a
# => [[1, 0], [2, 1], [3, 2], [4, 3], [5, 4]]
Each of these five elements is passed to the block and assigned to the block variables by Enumerator#each (which calls Array#each).
Enough of that, but the point is that having methods return enumerators when no block is given is what allows us to chain methods.
You do not have a test that ensures new_select! returns an enumerator when no block is given, so maybe that's not expected, but why not give it a go?
class Array
def new_select!(&block)
if block_given?
replace(select(&block))
else
to_enum(:new_select!)
end
end
end
Try it:
a = [1,2,3,4,5]
a.new_select! { |n| n.odd? }
#=> [1, 3, 5]
a #=> [1, 3, 5]
a = [1,2,3,4,5]
enum2 = a.new_select!
#=> #<Enumerator: [1, 2, 3, 4, 5]:new_select!>
enum2.each { |n| n.odd? }
#=> [1, 3, 5]
a #=> [1, 3, 5]
a = [1,2,3,4,5]
enum2 = a.new_select!
#=> #<Enumerator: [1, 2, 3, 4, 5]:new_select!>
enum2.with_index.each { |n,i| i>2 }
#=> [4, 5]
a #=> [4, 5]

Ruby Enumerator-based lazy flatten method

Michael Harrison has a great post on lazy enumerators in Ruby, providing an implementation of lazy_select and lazy_map. I am wondering whether the following implementation of lazy_flatten should have special processing for anything other than Enumerator and Enumerable types.
class Enumerator
def lazy_flatten
Enumerator.new do |yielder|
self.each do |value|
if value.kind_of? Enumerator
value.lazy_flatten.each do |v|
yielder.yield v
end
elsif value.kind_of? Enumerable
value.flatten.each do |v|
yielder.yield v
end
else
yielder.yield value
end
end
end
end
end
This doesn't seem lazy to me, as you are still performing old (non-lazy) flatten beneath.
Enumerator is Enumerable, so I think you don't need to handle it separately.
I would expect lazy_flatten to be method on Enumerable.
Here's how I would implement it:
module Enumerable
def lazy_flatten
Enumerator.new do |yielder|
each do |element|
if element.is_a? Enumerable
element.lazy_flatten.each do |e|
yielder.yield(e)
end
else
yielder.yield(element)
end
end
end
end
end
Note that in Ruby 2.0+, you don't need to do this, you can just use Enumerable#lazy, which returns an Enumerator::Lazy.
For reasons that aren't clear to me, Lazy doesn't have flatten, but it has flat_map, so in principle you could just use flat_map with the identity function.
module Enumerable
def lazy_flatten
self.lazy.flat_map { |x| x }
end
end
Lazy#flat_map mostly takes care of decomposing any decomposable elements, but not quite -- from the docs:
A value x returned by block is decomposed if either of the following conditions is true:
x responds to both each and force, which means that x is a lazy enumerator.
x is an array or responds to to_ary.
Note that to_ary is not a method on Enumerable, presumably to discourage implicit conversions from infinite sequences to arrays. This means, for instance, that if you try to lazy_flatten something that contains a Set or a Range with the above code, it (arguaby, see below) won't work:
a = [[1, 2, 3], Set[4, 5], 6, 7..8]
# => [[1, 2, 3], #<Set: {4, 5}>, 6, 7..8]
f = a.lazy_flatten
# => #<Enumerator::Lazy: #<Enumerator::Lazy: [[1, 2, 3], #<Set: {4, 5}>, 6, 7..8]>:flat_map>
f.to_a
# => [1, 2, 3, #<Set: {4, 5}>, 6, 7..8]
However, this is the same as the behavior of Array#flatten:
a.flatten
# => [1, 2, 3, #<Set: {4, 5}>, 6, 7..8]
(Although Array#flatten will not detect and decompose lazy enumerators, and Lazy#flat_map will.)
Whereas the OP's code or the code in Mladen Jablanović's answer will decompose the Set and the Range:
f = a.lazy_flatten # (M.J.'s code)
# => #<Enumerator: #<Enumerator::Generator:0x00007fd819c166c0>:each>
f.to_a
# => [1, 2, 3, 4, 5, 6, 7, 8]
That code, however, will also iterate infinitely if passed something that includes an infinite sequence:
a = [[1, 2, 3], Set[4, 5], 6, 7..8, 9..Float::INFINITY]
# => [[1, 2, 3], #<Set: {4, 5}>, 6, 7..8, 9..Infinity]
f = a.lazy_flatten # (M.J.'s code)
# => #<Enumerator: #<Enumerator::Generator:0x00007fd819a73d18>:each>
f.to_a
# => spins at 100% CPU for a while and eventually runs out of memory
If you consider that a feature, rather than a bug, one approach would be to modify the flat_map-based implementation to convert any enumerables it finds to lazy ones:
module Enumerable
def lazy_flatten
self.lazy.flat_map do |x|
x.respond_to?(:lazy) ? x.lazy : x
end
end
end
This works even for nested lazy enumerables, as Lazy#lazy is smart enough to return itself.

How do I dynamically call a method that takes an array as parameters?

I've a method like:
def method (a=[],b=[])
...
end
I am calling this method with variables like this:
method(h[0].values_at("value"), h[1].values_at("value"))
where h[0] and h[1] are hashes. It works fine.
I dont know if h[1] is going to be there in the next run, so it's giving me error if hash h[1] is not there.
How can I modify it so it makes the call dynamically depending on whether h[0], h[1] are there or not, and call the method with the correct number of parameters.
Hope I understood your problem right:
method(h[0].values_at("value"),
h[1] ? h[1].values_at("value") : []
)
Your problem: if h[1]does not exist, h[1].values_at will raise an exception. So you have to test first, if the value is available. In the code snipllet above I used a ternary operator.
An extended version would be:
par2 = []
par2 = h[1].values_at("value") if h[1]
method(h[0].values_at("value"),
par2
)
With my solution you don't need the default values in the method definition.
In your comment you extended your question.
With four parameters you could use it like this:
def method(p1,p2,p3,p4)
#...
end
method(
h[0] ? h[0].values_at("value") : [],
h[1] ? h[1].values_at("value") : [],
h[2] ? h[2].values_at("value") : [],
h[3] ? h[3].values_at("value") : [],
)
But I would recommend a more generic version:
def method(*pars)
#pars is an Array with all values (including empty arrays.
#The next check make only sense, if you need exactly 4 values.
raise "Expected 4 values, get #{pars.size}" unless pars.size == 4
end
method( *(h.map{|i|i.values_at("x")})
And another - probably not so good - idea:
Extend nil (the result of h[1] if h has not this element) to return [] for values_at:
class NilClass
def values_at(x)
[]
end
end
The simplest way to do exactly what you ask would be:
if h[1]
method(h[0].values_at("x"), h[1].values_at("x"))
else
method(h[0].values_at("x"))
end
Another idea is to put a default hash in the case where h[1] is nil
method(h[0].values_at("x"), (h[1]||{}).values_at("x") )
If you are sure h never has more than 2 items, you can do:
method *(h.map{|i|i.values_at("x")})
If you use Ruby On Rails you are able to execute try method:
method(h[0].values_at("value"), h[1].try(:values_at, 'value') || [])
Examples for method try:
nil.try('[]', :x) # => nil
{:x => 't'}.try('[]', :x) # => 't'
You can change the function signature to accept variable arguments.
def foo(*args)
options = args.extract_options!
p options
p args
end
Invocation without parameters
foo()
{}
[]
Invocation with 2 parameters
foo(1, 2)
{}
[1, 2]
Invocation with 3 parameters
foo(1, 2, 3)
{}
[1, 2, 3]
Invocation with 3 parameters and an option hash
foo(1, 2, 3, :debug => true)
{:debug=>true}
[1, 2, 3]

What does the * (asterisk) symbol do near a function argument and how to use that in others scenarios?

I am using Ruby on Rails 3 and I would like to know what means the presence of a * operator near a function argument and to understand its usages in others scenarios.
Example scenario (this method was from the Ruby on Rails 3 framework):
def find(*args)
return to_a.find { |*block_args| yield(*block_args) } if block_given?
options = args.extract_options!
if options.present?
apply_finder_options(options).find(*args)
else
case args.first
when :first, :last, :all
send(args.first)
else
find_with_ids(*args)
end
end
end
This is the splat operator, which comes from ruby (and is thus not rails specific). It can be applied in two ways depending on where it is used:
to "pack" a number of arguments into an array
to split up an array into an argument list
In your function, you see the splat operator used in the function definition. The result is that the function accepts any number of arguments. The complete argument list will be put into args as an array.
def foo(*args)
args.each_with_index{ |arg, i| puts "#{i+1}. #{arg}" }
end
foo("a", "b", "c")
# 1. a <== this is the output
# 2. b
# 3. c
The second variant would be when you consider the following method:
def bar(a, b, c)
a + b + c
end
It requires exactly three arguments. You can now call this method like follows
my_array = [1, 2, 3]
bar(*my_array)
# returns 6
The splat applied in this case to the array will split it and pass each element of the array as an individual parameter to the method. You could do the same even by calling foo:
foo(*my_array)
# 1. 1 <== this is the output
# 2. 2
# 3. 3
As you can see in your example method, these rules do apply to block parameters in the same way.
This is a splat argument, which basically means that any 'extra' arguments passed to the method will all be assigned to *args.

Resources