2014-09-18 3 views
4

Почему этот фрагмент:Weird `return` поведение при переходе от Мац рубинового до 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 

печать 2 в рубин 2.0.0p481 (2014-05-08) [x64-mingw32]
но 1 in jruby 1.7.15 (1.9.3p392) 2014-09-03 82b5cc3 на 64-разрядном сервере Java HotSpot (TM) VM 1.7.0_67-b01 + jit [Windows 8-amd64]?

Я хотел бы понять проблему.

UPD: должно ли быть сообщено где-нибудь?

ответ

0

«Возврат» внутри лямбда должен возвращаться из лямбда и не возвращаться от метода. В этом сложном случае, похоже, что jruby не уважает внутреннюю лямбду и вместо этого возвращается обратно к первому лямбда-вызову.

Сначала я думал, что это может быть вызвано лямбда-вызова в лямбда, но теперь я считаю, что это вопрос, связанный с блок-преобразования, после снижения на примере к этому:

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

Здесь только mri печатает «после», в то время как jruby ничего не печатает.

... но если мы не будем делать лямбда блокировать преобразование (тест &):

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

и мрт и JRuby печать «после» ...

+0

Вы уверены, что вы не заменили их друг другом? Мой jruby возвращается через все вызовы - вот как я заметил эту проблему. Исходный код проходил по дереву и должен был возвращать длинный вектор, но jruby выбрасывал мне ноль, увеличивая исключение в моей программе. – Nakilon

+0

Да, я сделал это в тексте после смены кода. Обновлено с другим ответом ... – adzdavies

+0

Как я помню, когда я попытался в своем самом оригинальном коде использовать '{test []}' вместо '& test', он имел другую область видимости и не видел модульных методов и т. поэтому эта замена не эквивалентна. – Nakilon

2

Я всегда был под впечатление, что return внутри блока было неопределенным поведением. Можете ли вы использовать next?

Например, Rubinius также имеет эту проблему, но гораздо более определенно:

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

Конечно, используя next дает ожидаемые результаты:

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

Мораль этой истории является то, что return определяется для методов, а Procs и lambdas - не методы. next и break - это ключевые слова, которые нужно использовать, если вы хотите остановить вызов блока.

Невозможно найти документацию о поведении return из официальной спецификации Ruby, но у rubyspec есть тесты, которые подтверждают, что return заставляет метод вызова возвращаться.

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

+0

Блоки не должны обрабатывать return, только окружающий метод. Однако лямбда должна иметь семантику методов. При преобразовании в блок лямбда сохраняет свой лямбда-статус, поэтому ... Это все, что я нашел для справки https://gist.github.com/mislav/4508988 – adzdavies

+0

Я только что протестировал это. В MRI лямбда сохраняет свои лямбда-свойства при вызове 'to_proc'. В rbx, с другой стороны, свойства лямбда теряются (например, отсутствующие методы не вызывают ошибки). – Max

 Смежные вопросы

  • Нет связанных вопросов^_^