Spec-Zone.ru › Ruby 3.3

Методы

Методы реализуют функциональность вашей программы. Вот простое определение метода:

def one_plus_one
  1 + 1
end

Определение метода состоит из ключевого слова def, имени метода, тела метода, return значения и ключевого слова end. При вызове метод выполнит тело метода. Этот метод возвращает 2.

С Ruby 3.0 также есть сокращённый синтаксис для методов, состоящих ровно из одного выражения:

def one_plus_one = 1 + 1

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

Method Имена

Method имена могут быть одним из операторов или должны начинаться с буквы или символа с установленным восьмым битом. Они могут содержать буквы, цифры, знак _ (подчеркивание) или символ с установленным восьмым битом. Принято использовать подчеркивания для разделения слов в имени метода из нескольких слов:

def method_name
  puts "use underscores to separate words"
end

Программы Ruby должны быть написаны в наборе символов, совместимом с US-ASCII, таком как UTF-8, ISO-8859-1 и т. д. В таких наборах символов, если восьмой бит установлен, это указывает на расширенный символ. Ruby разрешает именам методов и другим идентификаторам содержать такие символы. Программы Ruby не могут содержать некоторые символы, такие как ASCII NUL (\x00).

Ниже приведены примеры допустимых методов Ruby:

def hello
  "hello"
end

def こんにちは
  puts "means hello in Japanese"
end

Как правило, имена методов совместимы с US-ASCII, так как для их ввода есть клавиши на всех клавиатурах.

Method имена могут оканчиваться на ! (восклицательный знак), ? (вопросительный знак) или = (знак равенства).

Методы, оканчивающиеся на восклицательный знак (! в конце имени метода), вызываются и выполняются так же, как и любые другие методы. Однако по соглашению метод с восклицательным знаком считается опасным. В базовой библиотеке Ruby опасный метод подразумевает, что когда метод заканчивается восклицательным знаком (!), это указывает на то, что в отличие от своего аналога без восклицательного знака, он постоянно изменяет свой получатель. Почти всегда в базовой библиотеке Ruby будет аналог без восклицательного знака (имя метода, НЕ оканчивающееся на !) для каждого метода с восклицательным знаком (имя метода, оканчивающееся на !) , который не изменяет получателя. Это соглашение обычно справедливо для базовой библиотеки Ruby, но может или не может быть справедливо для других библиотек Ruby.

Методы, заканчивающиеся вопросительным знаком, по соглашению возвращают булево значение, но они не всегда возвращают только true или false. Часто они возвращают объект для обозначения истинного значения (или «истинного» значения).

Методы, заканчивающиеся знаком равенства, обозначают метод присваивания.

class C
  def attr
    @attr
  end

  def attr=(val)
    @attr = val
  end
end

c = C.new
c.attr      #=> nil
c.attr = 10 # calls "attr=(10)"
c.attr      #=> 10

Методы присваивания нельзя определить с помощью сокращённого синтаксиса.

Это имена методов для различных операторов Ruby. Каждый из этих операторов принимает только один аргумент. После оператора следует типичное использование или название оператора. Создание альтернативного значения для оператора может привести к путанице, так как пользователь ожидает, что плюс будет складывать, минус — вычитать и т. д. Кроме того, вы не можете изменить приоритет операторов.

+

сложение

-

вычитание

*

умножение

**

возведение в степень

/

деление

%

деление по модулю, String#%

&

И

^

Исключающее ИЛИ (XOR)

>>

сдвиг вправо

<<

сдвиг влево, добавление

==

равно

!=

не равно

===

равенство типов. См. Object#===

=~

совпадение шаблонов. (Не только для регулярных выражений)

!~

не совпадает

<=>

сравнение (оператор корабля). См. Comparable

<

меньше

<=

меньше или равно

>

больше

>=

больше или равно

Для определения унарных методов минус и плюс следует за оператором @ как в +@.

class C
  def -@
    puts "you inverted this object"
  end
end

obj = C.new

-obj # prints "you inverted this object"

Ключевое слово @ необходимо для различения унарных операторов минус и плюс от бинарных операторов минус и плюс.

Вы также можете следовать за тильдой и не (!) унарными методами с @, но это не обязательно, так как нет бинарных операторов тильды и не.

Унарные методы принимают ноль аргументов.

Кроме того, могут быть определены методы для ссылки на элементы и присваивания: [] и []= соответственно. Оба могут принимать один или несколько аргументов, а ссылка на элемент может принимать ни одного.

class C
  def [](a, b)
    puts a + b
  end

  def []=(a, b, c)
    puts a * b + c
  end
end

obj = C.new

obj[2, 3]     # prints "5"
obj[2, 3] = 4 # prints "10"

Значения возврата

По умолчанию метод возвращает последнее выражение, которое было вычислено в теле метода. В примере выше последним (и единственным) вычисленным выражением было простое суммирование 1 + 1. Ключевое слово return можно использовать, чтобы явно указать, что метод возвращает значение.

