ruby: может ли блок влиять на локальные переменные в методе? - PullRequest
11 голосов
/ 08 января 2009

Я просто изучаю ruby ​​и пытаюсь понять объем кода, выполняемого в блоках. Например, я хочу иметь возможность создать блок, который влияет на метод, к которому он прикреплен, например:

def test(&block)
  block.call() if block_given?
  puts "in test, foo is #{foo}"
  puts "in test, bar is #{bar}"
end

test() {
  foo="this is foo"
  bar="this is bar"
}

В этом случае я вообще не хочу изменять блок - я хочу иметь возможность писать его, используя простые ссылки на переменные и без параметров. Только путем внесения изменений в метод 'test' в приведенном выше примере можно ли получить доступ к переменным, определенным в блоке?

Опять же, цель состоит в том, чтобы оставить блок без изменений, но иметь возможность доступа к созданным переменным изнутри 'test' после выполнения блока.

Ответы [ 4 ]

11 голосов
/ 08 января 2009

Прежде всего, block.call() выполняется с yield, и вам не нужен параметр &block.

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

def test()
  foo = yield if block_given?
  puts "in test, foo is #{foo}"
end

test() {
  foo="this is foo"
}

Но это только побочный эффект, потому что foo "возвращается" блоком. Если вы вместо этого сделаете это:

def test()
  foo = yield if block_given?
  puts "in test, foo is #{foo}"
end

test() {
  foo="this is foo"
  "ha ha, no foo for you"
}

Вы заметите, что он делает что-то другое.

Вот еще волшебство:

def test(&block)
   foo = eval "foo", block.binding
   puts foo
   block.call
   foo = eval "foo", block.binding
   puts foo
end

foo = "before test"
test() {
  foo = "after test"
  "ha ha, no foo for you"
}

Это бы сработало, но оно сломается, если вы удалите foo = "before test", потому что foo становится локальной переменной в блоке и не существует в привязке.

Сводка: вы не можете получить доступ к локальным переменным из блока, только к локальным объектам, в которых был определен блок, и возвращаемому значению блока.

Даже это не сработает:

def test(&block)
   eval "foo = 'go fish'", block.binding
   block.call
   bar = eval "foo", block.binding
   puts bar
end

потому что foo в привязке отличается от локального в блоке (я этого не знал, спасибо).

3 голосов
/ 08 января 2009

Нет, блок не может влиять на локальные переменные в том месте, где он вызывается.

Блоки в Ruby - это замыкания , что означает, что они создают область вокруг себя при создании. Переменные, которые видны при создании блока, это те, которые он видит. Если бы в верхней части вашего кода были foo и bar вне какого-либо метода, этот блок изменит их при вызове.

2 голосов
/ 08 января 2009

Вы можете делать, что хотите, немного более многословно:

class Test
  def foo(t)
    @foo = t
  end
  def bar(t)
    @bar = t
  end
  def test(&block)
    self.instance_eval &block if block_given?
    puts "in test, foo is #{@foo}"
    puts "in test, bar is #{@bar}"
  end
end

Test.new.test() {
  foo "this is foo"
  bar "this is bar"
}

Вы можете создать такие методы, как attr_accessor, которые будут генерировать соответствующий установщик (методы foo и bar).

0 голосов
/ 08 января 2009
def test(&block)
  foo = yield
  puts "in test, foo is #{foo}"
end

test { "this is foo" }

отпечатков in test, foo is this is foo

Значение yield - это значение блока.

Вы также можете передать параметры в yield, который затем будет доступен для блока с помощью | param, другой | в начале блока.

Также ознакомьтесь с процедурами.

foo = "this is foo"
p = Proc.new { "foo is #{foo}" }
p.call

Отпечатки "foo is this is foo"

def test(p) 
  p.call
end

test p

Отпечатки "foo is this is foo"

def test2(p)
  foo = "monkey"
  p.call
end

test2 p

Отпечатки "foo is this is foo"

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...