passing method references in python & ruby

R

rpardee

Hey all,

Kind of an idle, syntax comparison question here...

In python, you can apparently pass method references around very
easily. So imagine you have these 2 functions defined:

def firstWay(arg1, arg2)
return 'Tastes great.'

def secondWay(arg1, arg2)
return 'Less filling.'

Then you can define a method that takes a method name as an argument,
and calls it just by throwing parens (and any expected arguments of
course) after it.

def doStuff(whichway, first_arg, second_arg)
return whichway(first_arg, second_arg)

So calling:

puts doStuff(firstWay, None, None)

Would result in 'Tastes great.'

What's the most graceful way to do this sort of thing in ruby? I
tried passing in e.g., firstWay.to_proc, but that got me a complaint
about not having enough arguments on the call. I can imagine doing,
e.g.,

first_pointer = Proc.new do |foo, bar|
return firstWay(foo, bar)
end

One for each alternate method & passing one of those in. I can also
imagine having doStuff take a block that would call the desired
function & yielding out to that. But neither of those are as pretty
as the python I think. Are those my best options in ruby, or is there
another way?

Thanks!

-Roy
 
J

Justin Collins

Hey all,

Kind of an idle, syntax comparison question here...

In python, you can apparently pass method references around very
easily. So imagine you have these 2 functions defined:

def firstWay(arg1, arg2)
return 'Tastes great.'

def secondWay(arg1, arg2)
return 'Less filling.'

Then you can define a method that takes a method name as an argument,
and calls it just by throwing parens (and any expected arguments of
course) after it.

def doStuff(whichway, first_arg, second_arg)
return whichway(first_arg, second_arg)

So calling:

puts doStuff(firstWay, None, None)

Would result in 'Tastes great.'

What's the most graceful way to do this sort of thing in ruby? I
tried passing in e.g., firstWay.to_proc, but that got me a complaint
about not having enough arguments on the call. I can imagine doing,
e.g.,

first_pointer = Proc.new do |foo, bar|
return firstWay(foo, bar)
end

One for each alternate method & passing one of those in. I can also
imagine having doStuff take a block that would call the desired
function & yielding out to that. But neither of those are as pretty
as the python I think. Are those my best options in ruby, or is there
another way?

Thanks!

-Roy

Here is another way:

irb(main):001:0> def dostuff(whichway, arg1, arg2)
irb(main):002:1> send(whichway, arg1, arg2)
irb(main):003:1> end
=> nil
irb(main):004:0> def first_way(arg1, arg2)
irb(main):005:1> "tastes great"
irb(main):006:1> end
=> nil
irb(main):007:0> def second_way(arg1, arg2)
irb(main):008:1> "less filling"
irb(main):009:1> end
=> nil
irb(main):010:0> dostuff:)first_way, nil, nil)
=> "tastes great"
irb(main):011:0> dostuff:)second_way, nil, nil)
=> "less filling"
irb(main):012:0> "No! #{dostuff:)first_way, nil, nil).capitalize}!"
=> "No! Tastes great!"


-Justin
 
R

Robert Klemme

2008/1/18 said:
Here is another way:

irb(main):001:0> def dostuff(whichway, arg1, arg2)
irb(main):002:1> send(whichway, arg1, arg2)
irb(main):003:1> end
=> nil

#dostuff is really superfluous because the way you defined it it's
just an alias for #send.
irb(main):004:0> def first_way(arg1, arg2)
irb(main):005:1> "tastes great"
irb(main):006:1> end
=> nil
irb(main):007:0> def second_way(arg1, arg2)
irb(main):008:1> "less filling"
irb(main):009:1> end
=> nil
irb(main):010:0> dostuff:)first_way, nil, nil)
=> "tastes great"
irb(main):011:0> dostuff:)second_way, nil, nil)
=> "less filling"
irb(main):012:0> "No! #{dostuff:)first_way, nil, nil).capitalize}!"
=> "No! Tastes great!"

So basically you would just do

send:)first_way, nil, nil)
send:)second_way, nil, nil)


However, if there are /many/ alternatives, then - depending on
circumstances - a Hash might be a better alternative:

algorithms = {
:first_way => lambda {|a,b| 'Tastes great.'},
:second_way => lambda {|a,b| 'Less filling'},
# more algorithms here...
}
# ...
puts algorithms[:first_way][nil, nil]
puts algorithms[:second_way][nil, nil]

Kind regards

robert
 
R

Rados³aw Bu³at

