# Ruby 控制流

> 原文： [https://zetcode.com/lang/rubytutorial/flowcontrol/](https://zetcode.com/lang/rubytutorial/flowcontrol/)

在 Ruby 教程的这一部分中，我们将讨论流控制。 我们将定义几个关键字，这些关键字使我们能够控制 Ruby 程序的流程。

条件和循环会改变 Ruby 程序的流程。 条件语句是在特定条件下执行特定语句的关键字。 循环是多次执行的程序块。 当程序运行时，语句从源文件的顶部到底部逐一执行。

## Ruby `if`语句

`if`关键字用于检查表达式是否为真。 如果为`true`，则执行一条语句。 该语句可以是单个语句或复合语句。 复合语句由该块包围的多个语句组成。 块是由`end`关键字括起来的代码。 `then`关键字是可选的。

```ruby
#!/usr/bin/ruby

num = gets.to_i

if num > 0 then

    puts "num variable is positive"
    puts "num variable equals to #{num}"
end

```

我们从用户那里读取了一个号码。 如果数字大于零，那么我们将两个消息打印到控制台。 如果没有，则什么也不做。

```ruby
$ ./simpleif.rb 
4
num variable is positive
num variable equals to 4

```

满足条件，并将消息写入控制台。

我们可以使用`else`关键字来创建一个简单的分支。 如果`if`关键字后方括号内的表达式的值为假，则将自动执行`else`关键字后方的语句。 代码块包含在`end`关键字中。

```ruby
#!/usr/bin/ruby

age = 17

if age > 18

    puts "Driving license issued"
else

    puts "Driving license not permitted"
end

```

我们有一个年龄变量。 布尔表达式的计算结果为`false`，并且在控制台中得到`"Driving license not permitted"`。

```ruby
$ ./licence.rb 
Driving license not permitted

```

我们可以使用`elsif`关键字创建多个分支。 仅当不满足先前条件时，`elsif`关键字才会测试其他条件。 请注意，我们可以在测试中使用多个`elsif`关键字。

```ruby
#!/usr/bin/ruby

print "Enter a number: "

num = gets.to_i

if num < 0

    puts "#{num} is negative"
elsif num == 0

   puts "#{num} is zero"
elsif num > 0

   puts "#{num} is positive"
end

```

我们有一个数值变量，并测试它是否为负数或正数或等于零。 根据从用户读取的值，我们将其中一条消息打印到控制台。

## Ruby `case`语句

`case`语句是选择控制流语句。 它允许变量或表达式的值通过多路分支控制程序执行的流程。 与使用`if`和`elsif`语句的组合相比，它以更简单的方式创建多个分支。

我们有一个变量或一个表达式。 `case`关键字用于根据值列表测试变量或表达式中的值。 值列表用`when`关键字显示。 如果值匹配，则执行`when`之后的语句。 有一个可选的`else`语句。 如果找不到其他匹配项，则执行该命令。

```ruby
#!/usr/bin/ruby

print "Enter top level domain: "

domain = gets.chomp

case domain
    when "us"
        puts "United States"
    when "de" 
        puts "Germany"
    when "sk" 
        puts "Slovakia"
    when "hu" 
        puts "Hungary"
    else
        puts "Unknown"
end

```

在我们的程序中，我们有一个域变量。 我们从命令行读取变量的值。 我们使用`when`语句测试变量的值。 有几种选择。 例如，如果该值等于`"us"`，则将`"United States"`字符串打印到控制台。

```ruby
domain = gets.chomp

```

我们使用`gets`方法从用户那里得到输入。 输入还包括换行符。 `chomp`方法排除换行符。

```ruby
$ ./domains.rb 
Enter top level domain: hu
Hungary

```

我们在控制台输入了`"hu"`字符串，程序以`"Hungary"`作为响应。

## Ruby `while`，`until`语句

`while`语句是一个控制流语句，它允许根据给定的布尔条件重复执行代码。 条件为真时，它将执行代码。

`while`关键字在`end`关键字包围的块内执行语句。 每次将表达式求值为`true`时都会执行这些语句。

```ruby
#!/usr/bin/ruby

i = 0
sum = 0

while i < 10  do
   i = i + 1
   sum = sum + i
end

puts "The sum of 0..9 values is #{sum}"

```

在代码示例中，我们从一系列数字计算值的总和。

`while`循环包含三个部分：初始化，测试和更新。 语句的每次执行都称为循环。

```ruby
i = 0
sum = 0

```

我们启动`i`和`sum`变量。 `i`用作计数器。

```ruby
while i < 10  do
   ...
end

```

`while`和`do`关键字之间的表达式是第二阶段，即测试。 请注意，`do`关键字是可选的。 执行主体中的语句，直到表达式的计算结果为`false`。

```ruby
i = i + 1

```

这是`while`循环的最后一个第三阶段-更新。 我们增加计数器。 请注意，对`while`循环的不正确处理可能会导致循环不断。

```ruby
$ ./while.rb 
The sum of 0..9 values is 55

```

这是示例的输出。

`until`是一个控制流语句，在条件为`false`时执行代码。 当条件为真时，循环停止。

```ruby
#!/usr/bin/ruby

hours_left = 12

until hours_left == 0

    if hours_left == 1
        puts "There is #{hours_left} hour left"
    else
        puts "There are #{hours_left} hours left"
    end

    hours_left -= 1
end

```

在我们的示例中，我们有一个变量`hours_left`。 我们开始倒数。 在每个循环周期中，我们打印那里还有多少小时。 当变量等于零时，循环停止。

```ruby
$ ./until.rb 
There are 12 hours left
There are 11 hours left
There are 10 hours left
There are 9 hours left
There are 8 hours left
There are 7 hours left
There are 6 hours left
There are 5 hours left
There are 4 hours left
There are 3 hours left
There are 2 hours left
There is 1 hour left

```

运行示例，我们得到了这个结果。

## Ruby `for`语句

如果在启动循环之前知道周期数，则可以使用`for`语句。 `for`循环与范围结合使用。 对于范围的每个元素，将执行语句块。 语句包含在`end`关键字中。 `do`关键字是可选的。

```ruby
#!/usr/bin/ruby

for i in 0..9 do

    puts "#{i}"
end

```

在此示例中，我们将数字`0..9`打印到控制台。 在每个循环中，`i`变量均包含一个数字范围内的值。 该值将打印到控制台。 `..`范围运算符创建一个数字列表，包括最后一个数字。

```ruby
$ ./forloop.rb 
0
1
2
3
4
5
6
7
8
9

```

This is the output of the example.

要使用`for`循环遍历元素数组，可以使用数组的`length`方法。

```ruby
#!/usr/bin/ruby

planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter",
    "Saturn", "Uranus", "Neptune"]

for i in 0...planets.length

    puts planets[i]
end

```

在这个例子中，我们有一系列的行星。 我们遍历数组并打印数组的每个元素。

```ruby
planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter",
    "Saturn", "Uranus", "Neptune"]

```

这是一系列行星。

```ruby
for i in 0...planets.length

```

`length`方法返回数组的长度。 由于数组以 0 开头，因此最后一个索引为`n-1`。 `...`范围运算符创建一个数字范围，但最后一个高值除外。

```ruby
puts planets[i]

```

我们在数组中打印具有特定索引的元素。

```ruby
$ ./planets2.rb 
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune

```

运行上面的 Ruby 程序将给出此输出。

## Ruby `each`方法

在 Ruby 中，我们可以使用`each`方法来遍历数组的各项。 它有两个参数。 一个元素和一个块。 元素放置在管道之间。 它是当前迭代项目的占位符。 块是在每次迭代中执行的代码。

```ruby
#!/usr/bin/ruby

planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter",
    "Saturn", "Uranus", "Neptune"]

planets.each do |planet| 

    puts planet
end

```

在此示例中，我们使用`each`迭代器遍历了一系列行星。

```ruby
planets.each do |planet| 

    puts planet
end

```

`each`迭代器是一种适用于行星数组的方法。 该行星是迭代中当前项目的占位符。 我们可以把任何想要的角色放在那里。 我们可以使用`{}`字符代替`do`和`end`关键字。

## Ruby `break`，`next`语句

`break`语句可用于终止由`while`，`for`或`case`语句定义的块。

```ruby
#!/usr/bin/ruby

while true

    r = 1 + rand(30)
    print "#{r} "

    if r == 22
        break
    end
end

puts

```

我们定义了一个无限的`while`循环。 我们使用`break`语句退出此循环。 我们从 1 到 30 中选择一个随机值。我们打印该值。 如果该值等于 22，则结束无穷的`while`循环。

```ruby
while true
   ...
end

```

这是一个无尽的循环。 while 循环的条件始终为 true。 摆脱这种无穷循环的唯一方法就是突破。

```ruby
r = 1 + rand(30)
print "#{r} "

```

我们计算一个从 1 到 30 的随机数，并将其打印到控制台。

```ruby
if r == 22
    break
end

```

如果数字等于 22，我们将打破循环。 while 循环终止。

```ruby
$ ./break.rb 
20 14 6 26 30 12 2 10 18 29 28 11 30 26 20 22 

```

我们可能会得到这样的东西。

`next`语句用于跳过循环的一部分，并继续循环的下一个迭代。 它可以与`for`和`while`语句结合使用。

在下面的示例中，我们将打印一个数字列表，这些数字不能除以 2 而没有余数。

```ruby
#!/usr/bin/ruby

num = 0

while num < 100

    num += 1

    if (num % 2 == 0)
        next
    end

    print "#{num} " 
end    

puts

```

我们使用`while`循环遍历数字`1..99`。

```ruby
if (num % 2 == 0)
    next
end

```

如果表达式`num % 2`返回 0，则可以将所讨论的数字除以 2。执行`next`语句，并跳过循环的其余部分。 在我们的例子中，循环的最后一条语句将被跳过，并且数字不会输出到控制台。 下一个迭代开始。

```ruby
$ ./next.rb 
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 
41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77 
79 81 83 85 87 89 91 93 95 97 99 

```

这是程序的示例输出。

## Ruby `redo`语句

`redo`语句重新启动循环的迭代，而无需检查循环条件。 最后一个示例将是一个更复杂的示例。 还将演示`redo`语句和其他功能。

```ruby
#!/usr/bin/ruby

options = ["rock", "scissors", "paper"]

while true

    print <<TEXT
1 - rock
2 - scissors
3 - paper
9 - end game
TEXT

    val = gets.to_i

    r = rand(3) + 1

    if val == 9 
        puts "End"
        exit
    end

    if ![1, 2, 3, 9].include?(val)
        puts "Invalid option"
        redo
    end

    computer = options[r-1]
    human = options[val-1]

    puts "I have #{computer}, you have #{human}"

    if val == r
        puts "Tie, next throw"
        redo
    end

    if val == 1 and r == 2
        puts "Rock blunts scissors, you win"

    elsif val == 2 and r == 1
        puts "Rock blunts scissors, you loose"

    elsif val == 2 and r == 3
        puts "Scissors cut paper, you win"

    elsif val == 3 and r == 2
        puts "Scissors cut paper, you loose"

    elsif val == 3 and r == 1
        puts "Paper covers rock, you win"

    elsif val == 1 and r == 3
        puts "Paper covers rock, you loose"

    end    
end

```

我们有一个简单的剪刀石头布游戏。 在此代码示例中，我们将利用`redo`语句，条件，随机数，数组和用户输入。

```ruby
options = ["rock", "scissors", "paper"]

```

在选项数组中，我们拥有游戏的所有可能性。 将消息打印到控制台时，将使用这三个词。

```ruby
    print <<TEXT
1 - rock
2 - scissors
3 - paper
9 - end game
TEXT

```

我们使用 heredoc 语法将菜单打印到控制台。 Heredoc 以`<<`开头，后跟一个字符串。 相同的字符串关闭构造。 它必须保持对齐。 这使我们可以一步打印多行。 每个游戏周期都会打印此菜单。

```ruby
val = gets.to_i

r = rand(3) + 1

```

在这些代码行中，我们从终端读取一个值。 然后，我们从 1、2 和 3 中随机选择一个数字。请注意`rand(3)`返回 0、1 和 2 中的一个数字。这就是为什么要加 1 的原因。

```ruby
if val == 9 
    puts "End"
    exit
end

```

如果来自用户的输入等于 9，我们将`"End"`打印到终端并结束游戏。 `exit`方法终止程序。

```ruby
if ![1, 2, 3, 9].include?(val)
    puts "Invalid option"
    redo
end

```

如果用户选择的值与菜单中提供的值不同，我们将告知无效选项并重做周期。

```ruby
computer = options[r-1]
human = options[val-1]

puts "I have #{computer}, you have #{human}"

```

数字将转换为字符串。 我们同时打印用户和计算机的选择。

```ruby
if val == r
    puts "Tie, next throw"
    redo
end

```

如果双方有相同的选择，那就是平局。 我们开始一个新的游戏周期。 我们利用`redo`关键字。

```ruby
if val == 1 and r == 2
    puts "Rock blunts scissors, you win"

elsif val == 2 and r == 1
    puts "Rock blunts scissors, you loose"
...

```

使用多个`if`和`elsif`分支，我们比较用户和计算机的选择。 我们决定谁是赢家。

```ruby
$ ./redo.rb 
1 - rock
2 - scissors
3 - paper
9 - end game
3
I have paper, you have paper
Tie, next throw
1 - rock
2 - scissors
3 - paper
9 - end game
2
I have rock, you have scissors
Rock blunts scissors, you loose
1 - rock
2 - scissors
3 - paper
9 - end game
1
I have scissors, you have rock
Rock blunts scissors, you win
1 - rock
2 - scissors
3 - paper
9 - end game
9
End

```

这是一个示例输出。

在 Ruby 教程的这一部分中，我们正在讨论控制流结构。