我目前正在苦学《算法导论》。里面的堆排序算法是用数组表示一棵二叉树,就像这样
                      A[1]
                   /         \
            A[2]              A[3]
          /     \            /   \
     A[4]       A[5]    A[6]    A[7]
    /   \
A[8]   A[9]       

正如您所看到的,要求数组的起始下标为1才方便。可是Ruby的数组的起始下标是0。我们的想法是为Ruby的Array增加一个属性“base_index”属性,用它来获取或设置数组的起始下标。先看一下完成后的效果吧:

a = ['a','b','c','d','e']
a.base_index  #=> 0
a[0]  #=> 'a'
a[1, 3]  #=> ['b','c','d']
a[1..4]  #=> ['b','c','d','e']
a[-1]  #=> 'e'


a.base_index = 1
a[1]  #=> 'a'
a[1, 3]  #=> ['a','b','c']
a[1..4]  #=> ['a','b','c','d']
a[0]  #=> 'e'

本来以为只要重新定义Array的[]和[]=操作符就行了,后来发现原来有n多函数需要重新定义呀!全部的实现代码如下(文件名:“dynimic_base_index.rb”)

1
# Enhances Array to support any base index.
  2
# It provides a property "base_index", indicates the current base index of the array object.
  3
# The value of "base_index" influnces [] operator
  4
#    a = ['a','b','c','d','e']
  5
#    a.base_index  #=> 0
  6
#    a[0]  #=> 'a'
  7
#    a[1, 3]  #=> ['b','c','d']
  8
#    a[1..4]  #=> ['b','c','d','e']
  9
#    a[-1]  #=> 'e'
 10
#
 11
#    a.base_index = 1
 12
#    a[1]  #=> 'a'
 13
#    a[1, 3]  #=> ['a','b','c']
 14
#    a[1..4]  #=> ['a','b','c','d']
 15
#    a[0]  #=> 'e'
 16
#
 17
# and []= operator
 18
#    a = Array.new
 19
#    a.base_index = 1
 20
#    a[4] = "4";                 #=> [nil, nil, nil, nil, "4"]
 21
#    a[1, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"]
 22
#    a[2..3] = [ 1, 2 ]          #=> ["a", 1, 2, nil, "4"]
 23
#    a[1, 2] = "?"               #=> ["?", 2, nil, "4"]
 24
#    a[1..3] = "A"               #=> ["A", "4"]
 25
#    a[0]   = "Z"               #=> ["A", "Z"]
 26
#    a[2..0] = nil              #=> ["A"]
 27
# 
 28
# and these functions:
 29
#    at()
 30
#    delete_at()
 31
#    each_index()
 32
#    fetch()
 33
#    fill()
 34
#    index()
 35
#    insert()
 36
#    rindex()
 37
#    slice()
 38
#    slice!()
 39
#    values_at()
 40
#    indexes()
 41
#    indices()
 42
class Array
 43
  alias original_index_reader []
 44
  alias original_index_writer []=
 45
  alias original_at at
 46
  alias original_delete_at delete_at
 47
  alias original_each_index each_index
 48
  alias original_fetch fetch
 49
  alias original_fill fill
 50
  alias original_index index
 51
  alias original_insert insert
 52
  alias original_rindex rindex
 53
  alias original_slice slice
 54
  alias original_slice! slice!
 55
  alias original_values_at values_at
 56
  alias original_indexes indexes
 57
  alias original_indices indices
 58
  
 59
  def base_index()
 60
    return defined?(@base_index)? @base_index : 0
 61
  end
 62
  
 63
  def base_index=(value)
 64
    @base_index = value
 65
  end
 66
  
 67
  def at(index)
 68
    return original_at(index - base_index)
 69
  end
 70
  
 71
  def [](*args)
 72
    if args.length == 1 && args.original_at(0).is_a?(Fixnum) then  # e.g. a[1]
 73
      return original_at(args.original_at(0)-base_index)
 74
    elsif args.length == 1 && args.original_at(0).is_a?(Range) then  # e.g. a[1..3]
 75
      range = Range.new(args.original_at(0).begin-base_index, 
 76
                                 args.original_at(0).end-base_index, 
 77
                                 args.original_at(0).exclude_end?)
 78
      return original_index_reader(range)
 79
    elsif args.length == 2  then  #e.g. a[1, 2]
 80
      return original_index_reader(args.original_at(0)-base_index, 
 81
                                             args.original_at(1))
 82
    else
 83
      return original_index_reader(*args)
 84
    end
 85
  end
 86
  
 87
  def []=(*args)
 88
    if args.length >= 2 then 
 89
      if args.original_at(0).is_a?(Fixnum) then  # e.g. a[1]='Y' or a[1,3] = 'Z' or a[1] = ['a','b'] or a[1..3] = ['a','b']
 90
        return original_index_writer(args.original_at(0)-base_index, 
 91
                                              *args.original_index_reader(1..args.length-1))
 92
      elsif args.original_at(0).is_a?(Range) then # e.g. a[1..3] = 'Y' or a[1..3] = ['Y','Z']
 93
        range = Range.new(args.original_at(0).begin-base_index, 
 94
                                   args.original_at(0).end-base_index, 
 95
                                   args.original_at(0).exclude_end?)
 96
        return original_index_writer(range, 
 97
                                              *args.original_index_reader(1..args.length-1))
 98
      end
 99
    end
