© Shine's Blog

Powered by LOFTER

[Ruby 中文教程 By Chris Pine] 7.阵列与迭代器

翻译:阳光

原文来自 <https://pine.fm/LearnToProgram/?Chapter=07> 

 

 

7.阵列与迭代器

 

让我们写一个程序,它能让我们输入任意多的单词(每个单词一行,一直持续下去直到我们在一个空行上敲击回车),然后把我们输入的单次按字母表顺序返回给我们,如何?

 

好……我们首先要——额……嗯……嗯嗯……好吧,我们也许可以——这个……

 

你知道,我觉得我们并不能做到这个。我们需要一种方式,能够储存位置数量的单次,然后检测它们的整体动向以避免它与别的变量混在一起。我们需要一个类似于列表的东西来储存它们。我们需要array。

 

array就是你电脑中的一个列表。其中的每个位置就像一个变量:你可以看到某个位置指向了哪个object,并且你能让它指向一个别的object。让我们来看一些array:

 

[]

[5]

['Hello','Goodbye']

 

flavor = 'vanilla' #当然这不是一个array

[89.9, flavor, [true, flase]] #但这个是array

 

首先我们有一个空的array,然后是一个只保留了单个数字的array,然后是一个包含了两个字符串的array。然后我们做了一个简单的赋值,并有了一个包含有三个object的array,其中最后一个object是个array:[true,flase]。记住,变量不是object,所以我们最后的那个array实际上是指向了一个浮点,一个字符串,和一个array(就算我们之后在程序中把flavor指向了其他东西,也不会改变这个array)。

 

为了帮助我们在array中找到某个特定object,每个位置都有一个编号。程序员(以及大多数数学家)喜欢从0开始计数,所以array中的第一个位置就是位置0。

以下是我们如何在一个array中抽调object:

 

输入:

names = ['Ada','Belle', 'Chris']

 

puts names

puts

puts names[0]

puts names[1]

puts names[2]

putsnames[3] #这个超出范围了

 

输出:

Ada

Belle

Chris

 

Ada

Belle

Chris

nil

 

puts names输出的是array中的每个名字。然后我们用puts names[0]来输出array中的第一个名字,putsnames[1]来输出第二个。我知道这看起来肯定很奇怪,但你会习惯它的。你只需要在思考的时候从0开始计数,不要再用“第一个”和“第二个”这种词来计数。如果你要吃5道菜,不要说第一道菜,而说菜0(在你的脑海中,用菜[0]思考)。你右手有五个指头,它们的序号就是0,1,2,3和4。我妻子和我会玩杂耍棒,当我们能同时玩六个杂耍棒时,我们在使用杂耍棒0-5号。过了几个月,我们希望能再加一个6号(因此实际我们在同时使用七个)。你用“第0个”来计数的时候就能了解我们到底有几个杂耍棒了。

最后,我们试着输入putsnames[3],只为了看看会发生什么。你以为会出现错误么?如我们之前看到的,有时你问你的计算机一个问题,而这个问题根本没什么意义(至少对计算机来说是这样),这时你会得到一个错误。但有时,你问一个问题,这个问题的答案是“无”。第三个位置有什么?什么都没有。什么是names[3]?nil:这是Ruby表示“无”的方式。nil是一个特殊的object来表示“不是其他任何的object”。

 

现在,我说过你的array中的位置就像是变量,这表示你也可以给它们赋值。如果你想知道如何用代码表示这其中到底发生了什么,可以这样表示:

输入:

other_peeps= []

 

other_peeps[3] ='beebee Meaner'

other_peeps[0] ='Ah-ha'

ohter_peeps[1] ='Seedee'

other_peeps[0] ='beebee Ah-ha'

 

puts other_peeps

输出:

beebee Ah-ha

Seedee

nil

beebee Meaner

 

如你所见,你不需要以某种顺序来进行赋值,你留下的空位会默认用nil来代替。

 

如果这些有趣的位置编号冲你来了,不要害怕!通常,我们能用很多array的method来避开它们,比如each。

 

7.1 each

 

each能让我们对array中的所有object都进行某种处理(不管我们想要什么)。(虽然它看起来很奇怪,而且能让你抓狂,所以勇敢点)。

 

例如,如果我们想要对下面这个array中的每种语言都说点什么,我们可能会这么写:

输入:

languages =['English', 'Norwegian', 'Ruby']

 

languages.each do|lang|

puts'I love ' + lang + '!'

puts'Don\'t you?'

end

 

puts 'And let\'shear it for Java!'

puts '<cricketschirp in the distance>'

输出:

I love English!

Don't you?

I love Norwegian!

Don't you?

I love Ruby!

Don't you?

And let's hear itfor Java!

<crickets chirpin the distance>

 

发生了什么?(除了Java被完败,嘿嘿。)好,我们能够对array中的每一个object都进行处理,而不需要任何数字,这很好。在lang旁边有奇怪的竖线,我会谈到这个。但首先,先确保你明白了这些代码的意思(如果不需要知道为什么它表示这些意思的话),让我们把它翻译过来:对languages中的每一个object,将这个object的值赋予变量lang,然后做所有我告诉你要做的事情,直到结束。

 

