Block equality

Is there a way to compare two blocks with each other?

If you promote a block into a Proc, how would you know that the
original block was the same?

This is what I want to do

(1…10).each do
once { puts “Hi Tom”}
end

I’ve tried to implement once like this

class << self
def once(&block)
@ids ||= {}
block.call unless @ids.key? block.object_id
@ids[block.object_id] = :true
puts @ids.keys.join(’, ')
end
end

I’ve even tried a varient using == and ===

def l(&b)
@t ||= b
puts b == @t
puts b === @t
@t = b
end

(1…2).each { l{x=1} }

output:
true
true
false
false

How can you compare two procs that have the same body?

Thanks,
– Tom.


“Nothing will ever be attempted, if all
possible objections must first be
overcome.” - Samuel Johnson

“Luck is what happens when
preparation meets opportunity.” - Seneca

ruby version:

ruby 1.8.4 (2006-04-14) [i386-mswin32]

On 8/15/06, Tom J. [email protected] wrote:


“Nothing will ever be attempted, if all
possible objections must first be
overcome.” - Samuel Johnson

“Luck is what happens when
preparation meets opportunity.” - Seneca


“Nothing will ever be attempted, if all
possible objections must first be
overcome.” - Samuel Johnson

“Luck is what happens when
preparation meets opportunity.” - Seneca

Marshall T. Vandegrift wrote:

a block to a Proc via &block in the argument list results in a new
Proc each time and (b) ruby 1.8 provides no introspection into the
actual contents of a block.

Yes, it’s kind of like asking if two equations or two methods are equal.
On the other hand, you could probably use the SerializeableProc
(developed here: Ruby Quiz - SerializableProc (#38)), add an equality
method, and then you’d have it. :slight_smile:

-Justin

“Tom J.” [email protected] writes:

Is there a way to compare two blocks with each other?

If you promote a block into a Proc, how would you know that the
original block was the same?

I haven’t dived into the code, but I’m pretty sure that (a) promoting
a block to a Proc via &block in the argument list results in a new
Proc each time and (b) ruby 1.8 provides no introspection into the
actual contents of a block.

This is what I want to do

This struck me as interesting, so here’s one possible solution:

class Object
  module OnceHelper; end

  def once
    execute = false
    vname = '_' << (begin; raise; rescue; $!.backtrace[1]; end).
      split(':')[0..1].join('_').gsub(/[^A-Za-z0-9]/, '_')

    begin
      critical, Thread.critical = Thread.critical, true
      execute = !OnceHelper.instance_variables.include?("@#{vname}")
      OnceHelper.instance_variable_set("@#{vname}", true)
    ensure
      Thread.critical = critical
    end

    yield if execute
  end
end

class OnceDoer
  def try_once
    once { "just this once!" }
  end
end

doer1 = OnceDoer.new
p doer1.try_once
p doer1.try_once

doer2 = OnceDoer.new
p doer2.try_once
p doer2.try_once

# >> "just this once!"
# >> nil
# >> nil
# >> nil

N.B. that with this solution a ‘once’ block is truly executed only
once ever, even across separate instances. If you instead squirrel
the generated instance variables away in the individual object
instance the block will only execute once per instance.

Hope this helps.

-Marshall

Tom J. wrote:

original block was the same?
Yes, it’s kind of like asking if two equations or two methods are equal.

Thanks,
– Tom.
Hi Tom,

Well, I did find one instance where this works. If you use Proc#dup or
Proc#clone, you will end up with a new Proc object, but they will
evaluate to being equal:

irb(main):001:0> a = Proc.new { n = 1 }
=> #Proc:0xb7d36224@:1(irb)
irb(main):002:0> b = a.dup
=> #Proc:0xb7d36224@:1(irb)
irb(main):003:0> a.object_id
=> -605441814
irb(main):004:0> b.object_id
=> -605445814
irb(main):005:0> a == b
=> true
irb(main):006:0> b == a
=> true
irb(main):007:0> c = Proc.new { n = 1 }
=> #Proc:0xb7d27ecc@:7(irb)
irb(main):008:0> d = c.clone
=> #Proc:0xb7d27ecc@:7(irb)
irb(main):009:0> c.object_id
=> -605470914
irb(main):010:0> d.object_id
=> -605475424
irb(main):011:0> c == d
=> true
irb(main):012:0> d == c
=> true

Other than that, I don’t see a way for two Procs to have the same block
(and know it). Take a look at the source for Proc#==, it’s somewhat
helpful as well.

I realize this doesn’t help you in the general case, though…

-Justin

On 8/15/06, Justin C. [email protected] wrote:

On the other hand, you could probably use the SerializeableProc
(developed here: Ruby Quiz - SerializableProc (#38)), add an equality
method, and then you’d have it. :slight_smile:

Thanks Marshall and Justin.

This class Proc - RDoc Documentation

“Return true if prc is the same object as other_proc, or if they are
both procs with the same body.”

led me to believe that Procs would evaluate to equality if they had
the same body.

So I would have expected that the following:

s = lambda { x = 1 }
t = lambda { x = 1 }
s == t # => true expected, false in reality

So my question is; when would the body of Procs ever be the same?

Thanks,
– Tom.


“Nothing will ever be attempted, if all
possible objections must first be
overcome.” - Samuel Johnson

“Luck is what happens when
preparation meets opportunity.” - Seneca

Hi,

On Wed, 16 Aug 2006 17:21:39 +0200, Tom J. [email protected]
wrote:

s = lambda { x = 1 }
t = lambda { x = 1 }
s == t # => true expected, false in reality

If you really want to check if procs have the same or equivalent bodies,
you can use RubyNode (http://rubynode.rubyforge.org/):

p1 = proc { 1 + 1 }
=> #Proc:0xb7a70b64@:1(irb)
p2 = proc { 1 + 1 }
=> #Proc:0xb7a6e580@:2(irb)
require “rubynode”
=> true
p1.body_node.transform
=> [:call, {:args=>[:array, [[:lit, {:lit=>1}]]], :mid=>:+,
:recv=>[:lit,
{:lit=>1}]}]
p1.body_node.transform == p2.body_node.transform
=> true
p1 == p2
=> false

But please be aware that the equality of the body node does not imply
that
the blocks have the same closure:

def foo(a) proc { a } end
=> nil
p1 = foo(1)
=> #Proc:0xb7a6217c@:8(irb)
p2 = foo(2)
=> #Proc:0xb7a6217c@:8(irb)
p1.body_node.transform == p2.body_node.transform
=> true
p1[]
=> 1
p2[]
=> 2

And it is also possible that different Ruby code parses to the same node
tree:

p1 = proc { if 1 then 2 else 3 end }
=> #Proc:0xb7ac83b4@:17(irb)
p2 = proc { unless 1 then 3 else 2 end }
=> #Proc:0xb7ac239c@:18(irb)
p3 = proc { 1 ? 2 : 3 }
=> #Proc:0xb7abec4c@:19(irb)
p1.body_node.transform == p2.body_node.transform
=> true
p1.body_node.transform == p3.body_node.transform
=> true
p2.body_node.transform == p3.body_node.transform
=> true
p1.body_node.transform
=> [:if, {:else=>[:lit, {:lit=>3}], :cond=>[:lit, {:lit=>1}],
:body=>[:lit, {:lit=>2}]}]

So this is probably not very useful for your once method, but
interesting
anyway.

Dominik