Chaining & to_proc on symbol

后端 未结 5 742
深忆病人
深忆病人 2020-12-09 22:26

It\'s well known to Rubyist & will call to_proc on a symbol, so

[:a, :b, :c].map(&:to_s)

is equivalent to

相关标签:
5条回答
  • 2020-12-09 22:46

    You will need to define some method in advance, but this will have generality. You can do like this:

    class Symbol
      def * other
        ->x{x.send(self).send(other)}
      end
    end
    
    [:a, :b, :c].map(&:to_s * :upcase)
    [:a, :b, :c].map(&:to_s * :capitalize)
    ...
    

    I chose * as a method for functional composition.

    And if you think you might use a third symbol, you can define like:

    class Proc
      def * other
        ->x{call(x).send(other)}
      end
    end
    
    0 讨论(0)
  • 2020-12-09 22:57

    So just for fun (and to prove that almost anything is possible in ruby if one puts in a bit of effort) we could define a method on Symbol (we'll call it Symbol#chain) to provide this functionality and a little more

    class Symbol
      def proc_chain(*args)
        args.inject(self.to_proc) do |memo,meth|
          meth, *passable_args = [meth].flatten
          passable_block = passable_args.pop if passable_args.last.is_a?(Proc)
          Proc.new do |obj|
            memo.call(obj).__send__(meth,*passable_args,&passable_block)
          end
        end
      end
      alias_method :chain, :proc_chain
    end
    

    This can then be called like so

    [:a, :b, :c].map(&:to_s.chain(:upcase))
    #=> ["A","B","C"]
    # Or with Arguments & blocks
    [1,2,3,4,5].map(&:itself.chain([:to_s,2],:chars,[:map,->(e){ "#{e}!!!!"}]))
    #=>  => [["1!!!!"], ["1!!!!", "0!!!!"], ["1!!!!", "1!!!!"],
    #        ["1!!!!","0!!!!", "0!!!!"], ["1!!!!", "0!!!!", "1!!!!"]]
    

    Can even be used as a standalone

    p = :to_s.chain([:split,'.'])
    p.call(123.45)
    #=> ["123","45"]
    # Or even 
    [123.45,76.75].map(&p)
    #=> => [["123", "45"], ["76", "75"]]
    
    0 讨论(0)
  • 2020-12-09 23:00

    While we're playing with syntax, how about monkey-patching Array with a to_proc method?

    class Array
      def to_proc
        return :itself.to_proc if empty?
        ->(obj) { drop(1).to_proc.call(first.to_proc.call(obj)) }
      end
    end
    
    ["foo", "bar", "baz"].map(&[:capitalize, :swapcase, :chars, ->a{ a.join("-") }])
    # => ["f-O-O", "b-A-R", "b-A-Z"]
    

    See it on repl.it: https://repl.it/JS4B/1

    0 讨论(0)
  • 2020-12-09 23:01

    There is no way to chain using the symbol to proc.

    However, you could monkey patch a method to the class you are mapping over that will do both, then call that.

    class Symbol
      def to_upcase_str
        self.to_s.upcase
      end
    end
    
    [:a, :b, :c].map(&:to_upcase_str)
    
    0 讨论(0)
  • 2020-12-09 23:02

    If you're only doing:

    %i[a b c].map { |e| e.to_s.upcase }
    

    then just use the block and get on with more important things. If you're really doing a chain of Enumerable calls and find the blocks too visually noisy:

    %i[a b c].map { |e| e.to_s.upcase }.some_chain_of_enumerable_calls...
    

    then you could toss your logic into a lambda to help clean up the appearance:

    to_s_upcase = lambda { |e| e.to_s.upcase }
    %i[a b c].map(&to_s_upcase).some_chain_of_enumerable_calls...
    

    or throw it in a method and say:

    %i[a b c].map(&method(:to_s_upcase)).some_chain_of_enumerable_calls...
    

    Either way, you're giving your little bit of logic a name (which is pretty much all &:symbol is doing for you) to make the code more readable and easier to understand. In the specific case of to_s.upcase, this is all a bit pointless but these approaches are quite useful when the block gets bigger.

    0 讨论(0)
提交回复
热议问题