Вызов методов
Вызов метода отправляет сообщение объекту, чтобы он мог выполнить некоторую работу.
В 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 не знает, какой аргумент метода argument3 должен быть отправлен:
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.
Аргумент блока
Аргумент блока отправляет замыкание из области вызова в метод.
Аргумент блока всегда последний при отправке сообщения методу. Блок отправляется в метод, используя 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 Преобразование 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–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.