Idiom I've not seen before

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

On Feb 6, 2008, at 10:55 PM, Rob S. 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 B. http://agileconsultingllc.com
[email protected]

On 2/6/08, Rob S. [email protected] 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 K. 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.

On Feb 7, 2008 4:14 AM, Rob S. [email protected] 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

On Thu, Feb 7, 2008 at 6:04 AM, Martin DeMello [email protected]
wrote:

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

Yes, it is in.
Raganwald has a good blog entry about it, he also mentions
String#to_proc:
http://weblog.raganwald.com/2007/11/fun-with-symboltoproc.html

On Feb 6, 2008, at 20:04 , Rob B. 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

On Feb 6, 8:55 pm, Rob S. [email protected] 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 K.”
#=> nick = “Phrogz”"

p( gk name )
#=> gk name is Gavin K.

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

[1] http://www.iolanguage.com/

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 H…

On Feb 7, 2008 1:16 PM, Ryan D. [email protected] wrote:

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

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.

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:into_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.