SW4gcHl0aG9uIHdoZW4geW91IHVzZSBtZXRob2QgbmFtZSB3aXRob3V0IHBhcmVudGhlc2lzZXMg
eW91IGdldAptZXRob2QgcmVmZXJlbmNlLiBJbiBSdWJ5IGl0IHdvbid0IHdvcmsgYmVhY3VzZSBt
ZXRob2QgY2FuIGJlIGNhbGxlZAp3aXRob3V0IHBhcmVudGhlc2lzZXMgKGFuZCBSdWJ5IHByb2dy
YW1tZXJzIGxvdmUgaXQgOikpLiBJZiB5b3Ugd2FudAp0byBnZXQgbWV0aG9kIHJlZmVyZW5jZSB5
b3UgY2FuIHVzZSBtZXRob2QoOnlvdV9tZXRob2RfbmFtZSkgYW5kIHlvdQpnZXQgcmVmZXJlbmNl
IHRvIE1ldGhvZCBpbnN0YW5jZS4gTGF0ZXIgeW91IGNhbiBqdXN0IGNhbGwgJ2NhbGwnCm1ldGhv
ZCBhbmQgcGFzcyBzb21lIHBhcmFtZXRlcnMuIERpcmVjdCB0cmFzbGF0aW9uIG9mIHlvdXIgcHJv
Z3JhbSB0bwpSdWJ5IGxvb2tzIGxpa2U6CgpkZWYgZmlyc3RXYXkoYXJnMSwgYXJnMikKICByZXR1
cm4gJ1Rhc3RlcyBncmVhdC4nCmVuZAoKZGVmIHNlY29uZFdheShhcmcxLCBhcmcyKQogIHJldHVy
biAnTGVzcyBmaWxsaW5nLicKZW5kCgpkZWYgZG9TdHVmZih3aGljaHdheSwgZmlyc3RfYXJnLCBz
ZWNvbmRfYXJnKQogIHJldHVybiB3aGljaHdheS5jYWxsKGZpcnN0X2FyZywgc2Vjb25kX2FyZykK
ZW5kCgpwdXRzIGRvU3R1ZmYobWV0aG9kKDpmaXJzdFdheSksIG5pbCwgbmlsKQoKCgotLSAKUmFk
b3OzYXcgQnWzYXQKCmh0dHA6Ly9yYWRhcmVrLmpvZ2dlci5wbCAtIG3zaiBibG9nCg==
 
J

Justin Collins

Rados³aw Bu³at said:
In python when you use method name without parenthesises you get
method reference. In Ruby it won't work beacuse method can be called
without parenthesises (and Ruby programmers love it :)). If you want
to get method reference you can use method:)you_method_name) and you
get reference to Method instance. Later you can just call 'call'
method and pass some parameters. Direct traslation of your program to
Ruby looks like:

def firstWay(arg1, arg2)
return 'Tastes great.'
end

def secondWay(arg1, arg2)
return 'Less filling.'
end

def doStuff(whichway, first_arg, second_arg)
return whichway.call(first_arg, second_arg)
end

puts doStuff(method:)firstWay), nil, nil

Ah, that's the one I was looking for. Then you can do:

def doStuff(whichway, first_arg, second_arg)
whichway[first_arg, second_arg]
end


-Justin
 
R

Robert Klemme

Radosław Bułat said:
In python when you use method name without parenthesises you get
method reference. In Ruby it won't work beacuse method can be called
without parenthesises (and Ruby programmers love it :)). If you want
to get method reference you can use method:)you_method_name) and you
get reference to Method instance. Later you can just call 'call'
method and pass some parameters. Direct traslation of your program to
Ruby looks like:

def firstWay(arg1, arg2)
return 'Tastes great.'
end

def secondWay(arg1, arg2)
return 'Less filling.'
end

def doStuff(whichway, first_arg, second_arg)
return whichway.call(first_arg, second_arg)
end

puts doStuff(method:)firstWay), nil, nil

Ah, that's the one I was looking for. Then you can do:

def doStuff(whichway, first_arg, second_arg)
whichway[first_arg, second_arg]
end

Yeah, but do you really consider

doStuff(method:)firstWay), nil, nil)

more concise than

send:)firstWay, nil, nil)

?

Cheers

robert
 
R

Robert Dober

