Ruby 1.9中真正酷炫的功能

23

随着Ruby 1.9.2版本即将到来,现在是让开发者对Ruby 1.9感到兴奋的时候了。Ruby 1.9中有哪些在Ruby 1.8中无法实现的好玩的事情呢?


请查看以下网址以了解Ruby 1.9的更改:http://eigenclass.org/hiki/Changes+in+Ruby+1.9 - andre-r
我已经了解大多数的变化。我希望通过在SO上列出它们,并且给出人们如何使用它们的示例,其他人会感到兴奋和有动力转移到Ruby 1.9。 - shinzui
16个回答

31

令人难以置信的是,这个事情还没有被提到过:Ruby 1.9.2+ 最大的特点在于,Ruby 将会有一个规范,这是 Ruby 历史上第一次有规范。

你可能听说过,所有针对 Ruby 1.9.2 的发布计划(原定于 2010 年春季发布)已经被 取消了,原因是:首先,在 RubySpec 项目中将开发完整的 Ruby 1.9.2 规范,然后才会发布 Ruby 1.9.2(编程语言),只有在通过 RubySpec 测试套件之后,才会发布 YARV 1.9.2。

这与之前的工作方式完全相反:首先发布 MRI,然后其他实现者阅读(非常糟糕的设计和文档不全的)MRI C 源码,试图弄清楚新功能应该做什么,然后他们尝试编写可执行规范,只有在此之后才有可能实现兼容性。但那时通常已经发布了新版本的 YARV,循环重新开始…更别提 MRI 和 YARV 的维护者甚至没有运行过 RubySpecs。

这个变化具有巨大的影响。例如,尽管目前有十几个不同的 Ruby 实现正在积极开发中,且在 Ruby 的存在多年以来已经有超过 30 种不同的语言实现,但这一事实并未被 Ruby 编程语言的维护者所认可。对他们来说,Ruby 和 MRI(或近期是 Ruby 和 YARV)一直都是一个东西:MRI 既是语言又是执行引擎,Ruby 既是执行引擎又是语言。Ruby 编程语言的“规范”就是 MRI 的 C 源代码。

自五周前起,这种情况已经改变:现在,Ruby编程语言(至少版本1.9.2及其后续版本)官方规范是RubySpec项目的可执行测试套件。而YARV只是另一个Ruby实现,与MacRuby、IronRuby、JRuby、Cardinal、tinyrb、SmallRuby、BlueRuby、MagLev等其他实现完全相同。
这意味着所谓的“替代”实现(现在不应再称为“替代”实现,因为YARV已失去其特殊地位)现在有机会实际追赶YARV中实现的最新语言功能。实际上,由于大部分其他实现实际上都比YARV更好地设计和用更好的语言实现(YARV基本上是C语言的巨大代码混乱),加上拥有更多的人力资源,其他实现真正成为Ruby 1.9.2兼容的可能性是完全合理的,甚至可能在YARV之前实现。

1
虽然并没有直接回答他的问题,但这是一个非常出色的回复/发泄。 - Jim Mitchener

16

我个人很喜欢新的哈希语法:{:a => 1} 可以变成 {a:1}


2
同意,但在我开始使用它并将其贡献给社区的代码中,还需要一段时间。它还不够酷,不能仅凭它自己就打破向后兼容性。(不过我会在测试中使用它,或者与其他功能导致无法在1.8中运行的代码一起使用。) - SFEley
同意。1.8.x 仍然太常见,无法使用许多 1.9.x 的功能。 - Jim Deville

13

枚举器。

["a", "b", "c"].map {|elem, i| "#{elem} - #{i}" }
# => ["a - ", "b - ", "c - "]
["a", "b", "c"].each_with_index.map {|elem, i| "#{elem} - #{i}" }
# => ["a - 1", "b - 2", "c - 3"]

Enumerable 方法在没有传递块的情况下返回 Enumerator 实例。在这种情况下,它被用于为 map 提供从 each_with_index 中获取的 index 参数。

这个功能也被后移至 1.8.7 版本。


请注意,上述代码的实际结果将是:["a - 0", "b - 1", "c - 2"] - Dana Woodman

11

Ruby 1.9有不同的行为:

  • 块参数始终局限于它们的块中,块的调用从不向现有变量分配值:

  • 块语法已扩展,允许您声明块本地变量,即使同名变量已存在于封闭范围中,也保证是本地变量。

线程也不同:

  • Ruby 1.8仅使用单个本机线程,并在其中运行所有Ruby线程。这意味着线程非常轻量级,但它们从不并行运行。

  • Ruby 1.9不同,它为每个Ruby线程分配本地线程。但由于某些使用的C库本身不是线程安全的,因此Ruby非常谨慎,从不允许超过一个本机线程同时运行(此限制可能在以后的版本中放宽)

其他较小的更改是将RubyGems包含在加载路径中,不再需要require“rubygems”


1
线程与 Ruby 没有任何关系。它们纯粹是一种实现问题。JRuby、IronRuby 和 Rubinius 对于 Ruby 1.8 和 Ruby 1.9 具有完全相同的线程实现。JRuby 自 Ruby 1.8(事实上,自 Ruby 1.6)以来就拥有本地线程近十年了。此外,至少 JRuby 可以并行运行多个线程,并且已经这样做了多年。 - Jörg W Mittag
1
我在谈论参考实现(MRI) - Filipe Miguel Fonseca
更严格的块行为非常好。非块局部变量是1.8中最大的陷阱之一。 - guns

