23

コードをきれいにしようとしています。最初のバージョンは を使用しeach_with_indexます。2 番目のバージョンでは、ここでEnumerable.inject_with_index-construct見つけた を使用してコードを圧縮しようとしました。

現在は機能していますが、最初のコードと同じくらいわかりにくいようです。さらに悪いことに、要素の周りの括弧、インデックスがわかりません

.. .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

2 番目のバージョン -- WITH "inject" AND インデックス

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

4 に答える 4

33

これらのブラケットの用途は何ですか?

これは ruby​​ の非常に優れた機能です。「破壊配列代入」と呼んでいますが、正式名称もあると思います。

仕組みは次のとおりです。配列があるとしましょう

arr = [1, 2, 3]

次に、この配列を次のように名前のリストに割り当てます。

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

ご覧のとおり、配列は個々の要素に「分解」されています。さて、へeach_with_index。ご存知のように、これは通常のに似てeachいますが、インデックスも返します。inject入力要素を受け取り、それらをそのままブロックに渡します。入力要素が配列 (からの要素/インデックスのペアeach_with_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​​ に示すために必要です。

お役に立てれば。

于 2013-05-06T19:14:42.380 に答える
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

初期値を渡さない場合、最初の要素が使用されるため、最初の要素のブロックは実行されません。

于 2016-05-26T10:23:20.773 に答える