Ruby tracking self inside Array#each - ruby

I'm having a little trouble tracking self in the following example:
# a simple class
class Foo; end
# open the Class class to add an instance method
class Class
# breakpoint 1 - self is equal to Class right here, I get why
puts self
# usage: Foo.some_method(1,2,3)
def some_method(*args)
# breakpoint 2 - when we call Foo.some_method(*args)
# self is equal to Foo, and once again I understand why
puts self
args.each do |arg|
# breakpoint 3 - self is still equal to Foo, even though we are calling each
# on an explicit receiver (an array called args)
puts self
end
end
end
So how come self does not change when I call each on the args array ? I was under the impression that self is always equal to the receiver, which would be the array surely?

self is always equal to the receiver in the method itself. A block you pass to each is a closure on the scope in which it is defined, where self is equal to a Foo.
This makes perfect sense when you think about it. In C++, should a for loop change this? Blocks are made to allow you to pass your code that executes in your environment to other code so that methods can replace language constructs like for, or some language's using, and so on.

In Ruby, almost anything is possible even if it is not advisable.
So as you noted, simply calling self in a normal each block will return the lexical scope, ie. in the REPL, main:
[1,2,3].each {|e| p "#{self} - #{e}" }
# "main - 1"
# "main - 2"
# "main - 3"
# => [1, 2, 3]
But, we can get the scope we want, which is the instance of the receiver of each by using instance_eval:
[1,2,3].instance_eval { each {|e| p "#{self} - #{e}" } }
"[1, 2, 3] - 1"
"[1, 2, 3] - 2"
"[1, 2, 3] - 3"
=> [1, 2, 3]
Periculo tuo ingredere!
Addendum:
I did a benchmark just out of curiosity, expecting a big performance penalty from instance_eval:
require 'benchmark'
a = Array.new(1_000_000, 1)
Benchmark.bmbm(100) do |x|
x.report("inline, self=main") { a.each {|e| self && e } }
x.report("assigned") { a.each {|e| a && e } }
x.report("inline, instance_eval") { a.instance_eval { each {|e| self && e } } }
end
These were the results:
Rehearsal ---------------------------------------------------------
inline, self=main 0.040000 0.000000 0.040000 ( 0.037743)
assigned 0.040000 0.000000 0.040000 ( 0.043800)
inline, instance_eval 0.040000 0.000000 0.040000 ( 0.041955)
------------------------------------------------ total: 0.120000sec
user system total real
inline, self=main 0.030000 0.000000 0.030000 ( 0.038312)
assigned 0.040000 0.000000 0.040000 ( 0.043693)
inline, instance_eval 0.040000 0.000000 0.040000 ( 0.040029)

Related

Difference between passing arguments to define_method and to the following block?

