Методы
Методы реализуют функциональность вашей программы. Вот простое определение метода:
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.