ruby - 配列を1番目からはじめてみる

ref: perl - 配列を1番目からはじめてみる
ref: 配列の先頭要素が「0番目」であることは気持ち悪いか…「N番目」という言葉を考察してみる - ’(rubikitch wanna be (a . lisper))
ref: JavascriptやPHPの配列や関数などで 配列の最初の要素が.. - 人力検索はてな

ruby も自由に設定できてしまいますね。

class Array
  alias aref_org []
  def [](x)
    aref_org(x - 1)
  end
end

a = [:foo, :bar, :baz]

p a[1] #=> :foo
p a[2] #=> :bar
p a[3] #=> :baz

言うまでもなく Its use is highly discouraged. ですね。irb で実行したらなんか落ちました (笑)

irb(main):001:0> class Array
irb(main):002:1>   alias aref_org []
irb(main):003:1>   def [](x)
irb(main):004:2>     aref_org(x - 1)
irb(main):005:2>   end
irb(main):006:1> end
=> nil
irb(main):007:0> [:foo, :bar, :baz][1]
SyntaxError: compile error
(irb):7: syntax error, unexpected $end, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END
        from (irb):7
        from :0


追記:

当然、これでは不十分です。 a[1, 2]やa[1..2]の場合に対応できていません。
他にも配列のインデックスを扱うArray#values_atとか、Array#indexとかもありますし。

http://d.hatena.ne.jp/rubikitch/20080524/1211627661

ごもっともです。本気でほしがられてるとは思いませんでした。るびきちさんに書いても釈迦に説法ですが、このくらいで許してください (笑)

class OffsetArray < Array
  @@o = 0

  def [](*r)
    a, b = r
    return super(a - @@o, b) if r.size == 2
    return super(a.begin - @@o .. a - @@o) if a.is_a?(Range)
    return super(a - @@o)
  end

  def []=(*r)
    a, b, c = r
    return super(a - @@o, b, c) if r.size == 3
    return super(a.begin - @@o .. a - @@o, b) if a.is_a?(Range)
    return super(a - @@o, b)
  end

  def at(a)
    super(a - @@o)
  end

  def delete_at(a)
    super(a - @@o)
  end

  def each_index
    super {|a| yield(a + @@o) }
  end

  def fetch(*r, &b)
    r[0] -= @@o
    super(*r, &b)
  end

  def fill(*r, &b)
    r[b ? 0 : 1] -= @@o
    super(*r, &b)
  end

  def index(*r, &b)
    r = super
    r ? r + @@o : nil
  end

  def indexes(*r)
    super(*r.map {|i| i - @@o })
  end
  alias indices indexes

  def insert(*r, &b)
    r[0] -= @@p
    super(*r, &b)
  end

  def rindex(*r, &b)
    r = super
    r ? r + @@o : nil
  end

  alias slice []

  def slice!(*r)
    a, b = r
    return super(a - @@o, b) if r.size == 2
    return super(a.begin - @@o .. a - @@o) if a.is_a?(Range)
    return super(a - @@o)
  end

  alias values_at indexes

  def self.begin
    @@o
  end

  def self.begin=(offset)
    @@o = offset
  end
end

a = OffsetArray[:foo, :bar, :baz]

p a[1] #=> :bar

OffsetArray.begin = 1

p a[1] #=> :foo

テストしてません。Enumerator の each_with_index はスルーで*1。あと 0 が終わりの要素でいいのかとか。

*1:Array 以外にも影響が及ぶのでやはりアスペクトが必要(←関係なかった) 、Array#begin、Array#begin= とかじゃなくて $[ みたいにグローバルっぽい設定の方が自然かも。