My momentary python envy (hmmm...) has now passed. ;-)
Well that is good, but for the wrong reason :(

Our Rubyists where cheating on you as you had a first class object
called a function, and we were just using names.
Now there are no functions in Ruby so we cannot pass them on, we do
however have proc objects which would mimick what you have done in
Python
firstway =lambda{ |x| x+1}
otherway = lambda{ |x| x - 1}
def anyway away, anumber
away.call anumber
end

However the interesting and somewhat frustrating stuff starts with
methods which are first class objects in Ruby
class A
def first x; x+1 end
def second x; x-1 end
@f = instance_method :first
@s = instance_method :second
class << self
attr_reader :f, :s
end

def any method, number
method.bind( self ).call number
end
end

p A.new.any(A.f, 41)
p A.new.any(A.s,43)

Things are however a little bit trickier when it comes to binding
instance_methods
look at this code
class B
def b; 42 end
end

p B.instance_method:)b).bind(Object.new).call # :(((
I still fail to see the need for this restriction
Now lots of trickery is needed if you want to do things like these

module M
def m; 42 end
end

p Object.new.send:)extend, M).m

If you are interested in this stuff you might have a look at the
implementation of Traits in Ruby
http://rubyforge.org/projects/ruby-traits/
HTH
Robert
 
R

Robert Dober

R> I still fail to see the need for this restriction

Without this restriction you can write

Array.instance_method:)[]).bind(Hash.new).call(0)

and if you are not lucky ruby just crash.
Bad example of mine, what disturbs me is the following

not working:
Module::new{ def a; 42 end}.instance_method:)a).bind(Object.new).call

not working:
a=Modul::new{ def a; 42 end}.i_m :a
Object.module_eval{ define_method :x, a }

working;
module M
def m; 42 end
end

Object.module_eval{
include M
define_method :x, M.instance_method:)m)
}
p Object.new.x

and I thought we were all ducktypers :(

Robert
 
J

Justin Collins

Robert said:
Radosław Bułat said:
In python when you use method name without parenthesises you get
method reference. In Ruby it won't work beacuse method can be called
without parenthesises (and Ruby programmers love it :)). If you want
to get method reference you can use method:)you_method_name) and you
get reference to Method instance. Later you can just call 'call'
method and pass some parameters. Direct traslation of your program to
Ruby looks like:

def firstWay(arg1, arg2)
return 'Tastes great.'
end

def secondWay(arg1, arg2)
return 'Less filling.'
end

def doStuff(whichway, first_arg, second_arg)
return whichway.call(first_arg, second_arg)
end

puts doStuff(method:)firstWay), nil, nil

Ah, that's the one I was looking for. Then you can do:

def doStuff(whichway, first_arg, second_arg)
whichway[first_arg, second_arg]
end

Yeah, but do you really consider

doStuff(method:)firstWay), nil, nil)

more concise than

send:)firstWay, nil, nil)

?

Cheers

robert

Clearly this is just a toy example to show options for how one might
call methods using variable names. If the doStuff method actually did
more stuff, then it would no longer be equivalent to send.


-Justin
 
G

Gregory Marton

What bugs me about ruby is that I can't do this
def add_one(n)
n + 1
end
[3,6,8].map(add_one)
=> [4,7,9]


I can't even do it more explicitly, because map won't take anything
other than a block:

