Spec-Zone.ru › Ruby 2.7

Присваивание

В Ruby присваивание использует символ = (знак равенства). В этом примере число пять присваивается локальной переменной v:

v = 5

Присваивание создаёт локальную переменную, если переменная ранее не была использована.

Имена локальных переменных

Имя локальной переменной должно начинаться с прописной буквы латинского алфавита или символа с установленным восьмым битом. Обычно локальные переменные совместимы с 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 разбирает выражение слева от 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.

Неявное присваивание массива

Вы можете неявно создать массив, перечислив несколько значений при присваивании:

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 во время присваивания, используя скобки:

(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–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API