自己重读了 ruby 入门书,做了点笔记,如果您对 block 这块不太熟悉,可以参考一下,若有错误,还请指教,谢谢
Block 在 ruby 中会像空气一般的存在于大量的代码当中。块最开始是用来循环时候用的东西,但慢慢的,block 变成了委托利器,贯穿于各种代码之间。
举个例子,我有一个数组 1,2,3,4,5,6 我想得到这个数组的平方 在传统的程序里面,我们这样实现
var arr = [1,2,3,4,5,6];
var answer = []`
for(var i = 0; i<arr.length;i++){
answer[] = arr[i]**2;
}
如果在 ruby 中,我们即可使用块来完成
answer = [1,2,3,4,5].map {|item| item**2}
这里的{|item| item**2} 就是一个 Block。
当然之所以这里的 ruby 代码简洁的原因是因为 map 本身隐藏了一些逻辑,实际上 map 由模块 Emuneration 实现 其实通过上面的代码基本上可以看出,Block类似其他语言中的C#或者java中的委托、事件的实现。
我们来仿照着写一个 Block 的应用(自行实现 map)
class MyArray < Array
def my_map
for item in self
p yield(item)
end
end
end
arr = MyArray.new
arr.push 1,3,5
arr.my_map do item
item**2
end
# 结果输出
# 1
# 9
# 25
我们把目光还是集中在{|item| item **2}
中,这个代码是我们手敲上去的,那有没有方式将其放入变量里呢?
答案是 有,就是 Proc 对象 就是我们的 Proc.new 和 lambda,他们会返回 Proc 对象,记得在做参数的时候要加&符号
class MyArray < Array
def my_map
for item in self
yield(item)
end
end
def my_map_using_proc(&proc) # 定义的时候就需要表示这是个Proc 对象
answer = self.class.new
for item in self
#p proc.class 输出为Proc 对象
answer.push(proc.call(item))
end
return answer
end
end
arr = MyArray.new
arr.push 1,3,5
my_proc = Proc.new do |i|
i**2
end
my_lambda = lambda do |i|
i**2
end
arr2 = arr.my_map_using_proc(&my_proc) # 使用的时候也要说明
p arr2
arr3 = arr.my_map_using_proc(&my_lambda)
p arr3
Ruby 下的 Proc.new 和 Lambda 都是为了生成我们的 Proc 而使用的指令,从大方向上来说,他们功能上非常相似,但从细节上来讲,他们又有一些不同点
相同点非常简单,他们都返回 Proc 对象,结构一样,那么不同点呢?
Proc.new 的参数不需要严格匹配,但是 lambda 需要严格匹配
比如这里的func1 = Proc.new {|p,q| p p,q};func1.call("x")
,则输出 "x" nil
,但是换做 lambda,就不行:
2.2.0 :014 > func2 = lambda {|p,q| p p,q};func2.call("x")
ArgumentError: wrong number of arguments (1 for 2)
from (irb):14:in `block in irb_binding'
from (irb):14:in `call'
from (irb):14
from /Users/atpking/.rvm/rubies/ruby-2.2.0/bin/irb:11:in `<main>'
Proc.new 如果里面出现了 return,则代表的外部的 return,而不是 Proc 自己的 return,则是返回一个 Proc 对象,比如说
2.2.0 :035 > def Hello
2.2.0 :036?> p = Proc.new{return "inner proc"}
2.2.0 :037?> p.call
2.2.0 :038?> return "func return"
2.2.0 :039?> end
2.2.0 :041 > Hello()
=> "inner proc"
注意此处,返回的是 inner proc,
注意,如果直接在 irb 里写 p = Proc.new{return 1}
之后 p.call 的化,是要报 localJumpError 的,因为此时 irb 并没有结束,还没有 return
2.2.0 :061 > def Hello2
2.2.0 :062?> p = lambda{return "inner proc"}
2.2.0 :063?> p.call
2.2.0 :064?> return "func return"
2.2.0 :065?> end
=> :Hello2
2.2.0 :068 > Hello2()
=> "func return"
此处是 func return。
那么这些区别会带来什么变化呢?答案是,lambda 可以带参数的回传,更加灵活了。比如下面的一个例子
2.2.0 :087 > def my_func(n)
2.2.0 :088?> b = lambda {|item| return item*n }
2.2.0 :089?> end
=> :my_func
2.2.0 :090 > my_func(2).call 5
=> 10
2.2.0 :091 > my_func(2).class
=> Proc
2.2.0 :092 >
而 proc,就会出现下列的问题
2.2.0 :094 > def my_func(n)
2.2.0 :095?> b = proc {|item| return item*n }
2.2.0 :096?> end
=> :my_func
2.2.0 :097 > my_func(2).class
=> Proc
2.2.0 :098 > my_func(2).call 5
LocalJumpError: unexpected return
from (irb):95:in `block in my_func'
from (irb):98:in `call'
from (irb):98
from /Users/atpking/.rvm/rubies/ruby-2.2.0/bin/irb:11:in `<main>'
2.2.0 :099 >