上一节我们讲到了Ruby块block的一些大致概念和定义方式,也说到了,块的使用在提升代码抽象和复用方面有很大的帮助,这一节我们谈谈块的“包装”类型Proc类
Intro
为了引出Proc类,我们稍微展开一下,在使用block的时候,block在使用的时候其实就是把它当匿名函数在传递,随写随用,大多数的使用情况下比如在我们自定义的方法结尾排序好的返回值的时候,我们秩序在最后一步可能调用个Array#sort_by方法,注入我们需要的排序的逻辑即可,并不需要事先定义一个不需要的通用发发或者啥的,然后指定,便利性非常高。
Proc让块使用更方便
但倘若需要复用代码块或者突破限制在其它的地方也能使用传递来的代码块的话,将代码块类型化,会更加的合适和方便:
def total2(from, to, &block)
result = 0
from.upto(to) do |num|
if block
result += block.call(num)
else
result += num
end
end
return result
end
这是上节例子使用Proc类的改写方式,最后一个参数前的&
符号会将传递的块自动转化为Proc类,然后Proc类调用的时候通过block#call的方式把块当方法一样调用得到使用结果,在这里使用block其实可以帮助用户摆脱ruby中常规情况下使用block是够多语法糖带来的困扰和不解,罗杰更佳的清晰和透明化。
Proc让块使用更自由
另外,突破限制的好处之一就是非常方便的传递给其它方法,把块的使用delegate下去:
def call_each(ary, &block)
ary.each(&block)
end
call_each [1, 2, 3] do |item|
p item
end
=> 1 2 3
Proc类的定义
Proc类的定义方式非常简单,把原来块的内容传递给Proc类的new方法就行:
hello = Proc.new do |name|
puts "Hello, #{name}."
end
hello.call("World") #=> Hello, World
创建出来的Proc类相当于给了块一个类型和复用的锚点,给了ruby代码实现抽象逻辑更多的自由,这应该也算是ruby多编程范式的一个体现。
block vs lambda
理论上来说,如果在一个方法中返回了一个Proc对象,这个方法的返回值其实可以当做函数进行调用:
def test_proc(n)
Proc.new do |x|
puts x ** n
end
end
cube = test_proc(3)
puts cube.call(2) => 8
puts cube.call(3) => 27
puts cube.call(4) => 64
这其实就相当于一种嵌套函数,而且形成了一个闭包,将父函数的变量捕捉了,运行的结果也能证明。但有个诡异的问题是,如果返回的Proc类型实例里面含有return这种退出指令,会有意想不到的现象:
def test_proc(n)
Proc.new do |x|
return x ** n
end
end
cube = test_proc(3)
puts cube.call(2) => 8
=> in `block in test_proc': unexpected return (LocalJumpError)
Proc提前返回了,索性还有lambda,出去参数检验更严格之外,lambda在这种情况下也能正确处理:
def test_proc(n)
lambda do |x|
return x ** n
end
# exponential = -> (m) {return m ** n}
end
cube = test_proc(3)
p cube.call(2) => 8
to_proc
很多方法有to_proc方法,借助于&
符号,代码块和Proc能够方便的互相转化:
%w(42 39 56).map{|i| i.to_i } => [42, 39, 56]
VS
%w(42 39 56).map(&:to_i) => [42, 39, 56]
总结
这些也只算事对block和Proc的一个大致的总结笔记,希望对你有用。
参考资料: Ruby基础教程