Ранний возврат из блока, переданного instance_exec - PullRequest
3 голосов
/ 11 ноября 2009

Мне нужно, чтобы блоки определялись и вызывались в пределах класса, используя instance_exec (через Rails 2.3.2). Однако некоторые из этих блоков должны возвращаться рано в некоторых ситуациях, что вызывает у меня проблему.

Мое приложение было построено с использованием ruby ​​1.8.6, но мне нужно запустить его и на 1.8.7. Кажется, что между двумя версиями была удалена возможность возврата изнутри лямбды. Следующее работает в 1.8.6, но выдает LocalJumpError (неожиданный возврат) в 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

Я решил, что смогу заставить его работать в 1.8.7, если заменит return в своем блоке на next, но next square(n) if n < 5 приведет к nil в 1.8.6.

Есть ли способ заставить это работать как в 1.8.6, так и в 1.8.7? Я знаю, что могу реструктурировать свои блоки для использования ветвления вместо раннего возврата, но некоторые блоки являются более сложными и имеют несколько ситуаций, когда требуется ранний возврат.

Кроме того, это изменится, если я хочу, чтобы мой код работал в ruby ​​1.9?

Редактировать: Я обнаружил, что причина, по которой он работает в 1.8.6, а не в 1.8.7, в том, что 1.8.7 определяет свой собственный instance_exec в источнике C, тогда как 1.8.6 использует Реализация рельсов. Если я переопределю instance_exec в 1.8.7 с версией Rails, он тоже будет работать.

1 Ответ

1 голос
/ 11 ноября 2009

Редактировать после комментариев См. сообщение для деталей.

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

Этот код ничего не дает, так как он возвращается за пределами операторов put.

Способ работы процедур и лямбд изменился в 1.9. Так что это помогает объяснить, что происходит.

Оригинал

Я реорганизовал ваш код, и он работал под всеми 3 виртуальными машинами. Интересно, что ваш код выполнялся под 1.9 без исключения.

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

Эта запись может иметь некоторую проницательность.

...