Pointer to function

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

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

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.

···

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 ]
               );

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 <fname> new
       resp. will add a new skeleton for method <mname> and a suitable entry in
       the @methods hash, if called like
         ruby xplore.rb <fname> add <mname> 'short description of <mname>'

   (c) 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

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.

···

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
    # The Ruby_Newbie Guide to Symbols

Brian Candler schrieb:
[...]

    # 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

···

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