Craig Norton Craig Norton - 4 years ago 104
Ruby Question

Comparing two lambdas/Procs in Ruby

This has been driving me nuts, I'm posting here after a lot of looking around.

I'd like to know if two variables pointing to the same Proc are the pointing to the same Proc.
I'm sure it must be something I'm not getting so for example why do all of these return false?

class LambdaFunctions
def self.LambdaFunction1
lambda { |t| t ** 2}

a = LambdaFunctions.LambdaFunction1
b = LambdaFunctions.LambdaFunction1

puts LambdaFunctions.LambdaFunction1
puts a
puts b

puts a == b
puts a === b
puts a.eql?(b)
puts a.equal?(b)
puts a == LambdaFunctions.LambdaFunction1
puts a === LambdaFunctions.LambdaFunction1
puts a.eql?(LambdaFunctions.LambdaFunction1)
puts a.equal?(LambdaFunctions.LambdaFunction1)

Thank you Mark, you made it a lot clearer.
In previous it was returning new objects each time so the equal? function was never going to return true. The two lambdas were functionally the same but not the same object.
So if you create one version and then return it back in the method you can test it's identity.
The following makes more sense and works as I intended.

class LambdaFunctions

@lambda1 = lambda { |t| t ** 2}
@lambda2 = lambda { |t| t ** 2}

def self.LambdaFunction1

def self.LambdaFunction2

func1 = LambdaFunctions.LambdaFunction1
func2 = LambdaFunctions.LambdaFunction1
func3 = LambdaFunctions.LambdaFunction2

puts func1.equal?(func2) # true
puts func1.equal?(func3) # false
puts func1.equal?(LambdaFunctions.LambdaFunction1) # true
puts func3.equal?(LambdaFunctions.LambdaFunction1) # false
puts func3.equal?(LambdaFunctions.LambdaFunction2) # true

Answer Source

While the lambda is effectively equivalent, each call to LambdaFunctions.LambdaFunction1 returns a new instance of the lambda. It would make sense for procs to be equivalent only by identity, not by value, since it would be virtually impossible to determine programmatic equivalence.

What I mean is, if procs could be determined to be equivalent based on what they did, then lambda { 3 } and lambda { 1 + 2 } would be equivalent. With lambdas any more complicated than that, to determine equivalence would basically require a solution to the halting problem.

To determine equivalent identity (per the comment), refer to Object#equal?:

Unlike ==, the equal? method should never be overridden by subclasses: it is used to determine object identity (that is, a.equal?(b) iff a is the same object as b).

If you really need a and b to be the same object, then you need to return the same lambda every time; this means you need to assign the lambda to an instance variable in the LambdaFunctions class, for instance, and return that.

ruby-1.9.1-p378 > class LambdaFunctions
ruby-1.9.1-p378 ?>   @func1 = lambda { |t| t ** 2 }
ruby-1.9.1-p378 ?>   def self.LambdaFunction1
ruby-1.9.1-p378 ?>      @func1
ruby-1.9.1-p378 ?>    end
ruby-1.9.1-p378 ?>  end
 => nil 
ruby-1.9.1-p378 > a = LambdaFunctions.LambdaFunction1
 => #<Proc:0x0000010099e370@(irb):10 (lambda)> 
ruby-1.9.1-p378 > b = LambdaFunctions.LambdaFunction1 # same address as a
 => #<Proc:0x0000010099e370@(irb):10 (lambda)> 
ruby-1.9.1-p378 > a == b
 => true 
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download