通常我们要定义一个 method 时会写
def say_hello puts 'hello'end
Functions stand alone and methods are members of a class. A method is a piece of code that is called by a name that is associated with an object.
有时,我们会想要给 method 传参数进去
def multiply(x) puts x * 2end
pry(main)> multiply(2)=> 4当传参数已经无法满足我了,我想要让 method 使用一段程式码( block )时,method的参数加上 & 。
def myfun(&p) p.callend
block是可以暂存一段ruby code的地方,如果只有一行可以用{...},若是很多行可以用do...end
block不是物件(object),没有办法单独的存在,也没办法把它指定给某个变数
block也不是参数(variable),block通常得像寄生虫一样依附或寄生在其它的方法或物件(或是使用某些类别把它物件化)ref
& 就像是一个声明,说得是我要传一段 block 进来,然后我用 call 调用传进来的 block
如此一来,我就能在我的method后面,接一个 block ,把这个 block 给物件化。
如此一来,method裡的 p 就是一个物件,可以调用 call 这方法,来取得 block 的内容
pry(main)> myfun { puts "Hello world" }=> Hello worldpry(main)> myfun { puts 2*2 }=> 4当参数使用 & ,如果直接呼叫 myfun 后面没给 block 就会喷错,因为 myfun 裡的 call 是一个method,method的存在是因为有著一个object来呼叫它。
pry(main)> myfun()=> NoMethodError: undefined method `call' for nil:NilClasspry(main)> myfun=> NoMethodError: undefined method `call' for nil:NilClasspry(main)> myfun() {}=> nil但是 block 的缺点是,他没办法把这段程式码存起来,只有要用时放在method后面。
如果有 想要重複使用的程式码,就用 Proc 包起来, Proc 也就是Procedure的意思。
使用 Proc 就可以把 block 物件化
# 原本用block的写法myfun { puts "use myproc" }# 改用Proc写myproc = Proc.new { puts "use myproc" }# 等同于myproc = proc { puts "use myproc" }Proc 的default method就是 call ,让我们能呼叫要重複使用的程式码
[40] pry(main)> myproc2 = proc { |x| x *2 }=> #<Proc:0x007fde7c666d60@(pry):66>[41] pry(main)> myproc2.call(2)=> 4[42] pry(main)> myproc2.call(3)=> 6&当method的参数使用 & ,后面又接 Proc 时
# methoddef myfun(&p) p.callendmyproc = proc { puts "use myproc" }执行程式时
pry(main)> myfun(&myproc)=> use myproc# 由于Ruby可以省略括号,所以也可以写成pry(main)> myfun &myproc=> use myproc# 由于我们在myfun这method有用 & 来定义进来的参数,所以myfun这method后面只能接block,# 如果 myfun 后面接 Proc 生成的 object,则会喷错pry(main)> myfun myproc=> ArgumentError: wrong number of arguments (1 for 0)如果说,我在method的参数,不用 & 则会怎样呢?
def myfun2(p) p.callendmyproc = Proc.new { puts "use myproc" }pry(main)> myfun2 myproc # 这就是一般的 Proc 调用 call 方法=> use myprocpry(main)> myfun2 &myproc # 使用 & 就是在声明使用 block,若是使用block,则 myfun2的参数就该定义 &,由于myfun2的参数没定义 & 所以喷错=> ArgumentError: wrong number of arguments (0 for 1)如果自定义的method,它的参数不用 & ,而method裡又使用了 call 。则必定要传 Proc 进去。因为 call 是 Proc 物件的default method。
一个重要的小结论:
当method的定义裡面有用到
call时,method的参数(变数)p
- 加上
&:&p代表method后面要接则block,透过&p使block物件化,当p.call调用时,就会执行block。- 没有
&:p代表要传proc这个object进到method裡去,当p.call调用时,就会执行传进来的proc。
当我们想要传 block 进入method时,会写
def myfun(&p) p.callend
如果想要省略掉 & 与 call ,则我们可以使用 yield ,写成
def myfun3 yieldend
换句话说
# 原始写法def myfun(&p) p.callend# 等同于def myfun3 yieldend
使用 yield :
# 能传block进来pry(main)> myfun3 { puts "Hello world" }=> Hello world# 传proc就会喷错myproc = Proc.new { puts "use myproc" }pry(main)>myfun3 myproc=> ArgumentError: wrong number of arguments (1 for 0)小结:
yield= 方便执行block的方式。yield语句同等于省略&block输入 以及block.call
要让method使用一段程式码,除了用 block 或 Proc 之外,也能用 lambda 。
lambda 的写法很简单,例如:
lambda1 = lambda {|x| x * 2}# 等同于lambda2 = ->(x) { return x * 2 }# 等同于lambda3 = ->(x) { x * 2 } # Ruby可以省略return我们先看一下 lambda 与 Proc 的关係
pry(main)> lambda2.class=> Proc可以看到, lambda 的class就是 Proc 。由于 lambda 是 Proc ,所以 lambda 能调用 Proc 的default method也就是 call 。
pry(main)> lambda1.call("hello world")=> "hello worldhello world"pry(main)> lambda2.call("hello world")=> "hello worldhello world"pry(main)> lambda3.call("hello world")=> "hello worldhello world"lambda 和 proc 的主要差别有两个
lambda 会check参数数量, proc 不会return 的处理不同,lambda的 return 只会跳出lambda,proc的 return 会跳出整个methodlambda 会check参数数量, proc 不会假如我定义 lambda 时只有一个参数,然后丢两个参数进去就会喷错
[3] pry(main)> lam2 = ->(x) {puts x*2}=> #<Proc:0x007fde7c5d6c60@(pry):3 (lambda)>[4] pry(main)> lam2.call(3)6=> nil[5] pry(main)> lam2.call(3,4)ArgumentError: wrong number of arguments (2 for 1)但若是 Proc ,只定义一个参数丢两个参数进去,则会只取第一个参数
[6] pry(main)> proc1 = proc {|x| puts x * 2}=> #<Proc:0x007fde7c54b610@(pry):6>[7] pry(main)> proc1.call(2,3,4)4=> nil换句话说, lambda 对参数的确认较为严谨
return 的处理不同lambda的 return 只会跳出lambda
#proc and lambdadef run_a_proc(p) puts 'start...' p.call puts 'end.'end#the lambda will be ignoredef run_couple run_a_proc lambda { puts 'I am a lambda'; return } run_a_proc proc { puts 'I am a proc'; return }endpry(main)> run_couple=> start...=> I am a lambda=> end.=> start...=> I am a proc=> nilproc的 return 会跳出整个method
#proc and lambdadef run_a_proc(p) puts 'start...' p.call puts 'end.'end#the lambda will be ignoredef run_couple2 run_a_proc proc { puts 'I am a proc'; return } run_a_proc lambda { puts 'I am a lambda'; return }endpry(main)> run_couple2=> start...=> I am a proc=> nil# 使用yield取代 & 与 calldef f1 yieldendf1 { puts "f1" }# 使用blockdef f2(&p) p.callendf2 { puts "f2" }# 使用proc与lambdadef f3(p) p.callendf3(proc{ puts "f3"})f3(lambda{puts "f3"})