def one_plus_one
  return 1 + 1
end

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

def two_plus_two
  return 2 + 2
  1 + 1  # this expression is never evaluated
end

Обратите внимание, что для методов присваивания значение возврата будет игнорироваться при использовании синтаксиса присваивания. Вместо этого будет возвращён аргумент:

def a=(value)
  return 1 + value
end

p(self.a = 5) # prints 5

Фактическое значение возврата будет возвращено при прямом вызове метода:

p send(:a=, 5) # prints 6

Область действия

Стандартный синтаксис для определения метода:

def my_method
  # ...
end

добавляет метод в класс. Вы можете определить метод экземпляра для конкретного класса с помощью ключевого слова class:

class C
  def my_method
    # ...
  end
end

Метод может быть определён на другом объекте. Вы можете определить «метод класса» (метод, который определён в классе, а не в экземпляре класса) следующим образом:

class C
  def self.my_method
    # ...
  end
end

Однако это просто частный случай большей синтаксической возможности в Ruby — возможности добавления методов к любому объекту. Классы являются объектами, поэтому добавление методов класса — это просто добавление методов к объекту Class.

Синтаксис добавления метода к объекту:

greeting = "Hello"

def greeting.broaden
  self + ", world!"
end

greeting.broaden # returns "Hello, world!"

self — ключевое слово, обозначающее текущий рассматриваемый компилятором объект, что может сделать использование self при определении метода класса выше немного более ясным. Действительно, пример добавления метода hello к классу String можно переписать следующим образом:

def String.hello
  "Hello, world!"
end

Метод, определённый таким образом, называется «методом-синглтоном». broaden будет существовать только в экземпляре строки greeting. Другие строки не будут иметь broaden.

Переопределение

Когда Ruby встречает ключевое слово def , он не считает это ошибкой, если метод уже существует: он просто переопределяет его. Это называется переопределением. Подобно расширению базовых классов, эта потенциально опасная возможность должна использоваться с осторожностью, потому что она может привести к непредсказуемым результатам. Например, рассмотрите эту сессию irb:

>> "43".to_i
=> 43
>> class String
>>   def to_i
>>     42
>>   end
>> end
=> nil
>> "43".to_i
=> 42

Это фактически сорвёт любую программу, которая использует метод String#to_i для разбора чисел из строк.

Аргументы

Метод может принимать аргументы. Список аргументов следует за именем метода:

def add_one(value)
  value + 1
end

При вызове пользователь метода add_one должен предоставить аргумент. Аргумент — это локальная переменная в теле метода. Затем метод добавит к этому аргументу единицу и вернёт значение. Если задано 1, этот метод вернёт 2.

Скобки вокруг аргументов необязательны:

def add_one value
  value + 1
end

Скобки обязательны в определениях методов с коротким синтаксисом:

# OK
def add_one(value) = value + 1
# SyntaxError
def add_one value = value + 1

Несколько аргументов разделяются запятой:

def add_values(a, b)
  a + b
end

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

Значения по умолчанию

Аргументы могут иметь значения по умолчанию:

def add_values(a, b = 1)
  a + b
end

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

def add_values(a = 1, b = 2, c)
  a + b + c
end

Это вызовет ошибку SyntaxError:

def add_values(a = 1, b, c = 1)
  a + b + c
end

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

def add_values(a = 1, b = a)
  a + b
end
add_values
# => 2

Но это вызовет ошибку NameError (если не определён метод с именем b):

def add_values(a = b, b = 1)
  a + b
end
add_values
# NameError (undefined local variable or method `b' for main:Object)

Array Разложение массивов

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

def my_method((a, b))
  p a: a, b: b
end

my_method([1, 2])

Это выводит:

{:a=>1, :b=>2}

Если у аргумента есть дополнительные элементы в массиве Array, они будут проигнорированы:

def my_method((a, b))
  p a: a, b: b
end

my_method([1, 2, 3])

Это выводит то же самое, что и выше.

Вы можете использовать * для сбора оставшихся аргументов. Это разделяет массив Array на первый элемент и остальную часть:

def my_method((a, *b))
  p a: a, b: b
end

my_method([1, 2, 3])

Это выводит:

{:a=>1, :b=>[2, 3]}

Аргумент будет разложен, если он отвечает методу to_ary. Вы должны определять to_ary только в том случае, если можете использовать свой объект вместо массива Array.

Использование внутренних скобок использует только один из отправленных аргументов. Если аргумент не массив Array, он будет присвоен первому аргументу в разложении, а оставшиеся аргументы в разложении будут nil:

def my_method(a, (b, c), d)
  p a: a, b: b, c: c, d: d
end

my_method(1, 2, 3)

Это выводит:

{:a=>1, :b=>2, :c=>nil, :d=>3}

Вы можете произвольно вкладывать разложение:

def my_method(((a, b), c))
  # ...
end

Аргумент массива/хеша

Префикс * к аргументу приводит к преобразованию любых оставшихся аргументов в массив:

def gather_arguments(*arguments)
  p arguments
