Example:
a = Proc.new do
b = 'hey'
end
def a_method
yield
end
a_method(&a) #=> 'hey'
I understand that yield (or block.call) can be used as a simple co-routine, but I was wondering, are there any more (practical) uses for it beyond simply getting the return value from it? Can we get some of the local variables from the proc into the main method etc.?
If you're not picky about the return value of the block, you can do it using binding.
a = Proc.new do
b = 'hey'
binding
end
def a_method
new_binding = yield
p new_binding.eval("b")
end
a_method(&a)
# => "hey"
If you're using a version of Ruby >= 2.1.0, you can avoid the ickiness of eval by using local_variable_get instead:
p new_binding.local_variable_get(:b)
There are a lot of uses for Procs. For example, you can use it to give instructions to an object. I made a bubble sort using a proc that tells it how to sort. Here it is monkey patched in the array class:
class Array
def bubble_sort(&prc)
self.dup.bubble_sort!(&prc)
end
def bubble_sort!(&prc)
return self if count <= 1
prc = Proc.new { |x, y| x <=> y } unless prc.class == Proc
(0...self.count).each do |x|
((x + 1)...self.count).each do |y|
self[x], self[y] = self[y], self[x] if prc.call(self[x], self[y]) == 1
end
end
self
end
end
Related
I have class (highly simplified for the sake of this discussion)
class MyClass
def initialize
#myrecs = %w(a b c d)
end
def each_rec
#myrecs.each {|r| yield(r)}
end
end
and use it as
x = MyClass.new
x.each_rec { |r| .... }
Since my method each_rec basically does just a myrecs.each, I wanted to define somehow that each_rec just forwards to each. I can achieve the desired effect by explicitly passing the block, i.e.
def each_rec(&block)
#myrecs.each(&block)
end
but I wonder wether it is also possible to achieve my goal without having the block as explicit parameter. I tried the following approaches without success:
(1) Exploit the fact that each returns an Enumerator when called without a block:
def each_rec
#myrecs.each
end
(2) Create an Enumerator:
def each_rec
#myrecs.enum_for(:each)
end
In both cases, I did not get an error, but the block passed to each_rec was simply not entered.
In Ruby, if you call Proc.new without a block inside a method that received a block, it'll convert that method's passed block to a proc. So, you can do:
class MyClass
def initialize
#myrecs = %w(a b c d)
end
def each_rec
#myrecs.each(&Proc.new)
end
end
x = MyClass.new
x.each_rec { |r| puts r }
and now, you no longer need an explicit parameter to your each_rec method. Now, however, you must supply a block to the method, or get an ArgumentError ("tried to create Proc object without a block"). We can fix that with a guard clause:
def each_rec
return #myrecs.each unless block_given?
#myrecs.each(&Proc.new)
end
and can now use it just like each:
x = MyClass.new
x.each_rec { |r| p r }
x.each_rec.with_index { |r, i| p [r, i] }
how do I write a bubble sort that will accept a proc?
it seems like we can't .call when writing a method for a class?
help!
class Array
def bubble_sort!
flag = true
while flag
flag = false
self.each_with_index do |x, y|
case x <=> self[y + 1]
when 1
self[y], self[y + 1] = self[y + 1], self[y]
flag = true
end
end
end
self
end
def bubble_sort!(&prc)
# With a proc
end
end
The &prc in a method signature:
def m(&prc)
is really just a way to convert the block to a Proc and give it a name; this is why you usually see it called &blk where "blk" is short for "block". You usually do this when you want to pass the block to another method:
# This is a highly contrived example of course.
def m1
# Do something with the "anonymous" block
yield
end
def m2(&blk)
m1(&blk)
end
m2 { "This is the block" }
So if your bubble_sort! method wants to take a block then you would name it all, you'd just yield with the appropriate arguments inside the method:
def bubble_sort!
self.each_with_index do |x, y|
# ...
something = yield x, y
# ...
end
end
If your bubble_sort! needed to pass the block to another method then you'd say def bubble_sort!(&blk) and some_other_method(&blk):
def bubble_sort!(&blk)
self.each_with_index do |x, y|
# ...
something = some_other_method(&blk)
# ...
end
end
and if you also needed to execute the block as well as passing it to another method, you'd treat it like any other Proc and say blk.call.
I am having problems refactoring out some duplicated code from two methods sharing a for loop. The two methods with the duplicated code are gcdOfFiveUpToFive and remainderStepsUpToFive. The two loops share in common setting instance variable #m to 5 and the both use a for x in 1..5 loop and then set #n to x as well as both of them need to call euclidGCD although one calls euclidGCD for its return value and the other to add +=1 to the #count variable. I do want want to return 2 values from one method. I guess I could make a 4th instance variable called #countArray and get an array of the remainder step count.
require 'minitest/autorun'
class GCDTest < Minitest::Test
def test_euclid_gcd
gcdTestObject=GCD.new(20,5)
assert gcdTestObject.euclidGcd==5
assert gcdTestObject.gcdRemainderSteps==1
end
def test_euclid_two
gcdTestObject=GCD.new(13,8)
assert gcdTestObject.euclidGcd==1
assert gcdTestObject.gcdRemainderSteps==5
end
def test_euclid_loop
gcdTestObject=GCD.new(0,0)
assert gcdTestObject.gcdOfFiveUpToFive==[1,1,1,1,5]
end
def test_count_of_loop
gcdTestObject=GCD.new(0,0)
assert gcdTestObject.remainderStepsUpToFive==[1,2,3,2,1]
end
end
class GCD
attr_accessor :m,:n
attr_reader :count
def initialize(m,n)
#m=m
#n=n
#count=0
end
def euclidGcd
#count=1
m=#m
n=#n
r= m % n
until r==0
m=n
n=r
r= m % n
#count+=1
end
return n
end
def gcdRemainderSteps
return #count
end
def gcdOfFiveUpToFive
#m=5
gcdArrayUpToFive=[]
for x in 1..5
#n=x
gcdArrayUpToFive << euclidGcd
end
return gcdArrayUpToFive
end
def remainderStepsUpToFive
#m=5
gcdStepArrayUpToFive=[]
for x in 1..5
#n=x
euclidGcd
gcdStepArrayUpToFive << gcdRemainderSteps
end
return gcdStepArrayUpToFive
end
def fiveLoopExtraction
end
Code that repeats itself is this:
array=[]
for x in 1..5
# result = do something with x
array << result
end
return array
That is exactly what map function does.
What does the "map" method do in Ruby?
Ruby methods names should be snake_case. Lets refactor this to use proper naming convention and map function.
def gcd_of_five_up_to_five
#m=5
(1..5).map do |x|
#n = x
# in ruby you don't have to write return
# value of last expression is returned automatically
euclid_gcd
end
end
def remainder_steps_up_to_five
#m=5
(1..5).map do |x|
#n = x
euclid_gcd
gcd_remainder_steps
end
end
I'd call it with params instead of using #m and #n. That would simplify the code. If you change euclid_gcd to this: def euclid_gcd(m:, n:) you'd get this:
def gcd_of_5_up_to_5
(1..5).map { |x| euclid_gcd(m: 5, n: x) }
end
def remainder_steps_up_to_5
(1..5).map do |x|
euclid_gcd(m: 5, n: x)
gcd_remainder_steps
end
end
Seems like this needs little or no further refactoring.
So, I'd like to be able to make a call
x = MyClass.new('good morning', 'good afternoon', 'good evening', 'good night',
['hello', 'goodbye'])
that would add methods to the class whose values are the values of the arguments. So now:
p x.methods #> [m_greeting, a_greeting, e_greeting, n_greeting,
r_greeting, ...]
And
p x.m_greeting #> "good morning"
p x.r_greeting #> ['hello', 'goodbye']
I realize that this is sort of what instance variables are to do (and that if I wanted them immutable I could make them frozen constants) but, for reasons beyond my control, I need to make methods instead.
Thanks!
BTW: I tried
def initialize(*args)
i = 0
%w[m_a, m_b, m_c].each do |a|
self.class.send(:define_method, a.to_s, Proc.new { args[i] })
i+=1
end
end
But that ended up giving every method the value of the last argument.
I guess this solves the problem:
def initialize(*args)
#args = args
%w[m_a m_b m_c].each_with_index do |a, i|
eval "def #{a}; #args[#{i}]; end"
end
end
You can do what you want, like so:
class Foo
def initialize(*args)
methods = %w[m_greeting a_greeting e_greeting n_greeting r_greeting]
raise ArgumentError unless args.size == methods.size
args.zip(methods).each do |arg, method|
self.class.instance_eval do
define_method method do
arg
end
end
end
end
end
foo = Foo.new(1, 2, 3, 4, 5)
p foo.m_greeting # => 1
p foo.a_greeting # => 2
p foo.e_greeting # => 3
p foo.n_greeting # => 4
p foo.r_greeting # => 5
But this may not be the droid you're looking for: More than a few positional arguments can make code difficult to read. You might consider using OpenStruct. You'll have to write almost no code, and the constructor calls will be easier to read:
require 'ostruct'
class Foo < OpenStruct
end
foo = Foo.new(:m_greeting=>1,
:a_greeting=>2,
:e_greeting=>3,
:n_greeting=>4,
:r_greeting=>5)
p foo.m_greeting # => 1
p foo.a_greeting # => 2
p foo.e_greeting # => 3
p foo.n_greeting # => 4
p foo.r_greeting # => 5
Don't sweat mutability. If you feel the need to write code to protect yourself from mistakes, consider writing unit tests instead. Then the code can be unfettered with sundry checks and protections.
Your last loop would send the last argument to redefine the method for each of your m_a, m_b, m_c Try looping over the args and sending to the indexed method.
e.g.
def initialize(*args)
methods = %w[m_a m_b m_c]
args.each_with_index {|item,index|
self.class.send(:define_method, methods[index], lambda { item })
}
end
each_with_index comes from the Enumerable module: http://ruby-doc.org/core/classes/Enumerable.html#M003137
I have a class with a custom each-method:
class CurseArray < Array
def each_safe
each do |element|
unless element =~ /bad/
yield element
end
end
end
end
And want to call different block methods, like "collect" or "inject" on those iterated elements. For example:
curse_array.each_safe.magic.collect {|element| "#{element} is a nice sentence."}
I know there is a specific function (which I called "magic" here) to do this, but I've forgotten. Please help! :-)
If a method yields you will need to pass it a block. There is no way define a block that automatically passes itself.
Closest I can get to your spec is this:
def magic(meth)
to_enum(meth)
end
def test
yield 1
yield 2
end
magic(:test).to_a
# returns: [1,2]
The cleanest way of implementing your request is probably:
class MyArray < Array
def each_safe
return to_enum :each_safe unless block_given?
each{|item| yield item unless item =~ /bad/}
end
end
a = MyArray.new
a << "good"; a << "bad"
a.each_safe.to_a
# returns ["good"]
The way you wrote your each_safe method, the easiest would be
curse_array.each_safe { |element| do_something_with(element) }
Edit: Oh, your each_safe method isn't correct, either. It has to be "each do", not "each.do"
Edit 2: If you really want to be able to do things like "each_safe.map", while at the same time also being able to do "each_safe { ... }" you could write your method like this:
require 'enumerator'
class CurseArray < Array
BLACKLIST = /bad/
def each_safe
arr = []
each do |element|
unless element =~ BLACKLIST
if block_given?
yield element
else
arr << element
end
end
end
unless block_given?
return Enumerator.new(arr)
end
end
end
The selected solution uses the common idiom to_enum :method_name unless block_given? which it's ok, but there are alternatives:
Leave your "unfriendly" yielder method untouched, use enum_for when calling it.
Use a lazy Enumerator.
Use lazy arrays (needs Ruby 2.0 or gem enumerable-lazy).
Here's a demo code:
class CurseArray < Array
def each_safe
each do |element|
unless element =~ /bad/
yield element
end
end
end
def each_safe2
Enumerator.new do |enum|
each do |element|
unless element =~ /bad/
enum.yield element
end
end
end
end
def each_safe3
lazy.map do |element|
unless element =~ /bad/
element
end
end.reject(&:nil?)
end
end
xs = CurseArray.new(["good1", "bad1", "good2"])
xs.enum_for(:each_safe).select { |x| x.length > 1 }
xs.each_safe2.select { |x| x.length > 1 }
xs.each_safe3.select { |x| x.length > 1 }.to_a