gpt4 book ai didi

ruby - 如何在 ruby​​ 中执行惯用的非递归展平?

转载 作者:数据小太阳 更新时间:2023-10-29 07:20:15 27 4
gpt4 key购买 nike

我有一个返回数组数组的方法。为方便起见,我在集合上使用 collect 将它们收集在一起。

arr = collection.collect {|item| item.get_array_of_arrays}

现在我想要一个包含所有数组的数组。当然,我可以遍历数组并使用 + 运算符来执行此操作。

newarr = []    
arr.each {|item| newarr += item}

但这有点丑陋,有没有更好的方法?

最佳答案

Ruby 中有一种方法可以展平数组:Array#flatten:

newarr = arr.flatten(1)

从你的描述来看,你似乎不再关心 arr 了,所以没有必要保留 arr 的旧值,我们可以修改它:

arr.flatten!(1)

(Ruby 中有一条规则说,如果你有两个方法基本上做同样的事情,但其中一个方法有点令人惊讶,你可以将该方法命名为与另一个方法相同但带有感叹号最后。在这种情况下,两种方法都将数组展平,但带有感叹号的版本通过破坏原始数组来实现。)

但是,虽然在这种特殊情况下实际上有一个方法可以完全满足您的要求,但您的代码中有一个更通用的原则在起作用:您有一系列事物,您对其进行迭代并尝试“减少”它归结为一个单一的东西。在这种情况下,很难看到,因为您从一个数组开始,最后以一个数组结束。但是,只需更改代码中的几个小细节,它就会突然变得非常明显:

sum = 0
arr.each {|item| sum += item } # assume arr is an array of numbers

这是完全相同的模式。

您尝试做的事情在范畴论中被称为变形,在数学中被称为折叠,在函数式编程中被称为化简inject:into: 在 Smalltalk 中由 Enumerable#inject 及其别名 Enumerable#reduce 实现(或者在这种情况下实际上是 Array #injectArray#reduce) 在 Ruby 中。

很容易发现:每当你在循环外初始化一个累加器变量,然后在循环的每次迭代中分配给它或修改它引用的对象,那么你就有了 reduce.

在这种特殊情况下,您的累加器是 newarr 并且操作是向其添加一个数组。

因此,您的循环可以更惯用地重写为:

newarr = arr.reduce(:+)

经验丰富的 Rubyist 当然会立即看到这一点。然而,即使是新手最终也会通过一些简单的重构步骤到达那里,可能与此类似:

首先,您意识到它实际上是 折叠:

newarr = arr.reduce([]) {|acc, el| acc += el }

接下来,您意识到分配给 acc 是完全没有必要的,因为 reduce 会覆盖 acc 的内容 无论如何 每次迭代的结果值:

newarr = arr.reduce([]) {|acc, el| acc + el }

第三,没有必要注入(inject)一个空数组作为第一次迭代的起始值,因为 arr 的所有元素无论如何都已经是数组了:

newarr = arr.reduce {|acc, el| acc + el }

当然,这可以通过使用 Symbol#to_proc 进一步简化:

newarr = arr.reduce(&:+)

实际上,我们在这里不需要 Symbol#to_proc,因为 reduceinject 已经接受了操作的符号参数:

newarr = arr.reduce(:+)

这确实一个通用模式。如果您还记得上面的 sum 示例,它看起来像这样:

sum = arr.reduce(:+)

代码没有变化,除了变量名。

关于ruby - 如何在 ruby​​ 中执行惯用的非递归展平?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3004452/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com