2010-07-16 6 views
3

Cela m'a rendu fou, je poste ici après beaucoup de regarder autour.En comparant deux lambdas/Procs en Ruby

Je voudrais savoir si deux variables pointant vers la même proc sont pointant vers la même proc. Je suis sûr que ça doit être quelque chose que je ne reçois pas, par exemple pourquoi toutes ces choses retournent fausses?

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

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) 

Merci Mark, vous l'avez fait beaucoup plus clair. Auparavant, il retournait de nouveaux objets à chaque fois, donc l'égal? la fonction n'allait jamais revenir vrai. Les deux lambdas étaient fonctionnellement identiques mais pas le même objet. Ainsi, si vous créez une version et que vous la retournez dans la méthode, vous pouvez tester son identité. Ce qui suit a plus de sens et fonctionne comme prévu.

class LambdaFunctions 

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

    def self.LambdaFunction1 
    @lambda1 
    end 

    def self.LambdaFunction2 
    @lambda2 
    end 
end 

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 

Répondre

5

Alors que le lambda est efficace équivalente, chaque appel à LambdaFunctions.LambdaFunction1 retourne une nouvelle instance du lambda. Il serait logique que les procs ne soient équivalents que par identité et non par valeur, car il serait pratiquement impossible de déterminer l'équivalence programmatique. Ce que je veux dire, c'est que si procs pouvait être considéré comme équivalent en fonction de ce qu'il avait fait, lambda { 3 } et lambda { 1 + 2 } seraient équivalents. Avec lambdas plus compliqué que cela, pour déterminer l'équivalence nécessiterait essentiellement une solution à la halting problem.

Pour déterminer l'identité équivalente (par le commentaire), reportez-vous à Object#equal?:

Contrairement ==, la méthode equal? ne doit jamais être remplacée par les sous-classes: il est utilisé pour déterminer l'identité de l'objet (qui est, a.equal?(b) ssi a est le même objet que b).

Si vous avez vraiment besoin a et b être le même objet, alors vous devez retourner le même lambda à chaque fois; cela signifie que vous devez affecter le lambda à une variable d'instance dans la classe LambdaFunctions, par exemple, et renvoyer que.

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:[email protected](irb):10 (lambda)> 
ruby-1.9.1-p378 > b = LambdaFunctions.LambdaFunction1 # same address as a 
=> #<Proc:[email protected](irb):10 (lambda)> 
ruby-1.9.1-p378 > a == b 
=> true 
+0

Je pensais que c'était quelque chose comme ça. Je suppose que la prochaine question que j'ai est de savoir comment vous tester par identité alors? –

+0

Je viens d'écrire l'exemple et je suis revenu pour voir que vous aviez écrit un exemple pour moi. Merci beaucoup, il est assez évident maintenant, il est souligné. –

+0

En fait, dans le cas général, il n'est pas simplement impossible de déterminer l'équivalence programmatique, c'est juste * est * impossible. Période. Preuve: si l'équivalence programmatique était décidable, je pourrais résoudre le problème de l'arrêt comme ceci: 'def halts? (Proc); proc == lambda {alors que c'est vrai; fin} end'. –

0

En utilisant la méthode equals pour l'égalité ne peut pas être ce que vous avez l'intention ne retournera true si les deux objets comparés font référence au même objet. Je pense que == est peut-être ce que vous pourriez préférer dans ce cas.

J'ai créé une gemme proc_extensions pour obtenir la source d'un proc ou lambda. Vous pouvez utiliser ceci pour comparer les sources de deux procs ou lambda comme ceci:

require 'proc_extensions' 

Proc.instance_eval { include ProcExtensions::Source } 

class LambdaFunctions 

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

    def self.LambdaFunction1 
    @lambda1 
    end 

    def self.LambdaFunction2 
    @lambda2 
    end 
end 

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

func1 == func2 # => true 
func1 == func3 # => true 
func1 == LambdaFunctions.LambdaFunction1.source # => true 
func3 == LambdaFunctions.LambdaFunction1.source # => true 
func3 == LambdaFunctions.LambdaFunction2.source # => true 
Questions connexes