第二天的主要内容包括定义函数、数组和散列表、代码块和yield、类的定义和Mixin。
Ruby允许在类外定义函数,和Ruby中的其他一切一样,函数也是对象。数组和散列表提供了丰富的API,可以在各种场合使用。代码块(block)和yield是最具Ruby风格的闭包方式。Ruby的类使用单继承,可以使用模块的方式集成其他方法。
练习
1.有一个数组,包含16个数字,仅用each方法打印数组中的内容,一次打印4个数字。然后用可枚举模块的each_slice方法重做一遍。
仅用each这种表述可能有歧义,好像必须得使用条件语句才能实现。
A = (1..16).to_a
#使用each
i = 1
A.each do |a|
if i % 4 == 0
puts a
else
print "#{a} "
end
i += 1
end
#使用each_slice
A.each_slice(4) do |g|
puts g.join(" ")
end
2.前面书中实现了一个有趣的树类Tree,有着简洁的用户接口,现在要改写一个新的初始化方法,接受散列表和数组嵌套的结构。写好后你可以接受这样的树:
{
"grandpa" => {
"dad" => {
"child1" => {},
"child2" => {}
},
"uncle" => {
"child3" => {},
"child4" => {}
}
}
}
原文中的类是这样的:
class Tree
attr_accessor :children, :node_name
def initialize(name, children=[])
@children = children
@node_name = name
end
def visit_all(&block)
visit &block
children.each { |c| c.visit_all &block }
end
def visit(&block)
block.call self
end
end
由于下面访问方法中代码块中调用的格式为数组的调用方法,所以显然必须得将输入的散列表转化为数组,这就要每次递归调用初始化函数。改写之后的方法如下:
class Tree
attr_accessor :children, :node_name
def initialize(tree={})
@node_name = tree.keys()[0]
@children = []
tree[@node_name].each do |key, value|
@children.push( Tree.new( { key => value } ) )
end
end
def visit_all(&block)
visit &block
children.each { |c| c.visit_all &block }
end
def visit(&block)
block.call self
end
end
测试用的代码:
load 'Tree.rb'
tree = Tree.new(
{
"grandpa" => {
"dad" => {
"child1" => {},
"child2" => {}
},
"uncle" => {
"child3" => {},
"child4" => {}
}
}
}
)
puts "Visiting a node"
tree.visit { |node| puts node.node_name }
puts
puts "Visiting entire tree"
tree.visit_all { |node| puts node.node_name }
个人感觉用散列表初始化不如保持散列表的格式,改写访问方法。
3.写一个简单的grep程序。
拖Ruby超强大的ARGF的福,这个实现起来易如反掌,两行:
tar = ARGV.shift
ARGF.each_with_index { |l, i| puts "#{ARGF.filename} #{i} : #{l}" if /#{tar}/.match(l) }
运行的时候使用
ruby grep.rb tar file1 file2 ...
可以将文件、行号、所在行内容都显示出来