I have a class that I've included the Enumerable module as follows:
class Paragraph
include Enumerable
attr_accessor :error_totals
def initialize(text, error_totals)
#original = text
#error_totals = error_totals
end
def each
error_totals.each {|category, total| yield category, total }
end
def totals
each.reduce(0) {|t,(category,total)| to += total }
end
end
I get the following error and I don't understand why:
LocalJumpError:
no block given (yield)
However when I do the following it works:
def total_errors_per(number_of_words:)
result = 0.0
each {|category, total| result += total.to_f/original.word_count*number_of_words}
result
end
Why does reduce cause this problem? I am passing a block after the call to reduce. I really would like to understand how to use the Enumerable module properly via understanding this issue.
In your each implementation you call yield no matter whether the block was given or not, and in each.reduce call each receives no block.
One should return an enumerator if no block was given:
def each
return enum_for(:each) unless block_given?
error_totals.each {|category, total| yield category, total }
end
Related
I am doing an exercise with Ruby blocks and procs and I have been stuck for a few hours now . In the exercise I first had to create a my_each method that would receive a proc and would emulate the behaviour of the each method without using each. It would then return the original array unchanged. We are then asked to create a method that would emulate the map method (without using map) by using the my_each method already created. In the solutions we were given the code below and I have been having some difficulty understanding how the mechanics of the code. After reading different blogs and watching a few tutorials I came up with the below explanation. Could you please take a look and tell me if I am on the right track - it would save me many hours! (below is the solution we were given)
def my_each(&prc)
i = 0
while i < self.count
prc.call(self[i])
i += 1
end
self
end
def my_map(&prc)
result = []
my_each { |el| result << prc.call(el) }
result
end
array_k.my_map
By calling my_map on the array_k, we are implicitly calling my_each on array_k, passing the { |el| result << prc.call(el) } in the my_each method. When going through the loop in the my_each method, we call the proc (prc.call(self[i])) on each one of the elemenets of array_k and in our case, this proc is the block { |el| result << prc.call(el) }. The prc.call in the block iteself refers to the a proc that we have created that is will alter the elements of the array_k and create a new one by putting the altered element in the result array.
Am I on the right track?
Thanks a lot!
I think your explanation is correct and you do have the right idea.
The &proc and proc.call syntax is fine, but you could alternately write it using yield like so:
def my_each
i = 0
while i < self.count
yield self[i]
i += 1
end
self
end
def my_map
result = []
my_each { |el| result << yield(el) }
result
end
Example code:
module Creatures
class << self
def to_h
{
squirtle: {full_name: 'Squirtle T. Turtle'},
pikachu: {full_name: 'Pikachu B. Pikachu'}
}
end
def keys
to_h.keys
end
def collect
to_h.keys.collect
end
def each
to_h.keys.each
end
end
end
module CompanionHelper
def get_companion_creature_experience(companion_data)
Creatures.each do |creature|
return companion_data[creature]["#{creature}_experience".to_sym] if companion_data.has_key?(creature)
end
end
end
include CompanionHelper
companion_data = {squirtle: {squirtle_experience: 8000}}
get_companion_creature_experience(companion_data)
Forgive me if the example is contrived. The original code is from the insurance world but I can't copy and paste it :)
The crux of the problem is I want to use Creatures.each in another module, pass it a block, and have it work just like Creatures.keys.each would work (i.e. w/ the given example companion data I get 8000 for get_companion_creature_experience(companion_data).
Currently I get Enumerator instead.
Problem is that to_h.keys.each returns Enumerator which does not expect any arguments. Pass a block inside each since you want to use it:
def each &block
to_h.keys.each &block
end
Or you can yield it:
def each
to_h.keys.each do |k|
yield k
end
end
I came across this code:
class RandomSequence
def initialize(limit,num)
#limit,#num = limit,num
end
def each
#num.times { yield (rand * #limit).floor }
end
end
i = -1
RandomSequence.new(10,4).each do |num|
i = num if i < num
end
Is it the case that the each method is called only once and will compute four different values, and then for each of those values we execute code block between do and end? Is my understanding of control flow correct?
Your understanding is close. The random number will be generated, then a block yielded, then another generated, etc 4 times. You can verify this easily by adding puts statements into you blocks to see when they are executed.
class RandomSequence
def initialize(limit,num)
#limit,#num = limit,num
end
def each
puts "in each"
#num.times { yield (rand.tap {|x| puts "Generated #{x}" } * #limit).floor }
end
end
i = -1
RandomSequence.new(10,4).each do |num|
puts "in block"
i = num if i < num
end
Outputs
in each
Generated 0.6724385316643955
in block
Generated 0.8906983274750662
in block
Generated 0.49038868732214036
in block
Generated 0.38100454011243456
in block
The each method on the RandomSequence class is called once. In it #num.times which creates an Enumerator. The Enumerator is iterated over and the block with the yield statement is called (with the argument to it ignored).
The yield statement calls the block that's passed to the each method passing the value of (rand * #limit).floor. In your code the block is not bound to a variable, i.e you could get a reference to the block by doing:
def each(&block)
#... do stuff with block, e.g. block.call("some args")
end
which can be useful at times.
A bit off topic, but one thing I found scary with Ruby starting out is that a return statement returns the flow of execution from where it was defined.
def create_proc
puts "Creating proc"
Proc.new do
puts "In proc!"
return "some value" # notice the explicit return
end
end
def do_stuff
my_proc = create_proc
my_proc.call # This will cause a runtime error
end
If the explicit return is removed everything works there is no error... Lesson being that in ruby you should probably avoid using explicit returns.
I have the following Query class that behaves like an Enumerator
class Query
include Enumerable
# ...
def each(&block)
#results.each do |id|
yield #repo.find(id)
end
end
end
With the above code I can build the following loops:
query_all.each do |x|
...
query_all.each_with_index(1) do |x, idx|
Just defining the each function, I got for free each_with_index. However, what I cannot get working is this:
query_all.each.with_index(1) do |x, idx|
puts x
end
Failure/Error: query_all.each.with_index(1) do |tag, idx|
LocalJumpError:
no block given (yield)
As far as I know the LocalJumpError is usually related with a missing block (which I am providing), so I am not sure if I am missing some parameter in my each, or if this time I must define a new function.
That happens because you're defining a block inside the each, which yields inside. Each can be called without a block (which is what you're doing after). You should just return the each result (without invoking the block) if the method is called without a block.
class Query
include Enumerable
# ...
def each(&block)
results = #results.map {|id| #repo.find(id)}
if block
results.each(&block)
else
results.each
end
end
end
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')