ruby Method proc lambda 和闭包

2018-03-05  本文已影响14人  风___________

Proc和Lambda的区别,主要是:

# 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>'
# 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

# 1. 显式传递proc对象
def sequence(n, m, b)
b.call(n*m)
end
p = Proc.new {|x| p x}

sequence(5,2,p)
# 2. 方法调用时使用& (如果在一个proc对象前用&,这个调用会想对一个普通代码块一样处理)
# 方法调用中,&只可以被用于修饰方法的最后一个参数,ruby中所有方法都可以传递一个代码块,即使方法不需要代码块也没有yield语句
# &通常出现在一个proc对象前,其实&可以出现在所有支持to_proc方法的对象之前。method类就有此方法,可以跟proc对象一样被传给迭代器
a, b = [1,2,3], [4,5]
summation = Proc.new {|total, x| total+x}
sum = a.inject(0, &summation) # => 6
sum = b.inject(sum, &summation) # => 15

# symbol类也定义了to_proc方法,使得符号也可用&修饰传给迭代器,当一个符号用此方法传递时,他将被假定为一个方法名
2.3.4 :133 > def sum(x,*y)
2.3.4 :134?>   total = x
2.3.4 :135?>   y.each{|nex| total+=nex}
2.3.4 :136?>   total
2.3.4 :137?>   end
 => :sum 
2.3.4 :138 > sum(1,*[1,2,3])
 => 7 
2.3.4 :139 > proc = :sum.to_proc
 => #<Proc:0x007f8e62818f88(&:sum)> 
2.3.4 :140 > def sys_print
2.3.4 :141?>   yield 1,1,2,3
2.3.4 :142?>   end
 => :sys_print 
2.3.4 :143 > sys_print &proc
 => 6  # 没弄懂为啥第一个参数没计算进去。。。
2.3.4 :144 > word = ['and','but','car']
 => ["and", "but", "car"] 
2.3.4 :145 > uppercase = word.map &:upcase
 => ["AND", "BUT", "CAR"] 
2.3.4 :147 > upc = :upcase.to_proc
 => #<Proc:0x007f8e6212ba40(&:upcase)> 
2.3.4 :148 > uppercase = word.map &upc
 => ["AND", "BUT", "CAR"] 
2.3.4 :149 > uppercase = word.map {|w| w.upcase}
 => ["AND", "BUT", "CAR"] 
2.3.4 :150 > 
def makproc(&block)
  block # 返回的是proc对象,可以供其他方法使用
end 
adder = makeproc{|x,y| x+y} # 创建了一个proc对象
adder.call 2,2 # => 4
1. 创建proc对象的三种方式:
# lambda 字面量
2.3.4 :156 > lambda = ->(x,y){x+y}
 => #<Proc:0x007f8e621e96a8@(irb):156 (lambda)> 
2.3.4 :159 > lambda_b = lambda {|x,y| x+y}
 => #<Proc:0x007f8e621aae58@(irb):159 (lambda)> 

2.3.4 :157 > proc = proc{|x,y| x+y}
 => #<Proc:0x007f8e621d1cd8@(irb):157> 
2.3.4 :158 > proc = Proc.new {|x,y| x+y}
 => #<Proc:0x007f8e621c0848@(irb):158> 

# 注意proc{|x,y| x+y}:
# ruby1.8中返回的是Proc(lambda)对象
# ruby1.9中则是Proc.new的简写返回的proc对象

# lambda 同样支持 声明一个与外部同名的块内局部变量的语法
# 这个语法是唯一支持设置代码块参数默认值的语法
# 下面是两个参数和三个块级局部变量的代码块
->(key, value=2; i, j, k){}

lambda字面量的圆括号是可选的如下写法都是成立的:

->x {x+1}
->x, y ;i, j, k {}
->x, y, fac = 2 {}
-> {}
2. 调用Proc 和 lambda
proc_f = Proc.new {|x, y| x+y}
# call 方法调用
proc.f.call 1, 2
# 第二种调用
proc_f[1, 2]
# 第三种调用
proc_f.(1, 2)

lambda

# 字面量定义lambda
lambda = ->(x, y){x+y}
lambda = ->x, y{x+y}
# 调用lambda
lambda.call 2,3
lambda[2,3]
def compose(f, g)
 ->(x){ f.call(g.call(x)) }
end
succ = compose(->x{x+1}, ->x{x*x})
succ.call 4  # => 17: (4*4)+1  
data.sort{|a,b| b-a}
# 重写为
data.sort &->(a,b){ b-a }

proc和lambda是对象而非方法。因此无法像方法一样直接调用,调用封装的代码的方式

2.3.4 :156 > proc = ->(x,y){x+y}
 => #<Proc:0x007f8e621e96a8@(irb):156 (lambda)> 
# 第一种 call 函数
2.3.4 :160 > proc.call 1,2
 => 3 
# 第二种
2.3.4 :161 > proc[1,2]
 => 3 
# 第三种
2.3.4 :162 > proc.(1,2)
 => 3 

lambda 使用的一个例子

2.3.4 :163 > def created_lambda(n)
2.3.4 :164?>   ->(array){array.collect {|x| x*n}}
2.3.4 :165?>   end
 => :created_lambda 
2.3.4 :166 > lambda_t = created_lambda 2
 => #<Proc:0x007f8e62821818@(irb):164 (lambda)> 
2.3.4 :167 > lambda_t.call([1,2,3])
 => [2, 4, 6] 

lambda 闭包的使用实例:

2.3.4 :173 > def accessor_pair(initialValue=nil)
2.3.4 :174?>   value = initialValue
2.3.4 :175?>   setter = ->(x){value = x}
2.3.4 :176?>   getter = ->{value}
2.3.4 :177?>   return getter,setter
2.3.4 :178?>   end
 => :accessor_pair 
2.3.4 :179 > getX,setX = accessor_pair("oldValue")
 => [#<Proc:0x007f8e6208e740@(irb):176 (lambda)>, #<Proc:0x007f8e6208e790@(irb):175 (lambda)>] 
2.3.4 :180 > getX.call
 => "oldValue" 
2.3.4 :182 > getX[]
 => "oldValue" 
2.3.4 :183 > setX["newValue"]
 => "newValue" 
2.3.4 :184 > getX[]
 => "newValue" 
2.3.4 :185 > def muti(*arge)
2.3.4 :186?>   arge.map {|x| lambda{|y| x*y}}
2.3.4 :187?>   end
 => :muti 
2.3.4 :188 > lam1,lam2 = muti 2,3
 => [#<Proc:0x007f8e622088a0@(irb):186 (lambda)>, #<Proc:0x007f8e62208850@(irb):186 (lambda)>] 
2.3.4 :189 > lam1[2]
 => 4 
2.3.4 :190 > lam1[3]
 => 6 
Method对象
# 创建method对象
# method 和 proc对象相互转化
# method的三个新方法在ruby1.9中

无绑定的Method对象
String.instance_method(:reverse).bind("hello").call
class Module
  # instance_method 定义了一个快捷方式
  alias [] instance_method
end
# 简写
String[:reverse].bind("hello").call
class UnboundMethod
  alise [] bind
end
# 再次简写
String[:reverse]["hello"][]
# 第一个[]索引一个方法 第二个[] 绑定一个对象 第三个[]调用
上一篇下一篇

猜你喜欢

热点阅读