跳到主要内容
版本:master

流程控制语句

本章节系统介绍了GS语言中的流程控制语句,通过语法说明和代码示例,详细讲解了各种语句的结构、规则及适用场景。主要包括:

  • 条件判断的if-else、?:、??、switch语句
  • 循环的for、while语句

本章适用于:

  • 正在学习GS语言编程基础的初学者
  • 需要快速回顾GS流程控制语法要点的开发者。

通过阅读本章,您将能够:

  • 理解并运用GS中的各种条件判断语句来编写分支逻辑。
  • 掌握使用循环语句重复执行代码块的方法。
  • 了解switch语句的语法规则及其与if-else的差异。

1 条件判断语句

和大部分其他编程语言相同,条件判断语句通过判定条件是否为真来决定是否执行某些代码。在 GS 中条件判断语句主要包含 if-else?:??switch 四种类型。

先使用 gip init branches 创建一个 branches 的项目,来学习 if 表达式。

1.1 if-else型

if-else型语句允许根据条件执行不同的代码分支, if-else型判断语句又分成三种不同的使用方式

1.1.1 if

针对你提供的条件表示 “如果条件满足,运行这段代码;如果条件不满足,不运行这段代码”。
所有的if表达式都以 if 关键字开头,其后跟一对小括号 (...),小括号内部即为判断条件。在之后跟一对大括号{...},大括号内部为条件满足时希望执行的语句,if语句基本语法形式如下:

if(boolean_expression)  // 如果boolean_expression == true, 就执行 execute body
{
// execute body
}
// 如果 boolean_expression == false,就不执行 execute body

接下来让我们实际尝试简单的 if 表达式语句,在 branches 项目新建 src/branch.gs 文件,并输入如下内容:

void run() 
{
int number = 3;
if (number < 5)
{
writeln("condition was true");
}
}
run();

示例 1-1:if 条件判断
运行如上程序输出结果如下:

condition was true

在这个例子中,条件检查函数参数 number 的值是否小于 5。在条件满足时希望执行的代码块位于紧跟条件之后的大括号中。if 表达式中与条件关联的代码块有时被叫做 分支

1.1.2 if-else

在简单if表达式的基础上我们可以在语句后续紧跟一个可选的 else 表达式来提供一个在if表达式条件不满足时应当执行的代码块。if-else语句基本语法如下:

if(boolean_expression)  // 如果boolean_expression = true, 就执行body1,跳过else,否则执行body2
{
// execute body1
}
else
{
// execute body2
}

接下来让我们实际尝试简单的 if-else 表达式语句,在 branches src/branch.gs 文件中输入如下内容:

void run() 
{
int number = 7;
if (number < 5)
{
writeln("condition was true");
}
else
{
writeln("condition was not true");
}
}
run();

示例 1-2:if-else 条件判断
示例执行的结果如下:

condition was not true

在这个例子中,条件检查函数参数 number 的值是否小于 5。在条件不满足的情况下执行了 else 分支的代码块。

1.1.3 if...else if...else

在简单if表达式的基础上我们可以在语句后续紧跟一个可选的 else if 表达式来提供一个在if表达式条件不满足时应当执行的条件判断,当else if 条件满足时,执行对应的分支代码块,并跳过后续其他 else if else的分支代码。if...else if...else语句基本语法如下:

if(boolean_expression_1)  // 从上往下,如果boolean_expression_x = true, 就执行body_x,跳过下面的else if和else。
{
// execute body1
}
...
else if(boolean_expression_m)
{
// execute body_m
}
...
else
{
// execute bodyn
}

可以将将 else if 表达式与 ifelse 组合来实现多重条件。例如:

void run() 
{
int score = 85;
if(score >= 90)
{
writeln("Excellent");
}
else if (score >= 80)
{
writeln("Good");
}
else if (score >= 70)
{
writeln("Fair");
}
else if (score >= 60)
{
writeln("Pass");
}
else
{
writeln("Fail");
}
}
run();

示例 1-3:多重条件处理
这个程序有五个可能的执行分支。运行后应该能看到如下输出:

Good

当执行上述示例的多重条件判断时,GS 按顺序检查每个 ifelse if 表达式,并执行第一个条件成立的的分支代码块。尽管 85 分满足 >= 70 以及 >= 60 的条件,但由于>= 80的条件先被满足,因此只有输出 Good 的分支代码会被执行。除此之外 ifelse ifelse 语句也可以逐层嵌套使用。

