【问题标题】:Behaviour of tap for recursive functions递归函数的点击行为
【发布时间】:2015-01-27 17:27:39
【问题描述】:

我喜欢偶尔使用 tap 作为方法返回的美化器。但是,当使用带有递归函数的 tap 时,它的行为与我的预期不同:

class Node
  attr_accessor :name, :children

  def initialize(name); self.name, self.children = name, []; end

  def render
    res = "Name: #{name}\n"
    children.each do |child|
      res += " - " + child.render + "\n"
    end
    res
  end
end

parent = Node.new('Parent')
parent.children = [Node.new('Child')]
puts parent.render

返回

Name: Parent
 - Name: Child

如果我将渲染功能更改为使用点击:

  def render
    "Name: #{name}\n".tap do |res|
      children.each do |child|
        res += " - " + child.render + "\n"
      end
    end
  end

返回

Name: Parent

我会假设行为与第一个渲染函数相同。文档指出它“向块生成 x,然后返回 x”......因为函数正在递归,它是否会以某种方式污染函数堆栈?

【问题讨论】:

    标签: ruby recursion combinators


    【解决方案1】:

    这与任何事情无关,只是赋值改变了一个变量,而变量是按值传递的。 tap 无关紧要,如果将字符串放入 any 变量,则行为相同。

    在您的情况下,您将字符串文字传递给 proc,该 proc 接收一个名为 res 的变量,其中包含该字符串的副本。然后,您正在修改该变量,不是原始字符串本身。

    考虑:

    def test(res)
      res += "bar"
    end
    
    x = "foo"
    test(x)
    puts x # outputs "foo", not "foobar"
    

    您的第一个示例有效的原因是您将字符串res 中的值替换 为一个新值。您实际上并未将数据附加到存储在res 中的字符串。

    【讨论】:

    • Strings 在 Ruby 中是非常可变的。 String 上有大量的变异方法:<<[]=clearconcatprependsetbyteforce_encodinginsert,@987654336 @,gsub!capitalize!chomp!chop!delete!downcase!upcase!encode!lstrip!succ!next!next!next! reverse!scrub!rstrip!slice!squeeze!swapcase!tr!tr_s!
    • 字符串不是不可变的,但是无论何时使用赋值运算符,实际上都是在更改= 左侧所指的对象,而不是更改该对象的值。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-30
    • 2019-09-14
    • 2016-01-06
    • 2020-09-16
    • 1970-01-01
    • 2021-10-17
    相关资源
    最近更新 更多