Spec-Zone.ru › Ruby 3.2

Вызов методов

Вызов метода отправляет сообщение объекту, чтобы он мог выполнить некоторую работу.

В Ruby вы отправляете сообщение объекту так:

my_method()

Обратите внимание, что скобки являются необязательными:

my_method

За исключением случаев, когда использование и опускание скобок различаются, в этом документе используются скобки при наличии аргументов, чтобы избежать путаницы.

Этот раздел рассматривает только вызов методов. Также см. документацию по синтаксису определения методов.

Получатель

self является получателем по умолчанию. Если вы не укажете получателя, self будет использован. Чтобы указать получателя, используйте .:

my_object.my_method

Это отправляет сообщение my_method объекту my_object. Любой объект может быть получателем, но в зависимости от видимости метода отправка сообщения может вызвать NoMethodError.

Вы также можете использовать :: для обозначения получателя, но это редко используется из-за потенциальной путаницы с :: для пространств имён.

Цепочечные вызовы методов

Вы можете «цеплять» вызовы методов, сразу следуя одному вызову метода другим.

В этом примере цепляются методы Array#append и Array#compact:

a = [:foo, 'bar', 2]
a1 = [:baz, nil, :bam, nil]
a2 = a.append(*a1).compact
a2 # => [:foo, "bar", 2, :baz, :bam]

Детали:

  • Первый метод merge создаёт копию a, добавляет (отдельно) каждый элемент a1 в копию и возвращает

    [:foo, "bar", 2, :baz, nil, :bam, nil]
    
  • Цепной метод compact создаёт копию этого возвращаемого значения, удаляет его nil-значения и возвращает

    [:foo, "bar", 2, :baz, :bam]
    

Вы можете цеплять методы из разных классов. В этом примере цепляются методы Hash#to_a и Array#reverse:

h = {foo: 0, bar: 1, baz: 2}
h.to_a.reverse # => [[:baz, 2], [:bar, 1], [:foo, 0]]

Детали:

  • Первый метод Hash#to_a преобразует a в массив и возвращает

    [[:foo, 0], [:bar, 1], [:baz, 2]]
    
  • Цепной метод Array#reverse создаёт копию этого возвращаемого значения, переворачивает её и возвращает

    [[:baz, 2], [:bar, 1], [:foo, 0]]
    

Оператор безопасной навигации

&., называемый «оператором безопасной навигации», позволяет пропустить вызов метода, когда получатель является nil. Он возвращает nil и не оценивает аргументы метода, если вызов пропущен.

REGEX = /(ruby) is (\w+)/i
"Ruby is awesome!".match(REGEX).values_at(1, 2)
# => ["Ruby", "awesome"]
"Python is fascinating!".match(REGEX).values_at(1, 2)
# NoMethodError: undefined method `values_at' for nil:NilClass
"Python is fascinating!".match(REGEX)&.values_at(1, 2)
# => nil

Это позволяет легко цеплять методы, которые могут возвращать пустые значения. Обратите внимание, что &. пропускает только один следующий вызов, поэтому для более длинной цепочки необходимо добавлять оператор на каждом уровне:

"Python is fascinating!".match(REGEX)&.values_at(1, 2).join(' - ')
# NoMethodError: undefined method `join' for nil:NilClass
"Python is fascinating!".match(REGEX)&.values_at(1, 2)&.join(' - ')
# => nil

Аргументы

Существует три типа аргументов при отправке сообщения: позиционные аргументы, ключевые (или именованные) аргументы и аргумент блока. Каждое отправленное сообщение может использовать один, два или все типы аргументов, но аргументы должны быть предоставлены в этом порядке.

Все аргументы в Ruby передаются по ссылке и не вычисляются лениво.

Каждый аргумент разделяется ,:

my_method(1, '2', :three)

Аргументы могут быть выражением, аргументом хэша:

'key' => value

или ключевым аргументом:

key: value

Hash и ключевые аргументы должны быть непрерывными и должны появляться после всех позиционных аргументов, но могут быть смешаны:

