return doesn't always exit my method

Discussion in 'Ruby' started by Benjamin Thomas, Mar 11, 2009.

  1. Hi, I'm new to Ruby and programming in general.
    I'm coding a little script to train on multiplication tables but
    something's wrong:

    If I type 'exit', the function should normally exit, via a return
    statement. But this does not always happen and I really can't understand
    why. I've been scratching my head for a few hours on that so I'm posting
    here hoping somebody will be able to spot my obvious mistake!

    I'm trying to apply here the concept of recursion so maybe this is where
    I'm messing up.

    Thanks for your input,
    Ben


    #########################################################################
    #!/usr/bin/ruby
    require 'yaml'

    system "clear"
    filename = "tested_list.txt"

    if File.exist?(filename)
    imported_list = File.read filename
    $tested = YAML::load imported_list
    else
    $tested = {}
    end

    def rand_multi()
    num1 = rand(10)
    num2 = rand(10)
    got_wrong = false
    operation = "#{num1}*#{num2}"
    if $tested[operation].nil?
    puts "creation!"
    $tested[operation] = 0
    elsif $tested[operation] > 1
    puts "need restart"
    rand_multi() # forces function call again
    end
    puts "iteration #{$tested[operation]}"
    print operation, " = "
    before = Time.now
    time_before = (before.min * 60) + before.sec # in case I go for a
    coffee :)
    answer = gets.chomp
    if answer =~ /exit/
    puts "will exit now"
    return ##### <-- return statement does not always exit method; why??
    ######
    # exit
    elsif answer =~ /[0-9]+/
    answer = answer.to_i
    else
    puts "Please enter numbers only!"
    rand_multi()
    end
    if answer != num1 * num2
    print "" # terminal colors
    puts "inside not equal"
    puts "No! Answer was '#{num1*num2}'"
    print ""
    sleep(1)
    # system "clear"
    got_wrong = true
    $tested[operation] -= 5 # give negative value
    rand_multi()
    elsif answer == num1 * num2
    after = Time.now
    time_after = (after.min * 60) + after.sec
    answer_time = time_after - time_before
    print ""
    puts "inside equal"
    puts "Yes!, you answered in #{answer_time} seconds"
    print ""
    sleep(0.3)
    # system "clear"
    if got_wrong == false
    if answer_time < 3
    $tested[operation] += 1 # give positive value
    else
    $tested[operation] -= 1 # small penalty if answer too
    slow
    end
    end
    rand_multi()
    end
    end

    puts "Multiplications!"
    puts "Type 'exit' to exit"
    puts "\n\n"

    rand_multi()
    puts "Bad answers:"
    $tested.each_pair {|key, value| puts "#{key} => #{value}" if value < 0}

    File.open filename, 'w' do |f|
    f.write $tested.to_yaml
    end
    ###############################################
    --
    Posted via http://www.ruby-forum.com/.
    Benjamin Thomas, Mar 11, 2009
    #1
    1. Advertising

  2. On Wed, Mar 11, 2009 at 6:18 AM, Benjamin Thomas
    <> wrote:
    > Hi, I'm new to Ruby and programming in general.
    > I'm coding a little script to train on multiplication tables but
    > something's wrong:
    >
    > If I type 'exit', the function should normally exit, via a return
    > statement. But this does not always happen and I really can't understand
    > why. I've been scratching my head for a few hours on that so I'm posting
    > here hoping somebody will be able to spot my obvious mistake!
    >
    > I'm trying to apply here the concept of recursion so maybe this is where
    > I'm messing up.
    >
    > Thanks for your input,
    > Ben


    Your intuition on where the problem is is correct. Note that when
    using recursion, each call to the function is a separate "layer", and
    calling return only jumps you out of the current layer. So if you are
    in the invocation of rand_multi() that you called from the top level,
    return will exit rand_multi() back to the top level, but if you are in
    an invocation of rand_multi() called from another invocation of
    rand_multi, return will return you from the current invocation of
    rand_multi() back to the calling invocation.

    I hope that's clear enough. If not, you can get an illustration by
    changing your rand_multi() method as follows:


    def rand_multi(call_depth=0) # <-- changed
    puts "entering rand_multi(#{call_depth})" # <-- added
    num1 = rand(10)
    num2 = rand(10)
    got_wrong = false
    operation = "#{num1}*#{num2}"
    if $tested[operation].nil?
    puts "creation!"
    $tested[operation] = 0
    elsif $tested[operation] > 1
    puts "need restart"
    rand_multi(call_depth+1) # <-- changed: forces function call again
    puts "returning to rand_multi(#{call_depth}" # <-- added
    end
    puts "iteration #{$tested[operation]}"
    print operation, " = "
    before = Time.now
    time_before = (before.min * 60) + before.sec # in case I go for a
    coffee :)
    answer = gets.chomp
    if answer =~ /exit/
    puts "will exit now"
    return ##### <-- return statement does not always exit method; why??
    ######
    # exit
    elsif answer =~ /[0-9]+/
    answer = answer.to_i
    else
    puts "Please enter numbers only!"
    rand_multi()
    end
    if answer != num1 * num2
    print " [0;31m" # terminal colors
    puts "inside not equal"
    puts "No! Answer was '#{num1*num2}'"
    print " [0;m"
    sleep(1)
    # system "clear"
    got_wrong = true
    $tested[operation] -= 5 # give negative value
    rand_multi(call_depth+1) # <-- changed
    puts "returning to rand_multi(#{call_depth}" # <-- added
    elsif answer == num1 * num2
    after = Time.now
    time_after = (after.min * 60) + after.sec
    answer_time = time_after - time_before
    print " [1;34m"
    puts "inside equal"
    puts "Yes!, you answered in #{answer_time} seconds"
    print " [0;m"
    sleep(0.3)
    # system "clear"
    if got_wrong == false
    if answer_time < 3
    $tested[operation] += 1 # give positive value
    else
    $tested[operation] -= 1 # small penalty if answer too
    slow
    end
    end
    rand_multi(call_depth+1) # <-- changed
    puts "returning to rand_multi(#{call_depth}" # <-- added
    end
    end
    Christopher Dicely, Mar 11, 2009
    #2
    1. Advertising

  3. Benjamin Thomas

    matt neuburg Guest

    Benjamin Thomas <> wrote:

    > If I type 'exit', the function should normally exit, via a return
    > statement. But this does not always happen and I really can't understand
    > why. I've been scratching my head for a few hours on that so I'm posting
    > here hoping somebody will be able to spot my obvious mistake!
    >
    > I'm trying to apply here the concept of recursion so maybe this is where
    > I'm messing up.


    To abort a deep call chain, you can use catch() and throw():

    def go_deeper(level)
    throw:)done) if level > 3
    puts "Level #{level}"
    go_deeper(level+1)
    end

    catch:)done) {go_deeper(0)}
    puts "finished"

    m.

    --
    matt neuburg, phd = , http://www.tidbits.com/matt/
    Leopard - http://www.takecontrolbooks.com/leopard-customizing.html
    AppleScript - http://www.amazon.com/gp/product/0596102119
    Read TidBITS! It's free and smart. http://www.tidbits.com
    matt neuburg, Mar 11, 2009
    #3
  4. [Note: parts of this message were removed to make it a legal post.]

    On Wed, Mar 11, 2009 at 6:18 AM, Benjamin Thomas
    <> wrote:
    > Hi, I'm new to Ruby and programming in general.
    > I'm coding a little script to train on multiplication tables but
    > something's wrong:
    >
    > If I type 'exit', the function should normally exit, via a return
    > statement. But this does not always happen and I really can't understand
    > why. I've been scratching my head for a few hours on that so I'm posting
    > here hoping somebody will be able to spot my obvious mistake!
    >
    > I'm trying to apply here the concept of recursion so maybe this is where
    > I'm messing up.
    >
    > Thanks for your input,
    > Ben


    Obviously the flow in your pro gramme is due to the recursive nature of
    it.As correctly pointed out by Christopher , return in a recursive algorithm
    doesn't necessarily mean your returning from the function entry point you
    have started it. It does only mean that you have returned from a specific
    instance of the function (or a stack level one may call it..) and ONLY when
    u have returned from all the instances of the function in the flow , we can
    safely say function has finally returned it's control . So don't be fooled
    by a return statement in a recursive funtion as it only provide a way for
    you to return the control in the current stack .Also Always remember to
    provide a return (or termination logic) in the context of a recursive
    function which otherwise will lead to disasterous unending loops in the
    flow.

    Regards
    -Udayanga
    usw wickramasinghe, Mar 11, 2009
    #4
  5. Christopher Dicely wrote:

    > Your intuition on where the problem is is correct. Note that when
    > using recursion, each call to the function is a separate "layer", and
    > calling return only jumps you out of the current layer.


    Thanks very much Christopher! I wasn't aware of this at all. I did run
    the code changes you provided and it helps!
    I think I grasp the general concept but some aspects remain a mistery.
    Mainly I do not understand what the code is doing when it reverts to a
    previous "layer":

    If the hash is new and fairly empty, the layers pop off in cascading
    style. No other bit of code seems to get executed.

    this is the output I get when I type 'exit'

    ##########console output#############
    creation!
    iteration 0
    1*8 = exit
    will exit now
    returning to rand_multi(12)
    returning to rand_multi(11)
    returning to rand_multi(10)
    returning to rand_multi(9)
    returning to rand_multi(8)
    returning to rand_multi(7)
    returning to rand_multi(6)
    returning to rand_multi(5)
    returning to rand_multi(4)
    returning to rand_multi(3)
    returning to rand_multi(2)
    returning to rand_multi(1)
    returning to rand_multi(0)
    Bad answers:
    7*6 => -1
    ######################################

    But if the hash gets populated, the first part of the function gets
    executed from time to time and 'return' "sticks" to some layers.

    This is the output I get then:

    ###############console output###################
    6*1 = exit
    will exit now
    returning to rand_multi(30)
    returning to rand_multi(29)
    iteration 2
    6*7 = exit
    will exit now
    returning to rand_multi(28)
    iteration 2
    6*7 = exit
    will exit now
    returning to rand_multi(27)
    iteration 2
    1*8 = exit
    will exit now
    returning to rand_multi(26)
    returning to rand_multi(25)
    returning to rand_multi(24)
    returning to rand_multi(23)
    iteration 2
    1*3 =
    #################################################
    I would guess return brings me back to the exact same spot the recursive
    call got called. Is that correct? And in that case, wouldn't that mean
    that my function logic is fundamentally flawed because unpredictable?

    Thanks very much Matt for your suggestion. Unfortunatly I cannot apply
    it here because I've barely touched procs and only have a vague idea of
    what throw and catch do. But it's definitly material for me to study so
    thanks!

    Thank you also Udayanga. On first analysis, it's true that my
    "termination logic" was to input 'exit' at the prompt but I have also an
    infinite loop going on when I have answered all possible operations
    correctly with a score of 2 (the badly named variable 'iteration').
    --
    Posted via http://www.ruby-forum.com/.
    Benjamin Thomas, Mar 11, 2009
    #5
    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. Eyal Bekerman
    Replies:
    2
    Views:
    406
    Steven Coco
    Oct 10, 2003
  2. QQ
    Replies:
    5
    Views:
    507
    Jonathan Adams
    May 10, 2005
  3. jacob navia
    Replies:
    3
    Views:
    543
    Nick Keighley
    Feb 24, 2010
  4. Keith Thompson
    Replies:
    10
    Views:
    676
    Tim Rentsch
    Mar 3, 2010
  5. chad
    Replies:
    14
    Views:
    567
    Arnaud Delobelle
    Oct 13, 2010
Loading...

Share This Page