idiom I've not seen before

R

Rob Saul

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
 
R

Rob Biedenharn

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
(e-mail address removed)
 
J

Jeremy Kemper

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
 
R

Rob Saul

Jeremy said:
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.
 
M

Martin DeMello

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
 
R

Ryan Davis

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
 
B

botp

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
 
P

Phrogz

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/
 
C

Clifford Heath

Phrogz said:
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.
 
D

Deepak Gole

[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.
 
T

ThoML

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top