Я изучаю рельсы и следую этой теме. Я придерживаюсь метода to_proc
. Я рассматриваю символы только как альтернативы строкам (они похожи на строки, но более дешевые с точки зрения памяти). Если есть что-то еще, что я пропускаю для символов, то, пожалуйста, скажите мне. Пожалуйста, объясните простым способом, что означает to_proc
и для чего он используется.
Что означает метод to_proc?
Ответ 1
Некоторые методы берут блок, и этот шаблон часто появляется для блока:
{|x| x.foo}
и люди хотели бы написать это более сжатым способом. Для этого в комбинации используются символ, метод Symbol#to_proc
, неявное литье классов и оператор &
. Если вы положите &
перед экземпляром Proc
в позиции аргумента, это будет интерпретироваться как блок. Если вы объединяете что-то, отличное от экземпляра Proc
с &
, то неявное кастинг класса будет пытаться преобразовать его в экземпляр Proc
, используя метод to_proc
, определенный на этом объекте, если он есть. В случае экземпляра Symbol
to_proc
работает следующим образом:
:foo.to_proc # => ->x{x.foo}
Например, предположим, что вы пишете вот так:
bar(&:foo)
Оператор &
объединяется с :foo
, который не является экземпляром Proc
, поэтому неявный класс применяет к нему Symbol#to_proc
, что дает ->x{x.foo}
. &
теперь применяется к этому и интерпретируется как блок, который дает:
bar{|x| x.foo}
Ответ 2
Самый простой способ объяснить это несколькими примерами.
(1..3).collect(&:to_s) #=> ["1", "2", "3"]
То же, что и:
(1..3).collect {|num| num.to_s} #=> ["1", "2", "3"]
и
[1,2,3].collect(&:succ) #=> [2, 3, 4]
То же, что и:
[1,2,3].collect {|num| num.succ} #=> [2, 3, 4]
to_proc возвращает объект Proc, который отвечает на данный метод символом. Поэтому в третьем случае массив [1,2,3] вызывает метод сбора и. succ - метод, определяемый классом Array. Таким образом, этот параметр представляет собой короткий способ сказать, собирать каждый элемент в массиве и возвращать его преемника, и из него создать новый массив, что приводит к [2,3,4]. Символ: succ преобразуется в объект Proc, поэтому он вызывает метод succ Array.
Ответ 3
Для меня самым ясным объяснением является его простая реализация. Вот как это могло бы выглядеть, если бы я переопределял Symbol # to_proc:
class Symbol # reopen Symbol class to reimplement to_proc method
def to_proc
->(object) { object.send(self) }
end
end
my_lambda = :to_s.to_proc
puts my_lambda.(1) # prints '1'; .() does the same thing as .call()
puts my_lambda.(1).class # prints 'String'
puts [4,5,6].map(&:to_s) # prints "4\n5\n6\n"
puts [4,5,6].map(&:to_s).first.class # prints 'String'
Ответ 4
Для кого-то еще немного запутанного, запуск следующего кода может сделать вещи немного яснее:
class Symbol
def to_proc
proc do |obj|
puts "Symbol proc: #{obj}.send(:#{self})"
obj.send(self)
end
end
end
class Array
def map(&block)
copy = self.class.new
self.each do |index|
puts "Array.map: copy << block.call(#{index})"
copy << block.call(index)
end
copy
end
end
remapped_array = [0, 1, 2].map &:to_s
puts "remapped array: #{remapped_array.inspect}"
Это не фактические реализации Symbol.to_proc
или Array.map
, они просто упрощенные версии, которые я использую, чтобы продемонстрировать, как работают map &:to_s
и подобные вызовы.