ruby Method proc lambdaと閉パッケージ

6960 ワード

ProcとLambdaの違いは、主に:
  • ProcとLambdaはオブジェクトですが、Blockは
  • ではありません.
  • パラメータリストには最大1つの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 `
    '
    # 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
     => #<0x007f8e62818f88> 
    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
     => #<0x007f8e6212ba40> 
    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オブジェクトを作成する3つの方法:
    # lambda    
    2.3.4 :156 > lambda = ->(x,y){x+y}
     => #<0x007f8e621e96a8> 
    2.3.4 :159 > lambda_b = lambda {|x,y| x+y}
     => #<0x007f8e621aae58> 
    
    2.3.4 :157 > proc = proc{|x,y| x+y}
     => #<0x007f8e621d1cd8> 
    2.3.4 :158 > proc = Proc.new {|x,y| x+y}
     => #<0x007f8e621c0848> 
    
    #   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}
     => #<0x007f8e621e96a8> 
    #     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
     => #<0x007f8e62821818> 
    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")
     => [#<0x007f8e6208e740>, #<0x007f8e6208e790>] 
    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
     => [#<0x007f8e622088a0>, #<0x007f8e62208850>] 
    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"][]
    #    []          []           []