ruby: может ли блок влиять на локальные переменные в методе?

import simplejson

class PrettyFloat(float):
    def __repr__(self):
        return '%.15g' % self

def pretty_floats(obj):
    if isinstance(obj, float):
        return PrettyFloat(obj)
    elif isinstance(obj, dict):
        return dict((k, pretty_floats(v)) for k, v in obj.items())
    elif isinstance(obj, (list, tuple)):
        return map(pretty_floats, obj)             
    return obj

print simplejson.dumps(pretty_floats([23.67, 23.97, 23.87]))

испускает

[23.67, 23.97, 23.87]

Не требуется привязка к monkey.

11
задан Bryan Oakley 8 January 2009 в 00:53
поделиться

4 ответа

В первую очередь, 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 в привязке отличается от локального в блоке (я не знал это, спасибо).

11
ответ дан 3 December 2019 в 08:05
поделиться

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

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

3
ответ дан 3 December 2019 в 08:05
поделиться

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

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 это генерирует соответствующий метод set ( foo и bar методы).

2
ответ дан 3 December 2019 в 08:05
поделиться
def test(&block)
  foo = yield
  puts "in test, foo is #{foo}"
end

test { "this is foo" }

печать in test, foo is this is foo

Значение урожая является значением блока.

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

Кроме того, выезд procs.

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"

-1
ответ дан 3 December 2019 в 08:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: