Ruby 2.0 有哪些新特性?

jopen 12年前

本月 24 日 Ruby 2.0 终于就要发布了。

        Ruby 核心团队的卜部昌平桑昨天在一个内部学习会上的 presentation,介绍了 Ruby 2.0 所包含的一些新特性。

        (本文内容选译自该幻灯片。)

Ruby 2.0 有哪些新特性?

        为什么有 Ruby 2.0?

  • 因为我们在改变事物。

  • 我们渴望让自己变得越来越快乐、健康、以及高产。

  • 不必畏惧。“拥抱变化。”

        Ruby 2.0 有什么新鲜的?

        什么不是 Ruby 2 中的新鲜货

  • 几乎所有的东西。

  • “100% 后向兼容”,matz 如是说。

  • 在现实中……
  • (举个例子来说)Rails 仍然能完好运行如初。

        也就是说,

  • 新的东西被加进来了。

  • 许多内部的东西得到了改进。

        Ruby 2.0 的新句法

        关键字参数(Keyword arguments)

        下面的代码在 1.x 中能够正常工作:

obj.method "with", :lots => "of",                     :args => "in",                     :hash => "form"

        但是,问题出在哪呢?

        问题是在定义该方法的时候: (Mort 注:在 Ruby 1.x 中,只能将多个带符号名称的参数作为一个 Hash 来传递给方法。要为参数指定默认值,实现起来就很累赘,参见如下代码)

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 将引入关键字参数:

def obj.method (a, b = 1, c: 1, d: 2)

        其中a为固定参数,b为可选参数,cd则为关键字参数。这样,局部变量abcd都将被恰当地赋值。

        在调用函数时,原有的调用方式无需更改。

        Mort 注:虽然幻灯片里没有写,传统的基于 Hash 参数的调用方法是这个样子的

obj.method ("foo", :c => 2, :d => 3)

        现在 Ruby 2.0 同时也支持直接采用关键字参数的调用方法:(Python 程序员一定会觉得这种语法更亲切)

obj.method ("foo", c: 2, d: 3)

        更详细的示例,可以参考这里:http://brainspec.com/blog/2012/10/08/keyword-arguments-ruby-2-0/

        其他细微的句法改进

  • 引入了符号数组字面值%i%I
%i(foo bar baz) # => [:foo, :bar, :baz]
  • 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 提供了细粒度的异步中断处理方案:

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

        模块前插

        有时候你想要给一个方法添加需要的安装或拆解代码,而相应的部分却定义在别处。

module ActiveRecordHelper    def save      ???    end end

        该如何去做呢?在 Ruby 2.0 中,你可以:

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. ...风格(所谓的“流水作业”)有时会传递许多并不必要的临时对象,而这些理论上都可以通过惰性求值来避免。

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)      }  }

        -

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 之前都不读取数据

        一个有趣的应用实例:无穷枚举器。

# Leibniz formula for π  (..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 核心特性的介绍,参考

        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 仍然值得你拥有。

        Don’t be afraid. Use Ruby today!

        视频:http://jpopsuki.tv/video/AKB48—Ruby/eb57d9ad12c4f7a324fc6854f673d40d (AKB48 演唱)

来自: www.soimort.org