14.md 12.3 KB
Newer Older
W
init  
wizardforcel 已提交
1 2
## 5.循环

W
wizardforcel 已提交
3
有时你可能需要执行一些重复性的任务,比如说我想编写一个火箭倒计时程序,我想创建一个自动为火箭倒数的机器人,当计数结束时会说“ Blast Off”,让我们写一个 看看
W
init  
wizardforcel 已提交
4

W
wizardforcel 已提交
5
```rb
W
init  
wizardforcel 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# count_down.rb

# Zigor tells about itself
puts "Hello, I am Zigor...."
puts "I count down for rockets"
# Count down starts
puts 10
p 9 # p is a short form for puts
p 8
p 7
p 6
p 5
p 4
p 3
p 2
p 1
p "Blast Off!"
```

W
wizardforcel 已提交
25
好吧,希望你能理解上面的程序。 我想说明的是,`p``puts`的一种简短形式,不用写`puts`就能使用`p`并得到相同的结果 <sup class="footnote">[ [16 [](#_footnotedef_16 "View footnote.") ]</sup> 。 上面的程序在运行时显示以下内容
W
init  
wizardforcel 已提交
26

W
wizardforcel 已提交
27
```rb
W
init  
wizardforcel 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
Hello, I am Zigor....
I count down for rockets
10
9
8
7
6
5
4
3
2
1
"Blast Off!"
```

如此完美的执行,但是我们可以提高代码的效率,我们很快就会看到

### 5.1。 向下

W
wizardforcel 已提交
47
在你的文本编辑器中,键入以下程序
W
init  
wizardforcel 已提交
48

W
wizardforcel 已提交
49
```rb
W
init  
wizardforcel 已提交
50 51 52 53 54 55 56 57 58 59 60 61
# count_down_1.rb

# Zigor tells about itself
puts "Hello, I am Zigor...."
puts "I count down for rockets"
# Count down starts
10.downto 1 do |num|
  p num
end
p "Blast Off!"
```

W
wizardforcel 已提交
62
运行它,看看。 好了,你的程序现在使用了更少的代码,但是却产生了相同的结果!
W
init  
wizardforcel 已提交
63

W
wizardforcel 已提交
64
注意事情`10.downto 1`,该语句使 Zigor 从 10 倒数到 1,而在倒数的同时,你可以使用倒数值做一些事情,还可以在循环块中放入一些代码。 循环以`do`开始,并在遇到`end`命令时结束。 你输入的任何代码都应介于`do``block` <sup class="footnote">[ [17](#_footnotedef_17 "View footnote.") ]</sup> 之间
W
init  
wizardforcel 已提交
65

W
wizardforcel 已提交
66
```rb
W
init  
wizardforcel 已提交
67 68 69 70 71 72 73
10.downto 1 do
  # do some thing! Anything!!
end
```

因此,可以在`do``end`(技术上称为块)之间放置代码以打印倒数。 首先如何获得号码? 我们将在名为`num`的变量中获取它,因此我们将代码重写为如下所示

W
wizardforcel 已提交
74
```rb
W
init  
wizardforcel 已提交
75 76 77 78 79 80 81
10.downto 1 do |num|
  # put the printing stuff here
end
```

上面请注意,`num``|``|`包围。 现在我们需要做的就是打印它,所以我们只打印如下图所示

W
wizardforcel 已提交
82
```rb
W
init  
wizardforcel 已提交
83 84 85 86 87 88 89
10.downto 1 do |num|
  p num
end
```

### 5.2。 次

W
wizardforcel 已提交
90
times 是一个非常简单的循环,如果你想让代码执行 N 次,则将代码放入其中。 现在让我们看看 Zigor 知道什么
W
init  
wizardforcel 已提交
91

W
wizardforcel 已提交
92
```rb
W
init  
wizardforcel 已提交
93 94 95 96 97 98 99 100 101 102 103
# times.rb

puts "Hi, I am Zigor"
puts "I am going to tell what I know"
7.times{
  puts "I know something"
}
```

好了,执行后程序会打印以下内容

W
wizardforcel 已提交
104
```rb
W
init  
wizardforcel 已提交
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
Hi, I am Zigor
I am going to tell what I know
I know something
I know something
I know something
I know something
I know something
I know something
I know something
```

Zigor 告诉它它知道七次。

好的,我们已经在程序中进行了更改,这次我们将打印 count 变量,在下面键入程序并执行

W
wizardforcel 已提交
120
```rb
W
init  
wizardforcel 已提交
121 122 123 124 125 126 127 128 129 130 131
# times_1.rb

puts "Hi, I am Zigor"
puts "I am going to tell what I know"
7.times{ |a|
  puts "#{a}. I know something"
}
```

这就是你得到的结果

W
wizardforcel 已提交
132
```rb
W
init  
wizardforcel 已提交
133 134 135 136 137 138 139 140 141 142 143
Hi, I am Zigor
I am going to tell what I know
0\. I know something
1\. I know something
2\. I know something
3\. I know something
4\. I know something
5\. I know something
6\. I know something
```

W
wizardforcel 已提交
144
为什么从零数到六而不是一七呢? 好吧,如果一切按你的意愿进行,那么就不需要像你和我这样的程序员了,所以不要打扰。 请注意,在这些程序中,我们使用`{``}`,而不是 do and end。 好吧,Ruby 鼓励使用不同的编程风格。
W
init  
wizardforcel 已提交
145 146 147 148 149

### 5.3。 取决于

`upto`将从某个数字数到另一个数字。 就像倒过来一样。 输入以下程序并执行

W
wizardforcel 已提交
150
```rb
W
init  
wizardforcel 已提交
151 152 153 154 155 156 157 158 159 160
# upto.rb

# upto is downto in reverse
17.upto 23 do |i|
  print "#{i}, "
end
```

这是输出的样子

W
wizardforcel 已提交
161
```rb
W
init  
wizardforcel 已提交
162 163 164 165 166 167 168
17, 18, 19, 20, 21, 22, 23,
```

### 5.4。 步

可以将`step`循环视为`upto``downto`的组合,将它们打包在一起,执行以下代码

W
wizardforcel 已提交
169
```rb
W
init  
wizardforcel 已提交
170 171 172 173 174 175 176 177 178
# step_1.rb
# explains step function
1.step 10 do |i|
  print "#{i}, "
end
```

这就是结果。 这与`upto`非常相似! 你没看到!!

W
wizardforcel 已提交
179
```rb
W
init  
wizardforcel 已提交
180 181 182 183 184
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
```

现在,如下所示修改程序并将其保存为其他名称

W
wizardforcel 已提交
185
```rb
W
init  
wizardforcel 已提交
186 187 188 189 190 191 192 193 194
# step_2.rb
# explains step function
10.step 1 do |i|
  print "#{i}, "
end
```

执行该程序时不产生任何输出。 我们做错了什么? 如下所示修改程序并运行

W
wizardforcel 已提交
195
```rb
W
init  
wizardforcel 已提交
196 197 198 199 200 201 202 203 204 205
# step_3.rb
# explains step function
# this time its stepping down
10.step 1, -1 do |i|
  print "#{i}, "
end
```

好了,这是程序的输出

W
wizardforcel 已提交
206
```rb
W
init  
wizardforcel 已提交
207 208 209 210 211
10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
```

接下来会发生什么? 步骤接收三个输入,请考虑以下代码

W
wizardforcel 已提交
212
```rb
W
init  
wizardforcel 已提交
213 214 215 216 217 218 219
10.step 1, -1
```

第一个是将调用步骤的号码作为初始号码,在上述情况下为`10`。 在这种情况下,下一个是结束号,即`1`,即此函数从 10 到 1 计数,在这种情况下,我们必须递减,因此计数必须以`-1`为步长。

我可以修改同一程序,以 10 到 1 的方式打印偶数,如下所示

W
wizardforcel 已提交
220
```rb
W
init  
wizardforcel 已提交
221 222 223 224 225 226 227 228 229 230 231
# step_4.rb
# explains step function
# this time its stepping down
p "Even numbers between 10 and 1:"
10.step 1, -2 do |i|
  print "#{i}, "
end
```

该程序输出以下输出

W
wizardforcel 已提交
232
```rb
W
init  
wizardforcel 已提交
233 234 235 236 237 238
Even numbers between 10 and 1:
10, 8, 6, 4, 2,
```

现在让我们尝试一个程序,该程序将按升序打印从 1 到 10 的偶数

W
wizardforcel 已提交
239
```rb
W
init  
wizardforcel 已提交
240 241 242 243 244 245 246 247 248 249 250
# step_4.rb
# explains step function
# this time its stepping upby two counts each loop
p "Even numbers between 1 and 10:"
2.step 10, 2 do |i|
  print "#{i}, "
end
```

输出量

W
wizardforcel 已提交
251
```rb
W
init  
wizardforcel 已提交
252 253 254 255 256 257 258 259 260 261
Even numbers between 1 and 10:
2, 4, 6, 8, 10,
```

在上面的代码中,从 2 开始,我们将在 10 结束,然后以 2 的步长跳过每个循环。在循环内部,我们仅打印在变量`i`中捕获的迭代值。

### 5.5。 而

尽管 <sup class="footnote">[ [18](#_footnotedef_18 "View footnote.") ]</sup> 循环是一个循环,直到满足条件为止。 阅读下面的代码

W
wizardforcel 已提交
262
```rb
W
init  
wizardforcel 已提交
263 264 265 266 267 268 269 270 271 272
# while.rb
i=1
while i<=10 do
  print "#{i}, "
  i+=1
end
```

执行后,将产生以下输出。

W
wizardforcel 已提交
273
```rb
W
init  
wizardforcel 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
```

现在让我们看看 while 循环是如何工作的。 While 循环通常包含四个重要部分

1.  初始化

2.  条件检查

3.  圈体

4.  更新中

**初始化**

参见语句`i=1`,在这里我们初始化名为`i`的变量并将其设置为`1`值。

**条件检查**

参见语句`while i⇐10`,在该语句中,我们指定我们开始一个 while 循环,每次迭代时,该 while 循环都会检查`i`的值,如果其小于或等于`10`,则循环主体会盲目地 被执行。

**环体**

注意程序中的`do``end``do`代表循环代码块的开始,`end`代表循环代码块的结束。 在它们之间,我们有一些陈述,我们将很快讨论。 声明之一是打印`i`的值,该值由`print "#{i}, "`完成

**更新**

W
wizardforcel 已提交
301
假设我们忘了在循环体中包含`i+=1`,在每次迭代结束时,`i`的值将始终保持为 1,而我将始终保持小于 10,因此循环将被执行无数次,并且 将打印无限的 1。 实际上,你的程序将崩溃,并可能导致不良后果。 为了避免这种情况,我们必须包含更新声明。 在这里,我们放置了`i+=1`,它在每次迭代继续时将`i`的值增加一,这确保`i⇐10`在某个阶段变为`false`,因此循环停止执行 &lt;sup class="footnote"&gt;[ [19 [](#_footnotedef_19 "View footnote.") ]&lt;/sup&gt;
W
init  
wizardforcel 已提交
302 303 304 305 306 307 308

因此,我们看到,为了使循环以理想的方式工作,我们需要使这四个部分交响。

### 5.6。 直到

`while`循环持续进行直到条件变为`false`,直到循环持续进行直到条件变为`true`。 阅读下面的代码,在文本编辑器中键入并执行它。

W
wizardforcel 已提交
309
```rb
W
init  
wizardforcel 已提交
310 311 312 313 314 315 316 317
# until.rb
i=1
until i>10 do
  print "#{i}, "
  i+=1
end
```

W
wizardforcel 已提交
318
这就是你将得到的结果
W
init  
wizardforcel 已提交
319

W
wizardforcel 已提交
320
```rb
W
init  
wizardforcel 已提交
321 322 323 324 325 326 327
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
```

那么这个循环如何工作? 首先,我们设置`i=1`,然后使用直到命令`i`大于 10 之前,请在此行`until i&gt;10 do`中继续执行操作。 在`do``end`关键字之间说了些什么。 因此,直到条件失败,循环主体中的代码将被执行,因此我们将输出 1 到 10。

### 5.7。 打破

W
wizardforcel 已提交
328
假设你想脱离循环,也就是停止执行循环,可以使用`break`命令。 下面给出一个例子。 在示例中,如果迭代变量`i`变为 6,我们将中断操作。因此,仅输出 1 到 5 范围内的数字。 当`i`变为 6 时,循环中断或终止
W
init  
wizardforcel 已提交
329

W
wizardforcel 已提交
330
```rb
W
init  
wizardforcel 已提交
331 332 333 334 335 336 337 338 339 340
#break.rb

1.upto 10 do |i|
  break if i == 6
  print "#{i}, "
end
```

执行后,上面的程序产生以下输出

W
wizardforcel 已提交
341
```rb
W
init  
wizardforcel 已提交
342 343 344 345 346
1, 2, 3, 4, 5,
```

### 5.8。 下一个

W
wizardforcel 已提交
347
`break`,退出循环并终止。 `next``break`有所不同,它不是中断循环,而是继续循环而不执行`next`之后发生的语句的信号。 这是一个供你理解的示例
W
init  
wizardforcel 已提交
348

W
wizardforcel 已提交
349
```rb
W
init  
wizardforcel 已提交
350 351 352 353 354 355 356 357 358 359 360
# next.rb

# This loop won't print 6
10.times do |num|
  next if num == 6
  puts num
end
```

Output

W
wizardforcel 已提交
361
```rb
W
init  
wizardforcel 已提交
362 363 364 365 366 367 368 369 370 371 372
0
1
2
3
4
5
7
8
9
```

W
wizardforcel 已提交
373
如果你注意到输出,则看到打印了从 0 到 9 的数字,但没有打印出 6。请注意 [next.rb](code/next.rb) 中的`next if num == 6`行,如果`num``6``next`被执行,换言之`do` `end`块之后的所有行均被跳过。 与`brake`不同,循环不会终止,而是仅跳过`next`之后的行,循环继续进行。
W
init  
wizardforcel 已提交
374 375 376 377 378

### 5.9。 重做

还有另一件事`redo``next`进一步跳过任何执行,并且迭代变量递增/递减到下一个可能的值 &lt;sup class="footnote"&gt;[ [20](#_footnotedef_20 "View footnote.") ]&lt;/sup&gt; ,另一方面,`redo`跳过循环块中代码的进一步执行 ,但迭代变量不会增加,而是重新运行循环。 输入以下代码并执行

W
wizardforcel 已提交
379
```rb
W
init  
wizardforcel 已提交
380 381 382 383 384 385 386 387 388 389
# redo.rb

5.times do |num|
  puts "num = #{num}"
  puts "Do you want to redo? (y/n): "
  option = gets.chop
  redo if option == 'y'
end
```

W
wizardforcel 已提交
390
运行它,希望你可以自己解释;-)
W
init  
wizardforcel 已提交
391 392 393

### 5.10。 环

W
wizardforcel 已提交
394
因此,到目前为止,我们已经看到了许多类型的循环,但是我省略了一个称为`loop`的基本循环。 为什么我忽略了它,因为使用 &lt;sup class="footnote"&gt;[ [21](#_footnotedef_21 "View footnote.") ]&lt;/sup&gt; 危险。 好吧,让我们看一个例子。 在下面键入程序并执行。 请注意,你需要按 Ctrl + C 才能停止执行。 所以要小心一点
W
init  
wizardforcel 已提交
395

W
wizardforcel 已提交
396
```rb
W
init  
wizardforcel 已提交
397 398 399 400 401 402 403
# loop.rb

loop do
  puts "I Love Ruby"
end
```

W
wizardforcel 已提交
404
```rb
W
init  
wizardforcel 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
Output
I Love Ruby
I Love Ruby
I Love Ruby
I Love Ruby
I Love Ruby
I Love Ruby
I Love Ruby
I Love Ruby
I Love Ruby
I Love Ruby
I Love Ruby
......
```

W
wizardforcel 已提交
420
输出将继续打印`I Love Ruby`,直到你按 Ctrl 和 C 键使输出断开为止。 基本是这样的:循环执行和结束之间的任何操作都将继续进行。
W
init  
wizardforcel 已提交
421 422 423

现在让我们说我们不希望这个循环永远持续运行。 让我们看看如何驯服它。 让我们打印一个可以打印 1 到 10 的程序。在下面键入该程序并运行。

W
wizardforcel 已提交
424
```rb
W
init  
wizardforcel 已提交
425 426 427 428 429 430 431 432 433 434 435 436
# break_at_10.rb

i = 1
loop do
  puts i
  break if i == 10
  i = i+1
end
```

Output

W
wizardforcel 已提交
437
```rb
W
init  
wizardforcel 已提交
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
1
2
3
4
5
6
7
8
9
10
```

因此,程序可以按我们希望的那样打印 1 到 10。 让我们来看看它如何工作。 第一行`i = 1`将值`1`存储在名为`i`的变量中。 接下来,我们有这条`loop do`行,在这行和`end`之间放置的任何东西都将连续运行。

在下一行`puts i`中,我们打印`i`的值,因此打印了`1`,现在在`break if i == 10`中,它检查`i`是否为 10,此处的条件是`false``i` 不是 10,因此循环将继续到下一个语句`i = i + 1`,我们将`i + 1`中的`i`加 1,因此它变成 2,因此通过说`i = i + 1`表示`i = 1 + 1`,因此`i`将 变为 2 并且它(程序)符合`end`语句,这并不意味着 end,它仅意味着循环结束的迭代,因此再次返回顶部(即`loop do`)。

因此,在循环执行中,现在`i`为 2,因此事情不断进行,直到`i``10`,在这种情况下,在`break if i == 10`中,`i == 10`变为`true`并中断了循环。

**练习**:尝试修改 [break_at_10.rb](code/break_at_10.rb) ,当我们完成打印 10 时,程序必须打印“妈妈我已经完成打印 10”。

**答案:** [telling_mom.rb](code/telling_mom.rb)

**练习:**这些西方人不喜欢 13,所以编写了一个程序以从 1 10 20 开始打印,但是省略了 13。答案: [no_13.rb](code/no_13.rb)

**练习:**请说明 [no_13_a.rb](code/no_13_a.rb) 是否有效。 如果是,怎么办? 如果否,为什么不呢? &lt;sup class="footnote"&gt;[ [22](#_footnotedef_22 "View footnote.") ]&lt;/sup&gt;