В этой статье мы обсудим привязку и то, как она влияет на область видимости. Если вы хотите освежить в памяти область видимости переменных перед тем, как заняться более сложной темой, см. мою предыдущую статью здесь. Прежде чем мы сможем углубиться в привязку, мы должны сначала понять замыкания, поэтому сначала краткое введение в замыкания.

Замыкание — это общая концепция программирования, которая существует во всех языках. Это относится к «фрагменту кода», который можно сохранить и выполнить позже, возможно, из другого места. Замыкания работают, связывая свои окружающие артефакты, которые могут включать переменные, методы или другие части данных. Связывание — это отношение, которое замыкания делят с окружающими артефактами (методами, переменными, константами) своей области после определения. Замыкания могут получать доступ и/или обновлять окружающие артефакты независимо от того, когда или где закрытие вызывается позже. Привязка создает оболочку вокруг всех артефактов (переменных, методов, констант) на основе области видимости при определении замыкания, чтобы на них можно было ссылаться при выполнении замыкания.

В Ruby блоки, объекты Proc и Lambda — все это замыкания. Может быть полезно отметить, что Proc — это объект, созданный из класса Proc, лямбда — это особый вид объекта Proc, а блок не является объектом. Это важно понимать, потому что блок не является объектом, поэтому это не значение, которое мы можем вернуть. Однако Proc — это объект, а также замыкание. Это означает, что мы можем назначать замыкания переменным и передавать их в виде Proc или Lambda. Это также означает, что мы можем вернуть их либо из метода, либо из блока. Важно осознавать, что замыкания могут быть возвращены и переданы другим методам, потому что это дает нам доступ к данным, которые в противном случае были бы вне области видимости. Это также позволяет нам передавать функциональность. Это позволяет использовать СУХОЙ и гибкий код. Также важно помнить, что замыкания, возвращаемые методом и блоками, которые определены в методах и/или блоках, будут иметь доступ к артефактам в рамках этих методов и/или блоков посредством привязки, что позволяет возвращаемому замыканию ссылаться на и/или изменить эти артефакты позже.

Пример привязки с Proc:

def give_info(element)
  puts element.call 
end
number = '123-456-7891'
my_proc = proc { number }
give_info(my_proc)
# => 123-456-7891

В этом примере мы определяем метод give_info для lines 1-3 с 1 параметром. Мы инициализируем локальную переменную number в line 4, что выходит за рамки метода give_info. В line 5 мы определяем объект Proc и назначаем его локальной переменной my_proc, чтобы мы могли передавать его позже. Определяя объект Proc, мы создаем замыкание, которое создает привязку ко всем объектам в пределах его области действия. В этом случае локальная переменная number и метод give_info находятся в области действия Proc в это время и являются частью его привязки. Таким образом, когда мы вызываем метод give_info для line 7 и передаем нашу процедуру в качестве аргумента, метод give_info имеет доступ к локальной переменной с number по my_proc. Этот пример показывает нам, что замыкание (процедура) привязано к локальной переменной number и может получить к ней доступ, даже если она вызывается внутри метода, где локальная переменная обычно недоступна, если она явно не передается в качестве аргумента при вызове метода. .

Чтобы локальные переменные были частью привязки замыканий, они должны быть инициализированы до создания замыкания, если только они не переданы в замыкание явно. Например:

def give_info(element)
  puts element.call 
end
my_proc = proc { number }
number = '123-456-7891'
give_info(my_proc)
# Raises NameError: undefined local variable or method `number`

Приведенный выше код вызывает ошибку, так как локальная переменная number инициализируется после создания замыкания и, следовательно, не находится в области видимости во время создания замыкания. Однако, если мы инициализируем локальную переменную перед созданием замыкания, получаем к ней доступ внутри замыкания, а затем переназначаем эту локальную переменную, последнее присвоенное значение будет отражено при выполнении замыкания:

def give_info(element)
  puts element.call 
end
number = '123-456-7891'
my_proc = proc { number }
number = '847-123-4567'
give_info(my_proc)
# => 847-123-4567

В этом примере Proc, на который ссылается my_proc, знает, что новое значение было присвоено number, хотя переназначение происходит после создания Proc.

Пример привязки к Блоку:

arr = ['a', 'b', 'c']
def foo(array)
  array.map do |el|
    yield(el)
  end
end
foo(arr) do |value|
  value << "a"
end
p arr # ==> ["aa", "ba", "ca"]
hsh = { a: 3, b: 2, c: 5 }
def bar(qux)
  qux  += 2
end

Выше вы можете видеть, как замыкание (в данном случае блок) связывает окружающие локальные переменные и ссылается на них во время выполнения. Начнем с определения локальной переменной arr на line 1 и присвоения ее массиву. Мы определяем метод foo для lines 3-7 и вызываем этот метод для line 9 с блоком. Сам блок непосредственно следует за вызовом метода, начиная с ключевого слова do и содержащего весь код до ключевого слова end. В нашей блочной реализации у нас есть доступ к локальной переменной arr, которая была инициализирована line 1. Это связано с тем, что блоки имеют доступ к переменным, инициализированным во внешней области. Замыкание, созданное блоком, может сохранять память об этой переменной и ее значении через привязку. Мы вызываем этот блок из метода foo с оператором yield для line 3. Код внутри блока изменяет массив. Поэтому, когда мы выведем массив arr на line 13, мы увидим обновленное значение ["aa", "ba", "ca"].

Локальные переменные должны быть определены до создания замыкания, чтобы быть включенными в его привязку, поэтому arr, инициализируемый в line 1, является частью привязки, созданной при создании замыкания в lines 9-11. Кроме того, сам метод foo также является частью привязки.

Эта статья может сбить с толку, если читатель плохо разбирается в области видимости переменных, а также в области видимости переменных, связанной с определениями методов, вызовами методов и блоками. Чтобы освежить в памяти, пожалуйста, посмотрите мою статью о переменной области видимости.