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

P b*_*sak 1 ruby ruby-on-rails

我希望在递归调用期间保持变量的值为静态,例如,如果foo是一个name作为参数的函数,我想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
Run Code Online (Sandbox Code Playgroud)

第一次调用就像,run_app "john", 1我希望在调用期间保留constant_var的值.我怎样才能做到这一点?

Jör*_*tag 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
Run Code Online (Sandbox Code Playgroud)

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

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 Code Online (Sandbox Code Playgroud)

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

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

# or

run_app('john', 2, nil)
Run Code Online (Sandbox Code Playgroud)

但是,这会给调用者泄漏大量内部实现细节,并给他们带来沉重的负担.例如,他们需要知道第一个和第三个参数需要是同一个对象.但只有当他们1作为第二个论点传递时.如果他们传递的东西不是1,那么他们需要传递nil第三个参数.

什么阻止某人打电话

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

# or 

run_app(tmp = 'john', 2, tmp)
Run Code Online (Sandbox Code Playgroud)

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

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 Code Online (Sandbox Code Playgroud)

现在你可以像你想要的那样调用它:

run_app('john', 1)

# or

run_app('john', 2)
Run Code Online (Sandbox Code Playgroud)

但是,你仍然可以像这样调用它:

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

# or 

run_app(tmp = 'john', 2, tmp)
Run Code Online (Sandbox Code Playgroud)

因此,我们所做的是将该逻辑移动到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 Code Online (Sandbox Code Playgroud)

像这样称呼它:

run_app('john', 1)

# or

run_app('john', 2)
Run Code Online (Sandbox Code Playgroud)

当然,你现在可以打电话

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

# or 

run_app_r(tmp = 'john', 2, tmp)
Run Code Online (Sandbox Code Playgroud)

但至少你现在有一个单独的方法,你可以清楚地记录private,例如通过使用YARD的@private标签或只是使用RDoc的:nodoc:标签完全离开文档.

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

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 Code Online (Sandbox Code Playgroud)

像这样称呼它:

run_app('john', 1)

# or

run_app('john', 2)
Run Code Online (Sandbox Code Playgroud)

由于块是闭包,我们甚至不需要明确传递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
Run Code Online (Sandbox Code Playgroud)

像这样称呼它:

run_app('john', 1)

# or

run_app('john', 2)
Run Code Online (Sandbox Code Playgroud)

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