passing a method

Discussion in 'Ruby' started by Wybo Dekker, Aug 10, 2005.

  1. Wybo Dekker

    Wybo Dekker Guest

    I want to make a method which can execute another method given to it as
    an argument. The following works but the call doesn't look elegant - is
    it the ruby way?

    wybo>cat test.rb
    #!/usr/bin/env ruby
    @t = ''
    def bye
    @t << 'bye'
    end
    def hello
    @t << 'hello'
    end
    def doit(f)
    f.call
    end
    doit(method:)hello))
    doit(method:)bye))
    doit(method:)hello))
    puts @t
    wybo>ruby test.rb
    hellobyehello
    wybo>

    --
    Wybo
     
    Wybo Dekker, Aug 10, 2005
    #1
    1. Advertising

  2. Hi Wybo,

    > I want to make a method which can execute another method
    > given to it as an argument.


    Why do you want to do this?

    > The following works but the call doesn't look elegant -
    > is it the ruby way?


    It's difficult to tell based on your contrived example,
    but the Ruby way would probably be more like this:

    def doit
    yield
    end

    doit { hello }
    doit { bye }
    doit { hello }

    You can write `doit { hello }' as `doit &method:)hello)'
    if you really want to, but the general block syntax is
    obviously much more flexible.

    > wybo>cat test.rb
    > #!/usr/bin/env ruby
    > @t = ''
    > def bye
    > @t << 'bye'
    > end
    > def hello
    > @t << 'hello'
    > end
    > def doit(f)
    > f.call
    > end
    > doit(method:)hello))
    > doit(method:)bye))
    > doit(method:)hello))


    This is a rather poor example, since the above three lines
    could just as well be written like this:

    hello
    bye
    hello

    > puts @t
    > wybo>ruby test.rb
    > hellobyehello
    > wybo>


    --
    Daniel Brockman <>

    So really, we all have to ask ourselves:
    Am I waiting for RMS to do this? --TTN.
     
    Daniel Brockman, Aug 10, 2005
    #2
    1. Advertising

  3. Wybo Dekker wrote:

    > I want to make a method which can execute another method given to it as
    > an argument. The following works but the call doesn't look elegant - is
    > it the ruby way?


    Since all your methods are on the same object, there's a simpler way:

    C:\>ruby
    @t = ''
    def bye
    @t << 'bye'
    end
    def hello
    @t << 'hello'
    end
    def doit(f)
    send f #ri Object#send
    end
    doit :hello
    doit :bye
    doit :hello
    puts @t
    ^D
    hellobyebello
    C:\>

    You could also try passing blocks:

    C:\>ruby
    @t = ''
    def bye
    @t << 'bye'
    end
    def hello
    @t << 'hello'
    end
    def doit
    yield
    end
    doit { hello }
    doit { bye }
    doit { hello }
    puts @t
    ^D
    hellobyebello
    C:\>

    Or modules/classes:

    C:\>ruby
    module Bye
    def self.greet(t)
    t << 'bye'
    end
    end
    class Hello
    def self.greet(t)
    t << 'hello'
    end
    end
    @t = ''
    def doit(c)
    c.greet @t
    end
    doit Hello
    doit Bye
    doit Hello
    puts @t
    ^D
    hellobyehello
    C:\>

    Longer, but maybe more elegant? Meh, I like the first one best.

    Devin
     
    Devin Mullins, Aug 10, 2005
    #3
  4. Hello Wybo,

    what about

    @t = ''
    def bye
    @t << 'bye'
    end
    def hello
    @t << 'hello'
    end
    def doit(f)
    send f
    end
    doit:)hello)
    doit:)bye)
    doit:)hello)
    puts @t

    ?

    Patrick
     
    Patrick Gundlach, Aug 10, 2005
    #4
  5. Hi --

    On Wed, 10 Aug 2005, Wybo Dekker wrote:

    > I want to make a method which can execute another method given to it as
    > an argument. The following works but the call doesn't look elegant - is
    > it the ruby way?
    >
    > wybo>cat test.rb
    > #!/usr/bin/env ruby
    > @t = ''
    > def bye
    > @t << 'bye'
    > end
    > def hello
    > @t << 'hello'
    > end
    > def doit(f)
    > f.call
    > end
    > doit(method:)hello))
    > doit(method:)bye))
    > doit(method:)hello))
    > puts @t
    > wybo>ruby test.rb
    > hellobyehello
    > wybo>


    Have you looked into the 'send' method?

    str = "a string"
    array = str.send:)split, //)
    p array # => ["a", " ", "s", "t", "r", "i", "n", "g"]

    The first argument is the method name; args 2+ are the args to the
    method. You can use strings as well as symbols for the method name.


    David

    --
    David A. Black
     
    David A. Black, Aug 10, 2005
    #5
  6. Wybo Dekker

    Wybo Dekker Guest

    Patrick Gundlach and Devin Mullans wrote:

    > @t = ''
    > def bye
    > @t << 'bye'
    > end
    > def hello
    > @t << 'hello'
    > end
    > def doit(f)
    > send f
    > end
    > doit:)hello)
    > doit:)bye)
    > doit:)hello)
    > puts @t


    That's exactly what I needed. Thanks!

    As for daniel's question:

    > Why do you want to do this?


    and

    > This is a rather poor example, since the above three lines
    > could just as well be written like this:


    > hello
    > bye
    > hello


    Of course, that's because an exmaple should be short.
    My actual code is an initializer for my scripts. I find myself all the
    time re-inserting, in many scripts, code to test if executables are
    available, code to read rc-files, to handle options, and more. I want to
    put most of that stuff in a module which now contains an init method
    using the above suggestion (for the last argument):

    # program initializer:
    # 1. check if all needed executables (in _needed_) are available
    # 2. set the defaults (values of _defaults_) for
    # option- and rc-variables (keys of _defaults_)
    # 3. read the standard rc-files
    # 4. handle the options, as defined in the _optionproc_ method
    # 5. if @rc appears to be defined, read it as an rc-file

    def init(needed,defaults,option_handler)
    @rcfiles = [] # collect names for a report in case @verbose is true

    # set defaults:
    defaults.each do |k,v|
    eval("@#{k} = #{v || 'nil'}")
    end

    # check if all needed executables (in _needed_) are available
    check_execs(needed)

    # run all standard rc-files
    read_rc_files(defaults.keys)

    # handle options
    send option_handler

    # in case a --rc option was used, read its argument as an rc-file:
    read_rc_file(@rc,defaults.keys)

    # if the --verbose option was used, report rc-files read:
    if @verbose
    if @rcfiles.size > 0
    puts "These rc files were read:",@rcfiles.join("\n")
    else
    puts "No rc files were read"
    end
    end
    end

    --
    Wybo
     
    Wybo Dekker, Aug 10, 2005
    #6
  7. Wybo Dekker wrote:
    > Patrick Gundlach and Devin Mullans wrote:
    >
    > > @t = ''
    > > def bye
    > > @t << 'bye'
    > > end
    > > def hello
    > > @t << 'hello'
    > > end
    > > def doit(f)
    > > send f
    > > end
    > > doit:)hello)
    > > doit:)bye)
    > > doit:)hello)
    > > puts @t

    >
    > That's exactly what I needed. Thanks!
    >
    > As for daniel's question:
    >
    > > Why do you want to do this?

    >
    > and
    >
    > > This is a rather poor example, since the above three lines
    > > could just as well be written like this:

    >
    > > hello
    > > bye
    > > hello

    >
    > Of course, that's because an exmaple should be short.
    > My actual code is an initializer for my scripts. I find myself all the
    > time re-inserting, in many scripts, code to test if executables are
    > available, code to read rc-files, to handle options, and more. I want
    > to put most of that stuff in a module which now contains an init
    > method using the above suggestion (for the last argument):
    >
    > # program initializer:
    > # 1. check if all needed executables (in _needed_) are available
    > # 2. set the defaults (values of _defaults_) for
    > # option- and rc-variables (keys of _defaults_)
    > # 3. read the standard rc-files
    > # 4. handle the options, as defined in the _optionproc_ method
    > # 5. if @rc appears to be defined, read it as an rc-file
    >
    > def init(needed,defaults,option_handler)
    > @rcfiles = [] # collect names for a report in case @verbose is true
    >
    > # set defaults:
    > defaults.each do |k,v|
    > eval("@#{k} = #{v || 'nil'}")
    > end
    >
    > # check if all needed executables (in _needed_) are available
    > check_execs(needed)
    >
    > # run all standard rc-files
    > read_rc_files(defaults.keys)
    >
    > # handle options
    > send option_handler
    >
    > # in case a --rc option was used, read its argument as an rc-file:
    > read_rc_file(@rc,defaults.keys)
    >
    > # if the --verbose option was used, report rc-files read:
    > if @verbose
    > if @rcfiles.size > 0
    > puts "These rc files were read:",@rcfiles.join("\n")
    > else
    > puts "No rc files were read"
    > end
    > end
    > end


    IMHO a superior solution would be to use a block instead of argument
    option_handler. AFAICS you have only one such callback and that's exactly
    what blocks are for. If you want access to the current instance you can
    use instance_eval:

    def init(needed,defaults,&option_handler)
    ....
    # handle options
    instance_eval &option_handler
    ....
    end

    Also, for setting instance variables, this idiom is better (because it
    avoids evil eval and you don't need special handling of nil):

    defaults.each {|k,v| instance_variable_set("@#{k}", v)}

    Kind regards

    robert
     
    Robert Klemme, Aug 10, 2005
    #7
  8. Hi --

    On Wed, 10 Aug 2005, Wybo Dekker wrote:

    > Patrick Gundlach and Devin Mullans wrote:
    >
    >> @t = ''
    >> def bye
    >> @t << 'bye'
    >> end
    >> def hello
    >> @t << 'hello'
    >> end
    >> def doit(f)
    >> send f
    >> end
    >> doit:)hello)
    >> doit:)bye)
    >> doit:)hello)
    >> puts @t

    >
    > That's exactly what I needed. Thanks!


    Why not just use send?


    David

    --
    David A. Black
     
    David A. Black, Aug 10, 2005
    #8
  9. Wybo Dekker

    Guest

    Hi,

    At Wed, 10 Aug 2005 19:05:04 +0900,
    Wybo Dekker wrote in [ruby-talk:151467]:
    > I want to make a method which can execute another method given to it as
    > an argument. The following works but the call doesn't look elegant - is
    > it the ruby way?


    Another way:

    > #!/usr/bin/env ruby
    > @t = ''
    > def bye
    > @t << 'bye'
    > end
    > def hello
    > @t << 'hello'
    > end
    > def doit

    yield
    > end

    doit(&method:)hello))
    doit(&method:)bye))
    doit(&method:)hello))
    puts @t

    --
    Nobu Nakada
     
    , Aug 10, 2005
    #9
  10. Wybo Dekker

    Wybo Dekker Guest

    Robert Klemme wrote:

    > IMHO a superior solution would be to use a block instead of argument
    > option_handler. AFAICS you have only one such callback and that's

    exactly
    > what blocks are for. If you want access to the current instance you can
    > use instance_eval:
    >
    > def init(needed,defaults,&option_handler)
    > ....
    > # handle options
    > instance_eval &option_handler
    > ....
    > end


    but that would need, in the calling program, something like

    init(needed,defaults) { handle_options }

    which I feel is less straightforward/consequent than

    init(needed,defaults,:handle_options)

    > Also, for setting instance variables, this idiom is better (because it
    > avoids evil eval and you don't need special handling of nil):
    >
    > defaults.each {|k,v| instance_variable_set("@#{k}", v)}


    Nice! I did'nt know about instance_variable_set yet.
    Learned a lot today, thanks!

    --
    Wybo
     
    Wybo Dekker, Aug 10, 2005
    #10
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Erik
    Replies:
    11
    Views:
    647
    Peter Duniho
    Mar 29, 2008
  2. Gene Wirchenko

    Passing a Method Name to a Method

    Gene Wirchenko, Jun 22, 2011, in forum: Java
    Replies:
    33
    Views:
    1,055
    Fuschia, President-Elect of the Bright Purplish-Gr
    Jun 25, 2011
  3. Gene Wirchenko

    Passing a Method Name to a Method, Redux

    Gene Wirchenko, Jun 24, 2011, in forum: Java
    Replies:
    51
    Views:
    1,194
    Martin Gregorie
    Jul 24, 2011
  4. Neutek
    Replies:
    11
    Views:
    199
    Robert Klemme
    Jan 18, 2007
  5. Arfon Smith

    Passing method name to method?

    Arfon Smith, Sep 28, 2007, in forum: Ruby
    Replies:
    3
    Views:
    102
    Arfon Smith
    Sep 28, 2007
Loading...

Share This Page