Вызов методов
Вызов метода отправляет сообщение объекту, чтобы он мог выполнить некоторую работу.
В 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.