my_method('a' => 1, b: 2, 'c' => 3)

Позиционные аргументы

Позиционные аргументы для сообщения следуют за именем метода:

my_method(argument1, argument2)

Во многих случаях скобки не нужны при отправке сообщения:

my_method argument1, argument2

Однако скобки необходимы, чтобы избежать неоднозначности. Это вызовет SyntaxError, потому что Ruby не знает, какой аргумент метода 3 следует отправить:

method_one argument1, method_two argument2, argument3

Если определение метода имеет *argument дополнительные позиционные аргументы будут присвоены argument в методе в качестве Array.

Если определение метода не включает ключевые аргументы, ключевые или аргументы типа хэша присваиваются как один хэш последнему аргументу:

def my_method(options)
  p options
end

my_method('a' => 1, b: 2) # prints: {'a'=>1, :b=>2}

Если предоставлено слишком много позиционных аргументов, будет поднята ArgumentError.

Позиционные аргументы по умолчанию

Когда метод определяет аргументы по умолчанию, вам не нужно предоставлять все аргументы методу. Ruby заполнит отсутствующие аргументы в порядке следования.

Сначала рассмотрим простой случай, когда аргументы по умолчанию появляются справа. Рассмотрим этот метод:

def my_method(a, b, c = 3, d = 4)
  p [a, b, c, d]
end

Здесь c и d имеют значения по умолчанию, которые Ruby применит для вас. Если вы отправите только два аргумента в этот метод:

my_method(1, 2)

Вы увидите, что Ruby выведет [1, 2, 3, 4].

Если вы отправите три аргумента:

my_method(1, 2, 5)

Вы увидите, что Ruby выведет [1, 2, 5, 4]

Ruby заполняет отсутствующие аргументы слева направо.

Ruby позволяет значениям по умолчанию появляться посередине позиционных аргументов. Рассмотрим этот более сложный метод:

def my_method(a, b = 2, c = 3, d)
  p [a, b, c, d]
end

Здесь b и c имеют значения по умолчанию. Если вы отправите только два аргумента в этот метод:

my_method(1, 4)

Вы увидите, что Ruby выведет [1, 2, 3, 4].

Если вы отправите три аргумента:

my_method(1, 5, 6)

Вы увидите, что Ruby выведет [1, 5, 3, 6].

Описание этого словами становится сложным и запутанным. Я опишу это в переменных и значениях вместо этого.

Сначала 1 присваивается a, затем 6 присваивается d. Это оставляет только аргументы со значениями по умолчанию. Поскольку 5 ещё не присвоено значение, оно передаётся b, и c использует его значение по умолчанию 3.

Ключевые аргументы

Ключевые аргументы следуют за любыми позиционными аргументами и разделяются запятыми, как и позиционные аргументы:

my_method(positional1, keyword1: value1, keyword2: value2)

Любые ключевые аргументы, которые не указаны, будут использовать значение по умолчанию из определения метода. Если ключевой аргумент указан, но не перечислен в определении метода, и определение метода не принимает произвольные ключевые аргументы, будет поднята ArgumentError.

Значение ключевого аргумента может быть опущено, что означает, что значение будет получено из контекста по имени ключа

keyword1 = 'some value'
my_method(positional1, keyword1:)
# ...is the same as
my_method(positional1, keyword1: keyword1)

Следите за тем, что при опущении скобок метода порядок парсинга может быть неожиданным:

my_method positional1, keyword1:

some_other_expression

# ...is actually parsed as
my_method(positional1, keyword1: some_other_expression)

Аргумент блока

Аргумент блока отправляет замыкание из области вызова в метод.

Аргумент блока всегда последний при отправке сообщения методу. Блок отправляется в метод с использованием do ... end или { ... }:

my_method do
  # ...
end

или:

my_method {
  # ...
}

do end имеет более низкий приоритет, чем { }, поэтому:

method_1 method_2 {
  # ...
}

Отправляет блок в method_2, в то время как:

method_1 method_2 do
  # ...
end

Отправляет блок в method_1. Обратите внимание, что в первом случае, если скобки используются, блок отправляется в method_1.

