




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、. RUBY文档中心 6、容器,代码块,迭代器 一个只有一首歌曲的点唱机很少见(除非在非常非常特殊的场合),所以不久我们就开始考虑制作一个歌单和播放列表。它们两个都是容器:用来保存对一个或者多个对象的引用的对象。歌单和播放列表有一些相似的方法:添加一首歌、删除一首歌,返回一个歌曲列表等等。播放列表还应该增加一些其它的功能,比如每次播放时插入广告,记录累计播放时间等。不过我们先不去考虑这些问题,现在最好先创建一个通用的SongList类供歌单和播放列表使用。容器在实现以前,我们需要决定怎样在SongList对象中储存歌曲列表,有三种方法,使用Ruby的A
2、rray类型或者Hash类型,要不然就自己做一个列表结构。偷懒的结果就是先来看看数组和哈希,从中选择一个用在我们的类中。数组Array类保存着对象引用的集合。每一个对象的引用在数组中都有一个位置,通过一个非负整数来索引。你可以使用字面值或者直接生成一个Array对象,一个字面上的数组是被方括号括住的一个对象序列。a = 3.14159, "pie", 99 a.type >>
3、; Array a.length >> 3 a0 >> 3.14159 a1
4、 >> "pie" a2 >> 99 a3
5、; >> nil b = Array.new b.type >> Array b.length >>
6、160; 0 b0 = "second" b1 = "array" b >> "second", "array"
7、; 数组通过操作符来索引,就像大多数的Ruby操作符,这实际上也是一个方法(在Array类中)所以也可以在子类中重载,如例中所示数组索引从0开始。使用一个single整数来索引数组,返回该位置的对象,如果在那个位置没有对象则返回nil。如果使用一个负整数索引数组,那么它从数组尾端返回,参看35页的表4.1。a = 1, 3, 5, 7, 9 a-1 >>
8、; 9 a-2 >> 7 a-99 >> nil 也可以使用一对数字来索引数
9、组,start,count。这会返回一个新数组,它由从start开始的count个对象的引用组成。a = 1, 3, 5, 7, 9 a1, 3 >> 3, 5, 7 a3, 1
10、60; >> 7 a-3, 2 >> 5, 7 (译者注:注意负整数索引的方向依然是从前向后)最后你也可以使用区间来索引数组,开始和结束位置之间插入两个或者三个点,两个点的形式表示包含结束位置,三个点不包含。
11、a = 1, 3, 5, 7, 9 a1.3 >> 3, 5, 7 a1.3 >> 3,
12、 5 a3.3 >> 7 a-3.-1 >> 5, 7, 9 操作符对应的有=操作符,通过它可以设置数组元素的值。用一个single整数索引,把操作符右
13、边的值赋给该位置的元素。中间所产生的空隙用nil来填补。a = 1, 3, 5, 7, 9 >> 1, 3, 5, 7, 9 a1 = 'bat'
14、 >> 1, "bat", 5, 7, 9 a-3 = 'cat'
15、 >> 1, "bat", "cat", 7, 9 a3 = 9, 8 >> &
16、#160; 1, "bat", "cat", 9, 8, 9 a6 = 99 >> 1, &
17、quot;bat", "cat", 9, 8, 9, nil, 99 如果=的索引有两个数(开始和长度)或者是一个区间,那么在原始数组中的对应元素就会被操作符右边的值代替;如果索引的长度为0,那么右边的值就插入到开始位置前面,不删除元素;如果右边也是一个数组,它的元素代替原始数组的元素;如果索引所选择的元素数目和右边的不一样,那么就自动调整数组的大小来适应。(译者注:值得一提的是如果出现了空隙,依旧用nil来填补)a = 1, 3, 5,
18、60;7, 9 >> 1, 3, 5, 7, 9 a2, 2 = 'cat' >>
19、; 1, 3, "cat", 9 a2, 0 = 'dog' >> 1, 3, "dog", "cat",
20、9 a1, 1 = 9, 8, 7 >> 1, 9, 8, 7, "dog", "cat", 9 a0.3 = &
21、#160; >> "dog", "cat", 9 a5 = 99
22、60;>> "dog", "cat", 9, nil, nil, 99 数组有大量有用的方法,通过它们你可以把数阻当成堆、栈、集、队列、双列、先入先出列等。278页有完整的数组方法列表。哈希哈希(有时被认为是数组和字典的结合)和数组一样是用来储存对象引用的集合。不过,区别于通过整数来索引数组,你可以通过任意类型的对象来索引哈希:字符、正则表达式等。在哈希中保存元素实际上是保存了两个对象-键和值。用键可以索引到对应的
23、值。哈希中的值可以是任意类型的对象,下面的例子使用了哈希字面值:括号括起来的键值对。h = 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' h.length
24、160;>> 3 h'dog' >> "canine" h'cow' = 'bovine' h12 = 'dod
25、ecine' h'cat' = 99 h >> "donkey"=>"asinine", "cow"=>"bovine", &q
26、uot;dog"=>"canine", 12=>"dodecine", "cat"=>99 对比于数组,哈希有一个显见的好处就是可以使用任意对象做索引,但同时一个显见的不好处就是它的元素是无序的,所以你不能简单地把哈希用作堆栈或者队列。你很快会发现哈希是ruby中使用最广泛的一类数据结构。317页有完整的哈希类中实现的方法列表。实现一个SongList容器上面简单讨论了一下数组和哈希,下面我们来实现点唱机的SongList类。我们先列出在SongList中所需的基本
27、的方法的一个列表,我们希望随着我们的进度不断丰富它,现在先把它做出来。append( aSong ) >> list 向列表中添加指定的歌曲deleteFirst() >> aSong 从列表中删除第一首歌曲并返回该歌曲deleteLast() >> aSong
28、; 从列表中删除最后一首歌并返回该歌曲anIndex >> aSong 从列表中返回anIndex所索引的歌曲,可以是整数索引或者歌曲的标题。(译者注:这里要实现一个操作符方法即方法)这个列表给我们一些实现方法的提示。在列表尾端添加歌曲的功能,在最前和最后位置删除歌曲的功能。建议使用双列-一个两端队列-这样我们可以使用Array来实现,同样,数组也支持用一个整数来索引歌曲。但是我们也需要使用歌曲标题来索引歌曲,可能会想到使用哈希,那样用标题做键歌曲做值。那么可
29、以使用哈希吗?也许可以,不过这样有问题。首先哈希是无序的,所以我们不得不使用一个辅助的数组来跟踪列表。一个更大的麻烦是哈希不支持多个键对应一个值,这会给我们的播放列表带来麻烦,因为一首歌可能会被播放许多次。我们可以在一个歌曲的数组中搜索需要的歌曲标题,如果这会成为执行上的瓶颈,那么我们会在后面加入一些基于哈希的查找特性。我们从一个基本的initialize方法开始我们的类,创建一个数组用来存放歌曲和一个引用它的实例变量songs。class SongList def initialize
30、60; songs=Array.new endendSongList#append方法在songs数组末尾添加歌曲,返回它自己也就是当前的SongList对象。这是一个有用的特性,可以让我们把多个append调用联接在一起,后面会看到这个例子。class SongList def append(aSong) songs.push(aSong)
31、160; self endend然后添加deleteFirst和deleteLast方法,简单地用Array#shift和Array#pop来分别实现。class SongList def deleteFirst songs.shift end
32、; def deleteLast songs.pop endend让我们来快速地测试一下,在列表中添加四首歌曲。炫耀一下,我们用append返回的SongList对象来联接这些方法调用。list = SongList.newlist. append(Song.new('title1', 'artist1', 1).
33、60;append(Song.new('title2', 'artist2', 2). append(Song.new('title3', 'artist3', 3). append(Song.new('title4', 'artist4', 4)然后检查一以下表的开始和结束位置是否正确,当列表空的时候返回nil。list.deleteFirst
34、160; >> Song: title1-artist1 (1) list.deleteFirst >> Song: title2-artist2 (2) list.deleteLast &
35、#160; >> Song: title4-artist4 (4) list.deleteLast >> Song: title3-artist3 (3) list.deleteLast&
36、#160; >> nil很好,下一个方法是,通过索引来访问元素。如果索引是整数(在这里我们用Object#kind of?来检查),那么返回该位置的元素。class SongList def (key) if key.kind_of?(Integer)
37、160;songskey else # . end endend再来测试一下list0 >> Song: title1-artist1 (1) list2
38、; >> Song: title3-artist3 (3) list9 >>
39、0; nil 现在需要添加通过歌曲标题来索引的功能,这要求扫描整个歌曲列表,检查每一首歌曲标题。在这之前,我们需要先来熟悉一下Ruby最简洁的一个特性:迭代器。代码块和迭代器(译者注:关于代码块,Ruby的作者在2003年9月的访谈中提及到,参看注3)下一个问题是实现SongList的方法,它用一个字符串来搜索一首歌曲的标题,看起来很简单:我们有一个歌曲的列表,遍历整个列表依次匹配每一首歌曲的标题即可。class SongList def (key) if key.
40、kind_of?(Integer) return songskey else for i in 0.songs.length return songsi if key =
41、 end end return nil endend它能工作,并且看上去也很熟悉,一个for循环遍历整个数组,能不能做的更自然些呢?事实上有更自然的方法。这里我们的for循环要求数组的一些私有信息,它要求数组的长度,然后按序匹配每一个值。为什么不要求数组仅提供一个对其每个元素的检测呢?这正是Array的find方法所做的。class SongList def (key)
42、160; if key.kind_of?(Integer) result = songskey else result = songs.find |aSong| key = aS end
43、60;return result endend我们还可以把if用作语句修饰符来缩短句子。class SongList def (key) return songskey if key.kind_of?(Integer) return songs.find |aSong| aS = key e
44、ndendfind方法是一个迭代器,一个重复调用代码块的方法。迭代器和代码块是Ruby最有趣的特性中的两个,所以我们花些时间来研究一下它们(这个过程中我们也可以看到我们的方法是如何真正工作的)。实现迭代器一个Ruby迭代器不过是一个简单的方法,它可以调用代码块。初看Ruby的代码块很像是C、Java或者Perl中的代码块,不幸的是,不是这样-Ruby的代码块是一种组合语句的途径但不是一种方便的途径。首先,一个代码块出现在一个方法调用的代码附近,代码块和方法的最后一个参数处在同一行;第二,代码块中的代码并不被执行,而是Ruby保存代码块出现时的上下文关系(局部变量、当前对象等等),然后进入到方法
45、中。这正是魅力所在。在方法中,代码块通过yield语句被调用,这使得代码块就好像是一个方法一样。当yield执行时,它调用代码块中的代码。代码块退出时,控制就马上被返回给yield后面的语句。程序设计语言的粉丝会很高兴看到yield关键字被采用在这里,它模拟了Liskov的CLU语言中的yield功能,这是一个有着20年历史的语言,仍然保留着远没有被非CLU语言所广泛采用的许多特性。(注2)我们看一个简单的例子:def threeTimes yield yield yieldendthreeTimes
46、60;puts "Hello" 结果: HelloHelloHello(译者注:这里的代码块是puts "Hello"这条语句,而方法是threeTimes,当语句执行到threeTimes puts "Hello"这行时,puts "Hello"不是马上被执行,而是由Ruby先保存puts "Hello"这条语句和threeTimes的关系,然后进入到threeTimes中,遇到第一条yie
47、ld语句的时候调用并执行puts "Hello"语句,执行完毕后返回到第一条yield语句的后面也就是第二条yield语句,直到三条yield语句都被执行了,才返回到threeTimesputs "Hello"这条语句后)代码块(大括号括住的部分)和threeTimes方法的调用相关联,在这个方法中,yield被调用了三次,每次它都调用代码块中的代码,然后打出一个愉快的问候。不过,代码块最有趣的地方是你可以给它传递参数还可以取得它的返回值。比如我们写一个简单的函数来返回一个特定值的菲波纳奇数列。菲波纳奇数列是一个整数序列,以两个1开始
48、,随后的每一项都是它前面两项之和,这个序列常常用在排序算法和分析自然现象当中。def fibUpTo(max) i1, i2 = 1, 1 # 并行赋值 while i1 <= max yield i1 i1, i2 = i2,
49、60;i1+i2 endendfibUpTo(1000) |f| print f, " " 结果:1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987在这个例子中,yield有一个参数,这个值被传递给关联的代码块。代码块的定义中,参数出现在两个竖条之间。这个例子中,变量f接受了yie
50、ld传递来的值,所以代码块就连续地显示出数列的数了。(这个例子也展示了并行赋值,75页还有更多介绍。)尽管经常给代码块传递一个参数,但不是必须的,代码块可以有很多参数。如果代码块的参数数目和yield传递来的不一样时该怎么办呢?这和并行赋值下的规则是一致的(些小的差别是:如果代码块只有一个参数那么yield传递来的多个参数会转换成一个数组)。代码块的参数也可能是已存在的局部变量,如果是这样的话,代码块执行完毕后变量的新值会被保留下来。这可能会导致不可预测的结果,但是她也带来一个性能上的优势,变量已经存在了。(关于这方面的更多信息和其它的“gotchas”参看127页,更多的性能信息在128页。
51、)代码块也可以给方法返回值,在代码块中最后被计算的表达式的值被作为yield的值回传给方法。Array类中的find方法就是如此。find方法实际上在Enumerable模块中被定义,该模块混合在Array中。它的实现就像下面这样。class Array def find for i in 0.size value =
52、;selfi return value if yield(value) end return nil end end1, 3, 5, 7, 9.find |v| v*v >
53、;30 >> 7 数组中的元素连续地传递给关联的代码块,如果代码块返回true方法就返回对应的元素,如果没有匹配的元素方法就返回nil。例子显示了使用这种迭代器的好处。Array类完成了它所能完成的,访问数组元素,撇开程序代码而专注于自身特定的需求(在这个例子中,找到符合数学标准的元素)。许多迭代器通用于大多数类型的ruby集合,我们已经见识过find,另外两个是each和collect
54、,each可能是最简单的迭代器-它所做的就是连续返回集合中的元素。 1, 3, 5 .each |i| puts i 结果:135each迭代器在ruby中有着特殊的地位,在85页我们描述了它怎样用在最基本的for循环中,在102页还会看到怎样定义一个each方法以便在你的类中自由地添加更多的功能。另一个通用的迭代器是collect,它从集合中取得每一个元素然后传递给代码块。代码块返回的结果用来生成一个新的数组,例如:"H", "A",
55、60;"L".collect |x| x.succ >> "I", "B", "M" Ruby与C+和Java的比较有必要用一个段落比较一下Ruby在迭代器方面和C+与Java的不同。迭代器就是一个简单的方法,和其他方法一样,当它产生一个新值的时候就调用yield。使用迭代器是很简
56、单的,把一个代码块和方法相关联即可,不需要Java和C+那样生成帮助类来承载迭代器的状态,在这方面,也在其它的很多方面,ruby是一种透明的语言,你写一个ruby程序,只要集中精力于完成工作,而不必搭建用来支持语言本身的脚手架。迭代器在访问已经存在数据的数组和哈希时没有限制,就像我们在菲波纳奇例子中看到的,一个迭代器能返回传来的值。这种能力被用在Ruby的输入/输出类中,它们实现了一个迭代器的界面来返回一个I/O流中连续的行(或者字节)。f = File.open("testfile")f.each do |line|
57、160;print lineendf.closeproduces: This is line oneThis is line twoThis is line threeAnd so on.看一下另一种迭代器的实现,在Smalltalk语言中也支持集合的迭代器,如果你要求Smalltalk程序员求数组元素的和,他们会像这样来使用inject函数:sumOfValues
58、60; "Smalltalk method" self values inject: 0 into: :sum :element | sum +
59、160;element valueinject是这样工作的,当关联的代码块第一次被调用时,sum被赋给inject的参数值(在这里是0),element取数组第一个元素。第二次和以后调用到代码块时,sum被赋给上次调用代码块时返回的值,这样sum就跑完了全程,inject最终的结果是代码块最后被调用的值。ruby没有inject方法,但是很容易实现。这个例子中,我们把它增加到Array类中。在100页我们可以看到如何使它更通用。class Array def inject(n)
60、60; each |value| n = yield(n, value) n end def sum inject(0) |n, value| n + value &
61、#160; end def product inject(1) |n, value| n * value end end 1, 2, 3, 4, 5 .sum
62、0; >> 15 1, 2, 3, 4, 5 .product >> 120 尽管迭代器经常使用在代码块上,但也可以用在其它方面,我们来看一下。事务代码块代码块可以定义为在某种事务控制下运行的一系列代码,举例来说,你经常要打开一个文件,对文件的内容进
63、行一些处理,然后确保在使用完毕后关闭了它。你可以使用常规的代码来完成这些,不过我们这里要表现的是如何让文件自己负责关闭它自己,我们要用代码块来做。看下面这个粗略的实现(忽略了错误处理):class File def File.openAndProcess(*args) f = File.open(*args) yield f f.close() endendFile
64、.openAndProcess("testfile", "r") do |aFile| print while aFile.getsend 结果:This is line oneThis is line twoThis is line threeAnd so on. 这个小例子展示了一些技巧。OpenAndProcess方法是一个类方法,这意味着它不依赖于
65、任何特定的File对象就可以被调用。我们希望它能像常规的File.open方法那样获得同样的参数,但我们又确实不想关心这些参数具体是什么,所以,我们指定参数为*args,意为“把实参放到数组中传递给方法”,然后我们调用File.open,把*args作为一个参数传递给它。数组被分成单独的参数。来来回回的结果是OpenAndProcess透明地传递它所接收的参数给File.open了。一旦文件被打开,OpenAndProcess调用yield,把打开的文件对象传递给代码块。当代码块返回后,文件被关闭,这样,关闭的责任就从文件对象的使用者转移给文件本身了。最后,这个例子使用do.end来定义代码块
66、。使用这种形式和使用大括号的形式之间的区别在于,do.end定义要低级于.,这在234页会有详细讨论。这种文件管理自己的生命周期的技巧非常有用,所以Ruby的File类直接就支持了这种特性。如果File.open有一个关联的代码块,那么这个代码块将会随一个文件对象而被调用,到代码块终止时文件对象被关闭。这很有趣,意味着File.open有两种不同的行为。当调用它的时候有代码块,它执行代码块然后关闭文件;如果没有代码块,它返回文件对象。这种特性因为Kernel:block_given?的存在而成为可能,如果当前方法有关联的代码块,返回true。使用它,你可以像下面这样来实现File.open(又
67、一次忽略了错误处理):class File def File.myOpen(*args) aFile = File.new(*args) # 如果有代码块,传递文件,然后等代码块返回时关闭文件 if block_given? yield aFile
68、160; aFile.close aFile = nil end return aFile endend代码块可以转换为闭包让我们回到我们的点唱机,还记得它吗。我们要用一些代码来完成用户界面,比如人们用来选择歌曲和控制点唱机的按钮。我们需要把这些按钮和某些行为联系起来:按下STOP按钮,歌曲停止。Ruby的代码块用来完成这个就最方便不过了。我们先假设人们已经用硬件实现了一个
69、Ruby的扩展,它给了我们一个基本的按钮类(在169页我们讨论扩展Ruby)bStart = Button.new("Start")bPause = Button.new("Pause")# .当用户按下其中一个按钮时发生了什么?在Button类中,硬件调用一个回调方法,buttonPressed。为这些按钮增加功能的显见的途径就是创建Button类的子类,在每一个子类中实现它们自己的buttonPressed方法。class StartButton < Button&
70、#160; def initialize super("Start") # 调用Button的initialize end def buttonPressed # 开始播放. endendbStart = StartButton.new这里有两个问题,第一个,这会导致出
71、现大量的子类。如果Button的界面改变了,我们需要非常多的维护。第二,按钮按下时执行的操作处在一个错误的级别,它们不应该是按钮的特性,而应该是点唱机的特性,我们用块来修改这些错误。class JukeboxButton < Button def initialize(label, &action) super(label) action = action end &
72、#160;def buttonPressed action.call(self) endendbStart = JukeboxButton.new("Start") songList.start bPause = JukeboxButton.new("Pause") songList.pause 这里关键是JukeboxButton#initialize的第二个参数。如果一个
73、方法的最后一个参数有&前缀,Ruby就会在方法被调用时查找一个代码块,这个代码块被转变成一个Proc类的对象并且分配成参数。你可以把这个参数当作任意的变量。在我们的例子中,我们把它赋给实例变量action。当回调方法buttonPressed被调用时,我们使用对象的proc#call方法来调用块。(译者注:如果你感到不好理解,我来解释一下:看这一句bStart = JukeboxButton.new("Start") songList.start ,当bStart引用的对象实例化时,调用JukeboxButton#
74、initialize,这时 songList.start 被作为第二个参数传递给JukeboxButton#initialize,在JukeboxButton#initialize中,实例变量action被赋值成 songList.start ,等到JukeboxButton#buttonPressed被执行时,action通过proc#call来调用块 songList.start )创建一个Proc对象需要很多工作吗?有趣的是,它不过比一堆代码多一点点东西而已。和一个代码块关联的(因此就是一个Proc对象)就是这个代码块被定义时的
75、上下文关系:self的值,方法,变量,常量等的视图,Ruby充满魔力的地方就是代码块可以一直使用所有的这些原始视图信息,即使定义它时的环境已经消失,在其它的语言中,这种能力被称为闭包。让我们看一个例子,这个例子使用了proc方法,它把一个代码块转换成一个Proc对象。def nTimes(aThing) return proc |n| aThing * n end p1 = nTimes(2
76、3) p1.call(3) >> 69 p1.call(4) >> 92 p2 = nTimes("Hello ") p2.call(3)
77、0; >> "Hello Hello Hello " nTimes方法返回一个Proc对象,它引用了方法的参数aThing,尽管参数已经不在代码块被调用的时间范围,但仍然保留了对代码块的访问能力。注2:谈到Liskov,想要简单介绍一下OOP的历史:面向对象技术鼻祖是挪威人克里斯坦.尼加德(Kristen Nygaard),他在1962年发明了颇具传奇色彩的Simula语言,并在该语言中
78、创造出了许多沿用至今的面向对象概念。1970年前后,阿兰.凯(Alan Kay)与他的同事们在施乐(Xerox)公司发明了优雅的、纯粹的Smalltalk语言。Smalltalk提出了许多新概念,如消息和继承机制等。同样在1970年代,芭芭拉.莉丝柯夫(Barbara Liskov)使抽象数据结构的理论和实现获得了重大进展。她在LISP语言的基础上,通过增加面向对象机制,发明了著名的CLU语言,该语言支持隐藏内部数据的设计方法。此外,1980年代初期诞生的Ada语言也为面向对象技术贡献了泛型和包等重要概念。在Ada语言的基础上,格雷迪.布彻(Grady Booch
79、)还首次提出了"面向对象设计"这一现代软件工程术语。Ruby汲取了许多OOP语言的精髓,所以在这个学习的旅程中,如果碰到了你的熟人,大可开怀一笑,万不可学那小气之人,以为某种语言是自己的独家兵器,别人使不得的样子。注2由夏克补充注3:在这里我们把Ruby作者关于代码块和闭包的谈话全文粘贴:使用 Blocks 做循环抽象Bill Venners: Ruby 支持 blocks 和 Closure 结构. 什么是 blocks 和 Closure,他
80、们如何使用? Yukihiro Matsumoto:Blocks 基本上就是匿名函数。你可能熟悉诸如Lisp 或 Python等其他语言中的Lambda 函数。 你可以向另外一个函数传递一个匿名函数,这个函数可以调用这个被传递过来的匿名函数。例如, 函数可以通过一次传递给匿名函数一个元素来执行循环迭代。在那些可以将函数当作第一类型的编程语言中,这是个通常的方式,称为高排序函数样式。 Lisp 可以这样,Python 也是如此,甚至就连C 也可以通过函数指针实现这点。很多其他语
81、言也可以做这样的编程。在 Ruby中,不同之处只是在高排序函数语法风格上有所不同。在其他语言中,你必须显示的指出一个函数可以接受另外一个函数作为参数。但是在Ruby 中,任何方法都可以 Block 作为一个隐性参数被调用。在方法中,你可以使用 yield关键字和一个值来调用 block. Bill Venners: Block 的好处是什么? Yukihiro Matsumoto:基本上,Block 是被设计来做循环迭代抽象的。Block 最基本的使用就是
82、让你以自己的方式定义如何循环迭代。 例如,如果你有一个列表,序列,矢量组或者数组,你可以通过使用标准库中提供的方法来实现向前循环迭代,但是如果你想从后往前实现循环迭代呢?如果使用 C 语言,你得先设置四件事情:一个索引,一个起始值,一个结束条件和一个递增变量。这种方式不好,因为它暴露了列表的内部实现方法,我们希望能够隐藏内部逻辑,通过使用 Block 我们可以将内部循环迭代的方式隐藏在一个方法或者函数中。比如,调用list.reverse_each,你可以对一个列表实现一个反向的循环迭代,而不需要知道列表内部是如何实现的。 Bill&
83、#160;Venners: 就是说,我传递一个 Block 结构,这个 Block 中的代码可对循环迭代中每个元素做任何事情,至于如何反向遍历就取决于List 本身了。换句话说,我就是把原本在 C 语言 Loop 循环中写的那些代码作为一个 Block 来传递。 Yukihiro Matsumoto:对,这意味着你可以定义许多迭代的方式。你可以提供一种向前循环迭代的方式,一种向后循环迭代的方式,等等。这全取决于你了。C#也有迭代器,但是它对于每个类只有一个迭代器。在 Ruby 中你可以拥有任意数量
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 泔水回收制式合同范本
- 项目挂靠劳务合同范本
- 瓷砖店面转让合同范本
- 过期牛奶采购合同范本
- 甲方工程合同解除协议
- 物流仓储配送合同范本
- 通信公司股东合同范本
- 门面转租租赁合同范本
- 软件销售解除合同范本
- 解除合同协议申请范本
- 毒品成瘾原因课件
- 金太阳福建省2025-2026学年高三上学期9月开学联考数学试卷
- 2025年公需课《人工智能赋能制造业高质量发展》试题及答案
- 2025-2026学年外研版七年级英语上册(全册)教学设计(附目录)
- 2025-2026学年人美版(2024)小学美术三年级上册教学计划及进度表
- 连锁药店考勤管理制度
- 安全防范系统升级和服务协议
- 2025年吉林省教育系统后备干部考试题及答案
- 富士康自动化培训知识课件
- 北宋名臣滕元发:才情、功绩与时代映照下的复合型士大夫
- 2025年中小学生宪法知识竞赛试题及答案
评论
0/150
提交评论