Ruby: Overloading Yield Function - ruby

I noticed while learning Ruby that both of these uses of the each method work and produce the same output, and I was wondering how Ruby makes this happen (and how I can make it happen for my own functions):
my_array = [["hello","goodbye"],["picture","perfect"]]
my_array.each do |array|
puts array[0] + " " + array[1]
end
my_array.each do |first, second|
puts first + " " + second
end
My understanding is that when writing the definition of a method that accepts a code block, the yield method is utilized to pass arguments to the code block and call the block. But how can you utilize the yield method such that it passes different arguments depending on the provided code block? In the example case, it appears that the yield method passes the individual array elements when two parameters (i.e., first, second) are used within the block, and it passes the arrays themselves when one parameter is used within the block (i.e., array).

Neither each not yield are doing anything special here, that's just how block arguments work. Consider this simple example:
def f(x) yield x end
and now we can see what happens:
>> f([1,2]) { |a| puts a.inspect }
[1, 2]
>> f([1,2]) { |a, b| puts "#{a} - #{b}" }
1 - 2
>> f([1,2]) { |a, b, c| puts "#{a} - #{b} - #{c}" }
1 - 2 -
You'll see similar destructing in assignments:
a, b = [1, 2]
You can also do it explicitly with a splat:
a, b = *[1, 2]
or like this:
def g(x) yield *x end
g([1, 2]) { |a, b| puts "#{a} - #{b}" }
Presumably the block knows what sorts of things it will be given so the block is well positioned to unpack the arguments. Note that the g function has to know that its argument is splatable (i.e. an array) but f doesn't. f nicely puts the "what sort of thing is x" logic together in the call to f, g buries half of the logic inside itself. One place where the difference becomes apparent is when you use Enumerable methods on a Hash:
{ :where => :is, :pancakes => :house? }.map { |k, v| ... }
Enumerable#map doesn't need to know that a Hash works in key/value two element arrays, it just passes things around and leaves it up everyone else to worry about the details.

Related

How to create a "clone"-able enumerator for external iteration?

