P basak
P basak

Reputation: 5004

How to keep value during recursive calls in ruby

i want to keep the value of a variable static during recursive calls, for example if foo is a function that takes name as a parameter, i want to save the value of name from the first call to foo in a variable and the variable will retain the value in every recursive call to foo.

def run_app (name, startr)
  if startr==1
    constant_var=name
  end

  some_name = modify name
  diff = compare some_name, constant_var

  # recursive call
  run_app some_name, 0
end

First call will be like, run_app "john", 1 I want the value of constant_var to be retained during the calls. How can I achieve that?

Upvotes: 1

Views: 2355

Answers (3)

Jörg W Mittag
Jörg W Mittag

Reputation: 369594

First off, there's some redundancy in your code. diff is assigned but never used. You can just get rid of it:

def run_app(name, startr)
  constant_var = name if startr == 1

  some_name = modify name
  compare(some_name, constant_var)

  # recursive call
  run_app(some_name, 0)
end

The standard way of solving this would be to add an extra parameter for passing along that extra information:

def run_app(name, startr, constant_var)
  some_name = modify name
  compare(some_name, constant_var)

  # recursive call
  run_app(some_name, 0, constant_var)
end

Then you need to call the method like this:

run_app(tmp = 'john', 1, tmp)

# or

run_app('john', 2, nil)

However, this leaks a ton of internal implementation details out to the caller and places a heavy burden on them. For example, they need to know that the first and third argument need to be the same object. But only if they pass 1 as the second argument. If they pass something other than 1, then they need to pass nil as the third argument.

What's to stop someone from calling

run_app('john', 1, 'ringo')

# or 

run_app(tmp = 'john', 2, tmp)

You can slightly improve that by using an optional parameter with a default argument:

def run_app(name, startr, constant_var = name if startr == 1)
  some_name = modify name
  compare(some_name, constant_var)

  # recursive call
  run_app(some_name, 0, constant_var)
end

Now you can call it like you want to:

run_app('john', 1)

# or

run_app('john', 2)

However, you can still call it like this:

run_app('john', 1, 'ringo')

# or 

run_app(tmp = 'john', 2, tmp)

So, what we do is to move that logic into a private helper method and give the public method the API we want:

def run_app(name, startr)
  constant_var = name if startr == 1

  run_app_r(name, startr, constant_var)
end

private

def run_app_r(name, startr, constant_var)
  some_name = modify name
  compare(some_name, constant_var)

  # recursive call
  run_app_r(some_name, 0, constant_var)
end

Call it like this:

run_app('john', 1)

# or

run_app('john', 2)

Of course, you can now still call

run_app_r('john', 1, 'ringo')

# or 

run_app_r(tmp = 'john', 2, tmp)

But at least you now have a separate method which you can clearly document as private, e.g. by using YARD's @private tag or just use RDoc's :nodoc: tag to leave it out of the docs completely.

The fact that the run_app_r method can be called from everywhere even though it is only meant to be called inside run_app is pretty annoying. In a language like Scala which supports nested methods you would just put the run_app_r method inside of the run_app method, but Ruby doesn't support nested methods, so we have to find another way: Procs can be nested inside methods!

def run_app(name, startr)
  constant_var = name if startr == 1

  (run_app_r = ->(name, startr, constant_var; some_name) {
    some_name = modify name
    compare(some_name, constant_var)

    # recursive call
    run_app_r.(some_name, 0, constant_var)
  }).(name, startr, constant_var)
end

Call it like this:

run_app('john', 1)

# or

run_app('john', 2)

And since blocks are closures, we don't even need to explicitly pass constant_var along:

def run_app(name, startr)
  constant_var = name if startr == 1

  (run_app_r = ->(name, startr; some_name) {
    some_name = modify name
    compare(some_name, constant_var)

    # recursive call
    run_app_r.(some_name, 0)
  }).(name, startr)
end

Call it like this:

run_app('john', 1)

# or

run_app('john', 2)

But all of this is moot, because your recursion doesn't have a base case and thus will loop infinitely. Or rather, you will get a stack overflow because Ruby doesn't guarantee proper tail calls.

Upvotes: 5

Karthik T
Karthik T

Reputation: 31972

One way would be to write a wrapper to your recursive call which stores the variable somewhere before calling the actual function. Perhaps as a member variable of your class.

def run_wrapper(name, startr)
    @orig_name = name

    run_app (name, startr)
end

def run_app(name, startr)
    p @orig_name
end

A more cleaner way might be to add a third param

def run_app(name, startr, orig_name = nil)
    ...
    run_app("blah", startr-1, orig_name || name)
end

Upvotes: 0

Patrick Oscity
Patrick Oscity

Reputation: 54734

I would go with a parameter for the modified name:

def run_app (name, modified_name=name)
  # do something with name
  modified_name = modify(name)

  # recursive call
  run_app name, modified_name
end

You can then call the method with just run_app("John")

Upvotes: 0

Related Questions