100
  end
101
  
102
  def delete_at(index)
103
    return original_delete_at(index - base_index)
104
  end
105
  
106
  def each_index
107
    (0
self.length).each do |i|
108
      yield(i+base_index)
109
    end
110
    
111
    return self
112
  end
113
  
114
  def fetch(*args)
115
    if args.length == 1  # e.g. a.fetch(1)  or  a.fetch(1) { |value| value**2 }
116
      if block_given?
117
        return yield(self.original_at(args.original_at(0) - base_index))
118
      else
119
        return self.original_at(args.original_at(0) - base_index)
120
      end
121
    else  # e.g. a.fetch(5, 'cat')
122
      return original_fetch(args.original_at(0)-base_index, 
123
                                   *args.original_index_reader(1..args.length-1))
124
    end
125
  end
126
  
127
  def fill(*args)
128
    if block_given? then
129
      if args.length == 0 then  # e.g. a.fill {|i| i*i }
130
        start_index = base_index
131
        end_index = base_index + self.length - 1
132
      elsif args.length == 1 && args.original_at(0).is_a?(Range)==false then  #e.g. a.fill(2) {|i| i*i }
133
        start_index = args.original_at(0)
134
        end_index = base_index + self.length - 1
135
      elsif args.length == 1 && args.original_at(0).is_a?(Range) then  # e.g. a.fill(2..5) {|i| i*i }
136
        start_index = args.original_at(0).begin
137
        end_index = args.original_at(0).exclude_end?? args.original_at(0).end-1 : args.original_at(0).end
138
      elsif args.length == 2 then  # e.g. a.fill(2,2) {|i| i*i }
139
        start_index = args.original_at(0)
140
        end_index = start_index + args.original_at(1) - 1
141
      else
142
        original_fill(*args)  # original_fill will raise exception :)
143
      end
144
      (start_index..end_index).each do |i|
145
        self[i] = yield(i)
146
      end
147
    else 
148
      if args.length == 1  # e.g. a.fill('x') 
149
        obj = args.original_at(0)
150
        start_index = base_index
151
        end_index = base_index + self.length - 1
152
      elsif args.length == 2 && args.original_at(1).is_a?(Range)==false  # e.g. a.fill('x', 2)
153
        obj = args.original_at(0)
154
        start_index = args.original_at(1)
155
        end_index = base_index + self.length - 1
156
      elsif args.length == 2 && args.original_at(1).is_a?(Range)  # e.g. a.fill('x', 2..5)
157
        obj = args.original_at(0)
158
        start_index = args.original_at(1).begin
159
        end_index = args.original_at(1).exclude_end?? args.original_at(1).end-1 : args.original_at(1).end
160
      elsif args.length == 3 # e.g. a.fill('x', 2, 2)
161
        obj = args.original_at(0)
162
        start_index = args.original_at(1)
163
        end_index = start_index + args.original_at(2) - 1
164
      else
165
        original_fill(*args)  # original_fill will raise exception :)
166
      end
167
      original_fill(obj, Range.new(start_index-base_index, end_index-base_index, false))
168
    end
169
      
170
    return self
171
  end
172
  
173
  def index(value)
174
    result = original_index(value) 
175
    return result && (result + base_index)
176
  end
177
  
178
  def indexes(*args)
179
    arguments = Array.new
180
    
181
    args.each do |arg|
182
      if arg.is_a?(Range)
183
        range = Range.new(arg.begin-base_index, 
184
                                   arg.end-base_index, 
185
                                   arg.exclude_end?)
186
        arguments << range
187
      else
188
        arguments << arg-base_index
189
      end
190
    end
191
    
192
    return original_indexes(*arguments)
193
  end
194
  
195
  def indices(*args)
196
    arguments = Array.new
197
    
198
    args.each do |arg|
199
      if arg.is_a?(Range)
200
        range = Range.new(arg.begin-base_index, 
201
                                   arg.end-base_index, 
202
                                   arg.exclude_end?)
203
        arguments << range
204
      else
205
        arguments << arg-base_index
206
      end
207
    end
208
    
209
    return original_indices(*arguments)
210
  end
211
  
212
  def insert(*args)
213
    if args.length >= 1 