I want to create an enumerator for external iteration via next that is clone-able, so that the clone retains the current enumeration state.
As an example, let's say I have a method that returns an enumerator which yields square numbers:
def square_numbers
return enum_for(__method__) unless block_given?
n = d = 1
loop do
yield n
d += 2
n += d
end
end
square_numbers.take(10)
#=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
And I want to enumerate the first 5 square numbers, and for each value, print the subsequent 3 square numbers. Something that's trivial with each_cons:
square_numbers.take(8).each_cons(4) do |a, *rest|
printf("%2d: %2d %2d %2d\n", a, *rest)
end
Output:
1: 4 9 16
4: 9 16 25
9: 16 25 36
16: 25 36 49
25: 36 49 64
But unlike the above, I want to use external iteration using two nested loops along with next and clone:
outer_enum = square_numbers
5.times do
i = outer_enum.next
printf('%2d:', i)
inner_enum = outer_enum.clone
3.times do
j = inner_enum.next
printf(' %2d', j)
end
print("\n")
end
Unfortunately, the above attempt to clone raises a:
`initialize_copy': can't copy execution context (TypeError)
I understand that Ruby doesn't provide this out-of-the-box. But how can I implement it myself? How can I create an Enumerator that supports clone?
I assume that it's a matter of implementing initialize_copy and copying the two variable values for n and d, but I don't know how or where to do it.
Ruby fibers cannot be copied, and the C implementation of Enumerator stores a pointer to a fiber which does not appear to be exposed to Ruby code in any way.
https://github.com/ruby/ruby/blob/752041ca11c7e08dd14b8efe063df06114a9660f/enumerator.c#L505
if (ptr0->fib) {
/* Fibers cannot be copied */
rb_raise(rb_eTypeError, "can't copy execution context");
}
Looking through the C source, it's apparent that Enumerators and Fibers are connected in a pretty profound way. So I doubt that there is any way to change the behavior of initialize_copy to permit clone.
Perhaps you could just write a class of your own that does what you ask:
class NumberSquarer
def initialize
#n = #d = 1
end
def next
ret = #n
#d += 2
#n += #d
ret
end
end
ns1 = NumberSquarer.new
Array.new(5) { ns1.next }
# => [1, 4, 9, 16, 25]
ns2 = ns1.clone
Array.new(5) { ns2.next }
# => [36, 49, 64, 81, 100]
Disclaimer: I'm answering my own question
One way to achieve this is by sub-classing Enumerator. In particular, the now-deprecated variant that takes an object and a method:
class ObjectEnumerator < Enumerator
attr_reader :object, :method
def initialize(object, method = :each)
#object = object
#method = method
super
end
def initialize_copy(orig)
initialize(orig.object.clone, orig.method)
end
end
That orig.object.clone above is where the magic happens: it clones the object we are traversing.
In addition, we need such clone-able object. A simple way is to have a class which holds the state as instance variables: (shamelessly copied from Kache's answer)
class NumberSquarer
def initialize
#d = -1
#n = 0
end
def each
return ObjectEnumerator.new(self, __method__) unless block_given?
loop do
#d += 2
#n += #d # had to be reordered b/c
yield #n # <- yield has to come last
end
end
end
This gives us a basic, clone-able enumerator:
e = NumberSquarer.new.each
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fde60915e10 #d=-1, #n=0>:each>
e.next #=> 1
e.next #=> 4
other = enum.clone
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fcf23842520 #d=3, #n=4>:each>
enum.next #=> 9
enum.next #=> 16
other.next #=> 9
I'm providing a different solution that is not a straight answer to the question:
How can I create an Enumerator that supports clone?
But if I'm not wrong the only purpose of cloning the not clonable Ruby's Enumerator is to get a reference to the next object in the enumerator.
In this case, we need both values stored in odd_sum and square in the example below.
We can store those values in an Array and return the array instead of a single value, then we can use Enumerator.peek in order to have the array that is used to initialize a new Enumerator.
def square_numbers(starters = {})
return enum_for(__method__, starters) unless block_given?
last_odd = starters.fetch(:square_odd, [1,1])[1]
square = starters.fetch(:square_odd, [1,1])[0]
loop do
yield [square, last_odd]
last_odd += 2
square += last_odd
end
end
outer_enum = square_numbers
5.times do
i = outer_enum.next[0]
printf('%2d:', i)
inner_enum = square_numbers(square_odd: outer_enum.peek)
3.times do
j = inner_enum.next[0]
printf(' %2d', j)
end
print("\n")
end

Method that returns result and modulo

I am trying to write a method that asks for two 2 integers, and divides the first by the second and returns the result including the remainder.
def remainder(a,b)
return a/b
return a%b
end
puts remainder(100,6)
This puts out
16
If I use this code
def remainder(a,b)
return a%b
end
puts remainder(100,6)
This puts out
4
I don't understand how to make both the modulus value and the remainder show in puts statement.
Update
Based on Simple Lime's advice I used the following code...
def remainder(a,b)
return a.divmod(b)
end
puts remainder(100,6)
Which puts
16
4
And is functioning as I had hoped.
You can return an array from the method when you need to return multiple values:
def remainder(a, b)
[a / b, a % b]
end
puts remainder(100, 6).inspect # => [16, 4]
and then you can assign each value to a different variable, if you need:
div, mod = remainder(100, 6)
puts div # => 16
puts mod # => 4
As a side note, if you are just needing both the quotient and modulus of 2 numbers, there's already a built-in function, divmod that does this, using the technique above:
100.divmod(6) # => [16, 4]

Ruby lazy enumerator returning different object types depending on use

I was attempting to solve Project Euler #58 in a functional manner with ruby.
Briefly, I created an enumerator to return the corner number of each ring. I was then chaining functional operators on the enumerator. When I get my result, I find that it has a different class depending on how I use it.
spiral = Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113]
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
The class of levels is a lazy enumerator. I would expect it to contain the number of primes in each ring [3, 2, 3, etc.] - see the project euler reference.
If I just print from the enumerator, I get what I expect:
levels.each do |level|
puts "#{level}"
end
Returns:
3
2
3
1
But if I loop .with_index I get an array result back where the expected value is the first member and the second is my .with_object parameter
levels.each.with_index do |level, ix|
puts "#{ix}: #{level}"
end
Returns:
0: [3, {:total=>5.0, :primes=>3}]
1: [2, {:total=>9.0, :primes=>5}]
2: [3, {:total=>13.0, :primes=>8}]
3: [1, {:total=>17.0, :primes=>9}]
Why does the lazy enumerator work this way and how could I predict for it in the future?
Update
I asked around on the IRC ruby channel and no one there had any idea about it. They said they had discussed it a day or two ago and hadn't come to any conclusions.
In general, it seems one must just deal with it and move on.
What's happening here is you're conveniently ignoring the structure that's returned and plucking out the first item to display. In this case the first item is the counts structure you produce.
Have a look at this:
levels.each do |*level|
puts level.inspect
end
That shows you what's actually in the levels results. When Ruby calls a lambda it will discard any additional data that doesn't fit with the number of arguments the block accepts.
If you don't need that metadata, strip it out:
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
.map { |r,_| r }
That removes the extraneous element in the results.
Here's a way of cleaning up your Enumerator a bit:
class Spiral
include Enumerable
def each
Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
end
end
Then you can create one with:
Spiral.new.each ...

Enumerator new and yield

At ruby docs I found this piece of code:
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a
a, b = b, a + b
end
end
Thing is i cant understand what it does. In general it calculates fibonacci's numbers, but I have hard time understanding the details. What is y (Enumerator::Yielder)? The docs says nothing about it. What does << method do? (I know its yield alias). Why does infinite loop happen when y << a
is removed?Thanks!
Consider the following code:
fib = Enumerator.new do |y|
puts "Enter enumerator"
a = b = 1
loop do
puts "Inside loop"
y << a
puts "y: #{y.inspect}, a: #{a}, b: #{b}"
a, b = b, a + b
end
end
puts fib.take(5)
It prints:
# Enter enumerator
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 1, b: 1
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 1, b: 2
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 2, b: 3
# Inside loop
# y: #<Enumerator::Yielder:0x000000059a27e8>, a: 3, b: 5
# Inside loop
# 1
# 1
# 2
# 3
# 5
Apparently, this output actually gives hints on all the question you’ve stated. Note, that we entered a yielder only once. Let’s dig into:
Why loop is infinite?
Because Fibonacci’s number sequence is infinite. This enumerator is intended to be used with Enumerable#take (see an example above.)
What is Enumerator::Yielder?
It is an abstraction. It’s method yield actually calls back the block of callee, passing a parameter as block parameters.
What does << method does?
Yields once. In other words, it calls back the caller code, passing it’s parameter to the caller’s block. In this particular example, it will call back each block, passing a from Yielder instance as block parameter (e as I named it there.)
Why does infinite loop happen when y << a is removed?
Because there are no yields happened. In my example, the callee will stop after yielding five (5 as parameter of take) times.

Is it necessary to refer to self in: yield(self[i])

In this example from a blog post,
class Array
def each
i = 0
while(i < self.length) do
yield(self[i])
i += 1
end
end
end
my_array = ["a", "b", "c"]
my_array.each {|letter| puts letter }
# => "a"
# => "b"
# => "c"
Is it necessary to use self in the statement:
yield(self[i])
Or would it be ok to simply say:
yield i
Those are two entirely different things. If you do yield i you will actually yield the number i, which will cause the output to be 1 2 3. The point of the code however is to yield the elements of the array, so you yield self[i], which means "the ith element of the array self", or more technically "call the method [] on self with the argument i and yield the result".
yield(i) would yield a block for index, while yield(self[i]) would yield a block for ith element

Resources