Pointer to function


#1

Just starting to learn Ruby, I’d like to ask if somebody would be so
kind as to help me ‘translate’ this Perl proof of concept code:

 package cApp;
 # POC application class that can run named Fncs via 'pointer to 

function’

 my %Fncs = (   'frsFnc' => [ 'just tell "frsFnc"', \&frsFnc ]
              , 'secFnc' => [ 'just tell "secFnc"', \&secFnc ]
            );

 sub new
 { my $class = shift;

   my $self  = {   name => shift
                 , Fncs => \%Fncs
               };
   bless( $self, $class );
 }

 sub run
 { my $self = shift;
   my $sFnc = shift;
   if( exists( $self->{ Fncs }->{ $sFnc } ) )
     { &{$self->{ Fncs }->{ $sFnc }->[ 1 ]}( $self )
     }
   else
     { print "no '$sFnc' in: ", join( ' ', keys( %{$self->{ Fncs }} 

) ), “\n”;
}
}

 sub frsFnc
 { my $self = shift;
   print $self->{ name }, '::frsFnc', "\n";
 }

 sub secFnc
 { my $self = shift;
   print $self->{ name }, '::secFnc', "\n";
 }

 package main;
 # main: create instance of cApp, get Fnc name, run named Fnc

 my $oApp = cApp->new( 'trivial' );
 my $sFnc = $ARGV[ 0 ] || 'frsFnc';
 $oApp->run( $sFnc );

to Ruby. That’s what I have now:

 class CAPP

   @name = 'anonym'
   @Fncs = nil

   def to_s
     'POC application class that can run named Fncs via "pointer to 

function"’
end

   def initialize( name )
     @name = name
     @Fncs = { 'frsFnc' => [ 'just tell "frsFnc"', nil ] ,
               'secFnc' => [ 'just tell "secFnc"', nil ]
             }
   end

   def run( sFnc )
     puts @name + '::run( "' + sFnc + '" )'
     if @Fncs.has_key?( sFnc )
        puts "can't call fnc by name yet"
     else
        puts 'no ' + sFnc + ' in: ' + @Fncs.keys.join( ' ' )
     end
   end

   def frsFnc
     puts @name + '::frsFnc'
   end

   def secFnc
     puts @name + '::secFnc'
   end

 end # class CAPP

 # main: create instance of cApp, get Fnc name, run named Fnc

 oApp = CAPP.new( 'trivial' )
 puts oApp.to_s

 puts "That's easy:"
 oApp.frsFnc()
 oApp.secFnc()

 sFnc = ARGV[ 0 ] || 'frsFnc'
 puts "Now call " + sFnc + ', please:'

 oApp.run( sFnc )

Is it possible to replace the “nil” in @Fncs and the
puts “can’t call fnc by name yet”
in run() with suitable Ruby expressions/statements?

I looked at lamba, but I don’t want to define the functions using
strings.

Thanks


#2

I have no idea what all that’s supposed to do.
In Ruby you have methods (and Procs), but focusing on the
methods–they are invoked by a message. Normally this is done
magically for you. my_obj.my_method sends “my_method” to “my_obj”.

Play with it in IRB:

class Me
def name
“Paul”
end
end
m = Me.new
m.name # => “Paul”
m.send :name # => “Paul”

class LetSomeRun
def initialize
@runnable = %w/first second/
end
def first; puts “running first” end
def second; puts “running second” end
def run (m)
if @runnable.include? m
send m
else
raise “Can’t run that!”
end
end
end

r = LetSomeRun.new
r.run “first”
r.run “oops” # oops :frowning:

