Ruby — один из самых популярных языков, используемых в сети. Мы начали новую серию скринкастов здесь, на Nettuts +, которая познакомит вас с Ruby, а также с отличными фреймворками и инструментами, которые сопровождают разработку на Ruby. В этой главе мы поговорим о блоках и итераторах.
Наверстать
- Урок 1. Установка Ruby и начало работы
- Урок 2. Переменные, типы данных и файлы
- Урок 3: Работа с классами
- Урок 4: условные выражения и циклы
- Урок 5. Итераторы и блоки
Посмотреть скринкаст
Блоки
На последнем уроке мы говорили о петлях. На самом деле вы не будете использовать циклы в Ruby слишком часто из-за функции, называемой блоками (и — в результате блоков — итераторами). Чтобы освежить вашу память, посмотрите на два следующих вызова метода (вы можете попробовать это в IRB):
1
2
3
4
5
|
name = «Joe»
name.reverse # => «eoJ»
name.concat(» the Plumber») # => «Joe the Plumber»
|
Как вы знаете, круглые скобки после вызова метода обычно необязательны. Мы узнаем сегодня, когда они требуются.
Итак, вот части вызова метода:
- Объект, получающий метод;
name
выше. - Точка
- Название метода;
reverse
илиconcat
выше - Аргументы;
" the Plumber"
во втором примере выше. - Блок кода; Будьте на связи!
Первые три части требуются, очевидно. Аргументы и блок кода являются необязательными. Что это за блок кода? Посмотрите на этот пример, и тогда мы обсудим это:
1
2
3
4
5
6
7
|
sites = [«net», «psd», «mobile»]
sites.map!
site += «.tutsplus.com»
end
sites # => [«net.tutsplus.com», «psd.tutsplus.com», «mobile.tutsplus.com»]
|
В этом случае массив sites
является получателем; метод map!
, Далее у нас есть блок. Если блок состоит из нескольких строк, вы можете использовать ключевые слова do
и end
для его разделения. Если вы помещаете его в одну строку, вы можете использовать фигурные скобки (это работает и для многострочных блоков).
После открытия блока у нас есть параметры блока внутри труб ( |
). Что это на самом деле зависит от метода, который вы выполняете. Чаще всего блоки используются в методах итераторов, поэтому параметр блока будет текущим элементом цикла. Если это звучит довольно абстрактно, мы сделаем несколько примеров.
итераторы
Начнем с рассмотрения итераторов массивов, потому что они чаще всего зацикливаются на чем-либо.
Вместо использования цикла for вы, вероятно, будете использовать each
:
1
2
3
4
5
6
7
8
|
sites = [«net», «psd», «mobile»]
sites.each { |site|
puts «#{site}.tutsplus.com»
}
# net.tutsplus.com
# psd.tutsplus.com
# mobile.tutsplus.com
|
Это похоже на цикл for; один за другим каждый элемент на sites
будет назначен параметру блока site
; тогда код внутри блока будет выполнен.
Если вам интересно, each
метод возвращает исходный массив.
Иногда вам захочется вернуть значение из блока. Это не сложно сделать, если вы используете правильный метод.
1
2
3
4
5
|
# assume sites above
sites = sites.map do |s|
«#{s}.tutsplus.com»
end
|
Метод map
собирает все значения, возвращаемые на каждой итерации блока. Затем массив этих значений возвращается из метода. В этом случае мы переназначаем переменную sites
в новый массив.
Хотя есть лучший способ сделать это. Некоторые методы Ruby имеют дубликаты с восклицательным знаком (или ударом); это означает, что они разрушительны: они заменяют ценность, над которой они работают. Таким образом, вышесказанное можно сделать следующим образом:
1
|
sites.map!
|
Теперь на sites
будет массив значений, возвращаемых из блока.
Однако, не только у массивов есть методы итератора. Числа имеют довольно крутой метод times
:
1
2
3
4
5
6
7
8
9
|
5.times do |i|
puts «Loop number #{i}»
end
# Loop number 0
# Loop number 1
# Loop number 2
# Loop number 3
# Loop number 4
|
Продолжая кодировать Ruby, вы найдете много полезных методов, которые используют блоки. Теперь давайте посмотрим, как создавать наши собственные блоки.
Строительные блоки
Теперь, когда вы знакомы с использованием блоков, давайте посмотрим, как писать методы, использующие их преимущества. Вот два других лакомых кусочка, которые вы еще не изучили:
- Параметры блока не обязательны; Вы можете написать блок, который не использует их.
- Сами блоки могут быть необязательными. Вы можете написать функцию, которая работает с или без блоков.
Вот что происходит, когда вы вызываете метод, который принимает блок. За кулисами Ruby выполняет некоторый код метода, затем уступая блочному коду. После этого управление возвращается методу. Давайте проверим это.
Поскольку большинство простых итерационных функций встроено в Ruby, мы «переписаем» одну из них. Давайте сделаем each
метод на массивах:
01
02
03
04
05
06
07
08
09
10
|
class Array
def each2
i = 0;
while self[i]
yield self[i]
i += 1
end
self
end
end
|
Как видите, это обычный метод. Помните, что в методе экземпляра ключевое слово self
ссылается на экземпляр класса, в данном случае Array
. Внутри метода мы используем цикл while для циклического перемещения по элементам в массиве. Затем внутри цикла мы используем ключевое слово yield
. Мы передаем это self[i]
; в конечном итоге это будет параметр блока. После этого мы увеличиваем i
для цикла и продолжаем.
Если бы мы хотели, чтобы этот метод возвращал массив значений, которые возвращал блок, мы могли бы просто захватить возвращенное значение yield
и вернуть, что вместо self
мы возвращаем этот массив.
01
02
03
04
05
06
07
08
09
10
11
|
class Array
def each2_returning_new_values
i = 0;
new_vals = [];
while self[i]
new_vals[i] = yield self[i]
i += 1
end
new_vals
end
end
|
Методы, пересмотренные
Давайте закончим разговор о методах. Мы знаем, что использование скобок необязательно … большую часть времени. Вот когда вам нужно использовать круглые скобки при вызове метода.
Иногда у вас будут как параметры метода, так и блок.
1
2
3
|
obj.some_method «param» { |x|
#block code here
}
|
То, что я только что сделал, не сработает; нам нужно использовать круглые скобки в этом случае, потому что в противном случае блок связан с последним параметром. Это только в том случае, если вы используете фигурные скобки для разделения блока; если вы используете do
— end
, скобки не требуются.
В другом случае скобки требуются, когда вы передаете литеральный хеш (не переменную, которая указывает на хеш) в качестве первого параметра метода. Руби будет думать, что это блок из-за фигурной скобки
1
2
|
arr.push { :name => «Andrew» } # Fails!
arr.push({ :name => «Andrew» }) # Passes
|