ЗмістНатисність на посилання, щоб перейти до потрібного місця
На перший погляд, такий код здається дивним - ми ж зазвичай пишемо map(&:to_s) або map { |x| ... }. Але Ruby дозволяє передавати не тільки символи, а й будь-які об’єкти після &, якщо вони можуть перетворитися на Proc через метод to_proc.
Коли Ruby бачить &Person, він викликає: Person.to_procТобто намагається перетворити Person на об’єкт Proc.
Отриманий об’єкт Proc використовується як блок для методу map.
Далі Ruby для кожного елемента масиву викликає цей Proc - proc.call(element)
Приклад
class Person
def initialize(name)
@name = name
end
def self.to_proc
->(name) { new(name) }
end
end
people = ["Bant", "Yuki", "Beanie"].map(&Person)
p people
Результат:
[#<Person:0x0000000103f2b3a8 @name="Bant">, #<Person:0x0000000103f2b0b0 @name="Yuki">, #<Person:0x0000000103f2adb8 @name="Beanie">]
Що реально відбувається під капотом?
Ruby викликає ваш Person.to_proc, який повертає лямбду:
->(name) { Person.new(name) } # зверніть увагу, що в методі ми пропустили Person (не обов'язково вказувати))
І потім фактично виконує те саме, що:
["Oleh", "Ira", "Marta"].map { |name| Person.new(name) }
Як працює .call?
Кожен Proc або lambda має метод .call, який запускає збережений всередині код. Тобто коли Ruby робить proc.call(element), він фактично виконує ваш блок:
my_proc = ->(x) { puts "Hello, #{x}" }
my_proc.call("Ruby") # => "Hello, Ruby"
Якщо коротко:
- &obj у Ruby → викликає obj.to_proc.
- Результат має бути Proc або lambda.
- Потім Ruby викликає proc.call(x) для кожного елемента.
- Тому map(&Person) працює, якщо клас має метод self.to_proc.
Останній пункт розглянемо трохи детальніше. Якщо клас немає методу .to_proc - очікуйте на помилку. Чому?
[1, 2, 3].map(&123)
Що робить Ruby:
- бачить &123
- думає: “ага, треба викликати 123.to_proc”
- але в числа (Integer) немає методу to_proc
- отже - помилка
irb(main):001> 123.to_proc (irb):1:in `<main>': undefined method `to_proc' for 123:Integer (NoMethodError) 123.to_proc ^^^^^^^^ Did you mean? to_r from /Users/mykyta/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/irb-1.15.2/exe/irb:9:in `<top (required)>' from /Users/mykyta/.rbenv/versions/3.2.1/bin/irb:25:in `load' from /Users/mykyta/.rbenv/versions/3.2.1/bin/irb:25:in `<main>' irb(main):002>
Цей допис поки що не має жодних доповнень від автора/ки.