1.1.4 嵌套使用if-else

if-else型支持多层嵌套使用,在满足某个 ifelse ifelse 条件的情况下,其分支代码中带有嵌套的 ifelse ifelse 语句,会继续进行分支代码中的条件判断选择执行的分支。嵌套型if-else语句基本语法如下:

if(boolean_expression1)  
{
if(boolean_expression2)
{
...
}
...
else
{
...
}
}
else if
{
if(boolean_expression3)
{
...
}
}
else
{
...
}
void attack()
{
int playerAttackPower = 5; // 玩家攻击力 (1-10)
int enemyHealth = 3; // 敌人血量 (1-10)

if (playerAttackPower >= 5) // 外部条件: 攻击受否有效
{
if (enemyHealth <= 3) // 内部条件: 敌人血量是否足够低?
{
writeln("One-hit kill!"); // // 高攻击力 + 低血量敌人
}
else
{
writeln("The enemy is injured but not dead!"); // 高攻击力但敌人血量较高
}
}
else if (playerAttackPower >= 2)
{
// 攻击力中等
writeln("The enemy takes minor damage!");
}
else
{
// 攻击力太低
writeln("The attack is ineffective! The enemy is laughing at you!");
}
}
attack();

示例 1-4:嵌套条件判断
这个程序有四个可能的执行分支。运行后应该能看到如下输出:

One-hit kill!

1.2 ?:型

?:型条件判断语句是一种三目运算符,与if-else型语句很相似,两者可以部分相互转换,但三目型判断语句的代码相对更简洁(只要一行)。同时三目运算符具有表达式返回值的概念。 下述的?:语句基本语法中当boolean_expression条件满足时则执行 expression1并将expression1作为?:型表达式返回值,否则执行 expression2并将expression2作为?:型表达式返回值。 :?语句基本语法如下:

boolean_expression ? expression1 : expression2 // 如果boolean_expression = true,则执行expression1,否则执行expression2

举个例子:

int a = 0, b = 1;   // a = (a和b中的最大值)
if(a > b)
a = a;
else
a = b;
write(a);

等价于:

int a = 0, b = 1;
a = a > b ? a : b;
write(a);

示例 1-5:三目运算符与等效 if-else 语句示例 输出结果如下:

1

同样实现当 a > b 条件满足时将变量 a 赋值给自身,条件不满足时将变量b赋值给自身,?:语句的写法更加简洁。

1.3 ??型

参照下方基本语法,二目??型判断语句特别的是,判断条件不再是判断expression1是否为true,而是判断expression1是否不为 nilvoid。首先执行expression1expression1不为 nilvoid 时表达式返回 epression1 否则执行并返回 expression2; ??语句基本语法:

expression1 ?? expression2 // expression1 为 nil 或 void 则取 expression2,若 expression1 不为 nil 或 void 则取 expression 1

举个例子:

write(0??1)     // expr1=0 不为 void 或 nil,输出 0
write(1??0) // 输出 1
write(nil??1) // expr1=nil 为 nil,输出 1
write(nil??0) // 输出 0

示例 1-6:二目??型运算符示例 输出结果如下:

0110

1.4 switch型

switch型判断语句跟if-else型判断语句可以相互转换,当判断层次较多且比较简单时使用switch语句更好。 switch 语句的基本语法如下:

switch(expression)
{
case constant_expression1 :
statement1;
case constant_expression2 :
statement2;
case constant_expression3 :
statement3;
...
default : // 可选的
statement4;
}
  • 语句会逐个比较 switch 语句中的的表达式与 case 语句中的表达式,当两者相等时,对应 case 语句后的分支代码块均会执行。
  • expressionconstant_expression1 相等时后续分支中的 statement1 statement2 statement3 statement4 等代码块均被执行。
  • expressionconstant_expression2 相等时后续分支中的 statement2 statement3 statement4 等代码块均被执行,而之前的 statment1 代码块不再被执行。后续以此类推。
  • 注意可选的default语句可以视为一个会匹配成功的case语句,当控制流执行到default语句时其后续代码块均会执行。

举个例子:

void run_switch(int n)
{
switch(n)
{
case 1:
write(1);
case 2:
write(2);
case 3:
write(3);
default:
write(4);
}
}
run_switch(3);

示例 1-7:switch 语句示例 输出结果如下:

34

