【问题标题】:Flattening an array recursively in Ruby [duplicate]在Ruby中递归地展平数组[重复]
【发布时间】:2017-05-15 11:45:57
【问题描述】:

我很难理解为什么这段代码有效:

def flatten(array, result = [])
  array.each do |element|
    if element.is_a? Array
      flatten(element, result)
    else
      result << element
    end
  end
  result
end

特别是为什么不用将 flatten 方法调用的结果分配给结果数组就可以工作,像这样:

def flatten1(array, result = [])
  array.each do |element|
    if element.is_a? Array
      result = flatten(element, result)
    else
      result << element
    end
  end
  result
end

两者产生相同的输出:

p flatten [1,2,[3,4,[5,[6]]]]  # [1, 2, 3, 4, 5, 6]
p flatten1 [1,2,[3,4,[5,[6]]]] # [1, 2, 3, 4, 5, 6]

【问题讨论】:

  • Here's a longer discussion 网站上可能还有更多,但简短的回答是,将Array result 传递给函数是传递实际对象,而不是它的副本,并且对象是由函数内的result &lt;&lt; element 修改,因此当递归传递时,它会不断修改而无需重新分配

标签: ruby recursion


【解决方案1】:

flatten 方法在第 6 行破坏性地修改了它的第二个参数 result,并将修改后的数组作为参数传递给第 4 行的递归调用。不需要从该方法返回任何内容,因为无论您传递什么数组因为第二个元素将被破坏性地修改以附加输入数组的扁平版本:

my_array = [:foo]

flatten([1, [2, [3, [4]]]], my_array)

my_array
#=> [:foo, 1, 2, 3, 4]

通常认为修改作为参数传递的对象或通过修改输入参数而不是仅仅返回它来返回值的形式是不好的。看起来代码是由 C 程序员编写的,他想将第二个参数用作输出缓冲区。

更惯用的 Ruby 版本看起来像这样:

def flatten(array)
  array.each_with_object([]) do |element, result|
    if element.is_a?(Array)
      result.concat(flatten(element))
    else
      result << element
    end
  end
end

或完全没有突变的纯功能版本:

def flatten(array)
  array.inject([]) do |result, element|
    result + if element.is_a?(Array)
      flatten(element)
    else
      [element]
    end
  end
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-23
    • 2012-09-10
    • 2015-12-06
    • 2015-07-14
    • 2021-06-23
    • 2018-11-13
    • 2018-02-28
    • 2011-07-21
    相关资源
    最近更新 更多