随笔 - 123  文章 - 24 评论 - 2701 阅读 - 101万

我目前正在苦学《算法导论》。里面的堆排序算法是用数组表示一棵二叉树,就像这样
                       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','b','c','d','e']
a.base_index  
#=> 0
a[0]  #=> 'a'
a[13]  #=> ['b','c','d']
a[1..4]  #=> ['b','c','d','e']
a[-1]  #=> 'e'

a.base_index 
= 1
a[
1]  #=> 'a'
a[13]  #=> ['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()
 42class 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    (0self.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
252end

单元测试代码(文件名“TestDynimicBaseIndex.rb”)
  1require 'dynimic_base_index'
  2require 'test/unit'
  3
  4class 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 = [ 11223344 ]
 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"22))
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(13))
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(13))
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(13))
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
208end

posted on   1-2-3  阅读(1092)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示