Методы
Методы реализуют функциональность вашей программы. Вот простое определение метода:
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 имена могут заканчиваться символом ! (bang или восклицательный знак), ? (вопросительный знак) или = (знак равенства).
Методы с окончанием символом bang (! в конце имени метода) вызываются и выполняются как и любые другие методы. Однако по соглашению метод с восклицательным знаком или bang считается опасным. В стандартной библиотеке Ruby опасный метод подразумевает, что когда метод заканчивается символом bang (!), это указывает на то, что в отличие от своего аналога без bang, он постоянно изменяет свой получатель. Почти всегда в стандартной библиотеке Ruby будет аналог метода без bang (имя метода, которое НЕ заканчивается !) для каждого метода с bang (имя метода, которое заканчивается !) , который не изменяет получатель. Это соглашение, как правило, справедливо для стандартной библиотеки 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–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.