[Ruby] Ruby で列挙型

by tanabe on August 26, 2007

Ruby には言語の機能としての列挙型がない。

ちょっと使いたいことがあって、いくつかのパターンで列挙型ぽく振舞うものを書いてみた。

require 'forwardable'
require 'ostruct'

class Array
  def to_enum_hash(start_from = 0)
    keys = self.dup
    vals = Array.new(self.size) {|i| start_from.to_i + i }
    Hash[*keys.zip(vals).flatten]
  end
end

module Enumeration
  def Enumeration.new(*args)
    initial_number, ordered_symbols = parse(args)
    OpenStruct.new(ordered_symbols.to_enum_hash(initial_number)).freeze
  end

  def enum(*args)
    initial_number, ordered_symbols = parse(args)
    define_enum(ordered_symbols, initial_number, self)
  end

  private
  def parse(args)
    if args.last.is_a?(Hash)
      initial_number = args.pop[:start_from]
    end
    initial_number ||= 0
    ordered_symbols = args.map {|a| a.to_sym }
    [initial_number, ordered_symbols]
  end

  def define_enum(names, value, target)
    return if names.size == 0
    target.const_set(names.shift, value)
    define_enum(names, value.next, target)
  end
end

class Module
  include Enumeration
end

使い方はそれぞれこんなかんじ。

require 'enumeration'
FeeTable = [
  [1200, 600], # sex[:male]
  [800,  400]  # sex[:female]
]

class Sex; enum :Male, :Female; end
class Generation; enum :Adult, :Child; end
p FeeTable[Sex::Male][Generation::Child] #=> 600

sex = Enumeration.new :male, :female
generation = Enumeration.new :adult, :child
p FeeTable[sex.female][generation.adult] #=> 800


sex = [:male, :female].to_enum_hash
generation = [:adult, :child].to_enum_hash
p FeeTable[sex[:female]][generation[:child]] #=> 400

OpenStruct を使った実装が使っているときの見た目が一番 Ruby ぽいかなぁ。

  

[Ruby] 大クラス主義

by tanabe on August 26, 2007

ruby-list から Ruby のデザインポリシー「大クラス主義」に関するメモ。

|> * 全く同じ機能で性質だけ異なるクラスを複数用意するのは大ク
|> ラス主義を標榜するRuby的でない
| 「大クラス主義」が良くわからかったのですが、「少機能のクラス
|を多数用意するよりも多機能なクラスを少数用意しましょう」というこ
|とだろう、と思っての提案なのですが、違ってたらご指摘ください。

そういうような意味です。

ruby-list:43937

過去の大クラス主義の話。

|> |そうですか……。直交性高くていいかなーと思ったんですが。
|> |このへんが大クラス主義なんでしょうか?
|>
|> いや、これは多クラス主義でしょう。大クラス主義は結果的に少ク
|> ラス主義につながります。
|
|あ、そうでなくて、「かえって複雑」と感じるのが大クラス主義
|なのかなあ、ということです。

ああ、そういうことですか。

私個人の印象ですが、小クラス主義で直交性が高いのは指数関数的
な複雑さの原因になりやすいと思ってます。逆に大クラス主義でメ
ソッドが多いのは線形の複雑さですね。私の好みはいうまでもない
でしょう。

ruby-dev:14675
  

[Ruby] しびれる配列のシャッフル方法

by tanabe on August 08, 2007

[ruby-list:43811]で挙がっていた配列のシャッフル方法にシビレタ。

sort_byとrandの連携で。

a=[1,2,3,4,5]                   # => [1, 2, 3, 4, 5]
a.sort_by{rand}                 # => [3, 1, 5, 2, 4]