Присваивание
В Ruby присваивание использует символ = (знак равенства). В этом примере число пять присваивается локальной переменной v:
v = 5
Присваивание создаёт локальную переменную, если переменная ранее не упоминалась.
Результат выражения присваивания всегда является присвоенным значением, включая методы присваивания.
Имена локальных переменных
Имя локальной переменной должно начинаться с строчной буквы US-ASCII или символа с установленным восьмым битом. Обычно локальные переменные совместимы с US-ASCII, так как клавиши для их набора присутствуют на всех клавиатурах.
(Программы Ruby должны быть написаны в наборе символов, совместимом с US-ASCII. В таких наборах символов, если установлен восьмой бит, это указывает на расширенный символ. Ruby допускает локальные переменные, содержащие такие символы.)
Имя локальной переменной может содержать буквы, цифры, символ _ (подчеркивание) или символ с установленным восьмым битом.
Область видимости локальных переменных
После присваивания имени локальной переменной все последующие использования этого имени в рамках области видимости считаются локальными переменными.
Вот пример:
1.times do
a = 1
puts "local variables in the block: #{local_variables.join ", "}"
end
puts "no local variables outside the block" if local_variables.empty?
Это выводит:
local variables in the block: a no local variables outside the block
Так как блок создаёт новую область видимости, любые локальные переменные, созданные внутри него, не влияют на окружающую область видимости.
Переменные, определённые во внешней области видимости, видны внутри внутренней области видимости:
a = 0
1.times do
puts "local variables: #{local_variables.join ", "}"
end
Это выводит:
local variables: a
Вы можете изолировать переменные в блоке от внешней области видимости, перечислив их после ; в аргументах блока. См. документацию по локальным переменным блоков в документации вызова методов для примера.
См. также Kernel#local_variables, но обратите внимание, что цикл for не создаёт новую область видимости, как это делает блок.
Локальные переменные и методы
В Ruby имена локальных переменных и имена методов практически идентичны. Если вы не присвоили значение одному из этих неоднозначных имён, Ruby предположит, что вы хотите вызвать метод. После того, как вы присвоили значение имени, Ruby предположит, что вы хотите обратиться к локальной переменной.
Локальная переменная создаётся, когда парсер встречает присваивание, а не когда происходит само присваивание:
a = 0 if false # does not assign to a p local_variables # prints [:a] p a # prints nil
Сходство между именами методов и локальных переменных может привести к запутанному коду, например:
def big_calculation 42 # pretend this takes a long time end big_calculation = big_calculation()
Теперь любая ссылка на big_calculation считается локальной переменной и будет кэширована. Для вызова метода используйте self.big_calculation.
Вы можете принудительно вызвать метод, используя пустые скобки аргументов, как показано выше, или используя явный получатель, как self. Использование явного получателя может вызвать NameError, если видимость метода не public или получатель — это литерал self.
Другой часто путающий случай возникает при использовании модификатора if:
p a if a = 0.zero?
Вместо вывода «true» вы получаете NameError, «неопределённая локальная переменная или метод ‘a’». Поскольку Ruby парсит «голое» a слева от if в первую очередь и ещё не видел присваивания a, он предполагает, что вы хотите вызвать метод. Затем Ruby видит присваивание a и предположит, что вы обращаетесь к локальному методу.
Путаница возникает из-за выполнения выражения в неправильной последовательности. Сначала локальной переменной присваивается значение, а затем вы пытаетесь вызвать несуществующий метод.
Локальные переменные и eval
Использование eval для оценки кода Ruby позволит получить доступ к локальным переменным, определённым в той же области видимости, даже если локальные переменные определяются после вызова eval. Однако локальные переменные, определённые внутри вызова eval не будут отражаться в окружающей области видимости. Внутри вызова eval, локальные переменные, определённые в окружающей области видимости, и локальные переменные, определённые внутри вызова eval будут доступны. Однако вы не сможете получить доступ к локальным переменным, определённым в предыдущих или последующих вызовах eval в той же области видимости. Каждый вызов eval — это отдельная вложенная область видимости. Пример:
def m eval "bar = 1" lvs = eval "baz = 2; ary = [local_variables, foo, baz]; x = 2; ary" eval "quux = 3" foo = 1 lvs << local_variables end m # => [[:baz, :ary, :x, :lvs, :foo], nil, 2, [:lvs, :foo]]
Переменные экземпляра
Переменные экземпляра разделяются между всеми методами для одного и того же объекта.
Переменная экземпляра должна начинаться со знака @ («со знаком @» или коммерческим знаком «@»). В противном случае имена переменных экземпляра следуют правилам, как и имена локальных переменных. Поскольку переменная экземпляра начинается со знака @, второй символ может быть заглавной буквой.
Вот пример использования переменной экземпляра:
class C
def initialize(value)
@instance_variable = value
end
def value
@instance_variable
end
end
object1 = C.new "some value"
object2 = C.new "other value"
p object1.value # prints "some value"
p object2.value # prints "other value"
Неинициализированная переменная экземпляра имеет значение nil. Если вы запускаете Ruby с включёнными предупреждениями, вы получите предупреждение при обращении к неинициализированной переменной экземпляра.
Метод value имеет доступ к значению, установленному методом initialize, но только для того же объекта.
Class Переменные
Class переменные разделяются между классом, его подклассами и его экземплярами.
Переменная класса должна начинаться со знака @@ (два знака «@»). Остальная часть имени следует тем же правилам, что и переменные экземпляра.
Вот пример:
class A
@@class_variable = 0
def value
@@class_variable
end
def update
@@class_variable = @@class_variable + 1
end
end
class B < A
def update
@@class_variable = @@class_variable + 2
end
end
a = A.new
b = B.new
puts "A value: #{a.value}"
puts "B value: #{b.value}"
Это выводит:
A value: 0 B value: 0
Продолжая тот же пример, мы можем обновить его, используя объекты из любого класса, и значение будет разделено:
puts "update A"
a.update
puts "A value: #{a.value}"
puts "B value: #{b.value}"
puts "update B"
b.update
puts "A value: #{a.value}"
puts "B value: #{b.value}"
puts "update A"
a.update
puts "A value: #{a.value}"
puts "B value: #{b.value}"
Это выводит:
update A A value: 1 B value: 1 update B A value: 3 B value: 3 update A A value: 4 B value: 4
Обращение к неинициализированной переменной класса вызовет исключение NameError.
Обратите внимание, что классы имеют переменные экземпляра, поскольку классы — это объекты, поэтому не путайте переменные класса и экземпляра.
Глобальные переменные
Глобальные переменные доступны везде.
Глобальные переменные начинаются со знака $ (знак доллара). Остальная часть имени следует тем же правилам, что и переменные экземпляра.
Вот пример:
$global = 0
class C
puts "in a class: #{$global}"
def my_method
puts "in a method: #{$global}"
$global = $global + 1
$other_global = 3
end
end
C.new.my_method
puts "at top-level, $global: #{$global}, $other_global: #{$other_global}"
Это выводит:
in a class: 0 in a method: 0 at top-level, $global: 1, $other_global: 3
Неинициализированная глобальная переменная имеет значение nil.
Ruby имеет некоторые специальные глобальные переменные, которые ведут себя по-разному в зависимости от контекста, такие как переменные сопоставления с регулярными выражениями, или у которых есть побочный эффект при присваивании. См. документацию по глобальным переменным для получения дополнительной информации.
Методы присваивания
Вы можете определить методы, которые будут вести себя как присваивание, например:
class C
def value=(value)
@value = value
end
end
c = C.new
c.value = 42
Использование методов присваивания позволяет вашим программам выглядеть лучше. При присваивании переменной экземпляра большинство людей используют Module#attr_accessor:
class C attr_accessor :value end
При использовании метода присваивания вы всегда должны иметь получателя. Если у вас нет получателя, Ruby предположит, что вы присваиваете локальной переменной:
class C
attr_accessor :value
def my_method
value = 42
puts "local_variables: #{local_variables.join ", "}"
puts "@value: #{@value.inspect}"
end
end
C.new.my_method
Это выводит:
local_variables: value @value: nil
Для использования метода присваивания необходимо установить получателя:
class C
attr_accessor :value
def my_method
self.value = 42
puts "local_variables: #{local_variables.join ", "}"
puts "@value: #{@value.inspect}"
end
end
C.new.my_method
Это выводит:
local_variables: @value: 42
Обратите внимание, что возвращаемое значение метода присваивания игнорируется, так как результат выражения присваивания всегда является присвоенным значением.
Сокращённое присваивание
Вы можете смешивать несколько операторов и присваивания. Чтобы добавить 1 к объекту, вы можете написать:
a = 1 a += 2 p a # prints 3
Это эквивалентно:
a = 1 a = a + 2 p a # prints 3
Вы можете использовать следующие операторы таким образом: +, -, *, /, %, **, &, |, ^, <<, >>
Также есть ||= и &&= операторы. Первый выполняет присваивание, если значение было nil или false, а второй выполняет присваивание, если значение не было nil или false.
Вот пример:
a ||= 0 a &&= 1 p a # prints 1
Обратите внимание, что эти два оператора ведут себя скорее как a || a = 0 чем a = a || 0.
Неявное присваивание Array
Вы можете неявно создать массив, перечислив несколько значений при присваивании:
a = 1, 2, 3 p a # prints [1, 2, 3]
Это неявно создаёт Array.
Вы можете использовать * или оператор «splat», или распаковать Array при присваивании. Это аналогично множественному присваиванию:
a = *[1, 2, 3] p a # prints [1, 2, 3]
Вы можете использовать splat в любом месте правой части присваивания:
a = 1, *[2, 3] p a # prints [1, 2, 3]
Множественное присваивание
Вы можете присвоить несколько значений в правой части нескольким переменным:
a, b = 1, 2
p a: a, b: b # prints {:a=>1, :b=>2}
В следующих разделах, где используется «переменная», метод присваивания, экземпляр, класс или глобальная переменная также будут работать:
def value=(value)
p assigned: value
end
self.value, $global = 1, 2 # prints {:assigned=>1}
p $global # prints 2
Вы можете использовать множественное присваивание для обмена значениями двух переменных на месте:
old_value = 1
new_value, old_value = old_value, 2
p new_value: new_value, old_value: old_value
# prints {:new_value=>1, :old_value=>2}
Если в правой части присваивания больше значений, чем переменных в левой части, лишние значения игнорируются:
a, b = 1, 2, 3
p a: a, b: b # prints {:a=>1, :b=>2}
Вы можете использовать * для сбора дополнительных значений в правой части присваивания.
a, *b = 1, 2, 3
p a: a, b: b # prints {:a=>1, :b=>[2, 3]}
* может появиться в любом месте в левой части:
*a, b = 1, 2, 3
p a: a, b: b # prints {:a=>[1, 2], :b=>3}
Но вы можете использовать только один * в присваивании.
Array Разложение
Как и разложение Array в аргументах методов, вы можете разложить Array при присваивании, используя скобки:
(a, b) = [1, 2]
p a: a, b: b # prints {:a=>1, :b=>2}
Вы можете разложить Array как часть более крупного множественного присваивания:
a, (b, c) = 1, [2, 3]
p a: a, b: b, c: c # prints {:a=>1, :b=>2, :c=>3}
Поскольку каждое разложение рассматривается как отдельное множественное присваивание, вы можете использовать * для сбора аргументов в разложении:
a, (b, *c), *d = 1, [2, 3, 4], 5, 6
p a: a, b: b, c: c, d: d
# prints {:a=>1, :b=>2, :c=>[3, 4], :d=>[5, 6]}
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.