本月24日(也就是明天)Ruby 2.0终于就要发布了。
Ruby核心团队的卜部昌平桑昨天在一个内部学习会上的presentation,介绍了Ruby 2.0所包含的一些新特性。
(本文内容选译自该幻灯片:https://speakerdeck.com/shyouhei/whats-new-in-ruby-2-dot-0 )
为什么有Ruby 2.0?
- 因为我们在改变事物。
- 我们渴望让自己变得越来越快乐、健康、以及高产。
- 不必畏惧。“拥抱变化。”
Ruby 2.0有什么新鲜的?
什么不是Ruby 2中的新鲜货
- 几乎所有的东西。
- “100%后向兼容”,matz如是说。
- 在现实中……
- (举个例子来说)Rails仍然能完好运行如初。
也就是说,
- 新的东西被加进来了。
- 许多内部的东西得到了改进。
Ruby 2.0的新句法
关键字参数(Keyword arguments)
下面的代码在1.x中能够正常工作:
1
2
3
|
obj.method
"with"
,
:lots
=>
"of"
,
:args
=>
"in"
,
:hash
=>
"form"
|
但是,问题出在哪呢?
问题是在定义该方法的时候: (Mort注:在Ruby 1.x中,只能将多个带符号名称的参数作为一个Hash来传递给方法。要为参数指定默认值,实现起来就很累赘,参见如下代码)
1
2
3
4
5
6
|
def
obj.method(arg, hash)
lots =
Hash
[
:lots
] ||
"default"
args =
Hash
[
:args
] ||
"another"
hand =
Hash
[
:by
hand] ||
"annoying"
...
end
|
注意到代码中错误的Hash[:by hand]
——手写代码是错误产生的根源!
从2.0开始,Ruby将引入关键字参数:
1
|
def
obj.method(a, b =
1
, c:
1
, d:
2
)
|
其中a
为固定参数,b
为可选参数,c
、d
则为关键字参数。这样,局部变量a
、b
、c
和d
都将被恰当地赋值。
在调用函数时,原有的调用方式无需更改。
Mort注:虽然幻灯片里没有写,传统的基于Hash参数的调用方法是这个样子的
1
|
obj.method(
"foo"
,
:c
=>
2
,
:d
=>
3
)
|
现在Ruby 2.0同时也支持直接采用关键字参数的调用方法:(Python程序员一定会觉得这种语法更亲切)
1
|
obj.method(
"foo"
, c:
2
, d:
3
)
|
更详细的示例,可以参考这里:
http://brainspec.com/blog/2012/10/08/keyword-arguments-ruby-2-0/
其他细微的句法改进
- 引入了符号数组字面值
%i
和%I
。
<code>%i(foo bar baz) # => [:foo, :bar, :baz] </code>
- Ruby现在默认把所有的输入都视作UTF-8编码。当然你也可以显式地指定需要的编码。
Ruby 2.0的核心性能改进
require
的改进
- 背景:今天,由于我们有了许多gems,启动Ruby有时甚至需要一次require 128+个库——这带来了糟糕的性能问题。
- 解决:
require
变得更快了(从计算复杂度的意义上来说)。- 若干技术被应用于减少多余的计算上。
Backtrace惰性生成
- 起初,backtraces只是字符串数组而已。
- 每当抛出异常时,这些字符串就被自上而下地生成出来,即使在它们没有实际用途的情况下。
- 这导致了超乎寻常的低效,尤其是当你有1024+个stack frames时(这在Rails应用中很常见)。
- 从Ruby 2.x开始,
Thread::Backtrace
被用来取代字符串。- 它们非常地轻量级。
- 当你需要查看backtrace时,只需将它们转换成字符串即可(调用
#to_s
)。
Flonum类
- 在64位平台(如今早就烂大街了)上,指针,整型和浮点型数均是64位宽度的。
- 在Ruby中,指针和整型均为C级别的
register
寄存器变量。而double
却是存储在内存中的,如果我们能够如操作指针一样操作它们,将如何呢? - 问题:如何让一个指针和一个double共存于一个union中?
- 解决:一些技巧性的位移。
Mort注:图片懒得搬运了……请参见原幻灯片。
GC(Garbage Collection)
- Bitmap标志:以前,GC标志位存储于每个对象中,但现在已经被转移到了专用的内存页中,以减少缓存的误查询(同时也更加CoW (Copy-on-Write)友好)。
- 非递归标志:标志函数如今避免了机器栈溢出的风险。
- 惰性清理(从1.9.3起):清理器只有在必须的地方才进行收集(减少了stop时间)。
Ruby 2.0的新核心特性:#1 调试工具
DTrace支持
TracePoint支持
GC stats
Ruby 2.0的新核心特性:#2 核心库
细粒度的异步中断处理
Ruby的执行有时会因为各种原因而中断,例如,超时。
Ruby 2.0提供了细粒度的异步中断处理方案:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
Thread
.async_interrupt_timing Timeout::Error =>
:defer
do
timeout(rand())
do
begin
Thread
.async_interrupt_timing Timeout::Error =>
:immediate
do
setup
handle
...
end
ensure
teardown
end
end
end
|
模块前插
有时候你想要给一个方法添加需要的安装或拆解代码,而相应的部分却定义在别处。
1
2
3
4
5
|
module
ActiveRecordHelper
def
save
???
end
end
|
该如何去做呢?在Ruby 2.0中,你可以:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class
Foo <
AR
::Base
prepend
AR
::Helper
def
save
bar
end
end
module
AR
::Helper
def
save
foo
super
baz
end
end
Foo.
new
.save
|
这避开了Rails中的所谓“别名方法链(alias method chain)”的困扰。AMC什么的已经不再必要了。
惰性枚举器
Ruby的foo.bar.baz. ...
风格(所谓的“流水作业”)有时会传递许多并不必要的临时对象,而这些理论上都可以通过惰性求值来避免。
1
2
3
4
5
6
7
8
9
10
11
12
|
File
.open(path) {|fp|
fp.each_line. \
select {|line|
# 生成了临时数组
/regexp/ =~ line
}. \
each_with_index.map {|line, no|
# 生成了临时数组
sprintf(
"%d: %s\n"
, no, line)
}. \
first(
10
).
each
{|str|
# 生成了临时数组
puts(str)
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
File
.open(path) {|fp|
fp.each_line.lazy \
select {|line|
# 没有临时数组产生
/regexp/ =~ line
}. \
each_with_index.map {|line, no|
# 没有临时数组产生
sprintf(
"%d: %s\n"
, no, line)
}. \
first(
10
).
each
{|str|
# 没有临时数组产生
puts(str)
}
}
# 甚至在到达EOF之前都不读取数据
|
一个有趣的应用实例:无穷枚举器。
1
2
3
4
|
# Leibniz formula for π
(
0
..Float::
INFINITY
).lazy.map {|i|
((-
1
) ** i) / (
2
*i +
1
).to_f
}.take(
65536
).reduce(:+) *
4
|
其他的新方法
Kernel.__dir__
:获取__FILE__
所在的目录名。Kernel#to_h
:通用的Hash转换方法。Random
类(1.9+):可重复的PRNG。IO#wait_writable
:等待直到可写。- Refinements: 实验性的。
Mort注:更多关于Ruby 2.0核心特性的介绍,参考
- http://globaldev.co.uk/2012/11/ruby-2-0-0-preview-features
- http://www.infoq.com/news/2012/11/ruby-20-preview1
Ruby 2.0标准库的改进
- CGI
- CGI已经为HTML5做好了一切准备。
- net/http
- 支持SNI(Server Name Indication)。
- Zlib绑定
- Zlib如今运行在解释器的进程锁之外。这意味着zlib在多线程的情形下运行速度将更快。
- 更新的stdlibs(标准库)
- Rubygems 2.0.0
- JSON 1.7.7
- Rake 0.9.5
- Rdoc 4.0
- 以及其它(REXML,yaml,openssl……)
总结
什么不是Ruby 2中的新鲜货
- 几乎所有的东西!
- “100%后向兼容”,matz如是说。
- (举个例子来说)Rails仍然能完好运行如初。
- 不必畏惧!开始使用2.0.0版吧!
也就是说,
- 新的东西被加进来了。
- 许多内部的东西得到了改进。
- 即使你对你当前的环境充分自信,2.0.0仍然值得你拥有。