函数
1 概述
函数是GS语言中组织代码和实现功能复用的基本单元。本章将介绍GS语言中函数的各种特性和使用方法,包括函数的基本概念、定义方式、参数传递、返回值处理、调用类型、匿名函数以及函数式编程支持等内容。本章面向GS编程初学者或需要回顾GS函数 基础知识的同学。通过学习本章,您将能够熟练运用GS函数来构建模块化、可维护的代码结构,了解函数式编程在GS中的应用,并初步知晓各个函数调用方式间的差别。
2 函数的概念
在GS语言中,函数是实现特定功能的代码块,它可以接受输入参数、执行特定操作并返回结果。函数的主要作用包括:
-
代码复用:将常用功能封装成函数,避免重复编写相同代码
-
模块化设计:将复杂问题分解为多个函数,提高代码的可读性和可维护性
-
抽象化:隐藏实现细节,只暴露必要的接口
GS中的函数分为两种类型:
-
外部函数(EFun):由GS底层使用C++实现的函数,提供核心功能。
-
内部函数:使用GS语言编写的函数,可以基于外部函数构建更复杂的功能。
-
注意:外部函数的作用域是整个源程序,而内部函数只能在它的域(domain)中使用或者被跨域调用。
3 函数基础
3.1 函数的定义
GS普通函数的定义基本语法为:
prefix return_type function_name(argument_list)
{
// 函数体
// 实现特定功能
return value; // 可选
}
普通语法点定义解释如下:
-
prefix表示类型前缀,它是可选的,默认为private,其类型有以下五种:-
public- 公有函数,可在任何地方访问 -
protected- 保护函数,只能在当前及子函数中访问 -
private- 私有函数,只能在本文件中访问(默认) -
virtual- 虚拟函数,支持多态行为 -
override- 覆盖函数,重写基类中的虚函数
-
-
return_type表示返回值类型,也就是这个函数执行完之后需要返回值的类型mixed- 所有已有数据类型或者自定义数据类型都可以作为返回值return_type- 是每个函数必须要有,不可省略-
void- 如果函数没有返回值,可以标注为void
-
function_name是函数的名字- 它也是不可省略的
- 不能跟系统函数同名,以免造成冲突
-
argument_list表示函数的参数- 函数的参数可以是零个或多个
- 参数表中参数的格式为
value_type value其中参数之间用逗号隔开 - 使用
...可以接受变长参数 - 支持以
value_type value = const_val的方式指定默认值const_val - 支持使用
readonly或parallel进行限定修饰
-
body_of_general_function是函数体- 函数体是实现函数的最主要模块
- GS 返回值带有控制流检查,若返回类型不匹配编译器会报错
- 当函数没有
return语句时或return语句后无变量时默认返回为void
看起来函数可指定的内容非常多甚至觉得有点复杂,但通常绝大多数函数的声明实际非常简单,一个简单的计算两个输入整型的最大值的函数定义示例如下:
public int max_int(int x, int y) // 计算x和y中的最大值,函数定义为公有函数
{
if(x > y)
return x;
return y;
}
printlnf("Max int for 2,3 is %d", max_int(2,3));
printlnf("Max int for 5,4 is %d", max_int(5,4));
示例3-1: 简单的取最大值函数定义示例
输出结果如下:
Max int for 2,3 is 3
Max int for 5,4 is 5
3.2 函数的参数
函数的参数支持默认参数,支持可变参数,支持使用 readonly 或 parallel对参数进行限定修饰。
3.2.1 基本参数声明
基本参数声明示例如下:
void simple_function(int num, string text, array data)
{
// 函数实现
}
示例3-2:基本参数传递示例
我们只需要指定参数的类型,参数的名称,将它们放入括号内并用逗号隔开就好。可以自行尝试在函数实现中添加一些简单的输出语句打印函数参数并调用 simple_function
3.2.2 参数默认值
若要为函数的指定默认参数,只需要在基本参数传递的基础上为需要默认值的参数声明后面添加= const_val 语句即可。
默认参数示例如下:
void greet_user(string name = "访客", int count = 1)
{
for(int i = 0; i < count; i++) {
write("你好, " + name + "!");
}
}
示例3-3:简单的默认参数示例
需要注意的是:
-
为参数指定的默认值必须是编译期可计算的常量。
-
默认参数遵循右连续原则,如果某个参数设置了默认值,那么它右边的所有参数都必须有默认值。
-
// ✓ 正确:默认参数从右向左连续
void func(int a, int b = 10, int c = 20){};
// ✗ 错误:默认参数出现间断
void func(int a = 5, int b, int c = 20){}; // 编译错误
-
3.2.3 参数限定修饰
若要求检查参数为只读或并行实例,则可以通过readonly或 parallel进行限定修饰。不过readonly或 parallel限定修饰只适用于非mixed类型以及非int或float基本类型。
GS支持的限定修饰符如下:
| 修饰符 | 用途 | 限制 |
|---|---|---|
readonly | 确保参数为只读实例 | 不能用于基本类型和mixed类型 |
parallel | 确保参数为并行实例 | 不能用于基本类型和mixed类型 |
参数限定修饰示例如下:
void foo(readonly map m)
{
// ...
}
foo({}); // 运行时异常,传入的参数并不是 readonly 的
foo(make_parallel({})); // 运行时异常,readonly 的参数只接受 readonly, 反之也一样
foo(make_readonly({})); // Ok
示例3-4:参数限定修饰示例
可以自行修改代码逐个尝试三个foo函数调用并查看代码运行结果。
3.2.4 变长参数
GS函数提供变长参数列表,在函数参数列表中使用 ... 语句表示函数有可变参数。可变参数必须作为函数参数列表的最后且唯一的可变元素。在其后声明的任何其他参数都会导致编译错误。
可变参数声明示例:
// ✓ 正确:可变参数在末尾
void log_message(string prefix, string level, ...){};
void print_values(...){};
// ✗ 错误:可变参数后还有其他参数
void process_data(..., string format){}; // 编译错误
void collect_args(int count, ..., bool validate){}; // 编译错误
示例3-5:正确的可变参数声明示例
正确的可变参数用法示例如下:
public array ping_args(...)
{
if($?) // 如果有参数
{
if(lengthof($<) == 1) // 如果只有一个参数
return [$1]; // 返回第一个参数
return ($<)[0..<1]; // 否则返回所有参数
}
else // 没有参数
return ["no argument"];
}
示例3-6:正确的可变参数使用
$ 相关参数介绍:
$<表示所有可变参数构成的数组$?布尔值,表示是否有参数$num表示第num个参数,如$1表示第一个参数($<)[begin_num..<end_num]从第begin_num个参数(第一个为0)开始到倒数第end_num个(包括)。- 比如
($<)[0..<1]表示从正数第0个参数开始到倒数第一个参数构成的子数组,也就是所有参数构成的数组
- 比如
3.3 函数的返回
3.3.1 函数返回类型
GS 支持多种返回值类型,比如void、 int、array、handle、mixed 等类型,出了nil这种值类型不可以作为 GS 的返回类型以外,其他的类型均可作为函数的返回值类型。
GS函数提供多种返回值类型,示例如下:
void test()
{
}
int add(int a, int b)
{
return a + b;
}
array get_arr()
{
return [1,2,3];
}
handle get_file_handle()
{
return this_object();
}
raw_pointer get_raw_pointer()
{
return raw_pointer.gc_allocate(8);
}
示例3-7:GS支持多种返回值类型
3.3.2 函数的返回值
3.3.2.1 return语句
在函数中使用 return; 或 return val;语句结束函数执行并返回函数的执行结果。函数可以返回与定义的返回类型匹配的任何函数类型。
GS 函数实际返回的值类型必须与 GS 函数定义的返回类型匹配,比如定义一个返回array的函数,那么我们在运行时就只能返回array 或nil