help with to_proc?

  1. Опитвам се да разбера какво точно е to_proc и за момента това което съм разбрал е, че това е алтернативен начин да подаваме блокове някъде. Тоест ние си дефинираме някакъв метод в даден клас който връща ламбда която можем да подадем като блок чрез :име_на_метода.

    примера който се опитвам да създам е този

    class Kon
      def to_proc
        proc{ |x| puts "obicham kone #{x}" }
      end
    end
    
    moq_kon = Kon.new
    
    [1,2,3].map(&:Kon)`
    

    Oчевидно, нещо съществено не разбирам затова, се надявам някоя добра душа да го обясни подробно като за идиот.

  2. Аз ще пробвам. : ))

    to_proc следва конвенцията на всички методи за кастване в Руби и връща прок, който прави нещо. Интересния случай %w(foo bar baz).map(&:length) е аналогичен на %w(foo bar baz).map{ |word| word.length } от което можем да заключим, че to_proc метода на Symbol класа би бил нещо такова:

    ...
    def to_proc
      ->(object) { object.send(self) }
    end
    ...
    

    Т.е. за символите to_proc връща парче код, което се опитва да извика метод с име самия символ върху единствения аргумент.

    Като цяло to_proc метода позволява да използваме map, each, etc. итаративните методи с малко по-къс синтаксис. Трудно ми е да измисля пример за клас на който бих дефинирал to_proc. Най-доброто, което ми хрумва е ако имаме Incrementator:

    class Incrementator
    
      def to_proc
        ->(increment_with) do
          if increment_with < 10
            DataBase.increment('counter_name', increment_with) }
          else
            DataBase.increment('another_counter_name', increment_with) }
          end
        end
      end
    end
    
    incrementator = Incrementator.new
    
    [4, 3, 5, 6, 23, 12, 564].map(&incrementator) # will throw an error if Incrementator does not have to_proc method
    

    По този начин енкапсулираме логиката за инкрементиране на някаква колона в база данни на едно място и е малко по-кратко от

    incrementator = Incrementator.new
    
    [4, 3, 5, 6, 23, 12, 564].map do |number|
      incrementator.increment(number) # assuming we have a method `increment` similar to the proc returned in `to_proc`
    end
    

    Дано съм помогнал малко. : ))

  3. Няколко полезни линка:

    "You can convert a different class to a proc (if it supports it) by calling the to_proc method. If you use & on an object that is not a proc, Ruby will try to coerce that object into a block by calling the to_proc method."

    "proc is a function that creates a new Proc object"

    "If you put & in front of a Proc instance in the argument position, that will be interpreted as a block. If you combine something other than a Proc instance with &, then implicit class casting will try to convert that to a Proc instance using to_proc method defined on that object if there is any."

    Подобни обяснения има и в Programming Ruby, която силно препоръчвам. :)

    Иначе за да примера ти - ти подаваш на map-а символ :Kon, и руби "търси" такъв метод Kon, но няма такъв - ти имаш клас Кон -> следователно излиза и грешката "undefined method 'Kon' for 1:Fixnum". И ако искаш все пак да заработи, to_proc трябва да се извика на класа Kon -> [1,2,3].map(&Kon), следователно самият ти клас Kon трябва да има метод to_proc:

        class Kon
         def self.to_proc
           Proc.new { |x| puts "obicham kone #{x}" }
         end
       end
    

    Така на Kon се извиква to_proc, а не на инстация от Kon. Резултатът е:

    [1,2,3].map(&Kon)
    obicham kone 1
    obicham kone 2
    obicham kone 3
    # => [nil, nil, nil]
    

    защото в крайна сметка блока за map-a принтира някакви неща и стойността на крайния израз е nil.

    И за край - аз най-просто го разбирам така - to_proc кара нещо да се превърне/ да върне блок, за да може този блок да се използва от друг метод. Блокът, който връща to_proc, е логично да приема някакви стойности, да прави нещо с тях и да връща резултат - и отделно този блок пък го подаваме на пример на map. При Symbol така е имплементирано, че когато напишем: &:метод, to_proc връща Proc, който приема обект obj, аргументи **args и на този обект му "изпраща" :метод - obj.send(:метод, **args)

    ПП. чак сега видях, че си направил инстанция на Kon - moq_kon, тогава за да работи map-ът трябва само да напишеш: [1,2,3].map(&moq_kon) и да не променяш to_proc в класа.

  4. Накратко: to_proc е просто метод, който се очаква да връща Proc. Нищо специално, има само две интересни неща:

    1. & ще подсигури, че нещото е Proc като му викне to_proc. След това ще превърне получения Proc в блок.
    2. Имплементацията на Symbol#to_proc връща Proc, който приема обект и му вика метод с име = самия символ.

    Дребни неща за обясненията горе:

    @Владимир, не е единствен аргумент.

    @Десислава, използваш блок и proc малко interchangeably, не са напълно едно и също нещо.

Трябва да сте влезли в системата, за да може да отговаряте на теми.