子程序(过程、函数、方法)
一般程序设计语言包含两种基本的抽象:过程抽象和数据抽象。过程抽象有时也称控制抽象。
子程序在1950年以前就发明了,作为一种抽象那时候并未被完全接受。相反,最初它被看做是一种节省代码的机制,但很快子程序就被认可为过程抽象的一种方式。意识到子程序可以作为一种抽象机制,这产生了三个重要结果。
- 人们发明了一些语言,支持各种参数传递机制
- 奠定了「结构化程序设计」的基础,语言开始支持嵌套的子程序(如JS的function,Java的inner class)
- 诞生了「结构化程序设计」,为试图构建大型系统提供了指导,利用子程序作为基本构建块
子程序是最主要过程抽象机制。面向对象语言中的方法与子程序的概念十分相似,不同在于它们的调用以及它们与类或对象关联的方式。
一、子程序的特征
- 每个子程序只有一个入口
- 在执行期间,调用程序单位被停止执行,即任意时刻只有一个子程序在执行
- 子程序执行完毕后,总是将控制返回给调用程序
注:协同程序 和 并发程序 的特征与子程序不同。
二、两种类型的子程序
子程序分为两种类型:过程 和 函数。
过程 是定义参数化技术的语句系列,通过单个调用语句来启动这些计算。过程实际上是定义了新的语句。在Ada中将过程称为procedures,但在Fortran中则将过程称为subroutines。
函数 在结构上模仿了过程,但在语义上却模仿了数学中的函数。如果它是一个纯函数,就不会有副作用。
注:某些编程语义同时提供了过程和函数,如Fortain和Ada;某些如基于C的语言则只有函数。但C中函数的行为却与过程相似,也可以定义这些函数不返回任何值,只要将它们的返回类型定义为void。Java/C++/C#类似。
三、子程序的首部定义
例如定义一个sum的子程序
Fortran
1 | Subroutine sum (parameters) |
Ada
1 | procedure sum (parameters) |
Python
1 | def sum (parameters) |
C/C++/Java
1 | int sum(parameters) |
JavaScript
1 | function sum(parameters) |
Ruby/JavaScript的函数与以上存在着一些有趣的不同之处。既可以在类定义时定义,也可以在类外定义。在类外定义的被认为是调用对象或根对象的方法。Ruby中如果return语句后没有表达式则返回nil,JavaScript中如果没有return则返回undefined。
四、参数
子程序通常描述的是计算,它通过两种方式来处理数据:
- 通过对非局部变量的直接访问
- 通过参数传递
前面已经提到过,纯函数只通过参数传递来计算。非纯函数是有 副作用 的。参数又分为形参和实参。
形参:子程序首部中定义的参数称为形参,又被称为 虚变量。因为它不是平常意义的变量,只有当子程序调用时它们才与存储空间 绑定。
实参:子程序调用语句包括子程序的名称及一组将子程序的形参绑定的参数,这些参数称为实参。
多数语言的实参与形参的绑定(关联) 是按位置进行的。即第一个实参对应于第一个形参,第二个实参对应于第二个形参。依次类推。如JavaScript代码
function sum(a, b) { return a + b; } sum(3, 5); |
函数sum定义了两个形参a, b。调用时对应的实参是3, 5。
使用位置将实参与形参关联起来是是十分有效且安全的方式。但当参数很多时(形参列表很长比如有10个以上参数),程序员很容易在调用子程序时在实参的次序上犯错。因此,有些语言中推出了解决方法称为关键字参数。
关键字参数 的意义是将形参名称与实参在一起声明。即在声明时就与实参一一对应了,它的好处是能以任何顺序出现在实参表中。如Python代码
1 2 3 | def sum ( len = myLen, list = myList, result = myResult) |
在函数定义时就将参数与变量一一对应。使用关键字参数的缺点是子程序的必须知道形参的名称。
参数默认值 在Python、Ruby、C++、Fortran 95、Ada和PHP中,形参可以有默认值。如果没有将实参传递给子程序的形参,那么将使用默认值。如Python
1 2 3 4 | def show(a = 5 ): print (a) show() |
定义函数show时形参a具有默认值5,调用show时如果没有传实参,那么默认打印出5。
注:Javascript之父Brendan Eich准备给该语言添加该项特性。Javascript中函数的定义有可能是如下形式:
1 2 3 | function fun(x=5,y=10){ } |
在多数语言中形参不具备默认值,调用时实参个数必须和形参匹配一致。但在C、C++、Perl和JavaScript中,则没有该项要求。虽然允许参数数目不同的设计容易引起错误,但有时也是很灵活方便的。例如C的printf函数就以打印任何数目的项。JavaScript也可以根据参数数目不同模拟函数的重载。
相关:
javascript-ecmascript-harmony-spidermonkey-spidernode-v8-v8monkey-nodejs-nodeconf/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端