214
      original_insert(args.original_at(0)-base_index, 
215
                         *args.original_index_reader(1..args.length-1))
216
    else
217
      original_insert(*args)  # original_insert will raise exception :)
218
    end    
219
  end
220
  
221
  def rindex(value)
222
    result = original_rindex(value)
223
    return result && (result + base_index)
224
  end
225
  
226
  def slice(*args)
227
    return self[*args]
228
  end
229
  
230
  def slice!(*args)
231
    result = self[*args]
232
    delete_at(*args)
233
    return result
234
  end
235
  
236
  def values_at(*args)
237
    arguments = Array.new
238
    
239
    args.each do |arg|
240
      if arg.is_a?(Range)
241
        range = Range.new(arg.begin-base_index, 
242
                                   arg.end-base_index, 
243
                                   arg.exclude_end?)
244
        arguments << range
245
      else
246
        arguments << arg-base_index
247
      end
248
    end
249
    
250
    return original_values_at(*arguments)
251
  end
252
end

单元测试代码(文件名“TestDynimicBaseIndex.rb”)

1
require 'dynimic_base_index'
  2
require 'test/unit'
  3

  4
class TestDynimicBaseIndex < Test::Unit::TestCase
  5
  def test_base_index
  6
     a = ['a','b','c','d','e']
  7
     assert_equal(0, a.base_index)
  8
     a.base_index = 1
  9
     assert_equal(1, a.base_index)
 10
   end
 11
   
 12
   def test_index_reader_operator()
 13
       a = ['a','b','c','d','e']
 14
       
 15
       assert_equal(0, a.base_index)
 16
       assert_equal('a', a[0])
 17
       assert_equal(['b','c','d'], a[1,3])
 18
       assert_equal(['b','c','d','e'], a[1..4])
 19
       assert_equal('e', a[-1])
 20
       
 21
       a.base_index = 1
 22
       assert_equal(1, a.base_index)
 23
       assert_equal('a', a[1])
 24
       assert_equal(['a','b','c'], a[1,3])
 25
       assert_equal(['a','b','c','d'], a[1..4])
 26
       assert_equal('e', a[0])
 27
       assert_equal(nil, a[6])
 28
       assert_equal([], a[6,1])
 29
       assert_equal([], a[6..10])
 30
       assert_equal(nil, a[7,1])
 31
     end
 32
     
 33
     def test_index_writer_operator()
 34
       a = ['a','b','c','d','e']
 35
       a.base_index = 1
 36
       a[1] = 'Y'
 37
       assert_equal(['Y','b','c','d','e'], a)
 38
       
 39
       b = ['a','b','c','d','e']
 40
       b.base_index = 1
 41
       b[1,3] = ['Y','Z']
 42
       assert_equal(['Y','Z','d','e'], b)
 43
       
 44
       c = ['a','b','c','d','e']
 45
       c.base_index = 1
 46
       c[1,3] = 'Y'
 47
       assert_equal(['Y','d','e'], c)
 48
       
 49
       d = ['a','b','c','d','e']
 50
       d.base_index = 1
 51
       d[1..3] = 'Y'
 52
       assert_equal(['Y','d','e'], d)
 53
       
 54
       e = ['a','b','c','d','e']
 55
       e.base_index = 1
 56
       e[1..3] = ['Y', 'Z']
 57
       assert_equal(['Y','Z','d','e'], e)
 58
     end
 59
     
 60
     def test_at()
 61
       a = ['a','b','c','d','e']
 62
       assert_equal('a', a.at(0))
 63
       a.base_index = 1
 64
       assert_equal('a', a.at(1))
 65
     end
 66
     
 67
     def test_delete_at()
 68
       a = ['a','b','c','d','e']
 69
       a.base_index = 1
 70
       assert_equal('a', a.delete_at(1))
 71
       assert_equal(['b','c','d','e'], a)
 72
     end
 73
     
 74
     def test_each_index()
 75
       a = ['a','b','c','d','e']
 76
       expected_indexes = [1,2,3,4,5]
 77
       actual_indexes = Array.new
 78
       
 79
       a.base_index = 1
 80
       
 81
       a.each_index do |i|
 82
         actual_indexes << i
 83
       end
 84
       
 85
       assert_equal(expected_indexes, actual_indexes)
 86
     end
 87
     
 88
     def test_fetch()
 89
       a = [ 11, 22, 33, 44 ]
 90
       a.base_index = 1
 91
       
 92
       assert_equal(11, a.fetch(1))
 93
       assert_equal(44, a.fetch(0))
 94
       assert_equal(121, a.fetch(1) { |value| value**2 } )
 95
       
 96
       assert_equal('cat', a.fetch(5, 'cat'))
 97
     end
 98
     
 99
     def test_fill()
100
       a = [ "a", "b", "c", "d" ]
101
       a.base_index = 1
102
       
