《Programming Ruby中文版》前3部分我并不准备细看,毕竟我接触ruby也有一段时间了,只准备快速地掠过一遍,查缺补漏;重点放在第3部分的核心内容上,至于第四部分的参考手册更多作为工具书了。仅在此记录下一些值的注意的东西。
1.全局变量$_,默认当gets方法返回输入的行时,同时保存在全局变量$_,并且正则表达式如果作为条件语句(if或者while)时默认是跟这个全局变量进行匹配,而print参数为空时也是打印这个全局变量。这是早期ruby向perl语言学习的结果。可以看看这个例子:
这样的风格不值的提倡,全局变量的使用应该尽力减少,ruby也在逐渐脱离perl主义的风格
2.ruby中的单例模式:
3.ruby中的block作用:
1)迭代器,通常是内部迭代器
2)事务Blocks,c#的using语句倒是跟这个有点像,其实就是让对象自身负责资源的打开和关闭,这是通过Kernel.block_given?实现的,比如File.open方法,当后面跟着一个block的时候,就会自动关闭打开的文件资源,如果不是,就需要自己处理。
3)作为闭包,与javascript和其他语言中的闭包概念一致,一个例子:
4.ruby中数字的最大长度取决于系统,这跟java,C#通过虚拟机规范的不同,数字类型的几个常用迭代器:times,upto,downto,step,如:
5.ruby中的字符串是8字节的序列,可以存储可打印的字符和二进制数据。比较有趣3种构建字符串常量方式:%q(对应于单引号定义的字符串),%Q(双引号)以及here documents,比如:
6.Range,书中翻译为区间,我倒更喜欢范围这个词。区间的3个用途:
1)用作序列,最常见的,如1..2,a..z等,可以定义自己的区间,只要实现succ和<=>比较方法
2)作为条件,书中的例子很经典:
3)作为间隔,看看某个值是否落入区间范围内,使用===操作符比较
7.正则表达式,这是重头戏。ruby中的perl风格的正则表达式,其实也是内建在ruby中的正则表达式对象的外部包装,关键的就是两个类Regexp类和MatchData类。一些peri程序员熟悉的记号:
$& 匹配的字符串
$` 匹配前的字符串
$' 匹配后的字符串
$1 第一个分组,$2,$3...类似
详细的就不抄书了,正则表达式我在学习javascript的时候已经系统地学过,倒是不感觉吃力。
8.在方法中定义可变长度参数,只要参数前加*号即可,java1.5也已经支持可变参数,比如Object...obj。
另外,在方法中,将数组展开为参数,可以在数组前加一个*号,比如:
9.ruby中的多线程:
1)ruby创建线程,见下面这个例子,开3个线程分别访问3个站点,并且对3个线程通过调用join方法,直到3个线程都结束,主线程才结束,来自书中例子:
2)线程中如何共享变量?可以通过[]=简单地把当前线程看成一个散列表,这里没有考虑同步问题:
3)通过设置abort_on_exception,如果是true,未处理的线程异常将杀死所有正在运行的线程,如果是false,则杀死当前运行的线程,其他线程继续运行。修改上面的例子查看下:
8, 1, 6, 3, Failed:boom!
2, 4, 7, 0, 5, count =9
在开头加上:
4)通过线程的一系列方法:pass,join,value,stop来进行线程的调度
5)互斥的实现,与其他语言一样,不外乎加锁、信号量、队列的方式。看看加锁是如何做的,通过monitor库的关键字synchronize实现,如下面这个例子,两个线程递增同一个变量,似乎结果应该是20000:
使用monitor,不一定要使用继承,也可以使用mixin,甚至:
6)条件变量和队列的方式不准备抄书了,ruby中对线程的操作都是直接调用操作系统的命令,特别是*nix支持的非常好,可惜我对linux也是个初哥。
10.ruby中表达式很重要的一个特点是:任何表达式都有返回值,包括赋值语句、条件语句、循环语句之类。
1)ruby中对布尔表达式的规定是:任何不是nil或者常量false的值都为真
2)注意,在方法中调用访问属性的函数,需要写上调用者self,否则将处理为局部变量
3)defined?方法用于返回参数的描述,如果未定义,返回nil
4)逻辑表达式中,and和or的优先级低于&&,||
5)ruby没有for语句,因为ruby通过内建在对象中的迭代器提供了循环访问的能力,最简单的内建迭代器:loop do ....end
6)只要你的类支持each方法,你就可以使用for ... in ..语句循环它
7)对循环可以使用break(打断跳出),redo(从头重新循环,当前迭代),next进行调度。另外,还有retry,用于完全重新开始循环
8)while,until和for循环内建到了ruby语言中,但没有引入新的作用域:前面存在的局部变量可以在循环中使用,而循环中新创建的局部变量也可以在循环后使用。而被迭代器使用的block则不同, 在block中创建的局部变量无法在block外访问。
11.ruby的异常处理
类似于java的try...catch...finnaly,ruby对应的是begin...rescue...ensure...end,将产生异常的代码放在这个块中进行处理。可以通过$!得到异常信息,或者提供局部变量名,我改写了一下我的google在线翻译机,增加异常处理,并用exit代替break:
12。关于模块,作用有二:作为命名空间和Mixin机制。模块的Mixin机制可以说是ruby的一个精华所在,通过Mixin,可以变相地实现了多重继承,并且可以动态地为类添加和删除功能。这一部分注意两点:
1)模块中定义的实例变量可能与包含模块的类的实例变量产生名称冲突。可以使用模块一级的散列表,以当前对象的ID做索引,来保存特定于当前模块的实例变量解决这个问题。比如:
1.全局变量$_,默认当gets方法返回输入的行时,同时保存在全局变量$_,并且正则表达式如果作为条件语句(if或者while)时默认是跟这个全局变量进行匹配,而print参数为空时也是打印这个全局变量。这是早期ruby向perl语言学习的结果。可以看看这个例子:
while
gets
if / Ruby /
print
end
end
if / Ruby /
end
end
这样的风格不值的提倡,全局变量的使用应该尽力减少,ruby也在逐渐脱离perl主义的风格
2.ruby中的单例模式:
class
Logger
private_class_method:new
@@logger = nil
def Logger.create
@@logger = new unless @@logger
@@logger
end
end
log1 = Logger.create
log2 = Logger.create
puts log1.object_id
puts log2.object_id
private_class_method:new
@@logger = nil
def Logger.create
@@logger = new unless @@logger
@@logger
end
end
log1 = Logger.create
log2 = Logger.create
puts log1.object_id
puts log2.object_id
3.ruby中的block作用:
1)迭代器,通常是内部迭代器
2)事务Blocks,c#的using语句倒是跟这个有点像,其实就是让对象自身负责资源的打开和关闭,这是通过Kernel.block_given?实现的,比如File.open方法,当后面跟着一个block的时候,就会自动关闭打开的文件资源,如果不是,就需要自己处理。
3)作为闭包,与javascript和其他语言中的闭包概念一致,一个例子:
def
n_times(thing)
return lambda { | n | thing * n}
end
p1 = n_times( 23 )
puts p1.call( 3 )
puts p1.call( 2 )
通过lambda方法将一个block转为Proc对象,尽管参数thing在block被真正调用时已经离开了作用范围,但是仍然可以使用
return lambda { | n | thing * n}
end
p1 = n_times( 23 )
puts p1.call( 3 )
puts p1.call( 2 )
4.ruby中数字的最大长度取决于系统,这跟java,C#通过虚拟机规范的不同,数字类型的几个常用迭代器:times,upto,downto,step,如:
2
.step(
10
,
2
){
|
i
|
print
i,
'
'
}
=>
2
,
4
,
6
,
8
,
10
5.ruby中的字符串是8字节的序列,可以存储可打印的字符和二进制数据。比较有趣3种构建字符串常量方式:%q(对应于单引号定义的字符串),%Q(双引号)以及here documents,比如:
s
=<<
END_OF_STRING
测试测试啦
END_OF_STRING
测试测试啦
END_OF_STRING
6.Range,书中翻译为区间,我倒更喜欢范围这个词。区间的3个用途:
1)用作序列,最常见的,如1..2,a..z等,可以定义自己的区间,只要实现succ和<=>比较方法
2)作为条件,书中的例子很经典:
while
line
=
gets
puts line if line =~/ start / ..line =~/ end /
end
# 利用全局变量简化为,不建议这样写
while gets
print if / start / .. / end /
end
puts line if line =~/ start / ..line =~/ end /
end
# 利用全局变量简化为,不建议这样写
while gets
print if / start / .. / end /
end
3)作为间隔,看看某个值是否落入区间范围内,使用===操作符比较
7.正则表达式,这是重头戏。ruby中的perl风格的正则表达式,其实也是内建在ruby中的正则表达式对象的外部包装,关键的就是两个类Regexp类和MatchData类。一些peri程序员熟悉的记号:
$& 匹配的字符串
$` 匹配前的字符串
$' 匹配后的字符串
$1 第一个分组,$2,$3...类似
详细的就不抄书了,正则表达式我在学习javascript的时候已经系统地学过,倒是不感觉吃力。
8.在方法中定义可变长度参数,只要参数前加*号即可,java1.5也已经支持可变参数,比如Object...obj。
另外,在方法中,将数组展开为参数,可以在数组前加一个*号,比如:
def
three(a,b,c)
print " this is #{a},#{b},#{c} "
end
three([ 1 , 2 , 3 )]
# 上面这样调用报参数数目错误,正确的用法如下:
three( * [ 1 , 2 , 3 )] => this is 1 , 2 , 3
将hash列表直接做为参数,可能在2.0支持,目前采用的要求散列数组在正常的参数之后,并位于任何的block或者数组之前
print " this is #{a},#{b},#{c} "
end
three([ 1 , 2 , 3 )]
# 上面这样调用报参数数目错误,正确的用法如下:
three( * [ 1 , 2 , 3 )] => this is 1 , 2 , 3
9.ruby中的多线程:
1)ruby创建线程,见下面这个例子,开3个线程分别访问3个站点,并且对3个线程通过调用join方法,直到3个线程都结束,主线程才结束,来自书中例子:
require
'
net/http
'
pages =% w(www.iteye.com www.sina.com.cn www.blogjava.net)
$proxy_addr = ' x.x.x.x '
$proxy_port = 80
threads = []
for page_to_fetch in pages
threads << Thread.new(page_to_fetch) do | url |
h = Net::HTTP.Proxy($proxy_addr, $proxy_port).new(url, 80 )
puts " Fetcing:#{url} "
resp = h.get( ' / ' ,nil)
puts " Got #{url}:#{resp.message} "
end
end
threads.each{ | thr | thr.join}
pages =% w(www.iteye.com www.sina.com.cn www.blogjava.net)
$proxy_addr = ' x.x.x.x '
$proxy_port = 80
threads = []
for page_to_fetch in pages
threads << Thread.new(page_to_fetch) do | url |
h = Net::HTTP.Proxy($proxy_addr, $proxy_port).new(url, 80 )
puts " Fetcing:#{url} "
resp = h.get( ' / ' ,nil)
puts " Got #{url}:#{resp.message} "
end
end
threads.each{ | thr | thr.join}
2)线程中如何共享变量?可以通过[]=简单地把当前线程看成一个散列表,这里没有考虑同步问题:
count
=
0
threads = []
10 .times do | i |
threads[i] = Thread.new do
sleep(rand( 0.1 ))
Thread.current[ " mycount " ] = count
count += 1
end
end
threads.each{ | t | t.join; print t[ " mycount " ], " , " }
puts " count =#{count} "
threads = []
10 .times do | i |
threads[i] = Thread.new do
sleep(rand( 0.1 ))
Thread.current[ " mycount " ] = count
count += 1
end
end
threads.each{ | t | t.join; print t[ " mycount " ], " , " }
puts " count =#{count} "
3)通过设置abort_on_exception,如果是true,未处理的线程异常将杀死所有正在运行的线程,如果是false,则杀死当前运行的线程,其他线程继续运行。修改上面的例子查看下:
count
=
0
threads = []
10 .times do | i |
threads[i] = Thread.new(i) do | j |
raise " boom! " if j == 4
sleep(rand( 0.1 ))
Thread.current[ " mycount " ] = count
count += 1
end
end
threads.each do | t |
begin
t.join
print t[ " mycount " ], " , "
rescue RuntimeError => e
puts " Failed:#{e.message} "
end
end
puts " count =#{count} "
输出(随机的):
threads = []
10 .times do | i |
threads[i] = Thread.new(i) do | j |
raise " boom! " if j == 4
sleep(rand( 0.1 ))
Thread.current[ " mycount " ] = count
count += 1
end
end
threads.each do | t |
begin
t.join
print t[ " mycount " ], " , "
rescue RuntimeError => e
puts " Failed:#{e.message} "
end
end
puts " count =#{count} "
8, 1, 6, 3, Failed:boom!
2, 4, 7, 0, 5, count =9
在开头加上:
Thread.abort_on_exception
=
true
杀死所有的运行进程,报出异常,而不会产生输出。
4)通过线程的一系列方法:pass,join,value,stop来进行线程的调度
5)互斥的实现,与其他语言一样,不外乎加锁、信号量、队列的方式。看看加锁是如何做的,通过monitor库的关键字synchronize实现,如下面这个例子,两个线程递增同一个变量,似乎结果应该是20000:
#
require 'monitor'
class Counter # <Monitor
attr_reader:count
def initialize
@count = 0
# super
end
def tick
# synchronize do
@count += 1
# end
end
end
c = Counter.new
t1 = Thread.new{ 10000 .times{c.tick}}
t2 = Thread.new{ 10000 .times{c.tick}}
t1.join;t2.join
print c.count
很遗憾,结果不会是20000,而是比它小的一个数值,这里的问题就是因为访问共享资源没有进行同步的缘故,使用monitor库,请将上面代码中的注释去掉,可以得到正确的结果
class Counter # <Monitor
attr_reader:count
def initialize
@count = 0
# super
end
def tick
# synchronize do
@count += 1
# end
end
end
c = Counter.new
t1 = Thread.new{ 10000 .times{c.tick}}
t2 = Thread.new{ 10000 .times{c.tick}}
t1.join;t2.join
print c.count
使用monitor,不一定要使用继承,也可以使用mixin,甚至:
lock
=
Monitor.new
t1 = Thread.new{ 10000 .times{lock.synchronize{c.tick}}}
还可以把特定的对象放入monitor,比如:
t1 = Thread.new{ 10000 .times{lock.synchronize{c.tick}}}
c
=
Counter.new
c.extend(MonitorMixin)
t1 = Thread.new{ 10000 .times{c.synchronize{c.tick}}}
.
c.extend(MonitorMixin)
t1 = Thread.new{ 10000 .times{c.synchronize{c.tick}}}
.
6)条件变量和队列的方式不准备抄书了,ruby中对线程的操作都是直接调用操作系统的命令,特别是*nix支持的非常好,可惜我对linux也是个初哥。
10.ruby中表达式很重要的一个特点是:任何表达式都有返回值,包括赋值语句、条件语句、循环语句之类。
1)ruby中对布尔表达式的规定是:任何不是nil或者常量false的值都为真
2)注意,在方法中调用访问属性的函数,需要写上调用者self,否则将处理为局部变量
3)defined?方法用于返回参数的描述,如果未定义,返回nil
4)逻辑表达式中,and和or的优先级低于&&,||
5)ruby没有for语句,因为ruby通过内建在对象中的迭代器提供了循环访问的能力,最简单的内建迭代器:loop do ....end
6)只要你的类支持each方法,你就可以使用for ... in ..语句循环它
7)对循环可以使用break(打断跳出),redo(从头重新循环,当前迭代),next进行调度。另外,还有retry,用于完全重新开始循环
8)while,until和for循环内建到了ruby语言中,但没有引入新的作用域:前面存在的局部变量可以在循环中使用,而循环中新创建的局部变量也可以在循环后使用。而被迭代器使用的block则不同, 在block中创建的局部变量无法在block外访问。
11.ruby的异常处理
类似于java的try...catch...finnaly,ruby对应的是begin...rescue...ensure...end,将产生异常的代码放在这个块中进行处理。可以通过$!得到异常信息,或者提供局部变量名,我改写了一下我的google在线翻译机,增加异常处理,并用exit代替break:
require
'
net/http
'
def translate
txt = STDIN.gets
exit if txt.strip == ' e ' or txt.strip == ' exit '
temp = txt.split( ' ' )
if temp[ 1 ] == ' 1 ' or temp.size == 1
langpair = ' en|zh-CN '
else
langpair = ' zh-CN|en '
end
# 使用代理
begin
$proxy_addr = ' localhost '
$proxy_port = 80
response = Net::HTTP.Proxy($proxy_addr,$proxy_port).post_form(URI.parse( " http://translate.google.com/translate_t " ),{ ' text ' => temp[0], ' langpair ' => langpair})
response.body =~ /< div id = result_box dir = ltr > (. * ) < \ / div >/
rescue StandardError => e
$stderr. print " 网络错误: " + e
else
result = $ 1
puts ' 翻译内容: ' + temp[0]
puts ' google返回: ' + result
puts ' -------------------退出请打e或者exit--------------- '
translate
end
end
translate
引发一个异常使用raise语句,重新引发当前异常,如果没有,就引发一个RuntimeError,常见使用方式:
def translate
txt = STDIN.gets
exit if txt.strip == ' e ' or txt.strip == ' exit '
temp = txt.split( ' ' )
if temp[ 1 ] == ' 1 ' or temp.size == 1
langpair = ' en|zh-CN '
else
langpair = ' zh-CN|en '
end
# 使用代理
begin
$proxy_addr = ' localhost '
$proxy_port = 80
response = Net::HTTP.Proxy($proxy_addr,$proxy_port).post_form(URI.parse( " http://translate.google.com/translate_t " ),{ ' text ' => temp[0], ' langpair ' => langpair})
response.body =~ /< div id = result_box dir = ltr > (. * ) < \ / div >/
rescue StandardError => e
$stderr. print " 网络错误: " + e
else
result = $ 1
puts ' 翻译内容: ' + temp[0]
puts ' google返回: ' + result
puts ' -------------------退出请打e或者exit--------------- '
translate
end
end
translate
raise
InterfaceException,
"
keyboard failure
"
,caller
其中的caller生成了栈的信息。另外,catch...throw语句用于在异常发生时从深度嵌套的结构中跳转出来。
12。关于模块,作用有二:作为命名空间和Mixin机制。模块的Mixin机制可以说是ruby的一个精华所在,通过Mixin,可以变相地实现了多重继承,并且可以动态地为类添加和删除功能。这一部分注意两点:
1)模块中定义的实例变量可能与包含模块的类的实例变量产生名称冲突。可以使用模块一级的散列表,以当前对象的ID做索引,来保存特定于当前模块的实例变量解决这个问题。比如:
module Test
State = {}
def state = (value)
State[object_id] = value
end
def state
State[object_id]
end
end
class Client
include Test
end
c1 = Client.new
c2 = Client.new
c1.state = ' A '
c2.state = ' B '
puts c1.state
puts c2.state
2)是关于方法的查找路径,顺序是:当前类-》类的mixin模块-》超类-》超类的mixin,另外mixin的模块,最后混入的同名方法将覆盖前面混入的。
State = {}
def state = (value)
State[object_id] = value
end
def state
State[object_id]
end
end
class Client
include Test
end
c1 = Client.new
c2 = Client.new
c1.state = ' A '
c2.state = ' B '
puts c1.state
puts c2.state