Подтвердить что ты не робот

Область блока в рубине

Мое понимание заключалось в том, что блоки ruby ​​имеют область блока, и все переменные, созданные внутри блока, будут жить только внутри блока.

Пример:

 food = ['toast', 'cheese', 'wine']
 food.each { |food| puts food.capitalize}
 puts food

Вывод:

"Toast"
"Cheese"
"Wine"
"Wine"

Если вы берете переменную food внутри блока (каждый блок), я понимаю, что она имеет область блокировки. Он живет только в пределах области блока и не влияет на внешнюю переменную food.

Но поведение отличается, внешняя переменная с именем food изменяется в этом случае. Правильно ли это понимание? В рубине у нас есть область охвата?

4b9b3361

Ответ 1

Это ожидаемое поведение для ruby ​​1.8. Это было исправлено в 1.9. Ниже приведены фрагменты ниже с ruby ​​1.9.3

food = ['toast', 'cheese', 'wine']
food.each { |food| puts food.capitalize.inspect} # !> shadowing outer local variable - food
puts food.inspect
# >> "Toast"
# >> "Cheese"
# >> "Wine"
# >> ["toast", "cheese", "wine"]

Вы правы, food из блока привязан к этому блоку и тени других переменных с этим именем. Но если вы сделаете что-то разрушительное для него, оно будет отражено в исходном массиве, потому что это ссылка на элемент массива, а не на его копию. Обратите внимание:

food = ['toast', 'cheese', 'wine']

food.each { |f| f.capitalize} # transform and discard
food # => ["toast", "cheese", "wine"]

food.each { |f| f.capitalize! } # transform destructively (bang-version)
food # => ["Toast", "Cheese", "Wine"]

Ответ 2

Блок наследует область действия из контекста, в котором она определена. Взгляните на этот пример:

def give_me_a_proc
  test = "foo"
  lambda { puts test }
end

test = "bar"
give_me_a_proc.call

# => "foo"

Итак, неважно, где выполняется proc/block, а скорее там, где он был определен.

В вашем случае переменная food внутри блока действительно затеняет массив food извне. Но вы действительно используете фактические элементы (не дубликаты/клоны) массива food в блоке, поэтому любые изменения в них будут отображаться вне.

Причина, по которой массив food изменяется на строку "Wine" после завершения блока, состоит в том, что блок работает в той же области действия, что и массив food, поэтому он перезаписывает его последним элементом массива, поскольку это последний объект, который присваивается переменной food.

Ответ 3

Эта ошибка была исправлена ​​в Ruby 1.9

food = ['toast', 'cheese', 'wine']
food.each { |food| puts food.capitalize }
puts food # ["toast", "cheese", "wine"]

Я думаю, что если вы хотите использовать локальную переменную в Ruby 1.8, попробуйте lambda (Извините, я не устанавливаю Ruby 1.8, поэтому не могу ее протестировать, но работаю в версии 1.9)

food = ['toast', 'cheese', 'wine']
lambda do |food|
    food.each {|food| puts food.capitalize}
end.call(food)
puts food # ["toast", "cheese", "wine"]