idiom I've not seen before

Discussion in 'Ruby' started by Rob Saul, Feb 7, 2008.

  1. Rob Saul

    Rob Saul Guest

    I came across this :
    elements.sort_by(&:position).each
    in something I've taken over. I thought sort_by
    took a block. Obviously I'm missing something.
    Can anyone shed some light on this trick?

    ~Rob
     
    Rob Saul, Feb 7, 2008
    #1
    1. Advertising

  2. On Feb 6, 2008, at 10:55 PM, Rob Saul wrote:
    > I came across this :
    > elements.sort_by(&:position).each
    > in something I've taken over. I thought sort_by
    > took a block. Obviously I'm missing something.
    > Can anyone shed some light on this trick?
    >
    > ~Rob



    You've found Symbol#to_proc

    That line is equivalent to:

    elements.sort_by {|e| e.send:)position) }.each

    The "something" is a Rails project, isn't it?

    == vendor/rails/activesupport/lib/active_support/core_ext/symbol.rb ==
    class Symbol
    # Turns the symbol into a simple proc, which is especially useful
    for enumerations. Examples:
    #
    # # The same as people.collect { |p| p.name }
    # people.collect(&:name)
    #
    # # The same as people.select { |p| p.manager? }.collect { |p|
    p.salary }
    # people.select(&:manager?).collect(&:salary)
    def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
    end
    end


    I don't know how you can send additional arguments however.

    -Rob

    Rob Biedenharn http://agileconsultingllc.com
     
    Rob Biedenharn, Feb 7, 2008
    #2
    1. Advertising

  3. On 2/6/08, Rob Saul <> wrote:
    > I came across this :
    > elements.sort_by(&:position).each
    > in something I've taken over. I thought sort_by
    > took a block. Obviously I'm missing something.
    > Can anyone shed some light on this trick?


    Sure. &:position == :position.to_proc == proc { |element, *args|
    element.position(*args) }

    So elements.sort_by(&:position) is shorthand for sorting the elements
    by position.

    jeremy
     
    Jeremy Kemper, Feb 7, 2008
    #3
  4. Rob Saul

    Rob Saul Guest

    Jeremy Kemper wrote:
    >
    > Sure. &:position == :position.to_proc == proc { |element, *args|
    > element.position(*args) }
    >
    > So elements.sort_by(&:position) is shorthand for sorting the elements
    > by position.
    >
    > jeremy


    thanks to both Jeremy and Rob for the enlightenment. And
    yes, it is a Rails project.
     
    Rob Saul, Feb 7, 2008
    #4
  5. On Feb 7, 2008 4:14 AM, Rob Saul <> wrote:

    > thanks to both Jeremy and Rob for the enlightenment. And
    > yes, it is a Rails project.


    Also, I believe Symbol.to_proc is making it into 1.9

    martin
     
    Martin DeMello, Feb 7, 2008
    #5
  6. Thomas Wieczorek, Feb 7, 2008
    #6
  7. Rob Saul

    Ryan Davis Guest

    On Feb 6, 2008, at 20:04 , Rob Biedenharn wrote:

    > You've found Symbol#to_proc
    >
    > That line is equivalent to:
    >
    > elements.sort_by {|e| e.send:)position) }.each


    Buyer beware:

    #!/usr/local/bin/ruby -w

    # # of iterations = 10000
    # user system total real
    # null_time 0.000000 0.000000 0.000000 ( 0.001561)
    # map 0.880000 0.000000 0.880000 ( 0.878500)
    # to_proc 2.790000 0.000000 2.790000 ( 2.799291)

    # # of iterations = 100000
    # user system total real
    # null_time 0.020000 0.000000 0.020000 ( 0.013951)
    # map 8.730000 0.010000 8.740000 ( 8.766693)
    # to_proc 27.960000 0.030000 27.990000 ( 28.001288)

    require 'benchmark'

    class Symbol
    def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
    end
    end

    a = (1..100).to_a

    raise "stupid" unless a.map { |n| n.to_s } == a.map(&:to_s)

    max = (ARGV.shift || 1_000_000).to_i

    puts "# of iterations = #{max}"
    Benchmark::bm(20) do |x|
    x.report("null_time") do
    for i in 0..max do
    # do nothing
    end
    end

    x.report("map") do
    for i in 0..max do
    a.map { |n| n.to_s }
    end
    end

    x.report("to_proc") do
    for i in 0..max do
    a.map(&:to_s)
    end
    end
    end
     
    Ryan Davis, Feb 7, 2008
    #7
  8. Rob Saul

    botp Guest

    On Feb 7, 2008 1:16 PM, Ryan Davis <> wrote:
    >
    > Buyer beware:
    >
    > # # of iterations = 10000
    > # user system total real
    > # null_time 0.000000 0.000000 0.000000 ( 0.001561)
    > # map 0.880000 0.000000 0.880000 ( 0.878500)
    > # to_proc 2.790000 0.000000 2.790000 ( 2.799291)
    >
    > # # of iterations = 100000
    > # user system total real
    > # null_time 0.020000 0.000000 0.020000 ( 0.013951)
    > # map 8.730000 0.010000 8.740000 ( 8.766693)
    > # to_proc 27.960000 0.030000 27.990000 ( 28.001288)


    that will change in ruby1.9 since to_proc is builtin.
    eg, a run in windows,

    C:\ruby1.9\bin>.\ruby.exe test.rb 1_000
    # of iterations = 1000
    user system total real
    null_time 0.000000 0.000000 0.000000 ( 0.000000)
    map 0.203000 0.000000 0.203000 ( 0.203000)
    to_proc 0.156000 0.000000 0.156000 ( 0.172000)

    C:\ruby1.9\bin>.\ruby.exe test.rb 10_000
    # of iterations = 10000
    user system total real
    null_time 0.016000 0.000000 0.016000 ( 0.015000)
    map 1.937000 0.000000 1.937000 ( 2.515000)
    to_proc 1.610000 0.000000 1.610000 ( 1.954000)

    C:\ruby1.9\bin>.\ruby.exe test.rb 100_000
    # of iterations = 100000
    user system total real
    null_time 0.328000 0.000000 0.328000 ( 0.328000)
    map 19.344000 0.000000 19.344000 ( 24.953000)
    to_proc 17.296000 0.015000 17.311000 ( 18.016000)

    not bad, imho.
    kind regards -botp
     
    botp, Feb 7, 2008
    #8
  9. Rob Saul

    Phrogz Guest

    Symbol#to_proc, lazy evaluation (was Re: idiom I've not seen before)

    On Feb 6, 8:55 pm, Rob Saul <> wrote:
    > I came across this :
    > elements.sort_by(&:position).each


    Loosely-related aside:

    I've been learning Io[1] recently. Io (like Lisp, I gather) allows
    lazy evaluation of method arguments. In Ruby terms, this would mean
    that I can write something like this:

    # Ruby-esque pseudo-code; neither Io nor Ruby
    class Array
    def select
    result = []
    self.each{ |el| result << el if el.sendArg(0) }
    result
    end
    end

    my_array.select( isCool? )
    my_array.select( > 5 )
    my_array.select( roughlyEquals( jim.newSize ) )

    In Io, the "isCool?" method/message isn't (necessarily) evaluated when
    you call the select method. Instead, you can perform some
    introspection on the parsed message tree for each argument and choose
    to ignore it, change it to a string, or send it as a message to any
    object you want.

    To be clear, in the above, the "isCool?" message/method would be send/
    invoked on each array element. Or each element would be sent a
    ">( 5 )" message.

    The Symbol#to_proc technique is clever, but not quite as clean as
    being able to write (in Io):
    elements sortBy( position )
    elements map( * 2 )
    elements select( size > 5 )


    This same ability in Io allowed me to add a debugging method "p" (in
    homage of Ruby) that labels a value with the exact call you made. For
    example:

    # In Io with my custom method (not Ruby)
    p( gk )
    #=> gk is Person_0x4de0d8:
    #=> name = "Gavin Kistner"
    #=> nick = "Phrogz""

    p( gk name )
    #=> gk name is Gavin Kistner

    p( gk nick size )
    #=> gk nick size is 6



    [1] http://www.iolanguage.com/
     
    Phrogz, Feb 7, 2008
    #9
  10. Re: Symbol#to_proc, lazy evaluation (was Re: idiom I've not seenbefore)

    Phrogz wrote:
    > I've been learning Io[1] recently. Io (like Lisp, I gather) allows
    > lazy evaluation of method arguments.

    ....
    > In Io, the "isCool?" method/message isn't (necessarily) evaluated when
    > you call the select method.


    This is known as "call by name", as opposed to by reference or by value.
    It was, more than anything, the single thing that caused the most difficulty
    for the authors of Algol68 optimising compilers :).

    Niklaus Wirt once joked that Europeans (who know how to pronounce his name)
    called him by name, whereas Americans called him by value (nickel's worth).
    Maybe apocryphal, but funny :).

    Clifford Heath.
     
    Clifford Heath, Feb 7, 2008
    #10
  11. Rob Saul

    Deepak Gole Guest

    [Note: parts of this message were removed to make it a legal post.]

    Hello Rob

    We can send arguments, Following is the way, But it throws an error.

    def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
    end

    arr.collect(&[:concat,"Rocking"])

    * *sample.rb:41:in `__send__': [:concat, "dee"] is not a symbol
    (TypeError)
    from sample.rb:41:in `to_proc'
    from sample.rb:51:in `collect'
    from sample.rb:51

    But if we change the implementation of to_proc then it works.

    def to_proc
    lambda {|target| target.send *self}
    end

    arr.collect(&[:concat,"Rocking"])

    o/p => ["rubyRocking", "railsRocking", "rjaxRocking"]

    I don't know how.


    On Feb 6, 2008 8:04 PM, Rob Biedenharn <> wrote:

    > On Feb 6, 2008, at 10:55 PM, Rob Saul wrote:
    > > I came across this :
    > > elements.sort_by(&:position).each
    > > in something I've taken over. I thought sort_by
    > > took a block. Obviously I'm missing something.
    > > Can anyone shed some light on this trick?
    > >
    > > ~Rob

    >
    >
    > You've found Symbol#to_proc
    >
    > That line is equivalent to:
    >
    > elements.sort_by {|e| e.send:)position) }.each
    >
    > The "something" is a Rails project, isn't it?
    >
    > == vendor/rails/activesupport/lib/active_support/core_ext/symbol.rb ==
    > class Symbol
    > # Turns the symbol into a simple proc, which is especially useful
    > for enumerations. Examples:
    > #
    > # # The same as people.collect { |p| p.name }
    > # people.collect(&:name)
    > #
    > # # The same as people.select { |p| p.manager? }.collect { |p|
    > p.salary }
    > # people.select(&:manager?).collect(&:salary)
    > def to_proc
    > Proc.new { |*args| args.shift.__send__(self, *args) }
    > end
    > end
    >
    >
    > I don't know how you can send additional arguments however.
    >
    > -Rob
    >
    > Rob Biedenharn http://agileconsultingllc.com
    >
    >
    >
    >
    >
     
    Deepak Gole, Feb 8, 2008
    #11
  12. Rob Saul

    ThoML Guest

    Re: Symbol#to_proc, lazy evaluation (was Re: idiom I've not seenbefore)

    > Io (like Lisp, I gather) allows lazy evaluation of method arguments.

    Are you referring to Lisp's macro facility? AFAIK macros are usually
    expanded at load/compile time. Since everything is a s-expression in
    lisp, the macro gets its arguments as list and can process them. The
    macro call is then replaced with the result. Or something along this
    line.

    It seems Io provides a similar facility to treat code as data, which
    is cool. Unfortunately I'm not able to compile it. IIRC Forth was able
    to do something similar too BTW (just a side-note).

    Since ruby doesn't have this feature (unless you use parsetree and
    ruby2ruby maybe), the ruby equivalent are strings, I'd say. One could
    thus write something like:

    class Array
    def select_by(snippet)
    rv = []
    self.each do |e|
    rv << e if e.instance_eval '%s %s' % [e.inspect,
    snippet]
    end
    rv
    end
    end

    a = [1,2,3,4,5]
    a.select_by '> 3'

    Which is about the same as your io example. It would be nice of course
    to be able to have real macros that are expanded at load time so that
    one could write something like the following which is incorrect ruby
    syntax and would have to be rewritten before the parser tries to make
    sense out of it:

    module Macros
    def select_by(code)
    %{select {|e| e #{code}}}
    end
    end

    a = [1,2,3,4,5]
    a.select_by(> 3)

    This would require that macro names are unique though. But it's just a
    delirious day-dreaming anyway.

    Regards,
    Thomas.
     
    ThoML, Feb 8, 2008
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Rob G
    Replies:
    2
    Views:
    2,289
    Rob G
    May 24, 2004
  2. David
    Replies:
    2
    Views:
    929
    David
    Jul 6, 2004
  3. Richard Lyman
    Replies:
    7
    Views:
    124
    Friedrich Dominicus
    Dec 17, 2004
  4. Aaron Gray

    Some new code I have not seen before

    Aaron Gray, Nov 13, 2005, in forum: Javascript
    Replies:
    11
    Views:
    194
    Dr John Stockton
    Nov 14, 2005
  5. Cartoper
    Replies:
    1
    Views:
    122
Loading...

Share This Page