end

gather_arguments 1, 2, 3 # prints [1, 2, 3]

Массивному аргументу должны предшествовать любые ключевые аргументы.

Можно собирать аргументы в начале или в середине:

def gather_arguments(first_arg, *middle_arguments, last_arg)
  p middle_arguments
end

gather_arguments 1, 2, 3, 4 # prints [2, 3]

Массив-аргумент захватывает Hash в качестве последней записи, если вызывающая сторона предоставила ключевые аргументы после всех позиционных.

def gather_arguments(*arguments)
  p arguments
end

gather_arguments 1, a: 2 # prints [1, {:a=>2}]

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

def gather_arguments_keyword(*positional, keyword: nil)
 p positional: positional, keyword: keyword
end

gather_arguments_keyword 1, 2, three: 3
#=> raises: unknown keyword: three (ArgumentError)

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

def ignore_arguments(*)
end

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

def delegate_arguments(*)
  other_method(*)
end

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

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

def add_values(first: 1, second: 2)
  first + second
end

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

def gather_arguments(first: nil, **rest)
  p first, rest
end

gather_arguments first: 1, second: 2, third: 3
# prints 1 then {:second=>2, :third=>3}

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

Чтобы потребовать конкретный ключевой аргумент, не указывайте значение по умолчанию для этого ключевого аргумента:

def add_values(first:, second:)
  first + second
end
add_values
# ArgumentError (missing keywords: first, second)
add_values(first: 1, second: 2)
# => 3

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

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

def ignore_keywords(**)
end

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

def delegate_keywords(**)
  other_method(**)
end

Чтобы пометить метод как принимающий ключевые аргументы, но не фактически использующий их, можно использовать **nil:

def no_keywords(**nil)
end

Вызов такого метода с ключевыми аргументами или непустым ключевым списком приведёт к исключению ArgumentError. Этот синтаксис поддерживается, чтобы ключевые аргументы могли быть добавлены в метод в будущем без нарушения обратной совместимости.

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

def meth(arg)
  arg
end
meth(a: 1)
# => {:a=>1}

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

Аргумент блока обозначается & и должен стоять последним:

def my_method(&my_block)
  my_block.call(self)
end

Чаще всего аргумент блока используется для передачи блока другому методу:

def each_item(&block)
  @items.each(&block)
end

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

def each_item(&)
  @items.each(&)
end

Если вы собираетесь только вызвать блок и не будете его иначе использовать или отправлять в другой метод, предпочтительнее использовать yield без явного параметра блока. Этот метод эквивалентен первому методу в этом разделе:

def my_method
  yield self
end

Передача аргументов

С Ruby 2.7 доступен синтаксис передачи всех аргументов:

def concrete_method(*positional_args, **keyword_args, &block)
  [positional_args, keyword_args, block]
end

def forwarding_method(...)
  concrete_method(...)
end

forwarding_method(1, b: 2) { puts 3 }
#=>  [[1], {:b=>2}, #<Proc:...skip...>]

Вызов с передачей ... доступен только в методах, определенных с ....

def regular_method(arg, **kwarg)
  concrete_method(...) # Syntax error
end

С Ruby 3.0, перед ... могут быть предваряющие аргументы как в определениях, так и в вызовах (но в определениях они могут быть только позиционными аргументами без значений по умолчанию).

def request(method, path, **headers)
  puts "#{method.upcase} #{path} #{headers}"
end

def get(...)
  request(:GET, ...) # leading argument in invoking
end

get('http://ruby-lang.org', 'Accept' => 'text/html')
# Prints: GET http://ruby-lang.org {"Accept"=>"text/html"}

def logged_get(msg, ...) # leading argument in definition
  puts "Invoking #get: #{msg}"
  get(...)
end

logged_get('Ruby site', 'http://ruby-lang.org')
# Prints:
#   Invoking #get: Ruby site
#   GET http://ruby-lang.org {}

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

def log(...)
  puts ...  # This would be treated as `puts()...',
            # i.e. endless range from puts result
end

log("test")
# Prints: warning: ... at EOL, should be parenthesized?
# ...and then empty line

Exception Обработка исключений

Методы подразумевают обработку исключений, поэтому вам не нужно использовать begin или end для обработки исключений. Это:

def my_method
  begin
    # code that may raise an exception
  rescue
    # handle exception
  end
end

Можно переписать как:

def my_method
  # code that may raise an exception
rescue
  # handle exception
end

Аналогично, если вы хотите всегда выполнять код, даже если возникает исключение, можно использовать ensure без begin и end:

def my_method
  # code that may raise an exception
ensure
  # code that runs even if previous code raised an exception
end

Вы также можете объединить rescue с ensure и/или else, без begin и end:

def my_method
  # code that may raise an exception
rescue
  # handle exception
else
  # only run if no exception raised above
ensure
  # code that runs even if previous code raised an exception
end

Если вы хотите обработать исключение только для части метода, используйте begin и end. Более подробная информация находится на странице по обработке исключений.

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