如何在ruby中的递归调用期间保持值

时间:2013-08-15 08:37:52

标签: ruby-on-rails ruby

我希望在递归调用期间保持变量的值为静态,例如,如果foo是一个以name为参数的函数,我想保存{{1}的值从变量中第一次调用name开始,变量将在每次递归调用foo时保留该值。

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 我希望在调用期间保留constant_var的值。我怎样才能做到这一点?

3 个答案:

答案 0 :(得分:5)

首先,您的代码中存在一些冗余。已分配diff但从未使用过。你可以摆脱它:

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

解决这个问题的标准方法是添加一个额外的参数来传递额外的信息:

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

然后你需要调用这样的方法:

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

# or

run_app('john', 2, nil)

然而,这会将 ton 的内部实现细节泄漏给调用者,并给他们带来沉重的负担。例如,他们需要知道第一个和第三个参数需要是同一个对象。但只有当他们通过1作为第二个参数时。如果他们传递的内容不是1,那么他们需要传递nil作为第三个参数。

阻止某人打电话

的是什么
run_app('john', 1, 'ringo')

# or 

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

通过使用带有默认参数的可选参数,您可以稍微改善一下:

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

现在您可以按照以下方式调用它:

run_app('john', 1)

# or

run_app('john', 2)

但是,你可以仍然这样称呼它:

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

# or 

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

因此,我们所做的是将该逻辑转移到private辅助方法中,并为公共方法提供我们想要的API:

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

这样称呼:

run_app('john', 1)

# or

run_app('john', 2)

当然,您现在可以调用

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

# or 

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

但至少你现在有一个单独的方法,你可以清楚地记录为private,例如使用YARD的@private标记,或者只使用RDoc的:nodoc:标记将其完全排除在文档之外。

run_app_r方法可以从任何地方调用,即使它只是在run_app内调用,这是非常烦人的事实。在像Scala这样支持嵌套方法的语言中,您只需将run_app_r方法放在run_app方法中,但Ruby不支持嵌套方法,因此我们必须找到另一种方法:{{1 s可以嵌套在方法中!

Proc

这样称呼:

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

由于块是闭包,我们甚至不需要明确地传递run_app('john', 1) # or run_app('john', 2)

constant_var

这样称呼:

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

但所有这一切都没有实际意义,因为你的递归没有基本情况,因此会无限循环。或者更确切地说,您将获得堆栈溢出,因为Ruby不保证正确的尾调用。

答案 1 :(得分:0)

我会使用修改名称的参数:

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

  # recursive call
  run_app name, modified_name
end

然后,您可以使用run_app("John")

调用该方法

答案 2 :(得分:0)

一种方法是在递归调用中编写一个包装器,它在调用实际函数之前将变量存储在某处。也许作为你班级的成员变量。

def run_wrapper(name, startr)
    @orig_name = name

    run_app (name, startr)
end

def run_app(name, startr)
    p @orig_name
end

更简洁的方法可能是添加第三个参数

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