You could also take a slightly different approach using Proc objects;
lambda works similar to how sub {} works in Perl.
x = lambda {|name| puts “hello #{name}!”}
x.call “fred”

So…

class Runnable2
def initialize
@runnable = {
“first” => lambda {|this, *args| this.run “second”, “hello!”},
“second” => lambda {|this, *args| puts args.first}
}
end
def run (m, *args)
@runnable.include? m and @runnable[m].call self, *args
end
end
Runnable2.new.run “first”

I’m not exactly sure if this answers your question but, enjoy.

Paul


#3

On Sun, Mar 25, 2007 at 09:10:12PM +0900, ekkehard.horner wrote:

my %Fncs = (   'frsFnc' => [ 'just tell "frsFnc"', \&frsFnc ]
             , 'secFnc' => [ 'just tell "secFnc"', \&secFnc ]
           );

You have several options, depending on how literally you want to
translate
this.

(1) this is a Method object which is bound to the current object. Use

fncs[‘frsFnc’][1].call(args…)

to invoke it

fncs = {
‘frsFnc’ => [ ‘just tell “frsFnc”’, method(:frsFnc) ],
}

(2) this is a Symbol giving the name of a method. Use

some_object.send(:method, args…)

to invoke it on an arbitary object (which could be ‘self’)

fncs = {
‘frsFnc’ => [ ‘just tell “frsFnc”’, :frsFnc ],
}

(Note: this means you may not need to build the fncs hash at all; just

let

the caller pass in a method name. This assumes you don’t mind the

caller

being able to call any method on your object)

(3) this is an anonymous function, like “sub { … }” in Perl. Use

fncs[‘frsFnc’][1].call(args…)

to invoke it.

fncs = {
‘frsFnc’ => [ ‘just tell “frsFnc”’, proc { puts “hello” } ],
}

A fourth option is to put the callable methods in a Module. Use
Modname.instance_method(:method_name) to get an ‘unbound method’ object,
which is like a method but not bound to a specific object instance. You
then
have to bind it to a particular object when you call it.

There are probably others :slight_smile:

HTH,

Brian.


#4

With the great help from Paul and Brian I changed just one line of the
run method
of my CAPP class:

def run( sFnc )
puts @name + ‘::run( "’ + sFnc + ‘" )’
if @Fncs.has_key?( sFnc )

use send to call method whose name is contained in sFnc

todo_stack.push( “relation between string variable and symbol” )

    send sFnc
 else
    puts 'no ' + sFnc + ' in: ' + @Fncs.keys.join( ' ' )
 end

end

and got a script that allows me to call CAPP methods by name specified
on the
command line, even though I cheated a little bit by using the name ==
key of
@Fncs hash instead of the second element of the contained array.

To make myself familiar with the solutions to the ‘pointer to function’
problem
provided by Paul and Brian, I added CAPP methods like:

 # just to have something to call
   def tell_it( sParm )
     puts @name + '::tell_it( "' + sParm + '" ) called 

successfully.’
end

and - more important:

 # obj.send( aSymbol [, args ]* ) -> anObject
 #   Invokes the method identified by aSymbol, passing it any 

arguments specified.
# You can use send if the name send clashes with an existing
method in obj.
# Paul: def run (m) … send m … r.run “first”
# Brian: some_object.send(:method, args…)
# fncs = { ‘frsFnc’ => [ ‘just tell “frsFnc”’, :frsFnc ], }

   def sendSym
     methods = { 'sym0' => [ 'using :tell_it' , :tell_it  ] ,
                 'str0' => [ 'using "tell_it"', "tell_it" ] ,
               }
     methods.each { |keyval| send keyval[ 1 ][ 1 ], keyval[ 1 ][ 0 ] 

}
end
# Does send ‘symbolize’ the string? todo: check
# http://www.troubleshooters.com/codecorn/ruby/symbols.htm

or:

 # obj.method( aSymbol ) -> aMethod
 # Looks up the named method as a receiver in obj, returning a 

Method object (or
# raising NameError). The Method object acts as a closure in obj’s
object instance,
# so instance variables and the value of self remain available

   def useCall
     methods = { '0' => [ 'using method( :tell_it )',  method( 

:tell_it ) ]
}
methods.each { |keyval| keyval[ 1 ][ 1 ].call keyval[ 1 ][ 0 ]
}
end
# I believe, that’s the way to go

Now I can start to

(a) put an enhanced version of the CAPP class into a module

(b) write a script that will create a new Ruby file <fname.rb>
(requiring/using
this class CAPP to come) in the current directory, if called like
ruby xplore.rb new
resp. will add a new skeleton for method and a suitable
entry in
the @methods hash, if called like
ruby xplore.rb add 'short description of

© explore basic data types, looping, database work, … with Ruby

Thank you very much Paul and Brian. Your answers were exactly what I was
looking for - and yes I did enjoy this.

Ekkehard


#5

On Mon, Mar 26, 2007 at 01:40:05AM +0900, ekkehard.horner wrote:

  def sendSym
    methods = { 'sym0' => [ 'using :tell_it' , :tell_it  ] ,
                'str0' => [ 'using "tell_it"', "tell_it" ] ,
              }
    methods.each { |keyval| send keyval[ 1 ][ 1 ], keyval[ 1 ][ 0 ] }
  end
# Does send 'symbolize' the string? todo: check
#  http://www.troubleshooters.com/codecorn/ruby/symbols.htm

Yes, send allows you to pass either a string or a symbol. The symbol is
actually used for method dispatch; if you pass a string then it will
convert
it into a symbol first (you can do this explicitly using String#to_sym
or
String#intern; I think they are just aliases)

Regards,

Brian.


#6

Brian C. schrieb:

On Mon, Mar 26, 2007 at 01:40:05AM +0900, ekkehard.horner wrote:
[…]

# Does send 'symbolize' the string? todo: check

[…]

Yes, send allows you to pass either a string or a symbol. The symbol is
actually used for method dispatch; if you pass a string then it will convert
it into a symbol first (you can do this explicitly using String#to_sym or
String#intern; I think they are just aliases)

Regards,

Brian.

Thanks Brian,

I added

def sendStr
methods = { ‘str0’ => [ ‘using “tell_it”’, “tell_it” ] ,
}
methods.each { |keyval|
send keyval[ 1 ][ 1 ].intern, keyval[ 1 ][ 0 ]
send keyval[ 1 ][ 1 ].to_sym, keyval[ 1 ][ 0 ]
}
end

and learned: When in doubt, use ri (String#to_sym wasn’t mentioned
in “ProgrammingRuby.chm”).

Regards

Ekkehard