2014-09-18 4 views
4

Pourquoi cet extrait:Comportement étrange de `return` lors du passage de rubis Matz à JRuby

def dump_dump 
    get_dump = lambda do 
     return 1 if $n 
     $n = true 
     module_exec &get_dump 
     2 
    end 
    p get_dump[] 
end 

Module.new do 
    module_exec &method(:dump_dump) 
end 

impression 2 en rubis 2.0.0p481 (08/05/2014) [64 x mingw32]
mais 1 dans jruby 1.7.15 (1.9.3p392) 2014-09-03 82b5cc3 sur Java HotSpot (TM) serveur 64 bits VM 1.7.0_67-b01 + jit [Windows 8-amd64]?

Je voudrais comprendre le problème.

UPD: devrait-il être rapporté quelque part?

Répondre

0

Un 'retour' à l'intérieur d'un lambda devrait revenir du lambda et ne pas revenir de la méthode. Dans ce cas délicat, il semble que jruby ne respecte pas le lambda interne, et retourne à la place au premier appel lambda.

Au début, je pensais que ce pourrait être causée par un appel lambda dans un lambda, mais maintenant je crois qu'il est un problème lié à bloquer la conversion, après avoir réduit l'exemple à ceci:

Module.new do 
    test = lambda do 
    return 
    end 
    module_exec &test 
    puts 'after' 
end 

Ici, seulement les impressions mri 'après', tandis que jruby n'imprime rien.

... mais si nous ne le faisons pas lambda pour bloquer la conversion (le & test):

Module.new do 
    test = lambda do 
    return 
    end 
    module_exec { test[] } 
    puts 'after' 
end 

et à la fois mri JRuby print 'après' ...

+0

Etes-vous sûr de ne pas les avoir remplacés? Mon jruby revient à travers tous les appels - c'est comme ça que j'ai repéré le problème. Le code original traversait un arbre et devait renvoyer un long vecteur, mais jruby me lançait à zéro, soulevant l'exception plus loin dans mon programme. – Nakilon

+0

Oui, je l'ai fait dans le texte après avoir changé de code. Mis à jour avec une réponse différente ... – adzdavies

+0

Si je me souviens, quand j'ai essayé dans mon code très original d'utiliser '{test []}' au lieu de '& test', il avait une autre portée et ne voyait pas les méthodes de module, etc. donc ce remplacement n'est pas équivalent. – Nakilon

2

J'étais toujours sous la l'impression que return à l'intérieur d'un bloc était un comportement indéfini. Pouvez-vous utiliser next à la place?

Par exemple, Rubinius a aussi ce problème, mais est beaucoup plus explicite:

[1].map(&lambda { |n| return -1 }) 
LocalJumpError: unexpected return 

Bien sûr, en utilisant next produit les résultats escomptés:

rbx-head :003 > [1].map(&lambda { |n| next -1 }) 
=> [-1] 

La morale de l'histoire est que return est défini pour les méthodes, et Procs et lambdas ne sont pas des méthodes. next et break sont les mots-clés à utiliser si vous souhaitez arrêter un appel de bloc.

Je ne trouve aucune documentation sur le comportement de return à partir de la spécification officielle Ruby, mais rubyspec effectue des tests qui vérifient return provoque le renvoi de la méthode appelante.

https://github.com/rubyspec/rubyspec/blob/master/language/return_spec.rb#L184

+0

Les blocs ne devraient pas gérer retour, seulement la méthode environnante. Cependant lambda devrait avoir des méthodes sémantiques. Lors de la conversion en un bloc, un lambda conserve son statut lambda donc .... C'est tout ce que j'ai trouvé pour référence https://gist.github.com/mislav/4508988 – adzdavies

+0

Je viens de le tester. En IRM, le lambda conserve ses propriétés lambda lors de l'appel de 'to_proc'. En revanche, dans rbx, les propriétés lambda sont perdues (par exemple, les méthodes manquantes ne génèrent pas d'erreur). – Max

Questions connexes