Spec-Zone.ru › Ruby 3.3

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

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

В 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.

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

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)

Вы также можете использовать оператор ** (описанный ниже), чтобы преобразовать 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 или лямбда-выражение в аргумент блока с помощью оператора & (преобразование блока):

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 в обратном порядке

END_OF_DOCUMENT_MARKER

Если 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