23

随着 Ruby 1.9.2 版本的发布,是时候让开发人员对 Ruby 1.9 感到兴奋了。有哪些在 Ruby 1.9 中可以做而在 Ruby 1.8 中不能做的好事情?

4

16 回答 16

31

我不敢相信这还没有被提及:Ruby 1.9.2+ 的一个最大特点是 17 年来 Ruby 将第一次有一个规范。

您可能听说过 Ruby 1.9.2(原定于 2010 年春季发布)的所有发布计划都被取消了,这就是原因:首先,Ruby 1.9.2 的完整规范将在RubySpec 项目,然后 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 更好地设计和实现(这基本上是一个巨大的意大利面条混乱的 C),加上有更多的人力,其他实现实际上是完全合理的在YARV之前兼容 Ruby 1.9.2 。

于 2009-10-29T13:07:51.393 回答
16

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

于 2009-10-27T07:45:24.417 回答
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"]

EnumerableEnumerator方法返回没有块传递给它的实例。在这种情况下,它用于给出map一个index参数,取自each_with_index.

这也被向后移植到 1.8.7。

于 2009-10-27T08:33:37.073 回答
11

Ruby 1.9 有不同的行为:

  • 块参数始终是其块的本地参数,并且块的调用永远不会为现有变量赋值:

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

线程也不同:

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

  • Ruby 1.9 不同,它为每个 Ruby 线程分配一个本地线程。但是因为使用的一些 C 库本身并不是线程安全的,所以 Ruby 非常保守,不允许同时运行多个本地线程(这个限制在以后的版本中可能会放宽)

其他小的变化是在加载路径中包含RubyGems,不再需要require "rubygems"了。

于 2009-10-27T18:42:32.177 回答
10

我非常喜欢 Enumerators - 不仅适用于现有类型,而且将我自己的集合编写为 Enumerator 类。自从切换到 1.9 后,我两次不得不为外部 Web 服务构建 API 适配器,这些服务会下拉大型 JSON 或 XML 结果集。有时我会限制一次可以检索多少条记录,这意味着我需要执行多个请求。(获取前 500 条,然后获取 501 到 1000 条记录,以此类推)

我处理这些的“旧”方法是获取第一批,用.eachor一次遍历它.collect,并创建一个大小相等的 Ruby 对象数组。如果我无法在一个请求中获取所有记录,我也会遍历 API 请求,每次都添加到数组中。这意味着所有时间都是前端加载的,被认为是一个缓慢的检索,并且我正在消耗大量内存:用于源数据,用于相同数量的 Ruby 对象,有时用于中间数组操作。当我可能一次只对一个对象进行操作时,这是一种浪费。

使用 Enumerators,我可以抓取第一批数据,将源数据作为我的“权威”集合,并在我进入每个 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 功能。

于 2009-10-27T15:56:39.637 回答
9

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

查看Method#params示例。

于 2009-10-30T23:18:11.500 回答
9

散列在 ruby​​ 1.9 中排序。在实现某些算法时非常有用。您必须依赖 gem 或在 ruby​​ 1.8 中编写自己的有序哈希。

于 2009-11-02T01:18:30.610 回答
6

Full native support for multibyte character encodings, particularly Unicode.

于 2009-10-28T03:17:36.763 回答
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 的实现比库方法优化了很多。

于 2009-10-27T16:46:31.457 回答
5

instance_exec 和 class_exec 是很棒的新特性,但对我来说,主要是小的变化(已经向后移植到 1.8.7)。Method#owner 之类的东西很棒——有没有想过在继承链中的哪个位置定义了特定的方法?my_object.method(:blah).owner 会告诉你:)

我喜欢 1.9 的其他方面是更一致的范围规则,尤其是。在评估上下文中。没有在 instance_eval 中查找常量和类变量是一个愚蠢的遗漏(IMO),1.9 修复了这个:)

于 2009-10-27T16:33:09.877 回答
3

改进的正则表达式支持。Ruby 1.9 支持命名的正则表达式组——在许多其他改进中——您可以稍后在您的正则表达式中回忆。Dave Thomas 举了一个很好的例子

于 2009-10-31T18:33:30.510 回答
3

YARV。1.9 中的新 Ruby VM 提供了一个速度明显更快的新现代 VM。

于 2009-10-31T18:15:55.147 回答
3

哦,而且:它的速度是原来的两倍多。如果您正在构建具有大量 I/O 延迟的多线程应用程序,则速度会更快。所有的工作仍在试图从 1.8 中提高一点性能,或者修复它的线程等,令我惊讶的是,人们并没有对 1.9 的速度或其本机线程感到更加兴奋。

于 2009-10-27T16:05:53.363 回答
2

更好的 Unicode 支持和更好的线程。


1.8 中的 Unicode 支持是通过额外的 gems 实现的,这给我带来了无尽的痛苦——如果能获得完整的语言支持,那就太好了。有些人抱怨实现的复杂性,但我猜他们从未在 C++ 中使用过 ICU!

此外,能够做到"Hello"[3]并获得一个角色会很好。


性能改进

由于 Matz 的 Ruby 中的新 VM (YARV),一些基准测试显示出比 1.8.6 和 1.9.1 的显着改进


很多简洁的小功能...

如果你有 Dave Thomas 的“Programming Ruby”(又名 The Pick-axe book)的副本,关于内置类和方法的部分可以很好地标记从 1.8 到 1.9 的更改

怎么样:

(a) 新的哈希语法:{:hello => "world"} 可以缩短为 {:hello:"world"}

(b) 有序哈希!!

于 2010-04-08T15:25:30.040 回答
2

Ruby 1.9 Fibers 提供了一个强大的新并发结构。Ruby Fibers:关于 Ruby 的新并发特性的 8 篇有用的读物​​包含与纤维相关的文章的链接。

于 2009-10-31T17:57:51.737 回答
1

在 Ruby 1.9.+ 中,还可以将方法链接到多行。这似乎不是一个疯狂的功能,但它可以让您的代码更具可读性,这是 Ruby 的一个关键概念。

例如 :

@results = Clients
  .from_category(current_category)
  .selected_by(current_user.id)
  .activated
  .order("created ASC")
  .limit(1000)
  .map{ |e| "#{e.id} - {e.fullname}" }
于 2013-01-30T13:53:47.663 回答