Ruby: Yield within enumerable - ruby

I'd like to be able to yield within an enumerable block, in order to create some boilerplate benchmarking code.
Basically I'd like to do something this (simplified):
def iterator( enumerable, &block )
iterations = enumerable.size
counter = 0
enumerable.each do |item|
counter +=1
puts "Iterating #{counter}/#{iterations}..."
yield
end
end
Then I'd like to be able to use this method in order to wrap this boilerplate benchmarking code around a block I would be iterating, so that I could call something like:
# assuming foo is an enumerable collection of objects
iterator foo do
item.slow_method
item.mundane_method
item.save
end
... and when this code executed I would get the following log output:
Iterating 1/1234...
Iterating 2/1234...
Iterating 3/1234...
It seems like this kind of thing must be possible, but I haven't been able to figure out the syntax, nor what such a thing is called (in order to look it up).
The problem is I need to wrap boilerplate both OUTSIDE the enumerable object that is going to iterate, and also INSIDE the iteration block. I can pass an enumerable object in just fine, but I can't seem to call methods on the iterated objects from within the block I pass in.
I hope this explanation makes sense, I'm having a hard time describing it. Please leave comments if you need clarification on anything, I'll try to explain better.

Ruby's yield statement can take arguments. You would want to say
yield item
This passes the "current" item to your "outside" block.
Hope I understood the question correctly.
ADDENDUM
And here is the code to show it in action:
class Item
def initialize(id)
#id = id
end
def slow_method()
puts "slow ##id"
end
def mundane_method()
puts "mundane ##id"
end
def save()
puts "save ##id"
end
end
foo = [Item.new(100), Item.new(200), Item.new(300)]
def iterator(enumerable, &block)
iterations = enumerable.size
counter = 0
enumerable.each do |item|
counter +=1
puts "Iterating #{counter}/#{iterations}..."
yield item
end
end
iterator foo do |item|
item.slow_method
item.mundane_method
item.save
end

Related

In this for/each statement, why do I need to specify an each method using &block in the class of the object I am iterating through?