Блок будет принимать аргументы из метода, в который он был отправлен. Аргументы определяются аналогично тому, как метод определяет аргументы. Аргументы блока идут в | ... | после открывающей do или {:

my_method do |argument1, argument2|
  # ...
end

Локальные аргументы блока

Вы также можете объявить локальные аргументы блока, используя ; в списке аргументов блока. Присвоение локальному аргументу блока не переопределяет локальные аргументы за пределами блока в области вызывающей стороны:

def my_method
  yield self
end

place = "world"

my_method do |obj; place|
  place = "block"
  puts "hello #{obj} this is #{place}"
end

puts "place is: #{place}"

Это выводит:

hello main this is block
place is world

Таким образом, переменная place в блоке не является той же переменной place, что и за пределами блока. Удаление ; place из аргументов блока даёт такой результат:

hello main this is block
place is block

Array в аргументы

Учитывая следующий метод:

def my_method(argument1, argument2, argument3)
end

Вы можете преобразовать Array в список аргументов с помощью оператора * (или splat):

arguments = [1, 2, 3]
my_method(*arguments)

или:

arguments = [2, 3]
my_method(1, *arguments)

Оба эквивалентны:

my_method(1, 2, 3)

Если метод принимает ключевые аргументы, оператор splat преобразует хэш в конце массива в ключевые аргументы:

def my_method(a, b, c: 3)
end

arguments = [1, 2, { c: 4 }]
my_method(*arguments)

Обратите внимание, что это поведение в настоящее время устарело и будет генерировать предупреждение. Это поведение будет удалено в Ruby 3.0.

Вы также можете использовать ** (описанный ниже) для преобразования Hash в ключевые аргументы.

Если количество объектов в Array не соответствует количеству аргументов для метода, будет поднята ArgumentError.

Если оператор splat стоит первым в вызове, скобки должны использоваться, чтобы избежать предупреждения:

my_method *arguments  # warning
my_method(*arguments) # no warning

Hash в ключевые аргументы

Учитывая следующий метод:

def my_method(first: 1, second: 2, third: 3)
end

Вы можете преобразовать Hash в ключевые аргументы с помощью оператора ** (ключевой splat):

arguments = { first: 3, second: 4, third: 5 }
my_method(**arguments)

или:

arguments = { first: 3, second: 4 }
my_method(third: 5, **arguments)

Оба эквивалентны:

my_method(first: 3, second: 4, third: 5)

Если в определении метода используется оператор ключевого splat для сбора произвольных ключевых аргументов, они не будут собраны *:

def my_method(*a, **kw)
  p arguments: a, keywords: kw
end

my_method(1, 2, '3' => 4, five: 6)

Выводит:

{:arguments=>[1, 2], :keywords=>{'3'=>4, :five=>6}}

Proc в блок

Учитывая метод, использующий блок:

def my_method
  yield self
end

Вы можете преобразовать proc или lambda в аргумент блока с помощью оператора & (преобразование блока):

argument = proc { |a| puts "#{a.inspect} was yielded" }

my_method(&argument)

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

my_method &argument  # warning
my_method(&argument) # no warning

Поиск метода

Method Поиск

Когда вы отправляете сообщение, Ruby ищет метод, соответствующий имени сообщения для получателя. Методы хранятся в классах и модулях, поэтому поиск метода проходит по ним, а не по самим объектам.

Вот порядок поиска метода для класса или модуля получателя R:

  • Префиксные модули R в обратном порядке

  • Для соответствующего метода в R

  • Включенные модули R в обратном порядке

Если R — класс со суперклассом, это повторяется с суперклассом R до тех пор, пока метод не будет найден.

После нахождения совпадения поиск метода останавливается.

Если совпадение не найдено, процесс повторяется с начала, но поиск ведется для method_missing. По умолчанию method_missing — BasicObject#method_missing, который вызывает NameError при вызове.

Если активны улучшения (экспериментальная функция), порядок поиска метода изменяется. Подробнее см. документацию по улучшениям.

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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