10

我非常喜欢枚举器——不仅适用于现有类型,也适用于编写自己的集合作为枚举器类。由于切换到1.9后,我曾两次构建API适配器来从外部Web服务中检索大型JSON或XML结果集。有时我会限制一次检索记录的数量,这意味着我需要进行多个请求。(先获取前500个,然后获取501到1000条记录等)

“旧”的处理方式是,抓取第一批数据,使用.each.collect一次迭代完整个集合,并创建与Ruby对象等大小的数组。如果我不能在一个请求中获得所有记录,则还需要循环进行API请求,并每次添加到数组中。这意味着所有时间都被前置,看起来是缓慢的检索,并且我正在消耗大量内存:对于源数据,对于相同数量的Ruby对象,有时还要进行中间数组操作。当我可能只同时处理一个对象时,这是浪费的。

通过枚举器,我可以获取第一批数据,将源数据保存为我的“权威”集合,并在遍历时处理和返回每个Ruby对象。当我传递最后一个元素时,如果我知道还有更多数据需要从源中提取,我可以立即进行下一次API调用。(即,惰性加载。)这意味着检索方法调用的返回速度更快,内存使用情况更好。每个Ruby对象在完成操作且移动到下一个对象时立即有资格进行垃圾回收。

实现这个想法的抽象实现如下:

class ThingyCollection < Enumerator
  attr_reader :total

  # Returns a new collection of thingies.
  def initialize(options={})

    # Make the request for the first batch
    response = ThingyAPIClient.get_thingies(options)
    @total = response.total   # Number of ALL thingies, not just first batch
    records = response.data  # Some array of JSON/XML/etc. from the API 

    # Create a closure which serves as our enumerator code
    enum = Proc.new do |yielder|
      counter = 0              # Initialize our iterator
      while counter < @total

        # If we're at the end of this batch, get more records
        if counter == records.length  
          more = ThingyAPIClient.get_next_thingies(counter, options)
          records += more.data
        end

        # Return a Ruby object for the current record 
        yielder.yield Thingy.new(records[counter])   
        counter += 1
      end
    end

    # Pass that closure to the Enumerator class
    super(&enum)
  end
end

一旦你拥有了那个,你可以像这样遍历它们:

thingies = ThingyCollection.new(foo: bar) # Whatever search options are relevant
puts "Our first thingy is #{thingies.next}"
puts "Our second thingy is #{thingies.next}"
thingies.rewind
thingies.each do |thingy|
  do_stuff(thingy)
end

你失去的主要是通过引用轻松跳转到特定元素的能力。这意味着你也会失去“最后一个”,排序等功能。仅使用.next和几个.each变体不如数组功能丰富,但对于我的大多数常见用例而言,它已经足够了。

是的,你可以通过回溯将Ruby 1.8.7做到这一点。但由于内部使用纤程技术,1.9在这方面要快得多。如果没有1.9,就不会有1.8.7,所以我认为它仍然是我最喜欢的1.9功能。


9

Ruby 1.9.2支持获取有关方法参数的信息。您可以获得参数的名称,以及有关它们的信息,例如可选、必需或块。

请查看Method#params以获取示例。


9

在Ruby 1.9中,哈希是有序的。这对于实现某些算法非常有用。在Ruby 1.8中,您必须依赖于一个gem或自己编写有序哈希。


我认为这是一个适合新手的功能,直到最近我使用它来优雅地改进了一个生产环境下的Sinatra应用程序。当时我感到非常高兴。 - guns

6

完全支持多字节字符编码,特别是Unicode。


Ruby 1.9全面内置的Unicode支持是一个巨大的变革,为Ruby提供了一流的多字节/Unicode支持。 - shinzui

5

instance_exec和class_exec是很棒的新功能,但对我而言,主要是一些小改变(已经被回溯到1.8.7)。像Method#owner这样的东西非常好 - 你是否曾经想知道特定方法在继承链中确切的定义位置?my_object.method(:blah).owner会告诉你:)

我喜欢1.9的其他东西是更一致的作用域规则,尤其是在eval上下文中。常量和类变量没有在instance_eval中查找是一个愚蠢的遗漏(在我看来),1.9修复了这个问题 :)


不错,我不知道 #owner。参数检查功能也很好。 - Jim Deville

5
我喜欢Symbol#to_proc,使用高阶函数时无需每次编写lambda表达式。因此,原来对数组求和的写法是arr.inject(0) {|memo, val| memo + val},现在可以直接写成arr.inject(&:+);而原先的houses.collect {|house| house.price}可以改为houses.collect(&:price)
一些库(如ActiveSupport)在1.8版本下提供了相同的功能,但将其作为核心语言的一部分仍然很好,而且1.9实现比库方法更加优化。

我不知道这是否是一个后移的问题,但这个功能已经在1.8版本中存在了,所以我不会认为它是全新的 ;) - samuil
不,这个功能在1.8.6中不存在。就像标准的枚举器一样,在1.9中引入后被移植到了1.8.7中,但它是一个新颖的1.9功能。 - Chuck
我尝试喜欢Symbol#to_proc,但我觉得语法非常丑陋 :/ 在我看来,'&'真的很难看(尽管功能非常好)。 - horseyguy
@banister:我同意语法方面的问题,但是当你只想说“使用+”时,编写整个函数仍然比较麻烦。我宁愿选择实用主义的语法,而不是堆积大量不必要的代码。 - Chuck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接