函数,参数,返回值介绍

我被代码海扁 @codebeatme-s @103466775
阅读 7:19·字数 2199·发布 

函数

函数是编程中的重要概念,他提供可重复使用的功能,其内部逻辑对外隐藏,调用方需要向函数传递必要的信息,函数则将运算结果返回给调用方。

为何使用函数?
避免麻烦是一种单纯的动机,谁都无法忍受维护大量重复的代码,将功能和逻辑相近的代码放入一个函数中,并通过传递不同的信息,对其实现的功能进行细微的调整,这会是一件很好的事。

函数种类

不同语言对函数种类的划分可能不同,但从提供功能这一角度来看,很多概念可以归入函数概念中。比如,类的方法,类的属性,类的运算符,虽然他们在语法上与函数有所区别,但本质上却是一致的。

参数

为了更加的灵活,运用范围更广,函数拥有自己的参数,可以接受调用方赋值。通过这样的方式,函数所实现的功能将是可调整的。

可选参数

可选参数,顾名思义,是在调用函数时可以选择性设置的参数,如果你忽略他们,那么他们将被赋予默认值。

为何使用可选参数?
试想某个参数总被赋予同样一个值的情况,将其改为可选参数会是一个很好的方案。

在下面的 C# 代码中,函数Welcome拥有一个可选参数message,他的默认值是"你好!{0}"

functions.cs
// 显示欢迎消息的函数
void Welcome(string name, string message = "你好!{0}")
{
	Console.WriteLine(message, name);
}

// 可以不为可选参数 message 指定值 Welcome("大花猫");
你好!大花猫

可变参数

在大多数语言中,可变参数是通过数组来实现的,他使得函数可输入实参的个数不确定,开发人员可以根据自己的需要,传递一个,两个或更多。当你为函数定义可变参数后,一般不能继续定义其他参数,因为在为这些参数转递值时,可能无法确定传递目标。

什么是实参?
实参可以被理解为,在函数被调用时,实际传递给参数的值。

在下面的 C# 代码中,函数ListFruit拥有可变参数list,他可以接受多个实参。

functions.cs
// 显示一组水果的函数
void ListFruit(params string[] list)
{
	Console.WriteLine($"一共有 {list.Length} 个水果");

foreach (string fruit in list) Console.WriteLine(fruit); }

// 传递多个实参 ListFruit( "一个橘子", "一个苹果", "还是一个苹果" );
一共有 3 个水果
一个橘子
一个苹果
还是一个苹果

命名参数,位置参数

命名参数是指按照名称来为指定的参数赋值,这种方式可以让你在一定程度上忽略参数的顺序。与命名参数相对应的是位置参数,他按照参数定义时的位置来为参数赋值,这种方式是最为常见的。

在实际的开发过程中,命名参数和位置参数经常被混合使用,不过一些语言规定使用位置参数必须在使用命名参数之前。

在下面的 C# 代码中,函数CreatePlan拥有三个参数,namedaysleader

functions.cs
// 一个用于创建计划的函数
void CreatePlan(string name, int days, string leader)
{
	// 显示计划的相关信息
	Console.WriteLine($"计划 {name}:");
	Console.WriteLine($"\t天数 {days}");
	Console.WriteLine($"\t领导 {leader}");
}

// 调用函数 CreatePlan,days 参数最后赋值 CreatePlan( name: "搬家计划", leader: "铲屎官", days: 5 );
// 混合使用两种方式为参数赋值 CreatePlan( "另一个计划", days: 100, "另一个铲屎官" );
计划 搬家计划:
天数 5
领导 铲屎官
计划 另一个计划:
天数 100
领导 另一个铲屎官

返回值

在结束时,函数可以将某个值返回给调用方,以说明本次的执行情况或结果,这个值也就是函数的返回值。当你不指定返回值时,大部分语言会为函数返回一个默认的值。

下面的 C# 函数Sum返回了加法运算的结果。

functions.cs
// 一个用于计算加法的函数
int Sum(int a, int b)
{
	return a + b;
}

int result = Sum(1, 2); Console.WriteLine($"1 加 2 的结果为 {result}");
1 加 2 的结果为 3

函数签名

如果一种语言允许出现名称相同的函数,那么函数签名将用于标识这些同名函数,以确定具体被使用的是哪一个。函数签名一般根据函数的名称,参数的位置和类型等信息生成。

事实上,函数签名更妥当的称呼是方法签名,因为同名这种情况通常发生在类中。

在 C# 类Cannon中,两个Attack方法虽然名称一样,但由于参数不同,所以他们的签名也不同,会被认为是不同的方法。在传递不同的参数后,可以确定调用其中的哪一个。

functions.cs
// 创建 Cannon 类的实例 cannon
Cannon cannon = new();

// 通过不同的参数,调用不同的 Attack 方法 // 调用第一个 Attack 方法 cannon.Attack(100); // 调用第二个 Attack 方法 cannon.Attack(100, true);
// 类 Cannon,表示加农炮 class Cannon { // 第一个 Attack 方法,只有一个整数类型参数 public void Attack(int point) { Console.WriteLine($"可造成 {point} 点伤害"); }
// 第二个 Attack 方法,有一个整数类型参数和一个布尔类型参数 public void Attack(int point, bool critical) { Console.WriteLine($"可造成 {point} 点伤害 [暴击?{critical}]"); } }
可造成 100 点伤害
可造成 100 点伤害 [暴击?True]

函数的嵌套调用

嵌套调用是指在一个函数中调用另外一个函数,在实际开发中,这是司空见惯的,经典的场景为,在 C 语言的main函数中调用其他函数。

函数的递归调用

递归调用是指一个函数调用其自身,当然,调用需要符合一定条件,否则函数会尝试无限的递归下去,这会是一场灾难。

下面的 C# 函数SumArray,会将数组中的非数组元素视为整数,并计算他们的和,当元素是数组时,SumArray将进行递归调用。

functions.cs
// 将数组中所有非数组元素视为整数,并计算他们的和
int SumArray(object[] array)
{
	int result = 0;

// 循环数组中的元素,如果是数组,则调用 SumArray 自身 foreach (object item in array) { if (item is object[] a) result += SumArray(a); else result += (int)item; }
return result; }
// 一个复杂的包含整数的数组 object[] myArray = new object[] { new object[] { 1, 2 }, 10, 20, new object[] { 100, 200 }, };
// 调用 SumArray 进行计算 int myResult = SumArray(myArray); Console.WriteLine($"计算结果为:{myResult}");
计算结果为:333

源码

functions.cs·codebeatme/programming·GitHub