2009-11-10 2 views
3

Je dois autoriser la définition et l'appel de blocs dans le cadre d'une classe, en utilisant instance_exec (via Rails 2.3.2). Cependant, certains de ces blocs doivent revenir tôt dans certaines situations, ce qui me cause un problème.Retour anticipé d'un bloc donné à instance_exec

Mon application a été compilée en utilisant ruby ​​1.8.6, mais je dois la faire tourner sur 1.8.7 aussi. Il semble que, entre les deux versions, la possibilité de revenir de l'intérieur d'un lambda a été supprimée. Les travaux suivants en 1.8.6, mais jette un LocalJumpError (retour inattendu) dans 1.8.7:

class Foo 
    def square(n) 
    n ** 2 
    end 

    def cube(n) 
    n ** 3 
    end 

    def call_block(*args, &block) 
    instance_exec *args, &block 
    end 
end 

block = lambda { |n| 
    return square(n) if n < 5 
    cube(n) 
} 

f = Foo.new 
f.call_block(5, &block) # returns 125 
f.call_block(3, &block) # returns 9 in 1.8.6, throws a LocalJumpError in 1.8.7 

Je déterminé que je pouvais le faire fonctionner en 1.8.7 si je l'ai remplacé return dans mon bloc avec next, mais next square(n) if n < 5 donne nil en 1.8.6.

Y at-il un moyen que je peux obtenir ce travail à la fois dans 1.8.6 et 1.8.7? Je sais que je peux restructurer mes blocs pour utiliser le branchement au lieu d'un retour anticipé, mais certains blocs sont plus complexes et ont plusieurs situations où un retour anticipé est nécessaire.

Aussi, est-ce que cela va changer plus loin si je veux que mon code fonctionne dans ruby ​​1.9?

Edit: J'ai découvert que la raison pour laquelle il travaille en 1.8.6 et non 1.8.7 est que 1.8.7 définit sa propre instance_exec dans la source de C, alors que 1.8.6 utilise la mise en œuvre de Rails. Si je remplace instance_exec en 1.8.7 avec la version de Rails, cela fonctionne aussi.

Répondre

1

Modifier après les commentaires Voir ce post pour plus de détails.

class Foo 

    def square(n) 
    n ** 2 
    end 

    def cube(n) 
    n ** 3 
    end 

    def call_block(*args, &block) 
     instance_exec *args, &block 
    end 
end 




def a 
    block = lambda { | n| 
    return square(n) if n < 5 
    cube(n) 
    } 
f = Foo.new 
puts f.call_block(3, &block) # returns 125 
puts "Never makes it here in 1.8.7" 
puts f.call_block(5, &block) # returns 9 in 1.8.6, returns nothing in 1.8.7 
end 

a 

Ce code ne donne rien, puisqu'il retourne en dehors des instructions puts.

La façon dont procs et lambdas fonctionnent a changé en 1.9. Cela aide donc à expliquer ce qui se passe.

originale

I votre code et remaniée avec cela a fonctionné sous les 3 années vm. Fait intéressant, votre code s'est exécuté sous la version 1.9 sans exception.

class Foo 

    def square(n) 
    n ** 2 
    end 

    def cube(n) 
    n ** 3 
    end 

    def call_block(*args, &block) 
    block.call(self, *args) 
    end 
end 

block = lambda { |obj, n| 
    return obj.square(n) if n < 5 
    obj.cube(n) 
} 

f = Foo.new 
puts f.call_block(5, &block) # returns 125 
puts f.call_block(3, &block) # returns 9 

Ce post pourrait être une idée.

+0

J'ai voté parce que votre refactoring fonctionne; cependant, le point d'utilisation de instance_exec était tel que l'objet n'a pas besoin d'être transmis aux blocs. Cela fait partie d'un moteur de templates où les utilisateurs (du code) sont capables de créer leurs propres commandes, donc je voulais que ce soit aussi simple que possible de le faire. –

+0

Ok, je comprends. Les utilisateurs vont définir le bloc. J'ai trouvé intéressant que votre code fonctionne correctement sous ruby ​​1.9 mais pas sous 1.8.7. Instance_exec n'existe que dans les versions 1.8.7 et 1.9. Rails a ajouté sa propre version dans Active_support ce qui lui a permis d'être utilisé dans 1.8.6. – coreypurcell

+0

Je pense que je comprends ce qui se passe maintenant. Voir l'édition. – coreypurcell

Questions connexes