美文网首页
ruby Method proc lambda 和闭包

ruby Method proc lambda 和闭包

作者: 风___________ | 来源:发表于2018-03-05 19:11 被阅读14次

    Proc和Lambda的区别,主要是:

    • Proc和Lambda都是对象,而Block不是
    • 参数列表中最多只能有一个Block,但是可以有多个Proc或Lambda
    • Lambda对参数的检查很严格,而Proc则比较宽松
    • Proc和Lambda中return关键字的行为是不同的(Proc中外围方法直接返回,Lambda则仅仅是代码块内返回)
    • 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 >
    

    简述

    • ruby 中所有的方法都是真正的方法,即有对象与之相关联,ruby会把无对象关联的方法隐式定义为object对象的私有方法
    • methord 对象代表一个方法
    • 可用proc和lambda代表一个代码块,他们都是闭包的(proc 行为和代码块一致,lambda和方法行为一致,但是其本质都是表示代码块的对象)

    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 > 
    
    • 方法定义不使用yield关键字时,以&开头的代码块参数,传递的是一个代码块{},&后面的形参是一个proc对象(call是proc对象的方法)
    • 方法定义不使用yield关键字时,最后一个参数不是&开头,且最后一个参数可以调用call函数,则为一个proc对象
    • 方法调用时,在proc对象前使用&,则proc被当作一个普通代码块使用,&在方法调用时可以被用在所有支持to_proc方法的对象之前,会隐式调用to_proc然后被当作一个普通代码块使用
    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调用方法不是很高效
    # 创建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"][]
    # 第一个[]索引一个方法 第二个[] 绑定一个对象 第三个[]调用
    

    相关文章

      网友评论

          本文标题:ruby Method proc lambda 和闭包

          本文链接:https://www.haomeiwen.com/subject/zyzhfftx.html