[#42735] [Ruby 1.9-Feature#4147][Open] Array#sample で重みを指定したい — Yoji Ojima <redmine@...>

Feature #4147: Array#sample で重みを指定したい

52 messages 2010/12/10
[#42791] [Ruby 1.9-Feature#4147][Assigned] Array#sample で重みを指定したい — Shyouhei Urabe <redmine@...> 2010/12/18

チケット #4147 が更新されました。 (by Shyouhei Urabe)

[#42800] Re: [Ruby 1.9-Feature#4147][Assigned] Array#sample で重みを指定したい — Masaya TARUI <tarui@...> 2010/12/19

> じゃあ反対ないので実装はともかく、この仕様は基本入れる方向で考えましょう。反対の人は意思表示お早めに。

[#42763] [Ruby 1.9-Bug#4159][Open] test_block_variables(TestRipper::ParserEvents) が失敗する — Kouhei Yanagita <redmine@...>

Bug #4159: test_block_variables(TestRipper::ParserEvents) が失敗する

8 messages 2010/12/14

[#42894] [Ruby 1.8-Feature#4207][Open] これから「1.8.8」の話をしよう -- 1.8がこの先生きのこるには — Shyouhei Urabe <redmine@...>

Feature #4207: これから「1.8.8」の話をしよう -- 1.8がこの先生きのこるには

24 messages 2010/12/26
[#42935] Re: [Ruby 1.8-Feature#4207][Open] これから「1.8.8」の話をしよう -- 1.8がこの先生きのこるには — Kenta Murata <muraken@...> 2011/01/04

むらたです。

[#42936] Re: [Ruby 1.8-Feature#4207][Open] これから「1.8.8」の話をしよう -- 1.8がこの先生きのこるには — Kenta Murata <muraken@...> 2011/01/05

むらたです。

[ruby-dev:42691] Re: Enumerable#categorize

From: Yusuke ENDOH <mame@...>
Date: 2010-12-06 11:22:41 UTC
List: ruby-dev #42691
遠藤です。

2010年11月27日18:45 Tanaka Akira <akr@fsij.org>:
> enumerable から hash を生成するメソッドとして
> Enumerable#categorize を追加するのはどうでしょうか。

みんなと同じ感想ですが、やはり 1 メソッドに詰め込み過ぎていると思います。
「要素の抽出」「分類集計処理」「後処理」のうち、2 番目に絞ったらいいのでは
ないでしょうか。

  [[1, 2], [1, 3], [2, 3]].aggregate          #=> {1 => [2, 3], 2 => [3]}
  [[1, 2], [1, 3], [2, 3]].aggregate(:op=>:+) #=> {1 => 5, 2 => 3 }
  [[1, 2, 3]].aggregate                       #=> {1 => {2 => [3]} }

# 議論の混乱防止のため、aggregate という名前にしています。
# 他の候補としては、to_hash 、hashtree とかどうでしょう。


コーナーケースはこんな感じでしょうか。

  [[1, 2], [1, 2, 3]].aggregate               #=> 例外
  [].aggregate                                #=> {}
  [[]].aggregate                              #=> 例外
  [[1]].aggregate                             #=> 例外


多少 categorize より長くなりますが、何をやっているかはずっとわかりやすく
なると思います。
以下、akr さんの例を書き換えてみました。


ary = [["matz", "Yukihiro Matsumoto"],
       ["nobu", "Nobuyoshi Nakada"],
       ["akr", "Tanaka Akira"],
       ["usa", "Usaku NAKAMURA"],
       ["naruse", "NARUSE, Yui"],
       ["ko1", "SASADA Koichi"]]

# akr さん版:
#   h = ary.categorize(1, 0)

h = ary.map {|k, v| [v, k] }.aggregate

#=> {"Yukihiro Matsumoto"=>["matz"],
#    "Nobuyoshi Nakada"=>["nobu"],
#    "Tanaka Akira"=>["akr"],
#    "Usaku NAKAMURA"=>["usa"],
#    "NARUSE, Yui"=>["naruse"],
#    "SASADA Koichi"=>["ko1"]}


# akr さん版:
#   ary.categorize(lambda {|elt| elt[1] }, lambda {|elt| elt[0] })

h = ary.map {|elt| [elt[1], elt[0]] }.aggregate

#=> {"Yukihiro Matsumoto"=>["matz"],
#    "Nobuyoshi Nakada"=>["nobu"],
#    "Tanaka Akira"=>["akr"],
#    "Usaku NAKAMURA"=>["usa"],
#    "NARUSE, Yui"=>["naruse"],
#    "SASADA Koichi"=>["ko1"]}


# akr さん版:
#   h = ary.categorize(lambda {|e| e[0][0] }, lambda {|e| e[0][1]}, 0)

h = ary.map {|e| [e[0][0], e[0][1], e[0]] }.aggregate

#=> {"m"=>{"a"=>["matz"]},
#    "n"=>{"o"=>["nobu"], "a"=>["naruse"]},
#    "a"=>{"k"=>["akr"]},
#    "u"=>{"s"=>["usa"]},
#    "k"=>{"o"=>["ko1"]}}


# akr さん版:
#   h = ary.categorize(lambda {|e| e[0][0] }, 1) {|ks, vs| vs.sort }

h = ary.map {|elt| [elt[0][0], elt[1]] }.aggregate
h.each {|ks, vs| h[ks] = vs.sort } # または vs.sort!

#=> {"m"=>["Yukihiro Matsumoto"],
#    "n"=>["NARUSE, Yui", "Nobuyoshi Nakada"],
#    "a"=>["Tanaka Akira"],
#    "u"=>["Usaku NAKAMURA"],
#    "k"=>["SASADA Koichi"]}


# akr さん版:
#   h = ary.categorize(1, 0) {|ks, vs|
#     raise "duplicate keys: #{ks.inspcet}" if vs.length != 1
#     vs[0]
#   }

h = ary.map {|k, v| [v, k] }.aggregate
h.each {|ks, vs|
  raise "duplicate keys: #{ks.inspcet}" if vs.length != 1
  h[ks] = vs[0]
}

#=> {"Yukihiro Matsumoto"=>"matz",
#    "Nobuyoshi Nakada"=>"nobu",
#    "Tanaka Akira"=>"akr",
#    "Usaku NAKAMURA"=>"usa",
#    "NARUSE, Yui"=>"naruse",
#    "SASADA Koichi"=>"ko1"}


# akr さん版:
#   h = ary.categorize(lambda {|e| e[0][0] }, lambda {|e| 1 }, :op=>:+)

h = ary.map {|e| [e[0][0], 1] }.aggregate(:op=>:+)

#=> {"m"=>1, "n"=>2, "a"=>1, "u"=>1, "k"=>1}


# akr さん版:
#   committers = open("ruby-committers.yml") {|f| YAML.load(f) }
#   pp committers.categorize("account", ["name", "nick"]) {|ks, vs| vs[0] }

h = committers.map {|e| [e["account"], [e["name"], e["nick"]]] }.aggregate
h.each {|ks, vs| h[ks] = vs[0] }



中間配列ができるのが嫌、と言われると思いますが、それは categorize 特有の
問題ではないので、map の Enumerator を返すバージョンを用意することで解決
すべきだと思います。

# 手前味噌ですが http://d.hatena.ne.jp/ku-ma-me/20091111/p2


また、Hash から Hash を返す map もあると便利かもしれません。
しばしば要望きてると思いますが、なんで用意されないんでしたっけ。


group_by との類似については、個人的にはあまり気になりません。
というか、group_by がどうにも使いにくすぎるのが行けないんだと思います。



あと、:seed は値ではなく値を生成する Proc を受け取るべきだと思います。
Hash.new([]) と同じ設計不良になってしまいます。

  p [[1, 2], [1, 3], [2, 5]].categorize(0, 1, seed: [], op: proc {|x,
e| x << e })
  {1=>[2, 3, 5], 2=>[2, 3, 5]}

-- 
Yusuke Endoh <mame@tsg.ne.jp>

In This Thread