[#42564] [Ruby 1.9-Feature#4043][Open] グローバル関数current_classの提案 — Makoto Kishimoto <redmine@...>

Feature #4043: =E3=82=B0=E3=83=AD=E3=83=BC=E3=83=90=E3=83=AB=E9=96=A2=E6=95=

15 messages 2010/11/11
[#42774] Re: [Ruby 1.9-Feature#4043][Open] グローバル関数current_classの提案 — Yukihiro Matsumoto <matz@...> 2010/12/16

まつもと ゆきひろです

[#42834] Re: [Ruby 1.9-Feature#4043][Open] グローバル関数current_classの提案 — "KISHIMOTO, Makoto" <ksmakoto@...4u.or.jp> 2010/12/21

きしもとです

[#42835] Re: [Ruby 1.9-Feature#4043][Open] グローバル関数current_classの提案 — Yukihiro Matsumoto <matz@...> 2010/12/21

まつもと ゆきひろです

[#42838] Re: [Ruby 1.9-Feature#4043][Open] グローバル関数current_classの提案 — "KISHIMOTO, Makoto" <ksmakoto@...4u.or.jp> 2010/12/21

きしもとです

[#42845] Re: [Ruby 1.9-Feature#4043][Open] グローバル関数current_classの提案 — Yukihiro Matsumoto <matz@...> 2010/12/21

まつもと ゆきひろです

[#42577] Rubyのバグレポートのガイドライン — "Shota Fukumori (sora_h)" <sorah@...>

sora_hです。

11 messages 2010/11/15
[#42588] Re: Rubyのバグレポートのガイドライン — Yugui <yugui@...> 2010/11/18

2010/11/15 Shota Fukumori (sora_h) <sorah@tubusu.net>:

[#42638] Enumerable#categorize — Tanaka Akira <akr@...>

enumerable から hash を生成するメソッドとして

25 messages 2010/11/27
[#42643] Re: Enumerable#categorize — Yukihiro Matsumoto <matz@...> 2010/11/27

まつもと ゆきひろです

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

From: Tanaka Akira <akr@...>
Date: 2010-11-27 10:51:28 UTC
List: ruby-dev #42640
2010年11月27日19:37 Urabe Shyouhei <shyouhei@ruby-lang.org>:
>> 幸いにして第一要素から第二要素へのハッシュは Hash[ary] で作れるようになったので、
>> 逆の第二要素から第一要素へのハッシュについて考えましょう。
>> (同姓同名を考慮して、ハッシュの値は第一要素の配列としましょう)
>>
>> これを得るには現在、残念な事に、自分でループを書く必要があります。
>
> この状況を改善するという選択肢はありませんか。つまりHash[ary].invertがもう少し
> 賢くなればいいんですよね?

いや、もっと一般的な事を考えています。
CSV から読み込む、という状況に触れたのもそうなのですが、
一般にフィールド (カラム) はふたつとは限りません。
また、必ずユニークなキーがあるとも限りません。

それもあって、最初にデータがハッシュとして存在する、
という仮定はしたくありません。

>> 引数の 1, 0 が何を意味するかというと、enumerable の要素から
>> ハッシュのキー及び値を取り出す指定です。
>> 具体的には ary の各要素に対し、[] メソッドを呼び出し、
>> その引数に引き渡してキー/値を得ます。
>> つまり、["matz", "Yukihiro Matsumoto"][1] として "Yukihiro Matsumoto" というキーを得て、
>> ["matz", "Yukihiro Matsumoto"][0] として "matz" という値を得るわけです。
>
> 分かりにくすぎると思います。

0, 1 というインデックスがわかりにくい、という意図でしょうか?

> これが存在すること自体には特に反対しませんが、ちょっと抽象的すぎて逆に使いづら
> いと感じます。最終的にこのアルゴリズムに落ちたとしても、ユーザーにはもう少し使
> いやすいAPIを提供すべきでしょう。たとえば数を数える専用のメソッドを作るとか。

ひとつひとつ進めていこうかと思っていて、
値を配列にする必要がない場合と、
数を数えるものについては、
また後で (気が向いたときに) やろうかと思っていました。

module Enumerable
  # :call-seq:
  #   enum.unique_categorize(ksel1, ksel2, ..., vsel, [opts]) -> hash
  #   enum.unique_categorize(ksel1, ksel2, ..., vsel, [opts]) {|s, v|
... } -> hash
  #
  # categorizes the elements in _enum_ and returns a hash.
  # This method assumes one element for a category by default.
  #
  # +unique_categorize+ takes one or more key selectors,
  # one value selector and
  # an optional option hash.
  # It also takes an optional block.
  #
  # The selectors specify how to extract a value from an element in _enum_.
  # See Enumerable#categorize for details of selectors.
  #
  # The key selectors, _kselN_, are used to extract hash keys from an element.
  # If two or more key selectors are specified, the result hash will be nested.
  #
  # The value selector, _vsel_, is used for the values of innermost hashes.
  # By default, this method assumes the key selectors categorizes
elements in enum uniquely.
  # If the key selectors generates same keys for two or more elements,
ArgumentError is raised.
  # This behavior can be customized by :seed option and the block.
  #
  #   a = [{:fruit => "banana", :color => "yellow", :taste => "sweet",
:price => 100},
  #        {:fruit => "melon", :color => "green", :taste => "sweet",
:price => 300},
  #        {:fruit => "grapefruit", :color => "yellow", :taste =>
"tart", :price => 200}]
  #   p a.unique_categorize(:fruit, :price)
  #   #=> {"banana"=>100, "melon"=>300, "grapefruit"=>200}
  #
  #   p a.unique_categorize(:color, :price)
  #   # ArgumentError
  #
  # If the block is given, it is used for combining values in a category.
  # The arguments for the block is a seed and the value extracted by _vsel_.
  # The return value of the block is used as the next seed.
  # :seed option specifies the initial seed.
  # If :seed is not given, the first value for each category is used
for the seed.
  #
  #   p a.unique_categorize(:taste, :price) {|s, v| s + v }
  #   #=> {"sweet"=>400, "tart"=>200}
  #
  #   p a.unique_categorize(:color, :price) {|s, v| s + v }
  #   #=> {"yellow"=>300, "green"=>300}
  #
  def unique_categorize(*args, &update_proc)
    opts = args.last.kind_of?(Hash) ? args.pop.dup : {}
    if update_proc
      opts[:update] = lambda {|ks, s, v| update_proc.call(s, v) }
    else
      seed = Object.new
      opts[:seed] = seed
      opts[:update] = lambda {|ks, s, v|
        if s.equal? seed
          v
        else
          raise ArgumentError, "ambiguous key: #{ks.map {|k| k.inspect
}.join(',')}"
        end
      }
    end
    categorize(*(args + [opts]))
  end

  # :call-seq:
  #   enum.category_count(ksel1, ksel2, ...)
  #
  # counts elements in _enum_ for each category defined by the key selectors.
  #
  #   a = [{:fruit => "banana", :color => "yellow", :taste => "sweet",
:price => 100},
  #        {:fruit => "melon", :color => "green", :taste => "sweet",
:price => 300},
  #        {:fruit => "grapefruit", :color => "yellow", :taste =>
"tart", :price => 200}]
  #
  #   p a.category_count(:color)
  #   #=> {"yellow"=>2, "green"=>1}
  #
  #   p a.category_count(:taste)
  #   #=> {"sweet"=>2, "tart"=>1}
  #
  #   p a.category_count(:taste, :color)
  #   #=> {"sweet"=>{"yellow"=>1, "green"=>1}, "tart"=>{"yellow"=>1}}
  #
  # The selectors specify how to extract a value from an element in _enum_.
  # See Enumerable#categorize for details of selectors.
  #
  def category_count(*args)
    categorize(*(args + [lambda {|e| 1 }, {:update => lambda {|ks, s,
v| s + v }}]))
  end

end

-- 
[田中 哲][たなか あきら][Tanaka Akira]

In This Thread