【问题标题】:Requesting an insight on reduce ruby code请求了解减少红宝石代码
【发布时间】:2019-12-17 21:27:01
【问题描述】:

我开始在可枚举部分解决hackerrank的练习。 该练习要求完成 sum 方法,该方法接受一个整数 n 并将总和返回到系列的 n 项。我从另一个来源找到了解决方案,但我不太明白 reduce 在这种情况下是如何工作的以及输出。

def sum_terms(n)
  series = []

  1.upto(n) do |i|
    series.push(i ** 2 + 1)
  end

  series.reduce(0, :+)
end

puts sum_terms(5)
# outputs 60

【问题讨论】:

  • 具体有什么不明白的?您看过reduce 文档(以及任何相关教程)吗?你知道:+ 是什么意思吗?一旦你有了这些想法,它应该是非常简单的。

标签: ruby algorithm reduce


【解决方案1】:

我们可以这样写这个方法:

def sum_terms(n)
  arr = create_series(n)
  arr.reduce(0, :+)
end

def create_series(n)
  series = []  
  1.upto(n) do |i|
    series.push(i**2 + 1)
  end
  series
end

sum_terms(5)
  #=> 60

步骤如下:

n = 5
arr = create_series(n)
  #=> [2, 5, 10, 17, 26] 
arr.reduce(0, :+)
  #=> 60

我们先来看create_series这个方法。此方法返回n 元素的数组,这些元素是整数12、...、n映射。 “映射”表明使用方法 Enumerable#map 比创建一个空数组 (series)、将 n 元素附加到它并返回该数组更明智:

def create_series(n)
  1.upto(n).map do |i|
    i**2 + 1
  end
end

create_series(5)
  #=> [2, 5, 10, 17, 26]

因为map 的代码块非常简短,我们可能会用大括号而不是do..end 来编写它:

def create_series(n)
  1.upto(n).map { |i| i**2 + 1 }
end

现在让我们看看方法sum_terms。对于n = 5,变为:

[2, 5, 10, 17, 26].reduce(0, :+) #=> 60

这是以下的简写形式:

[2, 5, 10, 17, 26].reduce(0) { |tot,x| tot + x) #=> 60

这里我使用Enumerable#reduce(又名inject)的形式,它接受一个参数(0),这是块变量tot的初始值。当 reduce 的接收者 (2) 的数组的第一个元素被传递到 map 的块时,块变量 x 被设置为等于该值。然后进行块计算:

tot + n
  #=> 0 + 2 => 2

tot (0) 的值现在替换为该总和 (2)。具体来说,memo(此处为tot)的值设置为等于块中最后执行的计算。接下来,将接收者的元素5 传递给块,并将x 设置为等于它。块计算现在是:

tot + n
  #=> 2 + 5 => 7

并且tot 设置为等于7。这样重复三次,导致tot 依次等于173460。由于没有更多元素可以传递给接收者,因此块返回 tot60 的最终值。

现在考虑以下几点:

[2, 5, 10, 17, 26].reduce(:+)                   #=> 60

简写为:

[2, 5, 10, 17, 26].reduce { |tot,x| tot + x }   #=> 60

这与第一个计算的不同之处在于reduce 没有参数。如文档中所述,在这种情况下,tot 最初设置为等于接收器的第一个值,2,然后接收器的四个剩余元素中的每一个都传递给块,导致tot依次等于7173460

显然reduce 的两种形式在这种情况下给出相同的结果1

不过,我们可以改进这段代码,跳过数组[2, 5, 10, 17, 26] 的计算,如下所示:

1.upto(5).reduce(0) { |tot,i| tot + i**2 + 1 }  #=> 60

注意reduce 必须在这里有一个零参数,因为

1.upto(5).reduce { |tot,i| tot + i**2 + 1 }     #=> 59

相当于:

1 + 2.upto(5).reduce(0) { |tot,i| tot + i**2 + 1 }

这是不正确的。

执行此计算的一种更简单的方法是使用方法Enumerable#sum,它在 Ruby v2.4 中首次亮相:

1.upto(5).sum { |i| i**2 + 1 }                  #=> 60

更简单的是评估Faulhaber 公式

n = 5
n + n*(n + 1)*(2*n + 1)/6                       #=> 60

1 在某些情况下,reduce 被分配一个参数(通常为零)只是为了处理所谓的边缘情况。例如,假设我们希望对数组arr 的元素求和并将其添加到10。我们可以写10 + arr.reduce(:+),只要arr 不为空,它就可以正常工作。但是,10 + [].reduce(:+) 会引发异常,例如 [].reduce(:+) #=> nil。相比之下,10 + [].reduce(0, :+) #=> 10.

【讨论】:

    【解决方案2】:

    您的代码有效,但您应该 see documentation 了解如何使用 reduce

    但您的用例中的简化版本只是对数组中的整数求和。您也不需要将0 作为第一个参数传递,因此您可以这样做:

    series.reduce(:+) # instead of series.reduce(0, :+)
    

    【讨论】:

      【解决方案3】:

      基本上reduceseries 数组中的每个元素执行操作。它需要两个参数。 该操作由第二个参数定义(在这种情况下,它是一个命名加号运算符:+ 的符号)。因此,所有元素都相互添加。第一个参数指定一个初始值。 您可以在此处阅读更多相关信息:https://ruby-doc.org/core-2.6.3/Enumerable.html#method-i-reduce

      【讨论】:

        【解决方案4】:

        这是一个绝对合法的问题,因为与Enumerable 中接受块的所有 其他方法不同,reduce(以及作为其别名的inject)有一个奇怪的“短”符号,接受方法名作为第二个参数,不带&

        所以,如果它是字面上的任何其他方法,接受一个块,它被写成series.reduce(0, &:+)

        这是series.reduce(0) { |e, acc| e + acc } 的简写。


        旁注:代码非常不符合 ruby​​ 的习惯,我想重写它

        def sum_terms(n)
          1.upto(n).map do |i|
            i ** 2 + 1
          end.reduce(0, &:+)
        end
        

        【讨论】:

        • 为什么要额外的步骤呢?丢弃地图并在减速器中计算。也可以省略默认值。
        • @SergioTulentsev 我不想彻底修改代码,我想用 ruby​​ 方式重写 same 步骤。那些依赖第一个元素作为累加器初始值的人应该被烧死:)
        • “应该在地狱里被烧死” - 嗯?如果您的备忘录的形状与元素的形状完全不同,您只需要指定初始备忘录。也就是说,您将整数序列减少为哈希或类似的东西。如果形状相同,它应该没有任何区别。事实上,我挑战你生成一个代码的 sn-p,它适用于指定的初始备忘录,但会破坏默认值。 :)
        • 接受挑战stackoverflow.com/a/57442860/2035262 请注意这里没有默认根本
        • @SergioTulentsev,我站在你这边:1)。教育提问者和其他读者是工作#1。这意味着解释最好使用reduce 或更好的sum 一步执行计算,因为在解释了所示方法的工作原理后可以轻松完成; 2) 我不喜欢添加任何不必要的位,无论是reduceself. 不需要的初始值,因为这些操作会导致读者浪费宝贵的时间问自己,“嗯,现在为什么那里?我敢肯定阿列克谢是有原因的。我最好看的更深一点……”
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-06
        • 1970-01-01
        相关资源
        最近更新 更多