Spec-Zone.ru › Ruby 3

Методы

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

def one_plus_one
  1 + 1
end

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

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

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. Часто они возвращают объект, указывающий на истинное значение (или «истинное» значение).

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

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

+

сложение

-

вычитание

*

умножение

**

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

/

деление

%

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

&

И

^

Исключающее ИЛИ

>>

сдвиг вправо

<<

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

==

равно

!=

не равно

===

равенство по типу. См. 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

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

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

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

def no_keywords(**nil)
end

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

Разделение ключевых и позиционных аргументов

В Ruby 2.0 и 2.6 ключевые и позиционные аргументы не разделялись, и ключевой аргумент мог использоваться как позиционный, и наоборот. В Ruby 3.0 ключевые и позиционные аргументы будут разделены, если определение метода включает ключевые аргументы. В Ruby 3.0, если определение метода не включает ключевые аргументы, ключевые аргументы, переданные при вызове метода, по-прежнему будут обрабатываться как финальный позиционный аргумент-хеш.

В настоящее время ключевые и позиционные аргументы не разделены, но в случаях, когда поведение изменится в Ruby 3.0, будет выведено предупреждение.

Существует несколько различных типов проблем с разделением ключевых и позиционных аргументов.

Преобразование Hash в ключевые аргументы

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

def my_method(**keywords)
  keywords
end
my_method({a: 1}) # {:a => 1}

Это происходит даже если хеш может быть необязательным позиционным аргументом или элементом аргумента-списка:

def my_method(hash=nil, **keywords)
  [hash, keywords]
end
my_method({a: 1}) # [nil, {:a => 1}]

def my_method(*args, **keywords)
  [args, keywords]
end
my_method({a: 1}) # [[], {:a => 1}]

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

def my_method(hash, **keywords)
  [hash, keywords]
end
my_method({a: 1}) # [{:a => 1}, {}]

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

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

def my_method(hash, **keywords)
  [hash, keywords]
end
my_method(a: 1) # [{:a => 1}, {}]

Это также справедливо для пустых ключевых сплитов:

kw = {}
my_method(**kw) # [{}, {}]

Разделение позиционных хешей или ключевых аргументов

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

def my_method(hash=3, a: 4)
  [hash, a]
end
my_method(a: 1, 'a' => 2)   # [{"a"=>2}, 1]
my_method({a: 1, 'a' => 2}) # [{"a"=>2}, 1]

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

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

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

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

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

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

def my_method
  yield self
end

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–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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