Самое простое руководство по аргументам методов Ruby

Самое простое руководство по аргументам методов Ruby

Содержание
  1. Позиционные аргументы
  2. Аргументы ключевых слов
  3. Когда использовать Positional / Keyword?
  4. Именованные остаточные аргументы
    1. Аргументы анонимного отдыха
    2. Аргументы оператора Nil Splat
    3. Аргумент блока
    4. TL;DR

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

В этой статье мы рассмотрим все различные типы аргументов, которые может принимать функция.

Позиционные аргументы

Позиционные аргументы являются наиболее часто используемыми и определяются в порядке их появления. Они напрямую связаны с порядком параметров метода. Давайте рассмотрим пример:

def add(a, b, c) puts a + b + c end add(1, 2, 3) # => 6.

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

def add(a, b = 2, c = 3) puts a + b + c end add(1) # => 6.

⚠️ Примечание: Невозможно обернуть позиционный аргумент вокруг двух позиционных аргументов со значением по умолчанию.

def add(a = 1, b, c = 3) # => SyntaxError puts a + b + c end.

Аргументы ключевых слов

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

def greet(name, age)
  puts "Hello #{name}! Вам #{age} лет."
end

greet("Alice", 30) # => Здравствуйте Alice! Вам 30 лет.

Вы можете смешивать позиционные и ключевые аргументы:

def greet(name, age)
  puts "Hello #{name}! Вам #{age} лет."
end

greet("Alice", 30) # => Hello Alice! Тебе 30 лет.

⚠️ Примечание: Невозможно поместить позиционный аргумент после аргумента ключевого слова.

def greet(name, age)
  puts "Hello #{name}! Вам #{age} лет."
end

Большим преимуществом использования Keywords является то, что он не выделяет память под отправленный ему Hash. Я объясню это на примере:

def greet(options = {})
  puts "Hello #{options.fetch(:name)}! Вам #{options.fetch(:age)} лет."
end

def greet(name:, age:)
  puts "Hello #{name}! Вам #{age} лет."
end

greet({name: "Alicia", age: 33})
greet(name: "Alicia", age: 33)

Позиционные и ключевые аргументы - два наиболее часто используемых типа аргументов. До сих пор мы не видели истинной силы Ruby. Прежде чем двигаться дальше, давайте вспомним некоторые правила использования аргументов Positional и Keywords.

Когда использовать Positional / Keyword?

Для этого у меня есть простое правило:

Если мой метод публичный → Аргумент по ключевому слову Примеры методов, вызываемых вне контекста моего класса, метод initialize, метод call для объектов Service Objects. Если мой метод приватный → Позиционный аргумент Методы внутри класса служат для разгрузки действий, выполняемых в публичных методах. Поскольку мы полностью контролируем вызывающих и вызываемых, мы можем позволить себе использовать только позиционные аргументы.

Именованные остаточные аргументы

Оператор Splat (*) и оператор Double Splat (**) помогают нам управлять переменным количеством аргументов, которые мы получаем в нашей функции.

Давайте рассмотрим пример:

def display_info(*args, **kwargs)
  puts "Positional Arguments: #{args}"
  puts "Аргументы ключевых слов: #{kwargs}"
end

display_info(1, 2, {fake: :love}, name: "Bob", age: 25)
# => Positional Arguments: [1, 2, {:fake=>:love}]
# => Аргументы ключевых слов: {:name=>"Bob", :age=>25}

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

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

Переменные args и kwargs названы так по соглашению; вы можете переименовать их по своему усмотрению.

Аргументы анонимного отдыха

В предыдущем примере мы видели, что происходит, когда мы называем использование Splat-операторов в определении метода.

На самом деле есть и второе применение операторов splat, когда мы не присваиваем их ничему:

def process_data(*args, processing_method: "default")
  puts "Обработка методом: #{processing_method}"
end

process_data("Hello", "World", processing_method: "custom") # => Обработка методом: custom

В примере выше вы видели, что с отправленными позиционными аргументами ничего не было сделано.

Аналогичное поведение наблюдается и с оператором Double Splat:

def process_data(card, method, **options)
  puts "#{card} была списана методом #{method}"
end

process_data("Hello", "World", processing_method: "custom") # => Hello была списана методом World

Но тогда вы, наверное, задаетесь вопросом, а зачем это нужно?

Делегируйте полномочия базовым функциям, игнорируя содержимое аргументов. Игнорируйте ”лишние” аргументы и не вызывайте исключение.

Давайте рассмотрим пример, который объясняет эти два варианта использования:

def greet(**args)
  greet_kindly(**args)
  greet_wickedly(**args)
end

def greet_kindly(name:, **)
  puts "Hi #{name}, nice to see you!"
end

def greet_wickedly(name:, surname:, **)
  puts "Hi #{name}, what a stupid surname #{surname}"
end

greet(name: "Hugo", surname: "The V")
# Output:
# Hi Hugo, nice to see you!
# Hi Hugo, what a stupid surname The V…

Метод greet_kindly получил имя и фамилию, но извлек имя и отправил фамилию в пустоту thуказывает на нотацию **. Без ** будет выдана ошибка ArgumentError.

Существует еще один метод делегирования аргументов; он называется Argument Forwarding, с нотацией ...:

def greet
  # method body
end

def greet_kindly
  # method body
end

def greet_wickedly
  # method body
end

Разница лишь в том, что * и ** рассматривают только один тип аргумента (позиционный / ключевой), а ... рассматривает все типы аргументов.

Аргументы оператора Nil Splat

Как вы думаете, если объединить оператор Double Splat и nil, что произойдет?

def greet(name, **nil)
  puts "Hello #{name}!"
end

greet("Charlie", other: "argument?")

Я дам вам ответ: возникает исключение нет принятых ключевых слов (ArgumentError).

Чем это полезно?

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

def greet(name)
  puts "Hello #{name}!"
end

greet("Hugo") # => Hello Hugo!

Если вы хотите предотвратить такое поведение в ваших методах, вам нужно добавить **nil в конце объявления параметров!

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

Этому разделу можно было бы посвятить целую статью. Управление блоками - это то, что делает Ruby таким ценным.

Суть блочного аргумента заключается в возможности делегировать часть внутреннего тела вашего метода вовне. Это обеспечивает невероятную гибкость. И что самое приятное? Вы можете напрямую отправлять информацию в блок:

def do_action
  yield(1, 2)
end

do_action do |a, b|
  puts "What does #{a} + #{b} mean? #{a + b}"
end

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

def do_action(&block)
  yield block
end

do_action do
  "Hello World!"
end
# => "Hello World!"

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

TL;DR

Вот краткое описание каждого типа аргументов, рассмотренных сегодня!

  • Позиционные аргументы (a, b, c)
  • Аргументы-ключи (имя:, возраст:)
  • Именованные остаточные аргументы (*args, **kwargs)
  • Анонимные остаточные аргументы (*, **)
  • Переадресация аргументов (...)
  • nil kwargs (**nil)
  • Аргумент блока (&block)

Вот и все на сегодня!

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