我们用了do和end来表示一块代码block,在这种情况下,我们把这段代码发送到了each,并说“这就是我想让你对array中的所有object进行的处理”。Block很好,但有一点难,我们在第13章《Block和Procs》之前都不会真的去讨论它。但在那之前,我们还是会使用它们。我们只是不会过多地去讲它。

 

现在我们来谈谈竖线,就像|lang|这样。它看起来很奇怪,但是观念却很简单:lang是一个变量,each会把它赋值给array中的object。否则我们怎么找到字符串'English'呢?(好吧,也许能用languages[0],但这里的重点是避免位置序号来干扰我们)。竖线不会对lang做任何处理,它们只是让each明白要赋值给object的变量到底是什么。

 

你可能自己会想:“这跟我之前学过的loop很像”。对,很相像。一个重要的不同就是,each本身就是一个method。而while、end(还有do、if、else这些其他关键词)都不是method,它们是Ruby的基础部分,就像=与括号一样,他们有点像英语中的标点符号。

 

但对each不是这样,each只是另一个arry的method。像each这样“干的事像”loop的method通常称为iterator迭代子。

 

关于迭代子要注意的一点是它们总是跟着一个block,也就是处于do和end之间的一段代码。另一方面,while和if从来不会有个do跟在后面。

 

这是另一个可爱的迭代子,但这回不是一个array的method了:

输入:

#Go-go-gadget-integer-method...

3.times do

puts'Hip-Hip-Hooray!'

end

输出:

Hip-Hip-Hooray!

Hip-Hip-Hooray!

Hip-Hip-Hooray!

这是一个ingeger(整体)method。现在你不得不告诉我这是你见过的最可爱的代码!正如之前展示过的,还是那个可爱的程序:

输入:

2.times do

puts'...you can say that again...'

end

输出:

...you can say thatagain...

...you can say thatagain...

 

7.2更多的array method

 

我们已经学了each,但是还有许多其他的array method,几乎跟字符串一样多。事实上,他们之中的一些(比如像length、reverse、+、*这种)跟字符串是一样的,只不过它们针对的是array中的某个位置,而不是字符串中的某个字母。其他的,像last和join这种,则是array中特有的。还有一些其他的,比如push和pop,则会改变array。正如字符串method一样,我们不用全部记住它们,只要记住在哪里找到它们就行了(也就是这儿)。

 

让我们来看看to_s和join。join跟to_s很像,只不过它是在array的各个object之间插入了一个字符串。实际上,我根本想不起来曾经在arry身上用过to_s。我总是用puts或者join。但是我知道你特别想知道用to_s会是什么结果,所以看一下下面的例子:

输入:

foods=['artichoke','brioche', 'caramel']

 

puts foods

puts

puts foods.to_s

puts

puts foods.join(',')

puts

puts foods.join(' :)') + ' 8)'

 

200.times do

puts[]

end

 

两百次?!没糖会剩下给我了!

输出:

artichoke

brioche

caramel

 

artichokebriochecaramel

 

artichoke,brioche,caramel

 

artichoke :) brioche:) caramel 8)

 

呦!还好puts对待array时跟其他object不一样,要是它真把某个东西写了200次的话会很无聊的。在处理array的时候,puts会对array中的每一个object都应用一个puts。这也是为什么对一个空的array应用200次puts不会有任何结果,这个array不包含任何东西,所以也就没有东西来输出了。对空的东西做200次处理依然会得到空(除非你是在玩一个角色扮演游戏,而你刚刚升级!)。试试输出一个包含了其他array的array,它跟你想象的一样吗?

 

现在让我们看一下push和pop,还有last。push和pop有点对立的意思,就像+跟-一样。push在array的最后加上一个object,pop则是移除最后的object(并告诉你它移除的是什么)。last与pop类似,它告诉你array最后一个object是什么,但不会改变array。再说一次,push和pop会改变array:

输入:

favorites = []

favorites.push'raindrops on roses'

favorites.push'whiskey on kettens'

 

puts favorites[0]

puts favorites.last

putsfavorites.length

 

puts favorites.pop

puts favorites

putsfavorites.length

输出:

raindrops on roses

whiskey on kittens

2

whiskey on kittens

raindrops on roses

1

 

7.3一些练习

 

·建立一个array并让它按字母表排列。写一个在本章开始时讨论过的那个程序,会让我们输入任意多的单词(一个单词一行,知道我们在某个空行敲击乐回车键),然后将我们输入的单词以字母表顺序排列反馈给我们。确保完整地测试你的程序,例如,在某个空行敲击回车总是会退出程序吗?即使是在第一行?第二行?提示:有一个绝妙的array method叫做sort,它能给把你的array自动按字母表顺序排列,用它试试!

·再试一次表格程序。把之前让你写的那个表格程序再写一次。程序开始运行的时候,它包含一个array,这个array包含着所有你的表格需要的东西(章节名称,页码,等等)。然后以漂亮的格式把这个表格输出出来。


发表于2014-07-13.