That title is not the best, so I will explain further here.
I have a Card class and a Deck class, and in Main, I am creating a Deck of Cards and then printing out all the Cards in the Deck. I've made a to_string method for Card (below):
def to_string
puts "#{self.rank} of #{self.suit}"
end
and then used that and a for/each statement in Main to print all the Cards in Deck:
for card in deck
puts card.to_string
end
But I received an error saying that there was an "undefined method 'each' for #Deck: (NoMethodError). I did some searching and found that the solution was to add this method to my Deck class:
def each(&block)
#deck.each(&block)
end
I do understand (or, I think I do) how .each works, as I used it in creating all the Card objects for my Deck--it will go through an array and grab each array item in turn. So it made sense that for card in deck is basically deck.each. But I'm not really sure what &block is doing here. I did some research on what blocks are (to my understanding, basically "anonymous code"--so for example, the instructions inside of a array.each statement. A method that isn't a formally written method) but I still don't know what &block itself does.
Every method in Ruby can (but doesn't have to) take an optional block argument, denoted with an ampersand & before the argument name. This argument is given the value of an explicit block when the method is called.
We can see this behavior explicitly by writing a function which simply returns its block.
def foo(&block)
p block
end
Then if we run
> foo() # Note: No block argument
nil
> foo { 1 } # Note: Block provided
#<Proc:0x...>
So if you pass an explicit block to a method, it gets passed as a Proc object to the method. This is how .each works. The line
[1, 2, 3].each { |x| puts x }
calls each on [1, 2, 3], passing a block argument whose call method runs puts x. You can think of it as similar to
[1, 2, 3].each(->(x) { puts x })
Here, we pass a regular argument which happens to be a lambda. This is not equivalent to the above (block arguments are treated as special), but we could theoretically have implemented each either way; the block syntax is just more convenient.
As you've correctly surmised, for loops desugar to something kind of like .each. Roughly speaking, the following two are equivalent.
for i in [1, 2, 3]
foo i
end
 
i = nil
[1, 2, 3].each do |i|
foo i
end
Note that for loops actually go to additional effort to ensure that the variable does escape the loop scope, whereas using .each directly produces a local-only variable that doesn't escape. For this reason, .each is generally considered more idiomatic in Ruby anyway, and most Ruby code shies away from explicit for loops in general. (IMO .each is also prettier and more consistent with all of the other aspects of Ruby syntax)
I'm not sure I succeed to clarify this properly, just a try...
Actually for item in deck is actually syntax sugar for those, that previously learned some procedural language, like Pascal or C, and is equivalent for method call: deck.each { |item| ... }. The part in curly brackets is a block or anynomous function. So, your code:
for card in deck
puts card
Is actually translated to call of each on deck object:
deck.each { |card| puts card }
What actually does this method - you define it for collection of something and it should pass its item one after another to given block. And inside block we can use those item as a variable with name we like (card in example).
You can actually ignore item in block, for example:
deck.each { puts "Hello" }
And if your deck has 52 cards, you'll see 52 times "Hello".
What is most interesting, is that having defined only this each method you actually can have a bunch of others, like count, min, max, map, etc. For this, you only need to include mixin Enumerable in your class.
Like this:
class Deck
include Enumerable
def each(&block)
#deck.each(&block)
And now, if you call deck.count it will actually return 52, or whatever is the number of cards, you've put there.
How it works: it actually executes something like:
c = 0
deck.each { c = c + 1 }
c
Well, hope that helps...
why do I need to specify an each method using &block in the class of the object I am iterating through?
You don't have to use the &block syntax.
each is supposed to yield each element to the given block, e.g.:
class Foo
def each
yield 1
yield 2
yield 3
end
end
foo = Foo.new
for i in foo
puts i
end
# or more idiomatic:
foo.each do |i|
puts i
end
Since you rarely yield hard-coded values, there's usually some kind of loop within each, e.g.:
class Foo
def each
1.upto(3) do |i|
yield i
end
end
end
In your case, the loop is based on #deck.each, so you could write:
def each
#deck.each do |card|
yield card
end
end
In order to make each more useful, it should also return an enumerator if no block is given:
def each
return enum_for(:each) unless block_given?
#deck.each do |card|
yield card
end
end
So what about that &block syntax?
If you read the documentation for Array#each you might notice that it already does all these things – it yields the elements to the block and returns an enumerator if no block is given. So you could avoid writing the above code just by passing the block that was given to your each along to Array#each.
But how can we refer to the given block? That's what the &block syntax is for – it assigns the block to a variable block which can be passed as a block argument to another method using the same &block syntax:
def each(&block)
#deck.each(&block)
end
Some considerations:
you should avoid for and prefer each-style loops
to_string should just return the string, not print it. You can omit self and you should call it to_s:
def to_s
"#{rank} of #{suit}"
end
which allows you to directly puts your card:
deck.each do |card|
puts card
end
it might be cleaner to call the method each_card

Block/Proc Returning Array within an Array Explanation

The prompt:
Extend the Array class to include a method named my_each that takes a block, calls the block on every element of the array, and then returns the original array.
class Array
def my_each(&prc)
if block_given?
proc.call(self)
else
for i in (0..self.length-1)
puts self[i]
end
end
self
end
end
This is what I put together and I don't have a good understanding of how Blocks/Procs work within this context, but somehow I magically wrote the code that passed 3 of the 4 RSPEC tests.
describe "#my_each" do
it "calls the block passed to it" do
expect do |block|
["test array"].my_each(&block)
end.to yield_control.once
end
it "yields each element to the block" do
expect do |block|
["el1", "el2"].my_each(&block)
end.to yield_successive_args("el1", "el2")
end
it "does NOT call the built-in #each method" do
original_array = ["original array"]
expect(original_array).not_to receive(:each)
original_array.my_each {}
end
it "is chainable and returns the original array" do
original_array = ["original array"]
expect(original_array.my_each {}).to eq(original_array)
end
end
All of the above RSPEC tests passes with the exception of the second one, where my code returns [["el1", "el2"]] when ["el1", "el2"] is expected. Can someone please give me an explanation of how or why I am receiving a nested array here?
Can someone also give me an explanation of how the code is running as a block is passing through this method? I'm not sure if my "else" condition is actually even necessary in the context of the RSPEC tests. I'm generally confused by the concept of passing blocks through self-written methods and how they interact with the method itself.
Thanks in advance!
In the first part of your condition, you pass the whole array to the block:
if block_given?
proc.call(self)
else
# ...
E.g. for an array of ["el1", "el2"] you do proc.call(["el1", "el2"]). What you expect in the test are two consecutive calls:
proc.call("el1")
proc.call("el2")
To do that you need to use a loop also in the first part of the condition and pass there an array element, not the whole array:
if block_given?
for i in (0..self.length-1)
proc.call(self[i])
end
else
for i in (0..self.length-1)
puts self[i]
end
end
proc.call(self)
is the culprit. self is the whole array.
Extend the Array class to include a method named my_each
class Array
def my_each
end
end
that takes a block,
#every method in ruby accepts a block, it is just ignored when not used (yielded to). Do nothing.
calls the block on every element of the array,
class Array
def my_each
for element in self
yield element
end
end
end
and then returns the original array.
# "for" loops do this. Do nothing. It should pass.

How can I rewrite this flatten method without using class level variables?

I am working on building the flatten method in Ruby. Here is my code:
require 'byebug'
class Array
##new_array = []
def new_flatten
self.each do |element|
if !element.is_a? Array
##new_array << element
elsif element.is_a? Array
element.new_flatten
end
end
##new_array
end
end
test_array = ([1,2,3,[4,5,6,[7]]])
puts test_array.new_flatten == test_array.flatten
My question is this. Do I need to use the class level variable? I would like to use an instance variable but it doesn't seem to get called when I do:
test_array = []
or
Array.new([])
So I had to create a class-level variable to hold the new flattened array.
The problem with using a class variable is that the same one is visible to all the instances, and new_flatten is an operation for an instance. You should refactor your new_flatten algorithm so it doesn't rely on what is, in effect, a "global" variable for the method calls.
For example, you can use a local variable and Ruby's facility to append arrays with +:
class Array
def new_flatten
a = []
self.each do |element|
if element.is_a? Array
a += element.new_flatten
else
a << element
end
end
a
end
end
Also, as some tweaks of style, when doing an if-else, it's often clearer to state the positive logic first. And, you don't need the elsif in this case since the else is the complement of the if. So rather than:
if not something
do_stuff
elsif opposite_of_not_something
do_other_stuff
end
It's clearer to say:
if something
do_stuff
else
do_other_stuff
end
Your question is really confusing. In your title, you talk about calling initialize, but there is no initialize in your code. In your question, you talk about calling an instance variable, but you can't call variables, only methods.
However, implementing flatten is just a simple fold, I don't see the need for storing any intermediate state:
class Array
def new_flatten
inject([]) {|acc, el| acc + Array(case el when Array then el.new_flatten else el end) }
end
end

how to pass a Ruby iterator as a parameter?

I'd like to write a method that yields values in one place and pass it as a parameter to another method that will invoke it with a block. I'm convinced it can be done but somehow I'm not able to find the right syntax.
Here's some sample (non-working) code to illustrate what I'm trying to achieve:
def yielder
yield 1
yield 2
yield 3
end
def user(block)
block.call { |x| puts x }
end
# later...
user(&yielder)
$ ruby x.rb
x.rb:2:in `yielder': no block given (yield) (LocalJumpError)
from x.rb:12:in `<main>'
FWIW, in my real code, yielder and user are in different classes.
Update
Thanks for your answers. As Andrew Grimm mentioned, I want the iterator method to take parameters. My original example left this detail out. This snippet provides an iterator that counts up to a given number. To make it work, I made the inner block explicit. It does what I want, but it's a bit ugly. If anyone can improve on this I'd be very interested in seeing how.
def make_iter(upto)
def iter(upto, block)
(1 .. upto).each do |v|
block.call(v)
end
end
lambda { |block| iter(upto, block) }
end
def user(obj)
obj.call Proc.new { |x| puts x }
end
# later...
user(make_iter(3))
This doesn't use a lambda or unbound method, but it is the simplest way to go...
def f
yield 1
yield 2
end
def g x
send x do |n|
p n
end
end
g :f
When you write &yielder, you're calling yielder and then trying to apply the & (convert-to-Proc) operator on the result. Of course, calling yielder without a block is a no-go. What you want is to get a reference to the method itself. Just change that line to user(method :yielder) and it will work.
I think this might be along the lines of what you want to do:
def yielder
yield 1
yield 2
yield 3
end
def user(meth)
meth.call { |x| puts x }
end
# later...
user( Object.method(:yielder) )
Some related info here: http://blog.sidu.in/2007/11/ruby-blocks-gotchas.html
As it has been pointed out the baseline problem is that when you try to pass a function as a parameter Ruby executes it – as a side effect of parenthesis being optional.
I liked the simplicity of the symbol method that was mentioned before, but I would be afraid of my future self forgetting that one needs to pass the iterator as a symbol to make that work. Being readability a desired feature, you may then wrap your iterator into an object, which you can pass around without fear of having code unexpectedly executed.
Anonymous object as iterator
That is: using an anonymous object with just one fuction as iterator. Pretty immediate to read and understand. But due to the restrictions in the way Ruby handles scope the iterator cannot easily receive parameters: any parameters received in the function iterator are not automatically available within each.
def iterator
def each
yield("Value 1")
yield("Value 2")
yield("Value 3")
end
end
def iterate(my_iterator)
my_iterator.each do |value|
puts value
end
end
iterate iterator
Proc object as iterator
Using a Proc object as iterator lets you easily use any variables passed to the iterator constructor. The dark side: this starts looking weird. Reading the Proc.new block is not immediate for the untrained eye. Also: not being able to use yield makes it a bit uglier IMHO.
def iterator(prefix:)
Proc.new { |&block|
block.call("#{prefix} Value 1")
block.call("#{prefix} Value 2")
block.call("#{prefix} Value 3")
}
end
def iterate(my_iterator)
my_iterator.call do |value|
puts value
end
end
iterate iterator(prefix: 'The')
Lambda as iterator
Ideal if you want to obfuscate your code so hard that no one else besides you can read it.
def iterator(prefix:)
-> (&block) {
block.call("#{prefix} Value 1")
block.call("#{prefix} Value 2")
block.call("#{prefix} Value 3")
}
end
def iterate(my_iterator)
my_iterator.call do |value|
puts value
end
end
iterate iterator(prefix: 'The')
Class as iterator
And finally the good ol' OOP approach. A bit verbose to initialize for my taste, but with little or none surprise effect.
class Iterator
def initialize(prefix:)
#prefix = prefix
end
def each
yield("#{#prefix} Value 1")
yield("#{#prefix} Value 2")
yield("#{#prefix} Value 3")
end
end
def iterate(my_iterator)
my_iterator.each do |value|
puts value
end
end
iterate Iterator.new(prefix: 'The')

How do I add 'each' method to Ruby object (or should I extend Array)?

I have an object Results that contains an array of result objects along with some cached statistics about the objects in the array. I'd like the Results object to be able to behave like an array. My first cut at this was to add methods like this
def <<(val)
#result_array << val
end
This feels very c-like and I know Ruby has better way.
I'd also like to be able to do this
Results.each do |result|
result.do_stuff
end
but am not sure what the each method is really doing under the hood.
Currently I simply return the underlying array via a method and call each on it which doesn't seem like the most-elegant solution.
Any help would be appreciated.
For the general case of implementing array-like methods, yes, you have to implement them yourself. Vava's answer shows one example of this. In the case you gave, though, what you really want to do is delegate the task of handling each (and maybe some other methods) to the contained array, and that can be automated.
require 'forwardable'
class Results
include Enumerable
extend Forwardable
def_delegators :#result_array, :each, :<<
end
This class will get all of Array's Enumerable behavior as well as the Array << operator and it will all go through the inner array.
Note, that when you switch your code from Array inheritance to this trick, your << methods would start to return not the object intself, like real Array's << did -- this can cost you declaring another variable everytime you use <<.
each just goes through array and call given block with each element, that is simple. Since inside the class you are using array as well, you can just redirect your each method to one from array, that is fast and easy to read/maintain.
class Result
include Enumerable
def initialize
#results_array = []
end
def <<(val)
#results_array << val
end
def each(&block)
#results_array.each(&block)
end
end
r = Result.new
r << 1
r << 2
r.each { |v|
p v
}
#print:
# 1
# 2
Note that I have mixed in Enumerable. That will give you a bunch of array methods like all?, map, etc. for free.
BTW with Ruby you can forget about inheritance. You don't need interface inheritance because duck-typing doesn't really care about actual type, and you don't need code inheritance because mixins are just better for that sort of things.
Your << method is perfectly fine and very Ruby like.
To make a class act like an array, without actually inheriting directly from Array, you can mix-in the Enumerable module and add a few methods.
Here's an example (including Chuck's excellent suggestion to use Forwardable):
# You have to require forwardable to use it
require "forwardable"
class MyArray
include Enumerable
extend Forwardable
def initialize
#values = []
end
# Map some of the common array methods to our internal array
def_delegators :#values, :<<, :[], :[]=, :last
# I want a custom method "add" available for adding values to our internal array
def_delegator :#values, :<<, :add
# You don't need to specify the block variable, yield knows to use a block if passed one
def each
# "each" is the base method called by all the iterators so you only have to define it
#values.each do |value|
# change or manipulate the values in your value array inside this block
yield value
end
end
end
m = MyArray.new
m << "fudge"
m << "icecream"
m.add("cake")
# Notice I didn't create an each_with_index method but since
# I included Enumerable it knows how and uses the proper data.
m.each_with_index{|value, index| puts "m[#{index}] = #{value}"}
puts "What about some nice cabbage?"
m[0] = "cabbage"
puts "m[0] = #{m[0]}"
puts "No! I meant in addition to fudge"
m[0] = "fudge"
m << "cabbage"
puts "m.first = #{m.first}"
puts "m.last = #{m.last}"
Which outputs:
m[0] = fudge
m[1] = icecream
m[2] = cake
What about some nice cabbage?
m[0] = cabbage
No! I meant in addition to fudge
m.first = fudge
m.last = cabbage
This feels very c-like and I know Ruby
has better way.
If you want an object to 'feel' like an array, than overriding << is a good idea and very 'Ruby'-ish.
but am not sure what the each method
is really doing under the hood.
The each method for Array just loops through all the elements (using a for loop, I think). If you want to add your own each method (which is also very 'Ruby'-ish), you could do something like this:
def each
0.upto(#result_array.length - 1) do |x|
yield #result_array[x]
end
end
If you create a class Results that inherit from Array, you will inherit all the functionality.
You can then supplement the methods that need change by redefining them, and you can call super for the old functionality.
For example:
class Results < Array
# Additional functionality
def best
find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
delete(ininteresting_result)
super
end
end
Alternatively, you can use the builtin library forwardable. This is particularly useful if you can't inherit from Array because you need to inherit from another class:
require 'forwardable'
class Results
extend Forwardable
def_delegator :#result_array, :<<, :each, :concat # etc...
def best
#result_array.find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
#result_array.delete(ininteresting_result)
#result_array.compact
self
end
end
In both of these forms, you can use it as you want:
r = Results.new
r << some_result
r.each do |result|
# ...
end
r.compact
puts "Best result: #{r.best}"
Not sure I'm adding anything new, but decided to show a very short code that I wish I could have found in the answers to quickly show available options. Here it is without the enumerator that #shelvacu talks about.
class Test
def initialize
#data = [1,2,3,4,5,6,7,8,9,0,11,12,12,13,14,15,16,172,28,38]
end
# approach 1
def each_y
#data.each{ |x| yield(x) }
end
#approach 2
def each_b(&block)
#data.each(&block)
end
end
Lets check performance:
require 'benchmark'
test = Test.new
n=1000*1000*100
Benchmark.bm do |b|
b.report { 1000000.times{ test.each_y{|x| #foo=x} } }
b.report { 1000000.times{ test.each_b{|x| #foo=x} } }
end
Here's the result:
user system total real
1.660000 0.000000 1.660000 ( 1.669462)
1.830000 0.000000 1.830000 ( 1.831754)
This means yield is marginally faster than &block what we already know btw.
UPDATE: This is IMO the best way to create an each method which also takes care of returning an enumerator
class Test
def each
if block_given?
#data.each{|x| yield(x)}
else
return #data.each
end
end
end
If you really do want to make your own #each method, and assuming you don't want to forward, you should return an Enumerator if no block is given
class MyArrayLikeClass
include Enumerable
def each(&block)
return enum_for(__method__) if block.nil?
#arr.each do |ob|
block.call(ob)
end
end
end
This will return an Enumerable object if no block is given, allowing Enumerable method chaining

Resources