I am confused by the following code from Poignant Guide:
# The guts of life force within Dwemthy's Array
class Creature
# Get a metaclass for this class
def self.metaclass; class << self; self; end; end
# Advanced metaprogramming code for nice, clean traits
def self.traits( *arr )
return #traits if arr.empty?
# 1. Set up accessors for each variable
attr_accessor( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
metaclass.instance_eval do
define_method( a ) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
# 3. For each monster, the `initialize' method
# should use the default number for each trait.
class_eval do
define_method( :initialize ) do
self.class.traits.each do |k,v|
instance_variable_set("##{k}", v)
end
end
end
end
# Creature attributes are read-only
traits :life, :strength, :charisma, :weapon
end
The above code is used to create a new class, as in the following:
class Dragon < Creature
life( 1340 ) # tough scales
strength( 451 ) # bristling veins
charisma( 1020 ) # toothy smile
weapon( 939 ) # fire breath
end
I need to study the basics of meta-programming more on my own, but for now I just want to know, where does the val block argument come from in define_method( a ) do |val|? It represents the point values assigned to each trait, but I don't understand how each of those numbers become a block argument.
Also, why is a passed in the parentheses to define_method, while val is passed in as a block argument?
I've read over this question on the subject of define_method arguments, but it doesn't address the reasons for passing arguments to define_method rather than to the block.
In the form
define_method(:foo){|x| ...}
:foo is the method name and x is the argument. They have different roles. It is the same as:
def foo(x)
...
end

What's a clean way to allow a block to handle a variable number of arguments?

I've run into this situation before, and something tells me the way I generally handle it is not the cleanest or most idiomatic.
Suppose I have a function that takes a block, which can in turn take 1 or 2 (say) parameters.
def with_arguments(&block)
case block.arity
when 1
block.call("foo")
when 2
block.call("foo", "bar")
end
end
with_arguments do |x|
puts "Here's the argument I was given: #{x}"
end
with_arguments do |x, y|
puts "Here are the arguments I was given: #{x}, #{y}"
end
Switching on arity seems pretty hacky. Is there a more standard Ruby way to achieve this kind of thing?
Here's how I'd pass arbitrary arguments to a lambda:
def with_arguments(&block)
args = %w(foo bar)
n = block.arity
block.call *(n < 0 ? args : args.take(n))
end
with_arguments &lambda { |foo| }
with_arguments &lambda { |foo, bar| }
with_arguments &lambda { |*args| }
with_arguments &lambda { |foo, *args| }
with_arguments &lambda { |foo, bar, *args| }
If n is negative, then the lambda takes an arbitrary number of arguments. Precisely (n + 1).abs of these arguments are mandatory. One can use that information to decide which arguments to pass.
If the lambda takes a finite number of arguments, then just pass the first n elements of args. If it takes an arbitrary number of arguments, then just pass the entire argument array.
The lambda itself will handle the cases where args is insufficient:
with_arguments &lambda { |foo, bar, baz, *args| }
# ArgumentError: wrong number of arguments (2 for 3)
You can simply pass the two arguments to the block:
def with_arguments(&block)
block.call 'foo', 'bar'
end
with_arguments { |x| puts x } # y is not used
with_arguments { |x, y| puts x, y } # All arguments are used
with_arguments { |x, y, z| puts x, y, z } # z will be nil
Unused block arguments are discarded, and any extra parameters will be set to nil.
This is specific to regular blocks and Procs – lambdas will raise an error if given the wrong number of parameters. You can actually find out whether this is the case by calling Proc#lambda?
Also, if you aren't going to store the block, it is cleaner to simply use yield:
def with_arguments
yield 'foo', 'bar'
end
Some solutions...
always pass 2 parameters, even if one has to be nil
just pass one array, which may have more or fewer elements. This kind of gives you the best of both worlds: because of the way multiple assignment works, you can leave out or supply extra block parameters with no warnings
def bar(&block)
puts 'In bar'
block.call(1) if block
puts 'Back in bar'
block.call(1,2) if block
end
1.9.3p392 :043 > bar do |*b| puts b.length end
In bar
1
Back in bar
2

arguments into instance methods in ruby

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

Ruby: Proc#call vs yield

What are the behavioural differences between the following two implementations in Ruby of the thrice method?
module WithYield
def self.thrice
3.times { yield } # yield to the implicit block argument
end
end
module WithProcCall
def self.thrice(&block) # & converts implicit block to an explicit, named Proc
3.times { block.call } # invoke Proc#call
end
end
WithYield::thrice { puts "Hello world" }
WithProcCall::thrice { puts "Hello world" }
By "behavioural differences" I include error handling, performance, tool support, etc.
I think the first one is actually a syntactic sugar of the other. In other words there is no behavioural difference.
What the second form allows though is to "save" the block in a variable. Then the block can be called at some other point in time - callback.
Ok. This time I went and did a quick benchmark:
require 'benchmark'
class A
def test
10.times do
yield
end
end
end
class B
def test(&block)
10.times do
block.call
end
end
end
Benchmark.bm do |b|
b.report do
a = A.new
10000.times do
a.test{ 1 + 1 }
end
end
b.report do
a = B.new
10000.times do
a.test{ 1 + 1 }
end
end
b.report do
a = A.new
100000.times do
a.test{ 1 + 1 }
end
end
b.report do
a = B.new
100000.times do
a.test{ 1 + 1 }
end
end
end
The results are interesting:
user system total real
0.090000 0.040000 0.130000 ( 0.141529)
0.180000 0.060000 0.240000 ( 0.234289)
0.950000 0.370000 1.320000 ( 1.359902)
1.810000 0.570000 2.380000 ( 2.430991)
This shows that using block.call is almost 2x slower than using yield.
The other answers are pretty thorough and Closures in Ruby extensively covers the functional differences. I was curious about which method would perform best for methods that optionally accept a block, so I wrote some benchmarks (going off this Paul Mucur post). I compared three methods:
&block in method signature
Using &Proc.new
Wrapping yield in another block
Here is the code:
require "benchmark"
def always_yield
yield
end
def sometimes_block(flag, &block)
if flag && block
always_yield &block
end
end
def sometimes_proc_new(flag)
if flag && block_given?
always_yield &Proc.new
end
end
def sometimes_yield(flag)
if flag && block_given?
always_yield { yield }
end
end
a = b = c = 0
n = 1_000_000
Benchmark.bmbm do |x|
x.report("no &block") do
n.times do
sometimes_block(false) { "won't get used" }
end
end
x.report("no Proc.new") do
n.times do
sometimes_proc_new(false) { "won't get used" }
end
end
x.report("no yield") do
n.times do
sometimes_yield(false) { "won't get used" }
end
end
x.report("&block") do
n.times do
sometimes_block(true) { a += 1 }
end
end
x.report("Proc.new") do
n.times do
sometimes_proc_new(true) { b += 1 }
end
end
x.report("yield") do
n.times do
sometimes_yield(true) { c += 1 }
end
end
end
Performance was similar between Ruby 2.0.0p247 and 1.9.3p392. Here are the results for 1.9.3:
user system total real
no &block 0.580000 0.030000 0.610000 ( 0.609523)
no Proc.new 0.080000 0.000000 0.080000 ( 0.076817)
no yield 0.070000 0.000000 0.070000 ( 0.077191)
&block 0.660000 0.030000 0.690000 ( 0.689446)
Proc.new 0.820000 0.030000 0.850000 ( 0.849887)
yield 0.250000 0.000000 0.250000 ( 0.249116)
Adding an explicit &block param when it's not always used really does slow down the method. If the block is optional, do not add it to the method signature. And, for passing blocks around, wrapping yield in another block is fastest.
That said, these are the results for a million iterations, so don't worry about it too much. If one method makes your code clearer at the expense of a millionth of a second, use it anyway.
They give different error messages if you forget to pass a block:
> WithYield::thrice
LocalJumpError: no block given
from (irb):3:in `thrice'
from (irb):3:in `times'
from (irb):3:in `thrice'
> WithProcCall::thrice
NoMethodError: undefined method `call' for nil:NilClass
from (irb):9:in `thrice'
from (irb):9:in `times'
from (irb):9:in `thrice'
But they behave the same if you try to pass a "normal" (non-block) argument:
> WithYield::thrice(42)
ArgumentError: wrong number of arguments (1 for 0)
from (irb):19:in `thrice'
> WithProcCall::thrice(42)
ArgumentError: wrong number of arguments (1 for 0)
from (irb):20:in `thrice'
I found that the results are different depending on whether you force Ruby to construct the block or not (e.g. a pre-existing proc).
require 'benchmark/ips'
puts "Ruby #{RUBY_VERSION} at #{Time.now}"
puts
firstname = 'soundarapandian'
middlename = 'rathinasamy'
lastname = 'arumugam'
def do_call(&block)
block.call
end
def do_yield(&block)
yield
end
def do_yield_without_block
yield
end
existing_block = proc{}
Benchmark.ips do |x|
x.report("block.call") do |i|
buffer = String.new
while (i -= 1) > 0
do_call(&existing_block)
end
end
x.report("yield with block") do |i|
buffer = String.new
while (i -= 1) > 0
do_yield(&existing_block)
end
end
x.report("yield") do |i|
buffer = String.new
while (i -= 1) > 0
do_yield_without_block(&existing_block)
end
end
x.compare!
end
Gives the results:
Ruby 2.3.1 at 2016-11-15 23:55:38 +1300
Warming up --------------------------------------
block.call 266.502k i/100ms
yield with block 269.487k i/100ms
yield 262.597k i/100ms
Calculating -------------------------------------
block.call 8.271M (± 5.4%) i/s - 41.308M in 5.009898s
yield with block 11.754M (± 4.8%) i/s - 58.748M in 5.011017s
yield 16.206M (± 5.6%) i/s - 80.880M in 5.008679s
Comparison:
yield: 16206091.2 i/s
yield with block: 11753521.0 i/s - 1.38x slower
block.call: 8271283.9 i/s - 1.96x slower
If you change do_call(&existing_block) to do_call{} you'll find it's about 5x slower in both cases. I think the reason for this should be obvious (because Ruby is forced to construct a Proc for each invocation).
BTW, just to update this to current day using:
ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-linux]
On Intel i7 (1.5 years oldish).
user system total real
0.010000 0.000000 0.010000 ( 0.015555)
0.030000 0.000000 0.030000 ( 0.024416)
0.120000 0.000000 0.120000 ( 0.121450)
0.240000 0.000000 0.240000 ( 0.239760)
Still 2x slower. Interesting.

How do you pass arguments to define_method?

I would like to pass an argument(s) to a method being defined using define_method, how would I do that?
The block that you pass to define_method can include some parameters. That's how your defined method accepts arguments. When you define a method you're really just nicknaming the block and keeping a reference to it in the class. The parameters come with the block. So:
define_method(:say_hi) { |other| puts "Hi, " + other }
... and if you want optional parameters
class Bar
define_method(:foo) do |arg=nil|
arg
end
end
a = Bar.new
a.foo
#=> nil
a.foo 1
# => 1
... as many arguments as you want
class Bar
define_method(:foo) do |*arg|
arg
end
end
a = Bar.new
a.foo
#=> []
a.foo 1
# => [1]
a.foo 1, 2 , 'AAA'
# => [1, 2, 'AAA']
...combination of
class Bar
define_method(:foo) do |bubla,*arg|
p bubla
p arg
end
end
a = Bar.new
a.foo
#=> wrong number of arguments (0 for 1)
a.foo 1
# 1
# []
a.foo 1, 2 ,3 ,4
# 1
# [2,3,4]
... all of them
class Bar
define_method(:foo) do |variable1, variable2,*arg, &block|
p variable1
p variable2
p arg
p block.inspect
end
end
a = Bar.new
a.foo :one, 'two', :three, 4, 5 do
'six'
end
Update
Ruby 2.0 introduced double splat ** (two stars) which (I quote) does:
Ruby 2.0 introduced keyword arguments, and ** acts like *, but for keyword arguments. It returns a Hash with key / value pairs.
...and of course you can use it in define method too :)
class Bar
define_method(:foo) do |variable1, variable2,*arg,**options, &block|
p variable1
p variable2
p arg
p options
p block.inspect
end
end
a = Bar.new
a.foo :one, 'two', :three, 4, 5, ruby: 'is awesome', foo: :bar do
'six'
end
# :one
# "two"
# [:three, 4, 5]
# {:ruby=>"is awesome", :foo=>:bar}
Named attributes example:
class Bar
define_method(:foo) do |variable1, color: 'blue', **other_options, &block|
p variable1
p color
p other_options
p block.inspect
end
end
a = Bar.new
a.foo :one, color: 'red', ruby: 'is awesome', foo: :bar do
'six'
end
# :one
# "red"
# {:ruby=>"is awesome", :foo=>:bar}
I was trying to create example with keyword argument, splat and double splat all in one:
define_method(:foo) do |variable1, variable2,*arg, i_will_not: 'work', **options, &block|
# ...
or
define_method(:foo) do |variable1, variable2, i_will_not: 'work', *arg, **options, &block|
# ...
... but this will not work, it looks like there is a limitation. When you think about it makes sense as splat operator is "capturing all remaining arguments" and double splat is "capturing all remaining keyword arguments" therefore mixing them would break expected logic. (I don't have any reference to prove this point doh! )
update 2018 August:
Summary article: https://blog.eq8.eu/til/metaprogramming-ruby-examples.html
In addition to Kevin Conner's answer: block arguments do not support the same semantics as method arguments. You cannot define default arguments or block arguments.
This is only fixed in Ruby 1.9 with the new alternative "stabby lambda" syntax which supports full method argument semantics.
Example:
# Works
def meth(default = :foo, *splat, &block) puts 'Bar'; end
# Doesn't work
define_method :meth { |default = :foo, *splat, &block| puts 'Bar' }
# This works in Ruby 1.9 (modulo typos, I don't actually have it installed)
define_method :meth, ->(default = :foo, *splat, &block) { puts 'Bar' }
With 2.2 you can now use keyword arguments:
https://robots.thoughtbot.com/ruby-2-keyword-arguments
define_method(:method) do |refresh: false|
..........
end

Resources