但通常情况下,我们仅希望 case 语句对应的单个代码块被执行,比如expressionconstant_expression1匹配时希望仅执行statment1,此时就需要在 statement 语句后添加break语句。

  • 执行break语句直接跳出所在的一层循环或switch型语句,在下面的switch-break基本语法中可以理解为跳到'}'的后面,也就是这个块的下一条语句前面。

switch-break 语句基本语法为:

switch(expression)
{
case constant_expression1 :
statement;
break; // 可选的
case constant_expression2 :
statement;
break; // 可选的
case constant_expression3 :
statement;
break; // 可选的
...
default : // 可选的
statement;
}

举个例子:

void run_switch(int n)
{
switch(n)
{
case 1:
write(1);
break;
case 2:
write(2);
break;
case 3:
write(3);
break;
default:
write(4);
}
}
run_switch(3);
示例1-8switch-break 示例

输出结果如下:

3

需要注意的是,switch语句必须遵循以下规则:

  • case语句中的待匹配值应当是一个编译期可以求得的常量即编译期常量
  • 一个switch中可以有任意个case语句,每个case语句后面加一个跟expression比较的值和一个冒号
  • case中的constant_expression必须和expression具有相同的数据类型
  • 当程序进入一个case中执行之后还会继续执行下面的case,直到遇到break语句为止。
  • 每个case都可以选择break
  • switch最后一个default表示前面所有的case都不符合就执行default,相当于if-else语句中的else,它是可选的。

同样,switch语句也是可以和if-else语句一样多层嵌套,具体语法类似if-else

2 循环语句

多次执行同一段代码的需求是很常见的,GS 为此提供了多种循环语句。每次循环会执行循环体中的代码知道循环体结尾后再回到循环开头继续执行。 循环语句主要包含for循环和while循环两种类型

在循环语句中,我们常会有停止当前循环,或跳过当前循环的后续语句直接执行下一次循环的需求。此时我们就需要使用 breakcontinue 语句。

  • 执行break语句直接跳出所在的一层循环,循环有''可以理解为就跳到循环结束的'}'符号后面的语句。
  • 执行continue语句会跳过当前循环中后续的所有语句,并执行下一次循环,可以理解为跳过'}'前面所有语句。

2.1 for型

for循环是一个允许执行特定次数的循环重复控制结构。 for循环语句基本语法有三种:

2.1.1 第一种for循环(常见)

第一种for循环的语法如下:

for( initial; condition; increment ) // initial、condition、increment两两之间要用分号分开,并且三者都可以为空
{
statements;
}

下面解释一下for循环的控制流:

  • 1、for循环一开始initial语句首先被执行,并且只会执行一次,initial语句一般充当初始化控制循环变量的作用。initial可以为空;
  • 2、接下来执行condition判断语句,如果为真就执行循环体statements,否则跳出循环,跳到紧接着for循环结束的后续语句;condition也可以为空,如果条件表达式为空,则始终满足循环条件,要有break退出循环;
  • 3、在执行完for循环主体之后,控制流会跳到increment语句,increment一般用来更新循环控制变量,这部分也可以为空;
  • 4、接下来控制流会跳到第2步。

举个例子:

for(int i = 0; i < 5; i ++)
{
write(i);
}
// 程序执行之后输出 01234

示例2-1:第一种 for 循环示例 输出结果如下:

01234

2.1.2 第二种for循环(不常见)

第二种for循环的语法如下:

for( int val = from upto(or downto) to ) // 表示val从from递增(或者)递减到to
{
statements;
}

下面解释一下for循环的控制流:

  • 1、for循环一开始先执行val = from,并且只会执行一次;
  • 2、接下来执行val <= to是否为真,注意是 <=,如果为真就执行循环体statements,否则跳到紧接着for循环的下一条语句;
  • 3、在执行完for循环主体之后,++val,表示自加1;
  • 4、接下来控制流会跳到第2步。

举个例子:

for(int i = 0 upto 5)
{
write(i);
}
// 程序执行之后输出 012345

示例2-2:第二种 for 循环示例 输出结果如下:

012345

2.1.3 第三种for循环(常见)

第三种循环实际是迭代器循环,该循环语句用于迭代 array 或 map,语法如下:

// 循环1
for(value_type idntifier: container)
{

}
// 循环2,专用于 map 迭代
for(value_type idntifier_key, value_type idntifier_val: container)
{

}

