在Ruby中递归地展平数组

时间:2016-12-31 13:25:51

标签: ruby recursion

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

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]

1 个答案:

答案 0 :(得分:2)

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