103
       assert_equal(["x","x","x","x"], a.fill("x"))
104
       assert_equal(["x","y","y","y"], a.fill("y", 2))
105
       assert_equal(["x","z","z","y"], a.fill("z", 2, 2))
106
       assert_equal(["x","a","a","a"], a.fill("a", 2..4))
107
       
108
       a = [ "a", "b", "c", "d" ]
109
       a.base_index = 1
110
       assert_equal([1,4,9,16], a.fill { |i| i*i })
111
       assert_equal([1,8,18,32], a.fill(2) { |i| a[i]*2 })
112
       assert_equal([1,4,9,32], a.fill(2,2) { |i| a[i]/2 })
113
       assert_equal([1,4,6,8], a.fill(2..4) { |i| i*2 })       
114
     end
115
     
116
     def test_index()
117
       a = [ "a", "b", "c", "d" ]
118
       a.base_index = 1
119
       assert_equal(2, a.index('b'))
120
       assert_equal(nil, a.index('x'))
121
     end
122
     
123
     def test_insert()
124
       a = [11,22,33]
125
       a.base_index = 1
126
       assert_equal([11,22,33], a.insert(2))
127
       assert_equal([11,'a',22,33], a.insert(2, 'a'))
128
       assert_equal([11,['!','@'],'a',22,33], a.insert(2, ['!','@']))
129
       assert_equal([11,'Q','S',['!','@'],'a',22,33], a.insert(2, 'Q','S'))
130
     end
131
     
132
     def test_rindex()
133
       a = [ "a", "b", "b", "b", "c" ]
134
       a.base_index = 1
135
       
136
       assert_equal(4, a.rindex("b"))
137
       assert_equal(nil, a.rindex("z"))
138
     end
139
     
140
     def test_slice()
141
       a = ['a','b','c','d','e']
142
       a.base_index = 1
143
       assert_equal(1, a.base_index)
144
       assert_equal('a', a.slice(1))
145
       assert_equal(['a','b','c'], a.slice(1,3))
146
       assert_equal(['a','b','c','d'], a.slice(1..4))
147
       assert_equal('e', a.slice(0))
148
       assert_equal(nil, a.slice(6))
149
       assert_equal([], a.slice(6,1))
150
       assert_equal([], a.slice(6..10))
151
       assert_equal(nil, a.slice(7,1))
152
     end
153
     
154
     def test_slice!()
155
       a = ['a','b','c']
156
       a.base_index = 1
157
       
158
       assert_equal('b', a.slice!(2))
159
       assert_equal(['a','c'], a)
160
       assert_equal(nil, a.slice!(100))
161
       assert_equal(['a','c'], a)
162
     end
163
     
164
     def test_indexes()
165
       a = [11,22,33,44,55,66,77,88]
166
       a.base_index = 1
167
       
168
       assert_equal([11], a.indexes(1))
169
       assert_equal([11,33], a.indexes(1,3))
170
       assert_equal([11,11], a.indexes(1,1))
171
       assert_equal([11,33,11], a.indexes(1,3,1))
172
       assert_equal([[11,22,33],[22,33,44],77], a.indexes(1..3,2..4,7))
173
       assert_equal([[11,22]], a.indexes(1
3))
174
       assert_equal([[]], a.indexes(2..1))
175
       assert_equal([], a.indexes())
176
       assert_equal([nil,nil], a.indexes(99,97))
177
     end
178
     
179
     def test_indices()
180
       a = [11,22,33,44,55,66,77,88]
181
       a.base_index = 1
182
       
183
       assert_equal([11], a.indices(1))
184
       assert_equal([11,33], a.indices(1,3))
185
       assert_equal([11,11], a.indices(1,1))
186
       assert_equal([11,33,11], a.indices(1,3,1))
187
       assert_equal([[11,22,33],[22,33,44],77], a.indices(1..3,2..4,7))
188
       assert_equal([[11,22]], a.indices(1
3))
189
       assert_equal([[]], a.indices(2..1))
190
       assert_equal([], a.indices())
191
       assert_equal([nil,nil], a.indices(99,97))
192
     end
193
     
194
     def test_values_at()
195
       a = [11,22,33,44,55,66,77,88]
196
       a.base_index = 1
197
       
198
       assert_equal([11], a.values_at(1))
199
       assert_equal([11,33], a.values_at(1,3))
200
       assert_equal([11,11], a.values_at(1,1))
201
       assert_equal([11,33,11], a.values_at(1,3,1))
202
       assert_equal([11,22,33,22,33,44,77], a.values_at(1..3,2..4,7))
203
       assert_equal([11,22], a.values_at(1
3))
204
       assert_equal([], a.values_at(2..1))
205
       assert_equal([], a.values_at())
206
       assert_equal([nil,nil], a.values_at(99,97))
207
     end
208
end