Независимо от того, новичок вы в программировании или в Ruby, вы быстро столкнетесь с идеей блока. И хотя использование блоков — это то, к чему вы быстро привыкнете, важно понимать всю картину блоков при изучении более сложных концепций, таких как замыкание.

Как это часто бывает в Ruby, наш блок — это, как это звучит, блок кода. Этот блок кода может быть простым однострочным оператором или набором из многих строк кода. Есть два основных способа создания блока. Во-первых, поместите блок между ключевыми словами do и end.

[1,2,3,4,5].map do |element|
    element * 2
end
=> [2, 4, 6, 8, 10]

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

[1,2,3,4,5].map{|element| element * 2}
=> [2, 4, 6, 8, 10]

В обоих этих примерах к нашему массиву [1,2,3,4,5] вызывается метод map. Говорят, что этот метод принимает блок, в данном случае |element| element * 2. Вторая часть блока, element * 2, представляет собой код, который выполняется для каждого элемента массива. Первая часть блока, |element|, является аргументом, предоставляемым нам методом map при каждом запуске нашего блока. Этот аргумент предоставляется нам map через ключевое слово yield. Чтобы понять, как используется ключевое слово yield, мы можем взглянуть на реализацию метода, подобного карте.

class Array
    def my_map
        mapped_arr = []
        self.each {|e| mapped_arr << yield(e)}
        mapped_arr
    end
end

В этом примере мы исправили класс Array с помощью метода my_map. Когда моя карта вызывается для массива, она возвращает новый массив, в котором каждый элемент сопоставлен с новым значением на основе предоставленного блока. Но как функция my_map получает сопоставленное значение для каждого элемента? Это дает значение, используя ключевое слово yield. Это говорит ruby, что этот метод должен запускать код в предоставленном блоке. Это похоже на вызов метода, который вы определили для каждого значения, но имеет большую гибкость, поскольку мы можем изменить код, который запускается, передав другой блок.

Важный сценарий, который следует учитывать, — это если метод, использующий yield, вызывается без блока. Если это не проверено, то Ruby выдаст ошибку «Блок не указан (выход)». Чтобы подобный вызов не нарушил наш код, мы можем использовать метод block_given?, чтобы проверить, был ли предоставлен блок методу.

def my_map
    mapped_arr = []
    if block_given?
        self.each {|e| mapped_arr << yield(e)}
    else
        puts "No Block Provided"
        return nil
    end
    mapped_arr
end

Теперь мы убеждаемся, что методу был предоставлен блок для запуска

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

x=11
[1,2,3,4,5].map do |element; x|
    x=element
    puts x
    element * 2
end
puts x

Здесь мы использовали точку с запятой после аргумента блока, предоставленного нам картой, а затем перечислили локальную переменную блока x. Будет выведен следующий код:

1
2
3
4
5
11

Это показывает, что наши манипуляции с x внутри блока не повлияли на нашу переменную с тем же именем за пределами блока.

Спасибо за чтение! Это первая запись в серии постов о закрытии в Ruby, так что следите за обновлениями.