我目前正在苦学《算法导论》。里面的堆排序算法是用数组表示一棵二叉树,就像这样
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”)
单元测试代码(文件名“TestDynimicBaseIndex.rb”)
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”属性,用它来获取或设置数组的起始下标。先看一下完成后的效果吧:












本来以为只要重新定义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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107


108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

单元测试代码(文件名“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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173


174

175

176

177

178

179

180

181

182

183

184

185

186

187

188


189

190

191

192

193

194

195

196

197

198

199

200

201

202

203


204

205

206

207

208

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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 重磅开源!