Ruby 2.2 中的新方法

目前围绕 Ruby 2.2 来说,讨论的最多的是垃圾回收的更新。GC 目前会清除掉 symbol,并且 在减少暂停时间的方面有算法的改善。这些改变确实令人感到兴奋,但是其实还是也增加了一些新方法。 让我们看看 2.2 里面增加什么新玩意。

binding#local_variables

如果你想知道一个 scope 范围内有哪些本地变量的话,就可以使用这个方法获取。

1
2
3
4
5
6
7
8
9
10
11
def addition(x, y)
  puts binding.local_variables.inspect
  z = x + y
  puts binding.local_variables.inspect
  z
end

> addition(2, 3)
[:x, :y, :z]
[:x, :y, :z]
=> 5

binding#receiver

这个方法可以让你知道调用方法的对象是谁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Cat
  def self.type
    binding.receiver
  end
end

> Cat.type
Cat

class Tiger < Cat
end

> Tiger.type
Tiger

Dir#fileno

得到文件夹的文件描述符。文件描述符在形式上是一个非负整数。实际上,它是一个索引值, 指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时, 内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。 但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。所以此方法在windows里面运行,将会报错。 习惯上,标准输入(standard input)的文件描述符是 0,标准输出(standard output)是 1, 标准错误(standard error)是 2。

1
2
3
4
> $stdout.fileno
1
> Dir.new('.').fileno
8

Enumerable#slice_after

这个方法是对目前已存在的方法slice_before的相对的实现。我们先看看slice_before是怎么工作的。

slice_before方法是用来按照一定的条件分割数组这样的enumerables。举个例子看下:

1
2
> [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f'].slice_before { |e| e.is_a?(Integer) }.to_a
[[1, "a"], [2, "b", "c"], [3, "d", "e", "f"]]

可以看到,这个方法在1,2,3前面做了分割。

其实也是可以传一个参数代替block的,如果是一个参数的话,参数是使用===运算符来检查的(和case一样)。 所以我们可以得到同样的结果如果我们传入Integer参数。

1
2
> [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f'].slice_before(Integer).to_a
[[1, "a"], [2, "b", "c"], [3, "d", "e", "f"]]

那再看看slice_after的结果,就可以非常容易理解了。

1
2
> [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f'].slice_after(Integer).to_a
[[1], ["a", 2], ["b", "c", 3], ["d", "e", "f"]]

Enumerable#slice_when

一个非常有趣的方法是slice_when,不像slice_after方法,这个方法只接受block。这个block接受的是 2个元素,根据block返回true来分割。

1
2
> [1, 3, 4, 5, 7, 8, 9, 10, 12].slice_when { |a, b| a + 1 != b }.to_a
[[1], [3, 4, 5], [7, 8, 9, 10], [12]]

这个方法也可以用来查找一个数组里面某个数字出现的频率。

1
2
> Array.new(10) { rand(3) + 1 }.sort.slice_when(&:!=).map { |x| [x.first, x.size] }
[[1, 4], [2, 4], [3, 2]]

从结果可以看出,1 和 2 分别出现了 4 次,3 出现了 2 次。

Float#next_float, Float#prev_float

这2个方法返回可表现的下一个或者前一个浮点数。注意并不是所有的浮点数都可以表现出来。

1
2
> 1.0.next_float
1.0000000000000002

可以注意到忽略了1.0000000000000001,因为1.0000000000000001不能通过Float表现出来。

1
2
> 1.0000000000000001
1.0

还有需要注意的是2个值之间的间隔并不一定是相等的。

1
2
3
4
5
> 2.0.prev_float
1.9999999999999998

> 2.0.next_float
2.0000000000000004

这2个方法是用来发现ULP(unit of least precision)的值是非常方便的,但实际开发中有可能很少会用到, 但是知道的话确实对于更好。

File.birthtime, File#birthtime, File::Stat#birthtime

我们已经有了文件的atime,ctimemtime方法,2.2里面增加了文件的birthtime的方法。即文件的创建时间。

1
2
3
4
5
6
7
8
> File.new('test', 'w').birthtime
2015-01-06 19:24:44 -0600

> File.birthtime('test')
2015-01-06 19:24:44 -0600

> File::Stat.new('test').birthtime
2015-01-06 19:24:44 -0600

Kernel#itself

一个返回本身自己的方法。可能你会问,这个方法有什么用?用的比较多的是group方法。例:

1
2
3
4
5
> 1.itself
1

> [2, 3, 3, 1, 2, 3, 3, 1, 1, 2].group_by(&:itself)
{2=>[2, 2, 2], 3=>[3, 3, 3, 3], 1=>[1, 1, 1]}

在map或select方法里面也是可以使用的。

Method#curry(arity)

返回一个方法的proc,arity是Proc接受的参数的个数。如果参数不足,会返回另外一个Proc。 只有当参数足够了的话才会调用方法。例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def foo(a,b,c)
  [a, b, c]
end

proc  = self.method(:foo).curry
proc2 = proc.call(1, 2)          #=> #<Proc>
proc2.call(3)                    #=> [1,2,3]

def vararg(*args)
  args
end

proc = self.method(:vararg).curry(4)
proc2 = proc.call(:x)      #=> #<Proc>
proc3 = proc2.call(:y, :z) #=> #<Proc>
proc3.call(:a)             #=> [:x, :y, :z, :a]

Method#super_method

看着这个方法的名字,估计也猜的出来是干嘛的,这个方法就是调用父类的方法。如果父类 没有这个方法,将会返回nil。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Cat
  def speak
    'meow'
  end
end

class Tiger < Cat
  def speak
    'roar'
  end
end

> Tiger.new.method('speak')
#<Method: Tiger#speak>
> Tiger.new.method('speak').super_method
#<Method: Cat#speak>
> Cat.new.method('speak').super_method
nil

Hash支持字符串 + 冒号作为 key 了

在 Ruby 2.2 之前,如果定义以字符串作为 key 的 Hash 的时候,是不可以写成这样的。 2.2 里面已经支持这种写法了。

1
{"key": "value"}

评论