Объясните понятие arity в контексте Procs и lambdas.ruby-34

Arity (арити) — это понятие, которое описывает количество аргументов, ожидаемых функцией или блоком кода. В Ruby arity играет особую роль при работе с Proc и lambda, так как их поведение в этом аспекте существенно отличается.

1. Что такое Arity?

Arity определяет:

  • Сколько аргументов ожидает Proc/lambda
  • Как они будут обрабатываться при вызове
  • Будет ли проверяться соответствие количества аргументов

2. Arity у обычных Proc

Обычные Proc (созданные через Proc.new или proc) имеют гибкую arity:

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

Пример:

pr = Proc.new { |a, b| [a, b] }
puts pr.call(1)      #=> [1, nil]
puts pr.call(1, 2)   #=> [1, 2]
puts pr.call(1, 2, 3) #=> [1, 2] (3 игнорируется)

3. Arity у Lambda

Lambda (созданные через lambda или ->) имеют строгую arity:

  • Проверяют количество аргументов
  • Вызывают ArgumentError при несоответствии
  • Ведут себя как обычные методы

Пример:

lm = ->(a, b) { [a, b] }
puts lm.call(1, 2)   #=> [1, 2]
# lm.call(1)         # ArgumentError (не хватает аргументов)
# lm.call(1, 2, 3)   # ArgumentError (лишние аргументы)

4. Методы для проверки Arity

arity — возвращает числовое представление:

  • Положительное число: точное количество обязательных аргументов
  • Отрицательное число: ````nозначаетn-1` обязательных и один необязательный (rest arg)

Примеры:

Proc.new {}.arity              #=> 0
Proc.new { |x| }.arity         #=> 1
Proc.new { |x, y| }.arity      #=> 2
Proc.new { |*args| }.arity     #=> -1
Proc.new { |x, *args| }.arity  #=> -2

parameters — детальная информация:

pr = Proc.new { |x, y=1, *z, &b| }
puts pr.parameters.inspect
#=> [[:opt, :x], [:opt, :y], [:rest, :z], [:block, :b]]

5. Практические различия

Обработка аргументов:

p = Proc.new { |a, b| puts "#{a.inspect}, #{b.inspect}" }
l = ->(a, b) { puts "#{a.inspect}, #{b.inspect}" }

p.call(1)      #=> "1, nil"
# l.call(1)    # ArgumentError

Использование в методах:

def test_proc
  Proc.new { return "из Proc" }.call
  "из метода"
end

def test_lambda
  -> { return "из lambda" }.call
  "из метода"
end

test_proc   #=> "из Proc" (выход из метода)
test_lambda #=> "из метода" (возврат только из lambda)

6. Особые случаи

Блоки с неявными аргументами:

pr = Proc.new { |x=1, y| [x, y] }
puts pr.call(2) #=> [1, 2] (неожиданно!)

Лямбда с аргументами по умолчанию:

lm = ->(x=1, y) { [x, y] }
puts lm.call(2) #=> [1, 2]

7. Когда что использовать?

Proc подходит когда:

  • Нужна гибкость в количестве аргументов
  • Требуется сохранить блок как объект
  • Нужно изменить поток выполнения (с return)

Lambda лучше когда:

  • Важна проверка аргументов
  • Нужно функциональное поведение
  • Работаете с аргументами как в методах

Резюмируем: понятие arity в Ruby описывает "строгость" проверки аргументов у Proc и lambda. Обычные Proc имеют гибкую arity и не проверяют аргументы, тогда как lambda обладают строгой arity и ведут себя как методы. Понимание этих различий критически важно для правильного выбора между Proc и lambda в различных сценариях.

Методы arity и parameters позволяют интроспектировать ожидаемые аргументы во время выполнения, что может быть полезно для метапрограммирования и создания гибких API.