ruby中的代码块(Code Blokcs)
ruby中的代码块对新手来说有点晕。其实这并不是一个什么新创的东西,在其他语言中你是碰到过的。例如C语言中的函数指针,C++的函数对 象,Python的lambda表达式和列表解析,Perls的匿名函数,Java的匿名内部类。如此看来ruby的代码块只不过是新瓶装旧酒罢了。
1、ruby提供了两种block的写法{}或者 do ... end。而且前者的运算优先级高于后者。
irb(main):144:0> 1.upto 3 {|x| puts x} SyntaxError: compile error (irb):144: syntax error, unexpected '{', expecting $end 1.upto 3 {|x| puts x} ^ from (irb):144 from /usr/lib/ruby/1.8/fileutils.rb:875 irb(main):145:0> 1.upto(3) {|x| puts x} 1 2 3 => 1 irb(main):146:0> 1.upto 3 do |x| irb(main):147:1* puts x irb(main):148:1> end 1 2 3 => 1通过上面可以看出ruby的block就是一个匿名函数,“| | ” 之间的参数相当与函数的参数。同时这里的参数又具有严格的限制:
- block参数不能有默认值
- block的参数不能是block对象
irb(main):001:0> def n_times(n) irb(main):002:1> lambda {|x| x*n} irb(main):003:1> end => nil irb(main):004:0> ten_times = n_times(10) => #<Proc:0x00007f3b30e51330@(irb):2> irb(main):005:0> ten_times.call(1) => 10 irb(main):006:0> ten_times.call(1.25) => 12.5 ####### 计算周长 ##################### irb(main):007:0> circumference = n_times(2*Math::PI) => #<Proc:0x00007f3b30e51330@(irb):2> irb(main):008:0> circumference.call(10) => 62.8318530717959 irb(main):009:0> [1,2,3].collect(&circumference) => [6.28318530717959, 12.5663706143592, 18.8495559215388]或许你之前听说过ruby的闭包(closures)。那么什么是闭包呢,它与block的区别是什么呢?其实在ruby中,closures和blocks没有区别。每一个ruby 的 block 就是一个 closure 。 3、创建一个block以及block的调用
- 用Proc的构造器构造block
irb(main):016:0> say = Proc.new {"Hello world"} => #<Proc:0x00007f3b30dde790@(irb):16> irb(main):017:0> say.call => "Hello world" irb(main):018:0> log = Proc.new {|str| puts "[LOG] #{str}" } => #<Proc:0x00007f3b30dd0028@(irb):18> irb(main):019:0> log.call("A test log message.") [LOG] A test log message. => nil
- 用Kernel#proc构造block
irb(main):020:0> say = proc {"Hello world"} => #<Proc:0x00007f3b30e8bda0@(irb):20> irb(main):021:0> say.call => "Hello world" irb(main):022:0> log = proc {|str| puts "[LOG] #{str}" } => #<Proc:0x00007f3b30e59cd8@(irb):22> irb(main):023:0> log.call("A test log message.") [LOG] A test log message. => nil
- 用Kernel#lambda构造block
irb(main):155:0> say = lambda {"Hello world"} => # irb(main):156:0> say.call => "Hello world" irb(main):157:0> log = lambda {|str| puts "[LOG] #{str}" } => # irb(main):158:0> log.call("A test log message.") [LOG] A test log message. => nil
- lambda:
irb(main):001:0> add_lambda = lambda {|x, y| x + y} => #<Proc:0x00007f43af642630@(irb):1> irb(main):003:0> add_lambda.call(1) ArgumentError: wrong number of arguments (1 for 2) from (irb):1 from (irb):3:in `call' from (irb):3 from :0 irb(main):004:0> add_lambda.call(1,2,3) ArgumentError: wrong number of arguments (3 for 2) from (irb):1 from (irb):4:in `call' from (irb):4 from :0
- Proc.new:
irb(main):005:0> add_proc_new = Proc.new {|x, y| x + y} => #<Proc:0x00007f43af6028a0@(irb):5> irb(main):006:0> add_proc_new.call(1) TypeError: nil can't be coerced into Fixnum from (irb):5:in `+' from (irb):5 from (irb):6:in `call' from (irb):6 from :0 irb(main):007:0> add_proc_new.call(1,2,3) => 3
注意:当实参少于形参时,缺少的参数默认值为nil;当实参多于形参时,多于的参数会被忽略。 - proc 在ruby 1.8 中,Kernel#proc与Kernel#lambda一致。在ruby 1.9中,Kernel#proc与Proc.new一致。
irb(main):010:0> def my_lambda(&aBlock) irb(main):011:1> aBlock irb(main):012:1> end => nil irb(main):013:0> b = my_lambda {puts "Hi, nice to meet you!"} => #<Proc:0x00007f3b30defe28@(irb):13> irb(main):014:0> b.call Hi, nice to meet you! => nil irb(main):015:0> b.class => Proc5、自定义可以接收块的方法 在ruby中,任何方法都可以接受块,只要方法的调用者传入的有block。在方法体中,通过yield调用blcok。
irb(main):019:0> def repeat(n) irb(main):020:1> if block_given? irb(main):021:2> n.times {yield} irb(main):022:2> else irb(main):023:2* raise ArgumentError.new("I can't repeat a block you don't give me!") irb(main):024:2> end irb(main):025:1> end => nil irb(main):026:0> repeat(3) { puts "Hello world!"} Hello world! Hello world! Hello world! => 3 irb(main):027:0> repeat(3) ArgumentError: I can't repeat a block you don't give me! from (irb):23:in `repeat' from (irb):27 from :0 yield 是可以传递参数的 irb(main):029:0> squares = {0=>0,1=>1,2=>4,3=>9} => {0=>0, 1=>1, 2=>4, 3=>9} irb(main):030:0> squares.find {|key,value| key > 1} => [2, 4] # Hash#find当找到满足条件的就停止,以数组的形式返回满足条件的键值对 irb(main):031:0> class Hash irb(main):032:1> def find_all irb(main):033:2> new_hash = Hash.new irb(main):034:2> each {|k,v| new_hash[k] = v if yield(k,v)} irb(main):035:2> new_hash irb(main):036:2> end irb(main):037:1> end => nil irb(main):038:0> squares.find_all {|key,value| key > 1} => {2=>4, 3=>9} irb(main):053:0> def invoke_on_each(*args, &block) irb(main):054:1> args.each {|arg| yield arg} irb(main):055:1> end => nil irb(main):056:0> invoke_on_each(1,2,3,4) {|x| puts x ** 2} 1 4 9 16 => [1, 2, 3, 4]