`
凌川__
  • 浏览: 84719 次
  • 性别: Icon_minigender_1
最近访客 更多访客>>
社区版块
存档分类
最新评论

8.4 迭代器、代码块、闭包

阅读更多

先看一句代码:

ruby 代码 

  1. (1..9).each {|i| print  i  if  i<7}    # =>123456  

迭代器each 是数组类的一个方法;大括号{  }里的代码是代码块,简称块。你可以用大括号{  }将代码组织成块,也可以用 do…end将代码组织成块。大括号{  }的优先级高于do…end。


我们来写一个最简单的块;

ruby 代码
  1. #E8.4-1.rb   
  2.   
  3. def one_block   
  4.   yield  
  5.   yield  
  6.   yield  
  7. end  
  8.   
  9. one_block { puts "This is a block. " }  

运行结果:
This is a block.
This is a block.
This is a block.


从程序 E8.4-1.rb 可以看到调用一个块要用关键字yield。每一次 yield,块就被调用一次。yield 还可以带参数调用块,看程序 E8.4-2.rb :

 ruby 代码

  1. #E8.4-2.rb   
  2.   
  3. def  one_block   
  4.   for num in 1..3   
  5.     yield(num)   
  6.   end  
  7. end  
  8.   
  9. one_block  do |i|   
  10.   puts "This is  block #{i}. "    
  11. end  

运行结果:
This is  block 1.
This is  block 2.
This is  block 3.


一个块可以接收yield 传来的参数,还可以将结果返回给调用它的方法。到目前为止,实在看不出使用代码块的优势,可以把块里的代码直接写在方法中。如果我们还没有决定块里写什么代码,又或者块里的代码会随着不同的情形而变化,那么就看出代码块的灵活性了。

 ruby 代码

  1. #E8.4-3.rb   
  2.   
  3. def do_something   
  4.    yield     
  5. end  
  6.   
  7. do_something do  
  8.   (1..9).each {|i| print  i  if  i<5}   
  9.   puts   
  10. end  
  11.   
  12. do_something do  
  13.   3.times { print  "Hi!" }    
  14.   puts   
  15. end  

运行结果:
>ruby E8.4-3.rb
1234
Hi!Hi!Hi!
>Exit code: 0


两次使用方法do_something,第一次do_something遇到yield,调用了代码块{ 输出1..9中小于5的数 } ;在程序的另一处do_something的时候,我们希望做一些不同的事,所以我们写了一个不同于前一次的代码块{ 输出3次“Hi!”}。

这是一个简单的例子,但是你能发现其中的技巧:先写出方法的大致框架,调用方法的时候才告诉方法要作什么。
虽然与代码块有关联的方法不都是迭代器,但是,迭代器确实是一个与代码块有关联的方法。让我们为数组类增加一个迭代器 one_by_one;

ruby 代码
  1. #E8.4-6.rb   
  2.   
  3. class Array     
  4.   def  one_by_one    
  5.     for i in 0...size     
  6.       yield(self[i] )     
  7.     end    
  8.     puts       
  9.   end     
  10. end  
  11.   
  12. arr = [1,3,5,7,9]   
  13. arr.one_by_one {|k| print  k ,  ", "}    # =>  1, 3, 5, 7, 9,   
  14. arr.one_by_one {|h| print h*h,  ", "}    # =>  1, 9, 25, 49, 81,  

 

代码块是一段代码,相当于一个匿名方法,被调用它的方法所调用。如果我们不仅仅想调用代码块,还想把代码块作为参数传递给其它方法,就要使用闭包了。闭包也是一段代码,一个代码块,而且能够共享其它方法的局部变量。
闭包既然是一段代码,也就有自己的状态,属性,作用范围,也就是一个可以通过变量引用的对象,我们称之为过程对象。一个过程对象用Proc.new创建,用call方法来调用。

先看一个闭包作为参数传递给其它方法的例子;

ruby 代码
  1. #E8.4-4.rb   
  2.   
  3. def method(pr)     
  4.   puts pr.call(7)      
  5. end  
  6.   
  7. oneProc=Proc.new{|k|  k *=3 }   
  8. method(oneProc)  


运行结果:
>ruby E8.4-4.rb
21
>Exit code: 0


再看一个闭包共享其它方法局部变量的例子;

ruby 代码
  1. #E8.4-5.rb   
  2.   
  3. def method(n)   
  4.   return  Proc.new{|i|  n +=i }   
  5. end  
  6.   
  7. oneProc=method(3)   
  8. puts oneProc.call(9)        # => 12   
  9. puts oneProc.call(5)        # => 17  

方法method 返回一个Proc对象,这个对象引用了这个函数的参数:n 。即使 n这个参数在闭包被调用时已经不在自己的作用域里了,这个闭包还是可以访问 n这个参数,并且和方法method 共同拥有变量 n 。开始的时候,方法method 的变量 n是3;oneProc.call(9)的时候,oneProc更新了变量 n,把n=12传回给方法method;oneProc.call(5)的时候,oneProc取出方法method的变量 n=12,更新为n=17,传回给方法method的同时,也把n=17作为自己的返回值输出。


完整阅读,请看我写的 Ruby语言中文教程all in one    
 

分享到:
评论

相关推荐

    lua程序设计

    - **闭包**:迭代器通常是闭包的形式,以便能够保存内部状态。 ##### 7.2 泛型for的语义 - **定义**:`for key, value in iterator do`,其中`iterator`是一个迭代器函数。 - **工作原理**:每次循环都会调用迭代器...

    Ruby编程语言

     5.3迭代器和可枚举对象130  5.4代码块140  5.5改变控制流146  5.6异常和异常处理154  5.7BEGIN和END165  5.8线程、纤程和连续体166  第6章方法、PROC、LAMBDA和闭包175  6.1定义简单方法177  6.2方法名180...

    Python核心编程(第二版).pdf (压缩包分2部分,第二部分)

     2.10 代码块及缩进对齐   2.11 if语句   2.12 while循环   2.13 for循环和range()内建函数   2.14 列表解析   2.15 文件和内建函数open()、file()   2.16 错误和异常   2.17 函数  ...

    Python核心编程(第二版).pdf (压缩包分2部分,第一部分)

     2.10 代码块及缩进对齐   2.11 if语句   2.12 while循环   2.13 for循环和range()内建函数   2.14 列表解析   2.15 文件和内建函数open()、file()   2.16 错误和异常   2.17 函数  ...

    Lua中文教程(pdf版)

    4.2 局部变量与代码块(block)......19 4.3 控制结构语句...........20 Programming in Lua iv Copyright ® 2005, Translation Team, www.luachina.net 4.4 break和return语句......23 第5章函数......24 ...

Global site tag (gtag.js) - Google Analytics