Присвоение
В 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] b = *1 p b # prints [1]
Вы можете использовать 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.