块和数组
块(Blocks)通常用于迭代数组(Array)。 因此,Array 类提供了许多可以传递块的方法。
一个有用的方法 collect
;会将数组的每个元素传递给一个 block,并创建一个新数组以包含该 block 返回的每个值。例如,这里的一个 block 会被传入一个数组中的每个整数(每个整数被分配给变量 x
),它将其值加倍并返回它。
collect
方法会创建一个包含了每个按顺序返回的整数的新数组:
b3 = [1,2,3].collect{|x| x*2}
上面的示例返回这个数组:[2,4,6]
。
在下一个示例中,block 返回原始字符串每个首字母大写的一个版本:
b4 = ["hello","good day","how do you do"].collect{|x| x.capitalize }
所以 b4
现在是…
["Hello", "Good day", "How do you do"]
Array 类的 each
方法看起来与 collect
类似;它也依次传递每个数组元素以由 block 处理。但是,与 collect
不同,each
方法不会创建包含返回值的新数组:
b5 = ["hello","good day","how do you do"].each{|x| x.capitalize }
这一次,b5
没有变化…
["hello", "good day", "how do you do"]
回想一下,有些方法 - 特别是以感叹号(!
)结尾的方法 - 实际上改变了原始对象而不是产生新值。如果你想使用 each
方法来将原始数组中的字符串首字母大写,你可以使用 capitalize!
方法:
b6 = ["hello","good day","how do you do"].each{|x| x.capitalize! }
所以 b6
现在是…
["Hello", "Good day", "How do you do"]
经过一番思考,你还可以使用 block 来迭代字符串中的字符(characters)。首先,你需要从字符串中拆分每个字符。这可以使用 String 类的 split
方法完成,如下所示:
"hello world".split(//)
split
方法基于分隔符将字符串划分为子字符串,并返回包含这些子字符串的数组。这里 //
是一个定义零长度字符串的正则表达式;这具有返回单个字符的效果,因此我们最终创建了包含字符串中所有字符的数组。我们现在可以迭代这个字符数组,返回每个字符的大写版本:
a = "hello world".split(//).each{ |x| newstr << x.capitalize }
因此,在每次迭代时,大写字符将附加到 newstr
,并显示以下内容…
H
HE
HEL
HELL
HELLO
HELLO
HELLO W
HELLO WO
HELLO WOR
HELLO WORL
HELLO WORLD
因为我们在这里使用 capitalize
方法(结尾没有 !
字符),所以数组中的字符 a
保持原样,全部小写,因为 capitalize
方法不会改变接收对象(这里接收对象是传入 block 的字符)。
但请注意,如果你使用 capitalize!
方法修改原始字符,此代码将不会运行。这是因为 capitalize!
如果没有进行任何更改则会返回 nil
,因此当遇到空格字符时,将返回 nil
,并且我们尝试向字符串 newstr
追加(<<
)一个 nil
值将会失败。
你还可以使用 each_byte
方法对字符串进行首字母大写转换。这将遍历字符串的字符,将每个字节(byte)传递给 block。这些字节采用了 ASCII 码的形式。因此,”hello world” 将以这些数值的形式传递:104
101
108
108
111
32
119
111
114
108
100
显然,你不能将整数大写,所以我们需要将每个 ASCII 值转换为一个字符。String 的 chr
方法执行此操作:
a = "hello world".each_byte{|x| newstr << (x.chr).capitalize }