语法中的循环二语句专用于迭代 map ,其中 value_type 代表用于接收迭代变量的类型,identifier 为变量名,container 为 array 或 map 变量。

注意

注意:在迭代过程中不可改变容器的长度,在迭代过程中不可改变容器的长度,在迭代过程中不可改变容器的长度。如调用push_back, clear 函数等

  • 1、循环一中的containerarray时,迭代变量idntifier用于接收从前往后遍历数组成员。
  • 2、循环一中的containermap时, 迭代变量idntifier用于接收由map的key-value pair及索引构成的[key, value, index]数组
  • 3、循环二中的containermap时,idntifier_key用于接收由map的key-value pair中的key, idntifier_val用于接收由map的key-value pair中的value

示例如下:

array arr = [1,2,3,4,5,6];
for(int val:arr)
{
write(val);
}

示例 2-3 :for循环迭代遍历array数组 输出结果如下:

123456

可以看到数组 arr 中的元素被逐个取出并赋值进入变量 val 中。可以尝试修改将变量 val 的类型变为与数组元素不匹配的类型 string,此时输出中就会报错。大致内容如下:

Error(-1): Invalid type of container[key] result, expected 'string', got 'int'

接下来是map表迭代的示例:

map m = {"attack":15, "defend":8, "health": 50};
// loop 1
for(string key, int val:m)
{
writeln(key," - ",val);
}

// loop 2
for(array item:m)
{
writeln(item);
}

示例 2-4 :for循环迭代遍历map表 输出结果如下:

attack - 15
defend - 8
health - 50
[ /* sizeof() == 3 */
"attack",
15,
0,
]
[ /* sizeof() == 3 */
"defend",
8,
1,
]
[ /* sizeof() == 3 */
"health",
50,
2,
]

从输出结果可以看到 loop1 中 map 表的键值对元素中,键逐个被赋值向变量key,值逐个被赋值向变量val。同样可以尝试改变相应变量类型为不匹配的类型观察报错输出。 从输出结果可以看到 loop2 中 map 表的成员键值对 + 索引构成了一个新的[key, value, index]数组,并被赋值给变量 item;

2.2 while型

while循环又分为两种,直接 while 循环和 do-while 循环,两者的主要区别是 while 循环先判断后执行,而 do-while 刚好相反,两者的选择要看实际用途。

2.2.1 直接while型

直接while型语句基本语法:

while(condition)
{
statements;
}

下面解释一下while循环的控制流:

  • 1、while循环一开始先判断condition是否为真;
  • 2、如果为真就执行循环体statements,否则跳出循环,跳到紧接着while循环结束后的后续语句。

下面是一个简单的 while 循环示例:

int i = 0;
while(i < 5)
{
write(i);
i++;
}

示例 2-5: while 循环示例 输出结果如下:

01234

从示例可以看到,在i++执行后且变量 i 等于 5 时,循环条件不满足,循环体不再执行,循环被跳出停止。

2.2.2 do-while型

do-while型语句基本语法:

do
{
statements;
} while(condition); // 注意最后有个';'号

下面解释一下do-while循环的控制流:

  • 1、do-while循环一开始先执行循环体statements;
  • 2、接下来判断condition是否为真,如果为真就跳到1,否则跳到紧接着while循环的下一条语句。

下面是一个简单的 do-while 循环的示例:

int i = 0;
do
{
write(i);
i++;
}while(i < 5);

输出结果如下:

01234

接下来让我们调整代码,将变量 i 的初始值设置为 5,输出结果如下:

5

从修改后的代码输出结果可以看出,由于 do-while 循环是先执行循环体后判断循环条件,尽管变量 i 的初始值不满足循环的判断条件 i < 5 但循环的循环体仍是执行了一次。

2 特殊控制流

由于 defertry-cath 对较为特殊,会在后续的程序结构的章节给出详细介绍,若感兴趣也可点击后续的链接提前

2.1 defer

2.2 try-catch

3 总结

通过本章学习,我们掌握了GS语言中流程控制的核心语法,包括条件判断(if-else、?:、??、switch)和循环语句(for、while)。这些控制结构是构建程序逻辑的基础,能够实现分支决策和重复执行功能。接下来可以继续学习数据类型及其操作,了解整型、浮点型、数组、表等数据类型的特性和使用方法,这将帮助编写更健壮的程序。编程能力的提升需要理论学习和实践相结合,建议在学习过程中多动手编写和调试代码。