【问题标题】:How to create a nested loop with Ruby the "Right Way!"?如何以“正确的方式!”使用 Ruby 创建嵌套循环?
【发布时间】:2013-10-22 16:19:22
【问题描述】:

我正在学习 Ruby,参加伯克利的 MOOC,并且,在其中一些 MOOC 的家庭作业中,我们有一个练习说:

定义一个方法sum_to_n?它需要一个整数数组和一个 附加整数 n 作为参数,如果有两个则返回 true 整数数组中的元素总和为 n。一个空数组应该求和 根据定义归零。

我已经创建了两种可以完成这项工作的方法,但我对其中任何一种都不满意,因为我认为它们不是用 Ruby 方式编写的。我希望你们中的一些人可以帮助我了解哪种方法是正确的!

我创建的第一种方法在两次迭代中都使用了each 方法,但我不喜欢这种方法的是,每个数字都与其他数字相加,即使是相同的数字,这样做如下:

arr[1, 2, 3, 4] => 1+1, 1+2, 1+3, 1+4, 2+1, 2+2, 2+3, 2+4, 3+1, 3+2... 4+3, 4+4

如您所见,有很多重复的和,我不希望这样。

这是代码:

def sum_to_n?(arr, n)
  arr.each {|x| arr.each {|y| return true if x + y == n && x != y}}
  return true if n == 0 && arr.length == 0
  return false
end

使用另一种方法,我得到了我想要的,只是几个总和而不重复任何一个,甚至是相同的数字相加,但它看起来很可怕,我很确定有人会因为这样做而杀了我方式,但是如您所见,该方法做得很好:

arr[1, 2, 3, 4] => 1+2, 1+3, 1+4, 2+3, 2+4, 3+4

这是代码:

def sum_to_n?(arr, n)
  for i in 0..arr.length - 1
    k = i + 1
    for k in k..arr.length - 1
      sum = arr[i] + arr[k]
      if sum == n
        return true
      end
    end
  end
  return true if n == 0 && arr.length == 0
  return false
end

好吧,我希望你们能像我尝试的那样做一个更好、更漂亮的方法。

感谢您的帮助。

【问题讨论】:

  • 通常情况下,我会厌倦在 Ruby 中使用 for,但在这种情况下,这是可以接受的,因为第二种方法的迭代次数将少于使用 each
  • 虽然 Ruby 有 for,但我们倾向于忽略它,因为它有一些副作用,例如它会留下中间变量以使变量空间变得混乱,并迫使我们迭代容器使用计算的索引,而不是允许 each 单独传递每个项目。索引缺少第一个或最后一个元素,或者从末尾脱落,是所有语言中的常见错误,each 有助于避免这种情况。因此,虽然它可能看起来像是一种风格选择,但它实际上是一种防御性编程选择。而且,欢迎来到 Stack Overflow!
  • 显然,伯克利的“大规模开放在线课程”正在引起人们对 SO 的极大兴趣 =)

标签: ruby-on-rails ruby arrays loops


【解决方案1】:

我会这样写:

def sum_to_n?(arr, n)
  return true if arr.empty? && n.zero?
  arr.combination(2).any? {|a, b| a + b == n }
end

这似乎是一个漂亮的 Rubyish 解决方案。

【讨论】:

  • 该死的。我是一名 Rubyist,甚至我都对这个解决方案的 Rubyish 程度感到惊讶。干得好。
  • 这正是我的意思。
  • 你是个聪明人,我对此毫不怀疑。你能用这个问题的通用答案来启发我们吗?您之所以有效,是因为存在一种执行 OP 想要的方法,但是如果您有一个通用的嵌套循环怎么办?或者你不应该有,如果你做错了?
【解决方案2】:

这个会在O(n.log(n))而不是O(n²)进行:

a = 1, 2, 3, 4

class Array
  def sum_to? n
    unless empty?
      false.tap {
        i, j, sorted = 0, size - 1, sort
        loop do
          break if i == j
          a, b = sorted[i], sorted[j]
          sum = a + b
          return a, b if sum == n
          sum < n ? i += 1 : j -= 1
        end
      }
    end
  end
end

a.sum_to? 7 #=> [3, 4]

【讨论】:

  • 如果使用Array#sort,则不是O(n)。
  • 你说得对,包括Array#sort,它(可能)是O(n.log(n)),编辑了答案。
【解决方案3】:

除了@jorg-w-mittag 的回答。我找到了另一个使用“排列”的解决方案。

https://stackoverflow.com/a/19351660/66493

def sum_to_n?(arr, n)
  (arr.empty? && n.zero?) || arr.permutation(2).any? { |a, b| a + b == n }
end

我以前不知道排列。 仍然喜欢@jorg-w-mittag 的答案,因为它更具可读性。

【讨论】:

  • permutation 认为顺序很重要。这将导致多余的总和,例如a + bb + a。因此combination在这种情况下是更好的选择。
【解决方案4】:

我在CodeWars 上遇到了这个问题。 accepted answer 确实看起来非常 Rubyish,但这是以性能为代价的。调用arr.combination(2) 会产生很多组合,逐个元素遍历数组并搜索“补码”sum - element 是否存在会更简单。这就是它的样子 -

def sum_to_n?(arr, n)
  (arr.empty? and n.zero?) or arr.any? { |x| arr.include?(n - x) }
end

【讨论】:

  • 为什么需要第一个括号表达式?空数组不会导致any?include? 抛出错误。这是一个很好的答案,我只是认为它可以更简洁一些。
  • @mark-thomas - “根据定义,空数组的总和应为零”是问题所说的 - 如果不是第一个表达式,sum_to_n?([], 0) would return false`。
【解决方案5】:

我认为这个问题的任何答案的开始都应该从修剪数组以获取多余数据开始:

不能用这个:

  arr.select! { |e| e <= n } # may be negative values      

但这可能会有所帮助:

  arr.sort!
  while arr[0] + arr[-1] > n # while smallest and largest value > n
    arr.delete_at(-1) # delete largest vaue
  end

【讨论】:

    【解决方案6】:

    我喜欢https://stackoverflow.com/a/30012616/8706387,但是当 n 将 x 翻倍时它会失败。我们应该在发送 include? n - x 之前从数组中删除 x。

    def sum_to_n?(arr, n)
      return true if arr.empty? && n.zero?
      arr.any? { |x| arr.tap { arr.delete_at arr.index x }.include? n - x }
    end
    

    【讨论】:

      【解决方案7】:

      我想知道为什么这里使用 hash 没有答案?

      def sum_to_n?(arr, n)
        return true if arr.empty? && n.zero?
      
        h = {}
        arr.any? { |x| complement = h[n-x]; h[x] = true; complement }
      end
      
      puts sum_to_n?([1,2,3,4,5,7], 6) # true
      puts sum_to_n?([6,2,3,5,7,9], 6) # false
      puts sum_to_n?([3,4,5,3], 6) # true
      puts sum_to_n?([3,4,5,7], 6) # false
      puts sum_to_n?([], 6) # false
      puts sum_to_n?([], 0) # true
      

      【讨论】:

        猜你喜欢
        • 2019-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多