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.
-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.
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