18
、对数组迭代
Array 类有个我们希望的标准的迭代器each 。但是,它还其它很有用的迭代器。
reverse_each 方法以逆序迭代。它先使用reverse 计算然后使用each ,但它更快。这儿是个例子:
words = %w(Son I am able she said)
str = ""
words.reverse_each {
|w| str += "#{ w}
"}
# str is now "said she able am I Son "
如果我们只想在索引上迭代,我们可以使用each_inex 。x.each_inex 相当于(0..(x.size-1)).each ( 也就是,在索引范围内迭代) 。
迭代器each_with_index( 混合了Comparable) 将元素与索引两者传递给块,像下面:
x = ["alpha", "beta", "gamma"]
x.each_with_index do |x,i|
puts "Element #{ i}
is #{ x} "
end
# Produces three lines of output
如何以随机次序在数组上迭代呢?下面例子使用了迭代器random_each( 它简单地调用randomize 方法) :
class Array
# 假设我们已经定义了randomize
def random_each
temp = self.randomize # 将数组打乱。
temp.each { |x| yield x}
end
end
dwarves = %w(Sleepy Dopey Happy Sneezy Grumpy Bashful Doc)
list = ""
dwarves.random_each { |x| list += "#{ x}
"}
# list is now:
# "Bashful Dopey Sleepy Happy Grumpy Doc Sneezy "
# (Your mileage may vary.)
译注:在文档中是这样描述
1.
array.each {|item| block } → array
为每个元素调用块。
2.
array.reverse_each {|item| block }
倒序each
3.
array.each_index {|index| block } → array 对每个索引调用块,each 是元素。
4.
enum.each_with_index {|obj, i| block } → enum 用元素,索引调用块
19 、以插入界定符的形式生成字符串
我们常常想在数组的两个元素之间以"fence post" 风格插入界定符;也就是说,我们想在元素之间放置界定符,但第一个或最后一个元素没有。方法join 将作这件事,就和你的* 操作符一样:
been_there = ["Veni", "vidi", "vici."]
journal = been_there.join(", ")
# "Veni, vidi, vici."
# Default delimiter is space
letters = ["
hi","Mu","Alpha"]
musicians = letters.join
# "
hi Mu Alpha"
people = ["Bob","Carol","Ted","Alice"]movie = people * " and "
# movie is now "Bob and Carol and Ted and Alice"
注意,如果我们真的想以不同方式处理最后一个元素,比如插入一个单词,我们得手工完成,像这样:
list = %w[A B C D E F]
with_commas = list[0..-2]*", " + ", and " + list[-1]
# with_commas is now "A, B, C, D, E, and F"
译注:文档是这样描述的
array.join(sep=$,) → str
将sep 字符串夹在各元素中间使数组转换为字符串,并返回该字符串。若sep 为nil 则使用空字符串。若参数sep 被省略,则使用变量$, 的值。$, 的默认值为nil 。
20 、逆转数组
要逆转数组的次序,使用reverse 或reverse! 方法:
inputs = ["red", "green", "blue"]
outputs = inputs.reverse
# ["green","blue","red"]
priorities = %w(eat sleep code)
priorities.reverse!
# ["code","sleep","eat"]
译注:文档是这样描述的
array.reverse → an_array
array.reverse! → array
21 、从数组移除重复元素
如果我们想从数组移除重复元素,uniq 方法( 或uniq! 方法) 会完成这个工作:
breakfast = %w[spam spam eggs ham eggs spam]
lunch = breakfast.uniq
# ["spam","eggs","ham"]
breakfast.uniq!
# breakfast has changed now
22 、交叉数组
假设你想让两个数组" 交叉" 它们的元素并放入新数组中。这有很多方式。这儿是其中一个:
a = [1, 2, 3, 4]
b = ["a", "b", "c", "d"]
c = []
a.each_with_index { |x,i| c << x << b }
# c is now [1, "a", 2, "b", 3, "c", 4, "d"]
23、计算数组的频率值
没有像用于字符串(计算每个数据条目的出现次数)那样用于数组的count方法。不过,我们可自己创建一个:
class Array
def count
k=Hash.new(0)
self.each{ |x| k[x]+=1 }
k
end
end
meal = %w[spam spam eggs ham eggs spam]
items = meal.count
# items is { "ham" => 1, "spam" => 3, "eggs" => 2}
spams = items["spam"]
# 3
注意,返回的是个哈希表。
24、将数组转换为哈希表
数组用索引关联数据。但是,若是我们想转换它呢(也就是,由索引关联的数据能产生哈希表吗)?下面方法会做到这些:
class Array
def invert
h={ }
self.each_with_index{ |x,i| h[x]=i}
h
end
end
a = ["red","yellow","orange"]
h = a.invert
# { "orange"=>2, "yellow"=>1, "red"=>0}
25、Synchronized Sorting of Multiple Arrays
假设你想排序一个数组,但你的另一个数组与它有元素对元素的对应关系。换句话说,你不能在同步之外获取它们。哪你应该怎样做呢?
class Array
def sort_index
d=[]
self.each_with_index{ |x,i| d=[x,i]}
if block_given?
d.sort { |x,y| yield x[0],y[0]} .collect{ |x| x[1]}
else
d.sort.collect{ |x| x[1]}
end
end
def sort_by(ord=[])
return nil if self.length!=ord.length
self.indexes(*ord)
end
end
a = [21, 33, 11, 34, 36, 24, 14]
p a
p b=a.sort_index
p a.sort_by b
p c=a.sort_index { |x,y| x%2 <=> y%2}
p a.sort_by c
26、为数组新元素指定缺省值
当一个数组增长时它会创建未定义的新元素,这些元素的缺省值是nil值:
a = Array.new
a[0]="x"
a[3]="y"
# a is now ["x", nil, nil, "y"]
如果我们想用一些其它值设置这些新元素,该怎么做呢?做为个通用原则,我们在Listing3.8中提供了ZArray类,它将未定义新元素设置为0。
Listing 3.8 Specifying a Default for Array Elements
class ZArray < Array
def [](x)
if x > size
for i in size+1..x
self=0
end
end
v = super(x)
end
def []=(x,v)
max = size
super(x,v)
if size - max > 1
(max..size-2).each do |i|
self = 0
end
end
end
end
num = ZArray.new
num[1] = 1
num[2] = 4
num[5] = 25
# num is now [0, 1, 4, 0, 0, 25]
[url=http://my4java.itpub.net/post/9983/@MSITStore:C
ocuments %20and%20SettingsAdministrator桌面the%20ruby%20way.chm::/comete/?x=1& mode=section&sortKey=rank&sortOrder=desc&view=book&xmlid=0-672-32083-5/30041534&g=&catid=&s=1&b=1&f=1&t=1&c=1&u=1&r=&o=1&n=1&d=1&p=1&a=0][/url]
二、用哈希表工作
哈希表被称为关联数组,字典等其它名称。事实上,Perl和Java程序员会很熟悉这个数组结构。
想想数组做为一个实体,它将索引x与数据条目y关联到一起。哈希表也创建了类似的关联,但至少有两个例外。首先,对于数组,x总是个整数;对于哈希表,它不需要这样。其次,数组是有序数据结构;典型的哈希表是无序的。
哈希表的键可以是任何类型。带来的副作用是,它使用哈希表成了非有序数据结构。在数组内,我们知道元素4跟随在元素3的后面;但在哈希表中,键可能是这样个类型,它没有定义的前任或后续。基于这个原因(也有其它的),Ruby不关注哈希表的任何特定次序。
你可以认为哈希表就像是带有特定的索引的数组,或者是数据库中带有两个字段的同义表。不论你怎样认为哈希表,它都有强大的有用的程序结构。
1、创建哈希表
就像数组,特定类方法[ ]被用于创建哈希表。方括号内的数据条目列表被用于哈希表内的映射。
下面显示了这个方法的六种不同的调用:
a1 = Hash.[]("flat",3,"curved",2)
a2 = Hash.[]("flat"=>3,"curved"=>2)
b1 = Hash["flat",3,"curved",2]
b2 = Hash["flat"=>3,"curved"=>2]
c1 = { "flat",3,"curved",2}
c2 = { "flat"=>3,"curved"=>2}
# 对于 a1, b1, 和c1: 元素数量必须是偶数
同样,类方法new可以接受一个指定缺省值的参数。注意这个缺省值不是哈希表的真正组成部分;它只是用返回值来替换nil。这儿是例子:
d = Hash.new
# 创建空哈希表
e = Hash.new(99)
#创建空哈希表
f = Hash.new("a"=>3) #创建空哈希表
e["angled"]
# 99
e.inspect
# { }
f["b"]
# { "a"=>3}
(default value is actually a hash itself)
f.inspect
# { }
译注:文档是这样描述的
1.
Hash[ [key =>|, value]* ] => hash
2.
Hash.new => hash
3.
Hash.new(obj) => aHash
4.
Hash.new {|hash, key| block } => aHash
使用第一种形式时,参数的个数必须是偶数。(奇数位参数是索引,偶数位参数是元素值)。也可以将一个哈希表对象指定给参数时,将生成并返回一个与指定哈希表相同的全新的哈希表。(生成的哈希表的默认值为nil。)
以下是从数组生成哈希表的方法示例(可能您觉得应该有更直接的方法,但实际上并没有)。
1 由[索引, 值, ...] 型的数组变为哈希表
ary = [1,"a", 2,"b", 3,"c"]
p Hash[*ary]
# => {1=>"a", 2=>"b", 3=>"c"}
2 由索引和值配对出现的数组变为哈希表
alist = [[1,"a"], [2,"b"], [3,"c"]]
p Hash[*alist.flatten]
#=> {1=>"a", 2=>"b", 3=>"c"}
3 由索引数组和值数组配对生成哈希表(version 1.7 以后)
keys = [1, 2, 3]
vals = ["a", "b", "c"]
alist = keys.zip(vals)
# 或 alist = [keys,vals].transpose
p Hash[*alist.flatten]
#=> {1=>"a", 2=>"b", 3=>"c"}
4 虽然索引和值都是数组,但还是无法使用(2)或(3)的方法时,只好老老实实地赋值了
h = Hash.new
alist = [[1,["a"]], [2,["b"]], [3,["c"]]]
alist.each {|k,v|
h[k] = v
}
p h
#=> {1=>["a"], 2=>["b"], 3=>["c"]}
new 生成一个全新的空哈希表.若索引没有对应值时,其默认值为ifnone.您必须谨慎地处理默认值的问题(trap::Hash).
ruby 1.7 特性:若指定了块时,则块的计算值将成为默认值.每当调用无值的哈希表元素时,都会对块进行计算,然后返回其结果.而调用哈希表时的索引将被传递给该块.
# 若没有使用块时, 一旦改变默认值将会影响到其他的值
h = Hash.new("foo")
p h[1]
# => "foo"
p h[1] << "bar"
# => "foobar"
p h[1]
# => "foobar"
p h[2]
# => "foobar"
# 若使用块的话,问题便会迎刃而解
h = Hash.new {|hash, key| hash[key] = "foo"}
p h[1]
# => "foo"
p h[1] &
Array 类有个我们希望的标准的迭代器each 。但是,它还其它很有用的迭代器。
reverse_each 方法以逆序迭代。它先使用reverse 计算然后使用each ,但它更快。这儿是个例子:
words = %w(Son I am able she said)
str = ""
words.reverse_each {
|w| str += "#{ w}
"}
# str is now "said she able am I Son "
如果我们只想在索引上迭代,我们可以使用each_inex 。x.each_inex 相当于(0..(x.size-1)).each ( 也就是,在索引范围内迭代) 。
迭代器each_with_index( 混合了Comparable) 将元素与索引两者传递给块,像下面:
x = ["alpha", "beta", "gamma"]
x.each_with_index do |x,i|
puts "Element #{ i}
is #{ x} "
end
# Produces three lines of output
如何以随机次序在数组上迭代呢?下面例子使用了迭代器random_each( 它简单地调用randomize 方法) :
class Array
# 假设我们已经定义了randomize
def random_each
temp = self.randomize # 将数组打乱。
temp.each { |x| yield x}
end
end
dwarves = %w(Sleepy Dopey Happy Sneezy Grumpy Bashful Doc)
list = ""
dwarves.random_each { |x| list += "#{ x}
"}
# list is now:
# "Bashful Dopey Sleepy Happy Grumpy Doc Sneezy "
# (Your mileage may vary.)
译注:在文档中是这样描述
1.
array.each {|item| block } → array
为每个元素调用块。
2.
array.reverse_each {|item| block }
倒序each
3.
array.each_index {|index| block } → array 对每个索引调用块,each 是元素。
4.
enum.each_with_index {|obj, i| block } → enum 用元素,索引调用块
19 、以插入界定符的形式生成字符串
我们常常想在数组的两个元素之间以"fence post" 风格插入界定符;也就是说,我们想在元素之间放置界定符,但第一个或最后一个元素没有。方法join 将作这件事,就和你的* 操作符一样:
been_there = ["Veni", "vidi", "vici."]
journal = been_there.join(", ")
# "Veni, vidi, vici."
# Default delimiter is space
letters = ["

musicians = letters.join
# "

people = ["Bob","Carol","Ted","Alice"]movie = people * " and "
# movie is now "Bob and Carol and Ted and Alice"
注意,如果我们真的想以不同方式处理最后一个元素,比如插入一个单词,我们得手工完成,像这样:
list = %w[A B C D E F]
with_commas = list[0..-2]*", " + ", and " + list[-1]
# with_commas is now "A, B, C, D, E, and F"
译注:文档是这样描述的
array.join(sep=$,) → str
将sep 字符串夹在各元素中间使数组转换为字符串,并返回该字符串。若sep 为nil 则使用空字符串。若参数sep 被省略,则使用变量$, 的值。$, 的默认值为nil 。
20 、逆转数组
要逆转数组的次序,使用reverse 或reverse! 方法:
inputs = ["red", "green", "blue"]
outputs = inputs.reverse
# ["green","blue","red"]
priorities = %w(eat sleep code)
priorities.reverse!
# ["code","sleep","eat"]
译注:文档是这样描述的
array.reverse → an_array
array.reverse! → array
21 、从数组移除重复元素
如果我们想从数组移除重复元素,uniq 方法( 或uniq! 方法) 会完成这个工作:
breakfast = %w[spam spam eggs ham eggs spam]
lunch = breakfast.uniq
# ["spam","eggs","ham"]
breakfast.uniq!
# breakfast has changed now
22 、交叉数组
假设你想让两个数组" 交叉" 它们的元素并放入新数组中。这有很多方式。这儿是其中一个:
a = [1, 2, 3, 4]
b = ["a", "b", "c", "d"]
c = []
a.each_with_index { |x,i| c << x << b }
# c is now [1, "a", 2, "b", 3, "c", 4, "d"]
23、计算数组的频率值
没有像用于字符串(计算每个数据条目的出现次数)那样用于数组的count方法。不过,我们可自己创建一个:
class Array
def count
k=Hash.new(0)
self.each{ |x| k[x]+=1 }
k
end
end
meal = %w[spam spam eggs ham eggs spam]
items = meal.count
# items is { "ham" => 1, "spam" => 3, "eggs" => 2}
spams = items["spam"]
# 3
注意,返回的是个哈希表。
24、将数组转换为哈希表
数组用索引关联数据。但是,若是我们想转换它呢(也就是,由索引关联的数据能产生哈希表吗)?下面方法会做到这些:
class Array
def invert
h={ }
self.each_with_index{ |x,i| h[x]=i}
h
end
end
a = ["red","yellow","orange"]
h = a.invert
# { "orange"=>2, "yellow"=>1, "red"=>0}
25、Synchronized Sorting of Multiple Arrays
假设你想排序一个数组,但你的另一个数组与它有元素对元素的对应关系。换句话说,你不能在同步之外获取它们。哪你应该怎样做呢?
我们在Listing3.7中的解答将排序一个数组和收集做为结果的索引。索引列表(本身是数组)可以应用到任何其它数组来以同样的次序放置它元素。
Listing 3.7 Synchronized Array Sortingclass Array
def sort_index
d=[]
self.each_with_index{ |x,i| d=[x,i]}
if block_given?
d.sort { |x,y| yield x[0],y[0]} .collect{ |x| x[1]}
else
d.sort.collect{ |x| x[1]}
end
end
def sort_by(ord=[])
return nil if self.length!=ord.length
self.indexes(*ord)
end
end
a = [21, 33, 11, 34, 36, 24, 14]
p a
p b=a.sort_index
p a.sort_by b
p c=a.sort_index { |x,y| x%2 <=> y%2}
p a.sort_by c
26、为数组新元素指定缺省值
当一个数组增长时它会创建未定义的新元素,这些元素的缺省值是nil值:
a = Array.new
a[0]="x"
a[3]="y"
# a is now ["x", nil, nil, "y"]
如果我们想用一些其它值设置这些新元素,该怎么做呢?做为个通用原则,我们在Listing3.8中提供了ZArray类,它将未定义新元素设置为0。
Listing 3.8 Specifying a Default for Array Elements
class ZArray < Array
def [](x)
if x > size
for i in size+1..x
self=0
end
end
v = super(x)
end
def []=(x,v)
max = size
super(x,v)
if size - max > 1
(max..size-2).each do |i|
self = 0
end
end
end
end
num = ZArray.new
num[1] = 1
num[2] = 4
num[5] = 25
# num is now [0, 1, 4, 0, 0, 25]
[url=http://my4java.itpub.net/post/9983/@MSITStore:C

二、用哈希表工作
哈希表被称为关联数组,字典等其它名称。事实上,Perl和Java程序员会很熟悉这个数组结构。
想想数组做为一个实体,它将索引x与数据条目y关联到一起。哈希表也创建了类似的关联,但至少有两个例外。首先,对于数组,x总是个整数;对于哈希表,它不需要这样。其次,数组是有序数据结构;典型的哈希表是无序的。
哈希表的键可以是任何类型。带来的副作用是,它使用哈希表成了非有序数据结构。在数组内,我们知道元素4跟随在元素3的后面;但在哈希表中,键可能是这样个类型,它没有定义的前任或后续。基于这个原因(也有其它的),Ruby不关注哈希表的任何特定次序。
你可以认为哈希表就像是带有特定的索引的数组,或者是数据库中带有两个字段的同义表。不论你怎样认为哈希表,它都有强大的有用的程序结构。
1、创建哈希表
就像数组,特定类方法[ ]被用于创建哈希表。方括号内的数据条目列表被用于哈希表内的映射。
下面显示了这个方法的六种不同的调用:
a1 = Hash.[]("flat",3,"curved",2)
a2 = Hash.[]("flat"=>3,"curved"=>2)
b1 = Hash["flat",3,"curved",2]
b2 = Hash["flat"=>3,"curved"=>2]
c1 = { "flat",3,"curved",2}
c2 = { "flat"=>3,"curved"=>2}
# 对于 a1, b1, 和c1: 元素数量必须是偶数
同样,类方法new可以接受一个指定缺省值的参数。注意这个缺省值不是哈希表的真正组成部分;它只是用返回值来替换nil。这儿是例子:
d = Hash.new
# 创建空哈希表
e = Hash.new(99)
#创建空哈希表
f = Hash.new("a"=>3) #创建空哈希表
e["angled"]
# 99
e.inspect
# { }
f["b"]
# { "a"=>3}
(default value is actually a hash itself)
f.inspect
# { }
译注:文档是这样描述的
1.
Hash[ [key =>|, value]* ] => hash
2.
Hash.new => hash
3.
Hash.new(obj) => aHash
4.
Hash.new {|hash, key| block } => aHash
使用第一种形式时,参数的个数必须是偶数。(奇数位参数是索引,偶数位参数是元素值)。也可以将一个哈希表对象指定给参数时,将生成并返回一个与指定哈希表相同的全新的哈希表。(生成的哈希表的默认值为nil。)
以下是从数组生成哈希表的方法示例(可能您觉得应该有更直接的方法,但实际上并没有)。
1 由[索引, 值, ...] 型的数组变为哈希表
ary = [1,"a", 2,"b", 3,"c"]
p Hash[*ary]
# => {1=>"a", 2=>"b", 3=>"c"}
2 由索引和值配对出现的数组变为哈希表
alist = [[1,"a"], [2,"b"], [3,"c"]]
p Hash[*alist.flatten]
#=> {1=>"a", 2=>"b", 3=>"c"}
3 由索引数组和值数组配对生成哈希表(version 1.7 以后)
keys = [1, 2, 3]
vals = ["a", "b", "c"]
alist = keys.zip(vals)
# 或 alist = [keys,vals].transpose
p Hash[*alist.flatten]
#=> {1=>"a", 2=>"b", 3=>"c"}
4 虽然索引和值都是数组,但还是无法使用(2)或(3)的方法时,只好老老实实地赋值了
h = Hash.new
alist = [[1,["a"]], [2,["b"]], [3,["c"]]]
alist.each {|k,v|
h[k] = v
}
p h
#=> {1=>["a"], 2=>["b"], 3=>["c"]}
new 生成一个全新的空哈希表.若索引没有对应值时,其默认值为ifnone.您必须谨慎地处理默认值的问题(trap::Hash).
ruby 1.7 特性:若指定了块时,则块的计算值将成为默认值.每当调用无值的哈希表元素时,都会对块进行计算,然后返回其结果.而调用哈希表时的索引将被传递给该块.
# 若没有使用块时, 一旦改变默认值将会影响到其他的值
h = Hash.new("foo")
p h[1]
# => "foo"
p h[1] << "bar"
# => "foobar"
p h[1]
# => "foobar"
p h[2]
# => "foobar"
# 若使用块的话,问题便会迎刃而解
h = Hash.new {|hash, key| hash[key] = "foo"}
p h[1]
# => "foo"
p h[1] &