Ruby注入了索引和括号

时间:2013-05-06 19:03:00

标签: ruby

我尝试清理我的代码。第一个版本使用each_with_index。在第二个版本中,我尝试使用Enumerable.inject_with_index-construct压缩代码,我发现here

它现在有效,但在我看来像第一个代码一样模糊。 更糟糕的是,我不理解元素周围的括号,

中的索引
.. .inject(groups) do |group_container, (element,index)|

但他们是必要的

  • 这些括号的用途是什么?
  • 如何使代码清晰易读?

第一版 - 带“each_with_index”

class Array

  # splits as good as possible to groups of same size
  # elements are sorted. I.e. low elements go to the first group,
  # and high elements to the last group
  # 
  # the default for number_of_groups is 4 
  # because the intended use case is
  # splitting statistic data in 4 quartiles
  # 
  # a = [1, 8, 7, 5, 4, 2, 3, 8]
  # a.sorted_in_groups(3) # => [[1, 2, 3], [4, 5, 7], [8, 8]]
  # 
  # b = [[7, 8, 9], [4, 5, 7], [2, 8]] 
  # b.sorted_in_groups(2) {|sub_ary| sub_ary.sum } # => [ [[2, 8], [4, 5, 7]], [[7, 8, 9]] ]
  def sorted_in_groups(number_of_groups = 4)
    groups = Array.new(number_of_groups) { Array.new }
    return groups if size == 0

    average_group_size = size.to_f / number_of_groups.to_f
    sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort

    sorted.each_with_index do |element, index|
      group_number = (index.to_f / average_group_size).floor 
      groups[group_number] << element
    end

    groups
  end
end

第二版 - 使用“注入”和索引

class Array
  def sorted_in_groups(number_of_groups = 4)
    groups = Array.new(number_of_groups) { Array.new }
    return groups if size == 0

    average_group_size = size.to_f / number_of_groups.to_f
    sorted = block_given? ? self.sort_by {|element| yield(element)} : self.sort

    sorted.each_with_index.inject(groups) do |group_container, (element,index)|
      group_number = (index.to_f / average_group_size).floor
      group_container[group_number] << element
      group_container
    end
  end
end

4 个答案:

答案 0 :(得分:27)

  

这些括号有什么用?

这是红宝石的一个非常好的功能。我把它称为“解构数组赋值”,但它也可能有正式名称。

这是它的工作原理。假设你有一个数组

arr = [1, 2, 3]

然后将此数组分配给名称列表,如下所示:

a, b, c = arr
a # => 1
b # => 2
c # => 3

你看,阵列被“解构”成了它的各个元素。现在,转到each_with_index。如您所知,它就像常规each,但也返回索引。 inject并不关心这一切,它接受输入元素并将它们按原样传递给它。如果input元素是一个数组(来自each_with_index的elem / index对),那么我们可以在块体中将它分开

sorted.each_with_index.inject(groups) do |group_container, pair|
  element, index = pair

  # or
  # element = pair[0]
  # index = pair[1]

  # rest of your code
end

或者在块签名中对该数组进行解构。括号中有必要给ruby一个暗示,这是一个需要拆分成几个参数的单个参数。

希望这有帮助。

答案 1 :(得分:7)

lines = %(a b c)
indexes = lines.each_with_index.inject([]) do |acc, (el, ind)|
  acc << ind - 1 if el == "b"
  acc
end

indexes # => [0]

答案 2 :(得分:1)

  

这些括号有什么用?

要理解括号,首先需要了解破坏在ruby中的工作原理。最简单的例子我可以想到这个:

1.8.7 :001 > [[1,3],[2,4]].each do |a,b|
1.8.7 :002 >     puts a, b
1.8.7 :003?>   end
1
3
2
4

您应该知道each函数如何工作,并且该块接收一个参数。那么当你传递两个参数时会发生什么?它需要第一个元素[1,3]并尝试将其拆分(破坏)为两个,结果为a=1b=3

现在,请注入takes two arguments in the block parameter,因此通常看起来像|a,b|。因此传递像|group_container, (element,index)|这样的参数我们实际上将第一个作为其他参数,并在另外两个参数中破坏第二个参数(因此,如果第二个参数是[1,3]element=1和{ {1}})。括号是必需的,因为如果我们使用index=3,我们永远不会知道我们是在破坏第一个还是第二个参数,所以括号用作消歧。

事实上,在底端的事情有点不同,但是让我们为这个问题隐藏它。)

答案 3 :(得分:0)

似乎已经有一些答案给出了很好的解释。我想添加一些关于清晰可读的信息。

除了您选择的解决方案之外,还可以扩展Enumerable并添加此功能。

module Enumerable
  # The block parameter is not needed but creates more readable code.
  def inject_with_index(memo = self.first, &block)
    skip = memo.equal?(self.first)
    index = 0
    self.each_entry do |entry|
      if skip
        skip = false
      else
        memo = yield(memo, index, entry)
      end
      index += 1
    end
    memo
  end
end

通过这种方式,你可以这样调用inject_with_index

# m = memo, i = index, e = entry
(1..3).inject_with_index(0) do |m, i, e|
  puts "m: #{m}, i: #{i}, e: #{e}"
  m + i + e
end
#=> 9

如果未传递初始值,则将使用第一个元素,因此不会对第一个元素执行块。