Эквивалент динамического ветра Схемы в Ruby - PullRequest
2 голосов
/ 06 октября 2010

В Ruby есть продолжения ... есть ли у него конструкция dynamic-wind, такая как Схема ?

1 Ответ

5 голосов
/ 07 октября 2010

[Этот ответ написан для программистов Scheme (OP уже задавал здесь другие вопросы Scheme, так что это безопасная ставка).Если вы здесь, потому что вы программист на Ruby, у которого нет опыта работы с Scheme, прочтите сноску для некоторого контекста.: -)]

МРТ не (см. Ниже);и если MRI этого не делает, это означает, что нет никакого портативного способа использовать любую такую ​​функциональность, даже если другая реализация предоставляет его.

Я действительно проверял исходный код MRI 1.9.1, просто чтобы быть уверенным.В любом случае, вот некоторый код, демонстрирующий, что даже обычная защита от раскрутки (ensure) не работает правильно с продолжениями на МРТ (протестировано с 1.8.7 и 1.9.1).(Он работает правильно с JRuby (я тестировал с 1.5), поэтому он показывает, что это специфическая для реализации вещь. Но обратите внимание, что JRuby предоставляет только escape-продолжения, а не универсальные.)

callcc do |cc|
  begin
    puts 'Body'
    cc.call
  ensure
    puts 'Ensure'
  end
end

(Для тестирования с MRI 1.9+ вам нужно либо запустить с параметром -rcontinuation, либо поставить require 'continuation' в верхней части файла.)


Для читателей, которые не знаютdynamic-wind - это способ указать код, который будет выполняться при выходе из покрываемого кода (очень похоже на ensure), , а также код , который будет выполняться, когда покрытый кодвновь вошел.(Это может произойти, когда вы используете call/cc внутри покрытого кода и вызываете объект продолжения после выхода из покрытого кода.)

Пример полностью надуманного:

def dynamic_wind pre, post, &block
  raise 'Replace this with a real implementation, kthx'
end

def redirect_stdout port, &block
  saved = $stdout
  set_port = lambda {$stdout = port}
  reset_port = lambda {$stdout = saved}
  dynamic_wind set_port, reset_port, &block
end

cc = nil
# cheap way to nuke all the output ;-)
File.open '/dev/null' do |null|
  redirect_stdout null do
    callcc {|cc|}
    puts 'This should not be shown'
  end
  puts 'This should be shown'
  cc.call
end

Итак,правильно функционирующая реализация dynamic_wind гарантирует, что $stdout будет возвращен потоку /dev/null при вызове продолжения, так что во всех случаях, когда выполняется puts 'This should not be shown', этот текст действительно не отображается.

...