本文持续更新中……
⚠ 注意:伪代码没有任何固定的格式和规范,本文提供的格式仅为一个参考格式!
代码
在研究伪代码之前我们要先明白“代码”是什么东西,代码是由一系列带有特定含义的符号组成,它可以命令计算机去完成人想要完成的任务。计算机完成任务时是严格按照代码进行执行的,不会违背传递给它的代码。
代码下还有很多细分:
源代码
源代码通常简称为“源码”,顾名思义,“源代码”就是构成一个程序的最初始的代码,通常会使用文本形式来保存源代码,方便人对其进行阅读和修改。
源代码通常具有严格的格式要求,必须按照预定的规则进行书写,否则将会出现错误,不同编程语言对源代码的要求各不相同。
计算机无法直接理解源代码的含义。
二进制代码
二进制代码通常由“源代码”编译而来(也有直接人手动写二进制代码的情况),“编译”指的是使用计算机程序将“源代码”按照预定的规则转换为计算机能够理解的代码的过程。
计算机程序是由二进制代码构成的,计算机可以直接理解二进制代码的含义,但是二进制代码对于人类来说可读性非常低。
注:此处说的计算机可以理解的二进制代码指的是机器码,部分语言会将源代码编译成字节码等非机器码的二进制代码,此处不讨论这种情况,读者可以暂时忽略字节码这个概念。
伪代码
“伪代码”就是“假的代码”,它不用来编译成二进制代码,也不要求计算机能够理解它。伪代码没有任何格式要求,只需要能让人看懂即可。
不过虽然伪代码没有固定的规则,但是为了能够尽可能让更多的人能够足够快地理解伪代码的含义,所以还是有一些墨守成规的内容的。
基本概念
接下来我们来介绍编写伪代码时需要用到的一些概念。
变量
变量用来存储代码运行过程中需要的一些值,这些值可以是提前写在纸面上的固定值,也可以是通过算式计算得到的非固定值。
一个变量由变量名和变量值共同构成,变量名就是变量的名称,变量值就是这个变量代表的内容。
比如:
1 |
|
上面这段伪代码中,a
和b
就是写在纸面上的固定值所构成的变量,而c
就是由算式计算得到的非固定值所构成的变量。
同时变量是可以多次赋值的:
1 |
|
数据类型
在伪代码中我们经常需要使用变量来存储数据,一个变量有且只有一个类型信息,我们常用的类型有如下几种:
- 数值类型(
number
):代表一个数字,可以是整数、小数、虚数……上面三个变量都是数值类型。1
2
3a = 5 // 整数
b = 5.6 // 小数
c = 1i + 5 // 虚数 - 布尔类型(
boolean/bool
):布尔类型用来标识真(true
)和假(false
),其只有这两个可能的值,一个比较运算符返回的类型就是布尔类型。以上三个变量都是布尔类型。1
2
3a = true
b = false
c = 5 > 6 - 数组类型(
Array<T>
):由若干个相同类型的值并列构成的数据结构,通常使用[]
或{}
包裹,<T>
中的T
表示数组中包含的元素的类型。以上四个变量都是数组类型。1
2
3
4a = [0, 1, 2, 3] // 内容为数值的数组 - Array<number>
b = ['this', 'is', 'a', 'array'] // 内容为字符串的数组 - Array<string>
c = [true, true, false] // 内容为布尔类型的数组 - Array<boolean> / Array<bool>
d = [[0, 1, 2], [3], [], [4, 5]] // 内容为数组类型的数组 - Array<Array<number>>
访问数组时使用数组名[下标]
的格式,下标从 0 开始递增;可以使用数组名.length
访问数组的长度。 - 字符串类型(
string
):代表一个长度任意的字符串,字符串是由零个、一个或多个字符构成的数组(字符串使用成对的单引号或双引号括住,用来区分字符串的内容和变量名等信息)。以上四个变量都是字符串类型。1
2
3
4a = 'this is a string'
b = '5'
c = "true"
d = '' - 复合类型(
object
):该类型使用多个类型组合而成,复合类型可以相互嵌套。上面的变量是复合类型。1
2
3
4
5a = {
ia: 5
ib: { a: ['hello'] }
ic: true
}
在编写伪代码时可以在声明变量时写明数据类型也可以不写。
作用域
作用域是一个区域的概念,用于控制变量的可见范围,一个作用域中可以有零个、一个或多个子作用域,子作用域不能包含其父作用域。
假设我们有 4 个作用域,其包含关系为A > B > C > D
,则我们称:A
是B
的直接父域,是C``D
的间接父域;D
是C
的直接子域,是A``B
的间接子域。
为了方便,下文中我们提及“父域”就表示“直接父域和间接父域”,“子域”表示“直接子域和间接子域”。
在编写伪代码时我们为了方便起见通常使用缩进来表明作用域的包含关系。
比如:
1 |
|
上面的例子中A
是其余所有作用域的直接或间接父域,C
是D
的直接父域,也就是说缩进越大等级越低,顶格写的作用域被称为“全局域”,在其中声明的内容在当前伪代码的任意地方都能使用。
在一个作用域中声明的变量、函数仅能在当前作用域及其子域中使用,在一个作用域中不能声明一个和当前作用域和其父域中已有变量名称相同的变量,下面这段伪代码就是不合理的:
1 |
|
注:虽然这段代码被我们称为“不合理”的,不过由于伪代码并没有严格的规范,所以真非要这么写也不是不行,只是不推荐这么做。
布尔表达式
布尔表达式指的是最终返回一个布尔类型的值的算式(通常所有返回布尔类型的语句我们都可以叫做布尔表达式),常见的有:
a == b
- 双等号判断两个值是否相等a != b
- 不等号(或者写成感叹号后跟一个等号)判断a
是否不等于b
a < b
- 小于号判断a
是否小于b
b > b
- 大于号判断a
是否大于b
a <= b
- 小于等于号(或者写成一个小于号后跟一个等号)判断a
是否小于等于b
a >= b
- 大于等于号(或者写成一个大于号后跟一个等号)判断a
是否大于等于b
我们可以通过布尔运算符来连接两个布尔表达式,将其合并为一个表达式,常用的布尔运算符有:
a && b
- 且操作,当a
和b
的值都为true
时返回true
,否则返回false
a || b
- 或操作,当a
和b
的值至少有一个true
时返回true
,否则返回false
!a
- 非操作,当a
为true
时返回false
,否则返回true
,使用时注意感叹号紧贴后面的表达式
布尔运算符可以连用,以下用法都是合法的:
a && b && c && d
a || b || c
!a && b && !c
a || !b || !c
当&&
与||
一起使用时必须使用小括号表明运算顺序:
a && (b || c)
- 表示a
为真并且b
和c
中至少有一个为真(a && b) || c
- 表示a
和b
都为真或者c
为真
小括号可以无限嵌套,运算时先计算小括号内的表达式。
函数
函数是一段有名称、有输入、有动作的代码,与普通代码不同的是函数的输入一般不使用input
之类的语句从控制台输入,而是通过“函数参数”输入。
函数由三部分组成:
- 函数名称和参数
- 函数体(即函数执行的代码)
- 返回值
调用函数时代码将转移到函数中执行,函数执行完毕后携带返回值返回到调用位置。伪代码中常用的input
、print
其实就是特殊的函数,它们可以从控制台输入内容或输出内容到控制台。
比如我们可以使用类似以下的语法从控制台输入数字:
1 |
|
输入、输出的写法有很多种,只要能够清晰地表达出代码的含义即可。
自定义函数
我们可以使用类似以下的语法来自定义一个函数:
1 |
|
其中fun
用来表明这个语句是在定义一个函数;sum
是函数名称,可以使用任意数量的有实际含义的英文单词组合而成;冒号后面的是函数的返回值类型,可写可不写;下面缩进的内容就是函数体,其中的return
语句就是向函数的调用处输出返回值的语句。
函数参数的作用域为整个函数体,不得直接修改参数(参数类型为数组或复合类型时可以修改其中的值)。
调用函数的方法也有很多种,常用的方法就是函数名(参数列表)
的形式,参数之间使用逗号间隔,比如可以用a = sum(1, 5)
来调用并存储sum
函数的返回值,调用后a
的值为6
。
对于一些特殊的函数我们还可以使用其它的调用方法,比如我们想要表示将一个字符串转换为数字就可以使用类似a = '12345'.toNumber()
的调用方法,调用后a
的值为12345
而不是'12345'
。
return
return
是函数中的一个非常特殊的控制语句,它用来设置函数的返回值,同时return
语句执行后该函数就会结束执行,剩下的代码将被跳过,比如下面两个代码的效果就是完全等价的:
1 |
|
1 |
|
内置函数
有一些非常简单的功能我们可以直接假定该函数已经存在,不需要再自己手动编写一遍,比如说:
- 使用
sum
函数求任意个数字的和 - 使用
max
求任意个数字之间的最大值 - 使用
min
求任意个数字之间的最小值 - 使用
floor
表示对数字进行向下取整 - 使用
random
生成一个随机数 - ……
只要功能足够简单且函数名字能够准确清晰地描述函数的功能,我们就可以完全省略函数的定义,假设其为一个已存在的函数。
常用结构
接下来我们来介绍伪代码编写时常用的三种结构。
顺序结构
顺序结构是最常见的结构,它随处可见。顺序结构就是代码按照顺序向下执行,比如最常见的:
1 |
|
分支结构
分支结构就是在顺序结构的基础上添加一个或多个条件分支,分支结构中通常包含if
或者when
语句,下面我们分别介绍这两种语句的作用。
if
if
语句是最常用的条件判断语句,其作用是判断指定条件是否成立,if
语句的写法如下:
1 |
|
其中else if
是当其前面所有的if
和else if
条件都不成立且当前else if
条件成立时会进入当前分支;else
是当前面所有if
和else if
条件都不成立时触发。
else if
和else
都是可选项,可有可无;else if
可以有任意个,else
只能有一个且必须放置在最后。
expression
是一个布尔表达式,表示当前分支的分支条件,仅有布尔表达式值为真时才会进入当前分支。
比如下面两个伪代码的效果是完全相同的,现在我们假设有两个数值变量a
和b
:
1 |
|
1 |
|
不过虽然效果等价,但是为了减少冗余的内容,逻辑上需要连起来的if
我们还是使用if-else-if
结构将其连接起来。
when
when
是if
的特化版,可以用来判断某个变量是否满足某个条件,比如下面的代码:
1 |
|
这个代码和上面给的if-else-if
的代码效果是完全等价的,在条件比较多时比较好用。
when
的条件语句中还可以放一些其它比较复杂的表达式,执行时从上向下判断,遇到第一个满足的表达式时执行其对应的分支内容然后退出when
语句。
循环结构
循环结构就是重复执行循环体中的代码,在循环条件不满足后退出循环。
有两种常用的循环代码,for
和while
:
for
for
有两种写法,第一种也是最基本的是:
1 |
|
其中“初始化语句”用于初始化循环变量,我们可以在这个表达式的位置声明变量或修改已有变量的值,在初始化表达式中声明的变量仅在for
循环中可用。
“循环条件”应当是一个布尔表达式,用来表示是否继续循环,当布尔表达式返回false
时将自动退出循环。
“循环变量更新表达式”用于更新循环变量,此处可以修改循环变量的值。
for
循环的执行顺序如下:
- 初始化循环变量,执行初始化语句
- 判断循环条件,如果为
false
则退出循环 - 执行循环体
- 执行循环变量更新表达式
- 回到 (2)
如果我们想遍历一个数组,我们可以这么写:
1 |
|
for
循环还有另一种写法,用于遍历一个可迭代的结构(通常为数组、链表……),格式为:
1 |
|
现在让我们使用这种写法来遍历数组(遍历指的是顺序访问可迭代结构中的所有值):
1 |
|
我们给出的这两个for
循环的代码示例效果是完全相同的,很显然如果for
循环用于遍历一个可迭代结构的话使用后者会更加简单便捷。
如果我们需要在遍历过程中访问下标,可以使用下面的写法:
1 |
|
下标的变量名我们通常使用i
、j
、k
或index
。
while
while
循环也有两种写法,第一种是先判断循环条件再执行循环体:
1 |
|
第二种是先执行循环体再判断循环条件:
1 |
|
while
相比于for
就是少了初始化语句和变量更新语句,其它没有任何区别。
控制语句
我们可以在循环体中使用控制语句来控制循环的执行,使用break
即可立即跳出循环,使用continue
可以在本次循环中跳过循环体中剩余的代码。