irb(main):001:0> class A; def A.addone(x); x + 1; end; end
=> nil
irb(main):002:0> A.method:)addone)
=> #<Method: A.addone>
irb(main):003:0> [3,6,8].map{|x| x + 1}
=> [4, 7, 9]
irb(main):004:0> [3,6,8].map(A.method:)addone))
ArgumentError: wrong number of arguments (1 for 0)
from (irb):4:in `map'
from (irb):4
irb(main):005:0> [3,6,8].map(Proc.new{|x| A.method:)addone).call(x)})
ArgumentError: wrong number of arguments (1 for 0)
from (irb):5:in `map'
from (irb):5
irb(main):006:0> [3,6,8].map Proc.new{|x| A.method:)addone).call(x)}
ArgumentError: wrong number of arguments (1 for 0)
from (irb):6:in `map'
from (irb):6


Any of these would be defeating the purpose of brevity anyway. The best
I can do is to make a block that calls the proc I want:

irb(main):007:0> [3,6,8].map{|x| A.method:)addone).call(x)}
=> [4, 7, 9]

or more simply:
irb(main):008:0> [3,6,8].map{|x| A.addone(x)}
=> [4, 7, 9]

this seems a waste of keystrokes, a cognitive indirection, and probably
even a few processor cycles. Is there a way?

Grem
 
A

Arlen Cuss

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

Hi there,

You can skip the between and do this:
def add_one(n)
n+1
end => nil
[3,6,8].map &method:)add_one)
=> [4, 7, 9]

Personally, I like doing it this way. You could always try monkey-patching
(ooh, buzzword) Symbol by adding Symbol#to_proc to do some method
resolution. Not sure how well it'll work for you, but a guess.

Arlen
 
A

Arlen Cuss

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

Sorry, I missed your exact example. It works the same:
class A; def A.add_one(n); n + 1; end; end => nil
[3, 6, 8].map &A.method:)add_one) => [4, 7, 9]

Cheers,
Arlen.
 
R

Robert Dober

Hi there,

You can skip the between and do this:

def add_one(n)
n+1
end => nil
[3,6,8].map &method:)add_one)
=> [4, 7, 9]

Personally, I like doing it this way. You could always try monkey-patching
(ooh, buzzword) Symbol by adding Symbol#to_proc to do some method
resolution.
Oh you might do this even very often if you are writing Ruby1.8/1.9
agnostic libraries.
Cheers
Robert
 
S

Sebastian Hungerecker

Gregory said:
What bugs me about ruby is that I can't do this
def add_one(n)
=C2=A0 n + 1
end
[3,6,8].map(add_one)
=C2=A0=3D> [4,7,9]

[3,6,8].map(&method:)add_one))
#=3D> [4, 7, 9]

And yeah, it'd be nice if there was a more concise syntax than that, but=20
that's what we've currently got. Thinking about it, you could do something=
=20
like this if you want a shorter syntax:

class Symbol
def -@()
method(self)
end
end
[3,6,8].map(&-:add_one)

But that's kinda hackish.


HTH,
Sebastian
=2D-=20
Jabber: (e-mail address removed)
ICQ: 205544826
 
G

Gregory Marton

Robert said:
=> [4, 7, 9]

Personally, I like doing it this way. You could always try monkey-patching
(ooh, buzzword) Symbol by adding Symbol#to_proc to do some method
resolution.
Oh you might do this even very often if you are writing Ruby1.8/1.9
agnostic libraries.
Cheers
Robert

Could you both elaborate? I'm not sure what it would mean to
"monkeypatch" this, or which differences with 1.9 will require it.
Thanks already: this is a bit C-like, but nicer than wrapping a block
around things.

Grem
 
G

Gregory Marton

Another potentially easy question:

I'd like to pass a surround method into another, where the surround
method would take a block, and should yield to it, perhaps doing stuff
before and after. This is not a real call/cc surround, just something
simple. So:

def do_things(options={})
opts = {:surround => Proc.new{yield}}.merge(options)
opts[:surround] do
the actual task
end
end

except this doesn't actually work with Procs, because opts[:surround] is
an undefined method, of course. It contains a method reference. Well,
rather, a procedure reference. In any case, I want to invoke it with a
block.

so I think
y = lambda{ yield }
y.call{ 3 }
perhaps, but no:
LocalJumpError: no block given

How shall I pass it a block?

Thanks,
Grem
 
R

Robert Dober

Robert said:
=> [4, 7, 9]

Personally, I like doing it this way. You could always try monkey-patching
(ooh, buzzword) Symbol by adding Symbol#to_proc to do some method
resolution.
Oh you might do this even very often if you are writing Ruby1.8/1.9
agnostic libraries.
Cheers
Robert

Could you both elaborate? I'm not sure what it would mean to
"monkeypatch" this, or which differences with 1.9 will require it.
Thanks already: this is a bit C-like, but nicer than wrapping a block
around things.
Symbol#to_proc exists in Ruby1.9 standard, if you want to have
programs run in Ruby1.8 too you will be forced to define your
Symbol#to_proc yourself.
But I guess that I am wrong that you have to do that often, that was
written with very little thinking done :(.

Robert
 
R

Robert Klemme

You can skip the between and do this:
def add_one(n)
n+1
end => nil
[3,6,8].map &method:)add_one)
=> [4, 7, 9]

Personally, I like doing it this way. You could always try monkey-patching
(ooh, buzzword) Symbol by adding Symbol#to_proc to do some method
resolution. Not sure how well it'll work for you, but a guess.

Not necessary. Here's another approach that has the added advantage
that it will also work with 1.8 - and it's short and conscise:

irb(main):001:0> add_one = lambda {|x| x + 1}
=> #<Proc:0x7ff9c4bc@(irb):1>
irb(main):002:0> (1..3).map &add_one
=> [2, 3, 4]
irb(main):003:0>

Although I have to say it's not clear to me what speaks against doing
(1..3).map {|x| x + 1}.

Kind regards

robert
 

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

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top