• Когда итератор заканчивается левой фигурной скобкой (или словом
end
) и возвращает значение, это значение можно использовать для вызова последующих методов, например:
squares = [1,2,3,4,5].collect do |x| x**2 end.reverse
# squares теперь равно [25,16,9,4,1]
• В конце программы на Ruby часто можно встретить идиому
if $0 == __FILE__
Таким образом проверяется, исполняется ли файл как автономный
кусок кода (
true
) или как дополнительный, например библиотека (
false
). Типичное применение — поместить некую «главную программу» (обычно с тестовым кодом) в конец библиотеки.
• Обычное наследование (порождение подкласса) обозначается символом
<
:
class Dog < Animal
# ...
end
Однако для создания синглетного класса (анонимного класса, который расширяет единственный экземпляр) применяется символ
<<
:
class << platypus
# ...
end
• При передаче блока итератору есть тонкое различие между фигурными скобками (
{}
) и операторными скобками
do-end
. Связано оно с приоритетом:
mymethod param1, foobar do ... end
# Здесь do-end связано с mymethod.
mymethod param1, foobar { ... }
# А здесь {} связано с именем foobar, предполагается, что это метод.
• Традиционно в Ruby однострочные блоки заключают в фигурные скобки, а многострочные — в скобки do-end, например:
my_array.each { |x| puts x }
my_array.each do |x|
print x
if x % 2 == 0
puts " четно."
else
puts " нечетно."
end
end
Это необязательно и в некоторых случаях даже нежелательно.
• Помните, что строки (strings) в некотором смысле двулики: их можно рассматривать как последовательность символов или как последовательность строчек (lines). Кому-то покажется удивительным, что итератор
each
оперирует строками (здесь под «строкой» понимается группа символов, завершающаяся разделителем записей, который по умолчанию равен символу новой строки). У
each
есть синоним
each_line
. Если вы хотите перебирать символы, можете воспользоваться итератором
each_byte
. Итератор
sort
также оперирует строками. Для строк (strings) не существует итератора
each_index
из-за возникающей неоднозначности. Действительно, хотим ли мы обрабатывать строку посимвольно или построчно? Все это со временем войдет в привычку.
• Замыкание (closure) запоминает контекст, в котором было создано. Один из способов создать
замыкание — использование объекта
Proc
. Например:
def power(exponent)
proc {|base| base**exponent}
end
square = power(2)
cube = power(3)
a = square.call(11) # Результат равен 121.
b = square.call(5) # Результат равен 25.
с = cube.call(6) # Результат равен 216.
d = cube.call(8) # Результат равен 512.
Обратите внимание, что замыкание «знает» значение показателя степени, переданное ему в момент создания.
• Однако помните: в замыкании используется переменная, определенная во внешней области видимости (что вполне допустимо). Это свойство может оказаться полезным, но приведем пример неправильного использования:
$exponent = 0
def power
proc {|base| base**$exponent}
end
$exponent = 2
square = power
$exponent = 3
cube = power
a = square.call(11) # Неверно! Результат равен 1331.
b = square.call(5) # Неверно! Результат равен 125.
# Оба результата неверны, поскольку используется ТЕКУЩЕЕ
# значение $exponent. Так было бы даже в том случае, когда
# используется локальная переменная, покинувшая область
# видимости (например, с помощью define_method).
с = cube.call(6) # Результат равен 216.
d = cube.call(8) # Результат равен 512.
• Напоследок рассмотрим несколько искусственный пример. Внутри блока итератора
times
создается новый контекст, так что
x
— локальная переменная. Переменная
closure
уже определена на верхнем уровне, поэтому для блока она не будет локальной.
closure = nil # Определим замыкание, чтобы его имя было известно.