激活函数

指数类

Sigmoid

将实数映射到(0, 1)区间

  • $z= wx+b$
  • 用途

    • 隐层神经元输出
    • 二分类输出
  • 缺点

    • 激活函数计算量大,BP算法求误差梯度时,求导涉及除法
    • 误差反向传播时容易出现梯度消失
    • 函数收敛缓慢

Hard_Sigmoid

计算速度比sigmoid激活函数快

  • $z= wx+b$

Softmax

主要用于多分类神经网络输出

  • $z_i = w_i x + b_i$:$(w_i, b_i)$组数同分类数量,和输入 $x$维度无关

  • $K$:分类数目

  • 工程意义:指数底

    • 可导$max$:拉开数值之间差距
    • 特征对输出结果为乘性:即$z_i$中输入增加会导致输出 随对应权重倍数增加
    • 联合交叉熵损失避免导数溢出,提高数值稳定性
  • 理论意义:概率论、最优化

    • softmax符合最大熵原理
    • 假设各标签取值符合多元伯努利分布,而softmax是其 link functiond的反函数#todo
    • 光滑间隔最大函数
  • Softmax回归参数$(w_i, b_i$$冗余,可以消去一组

Softplus

  • $z = wx + b$

Tanh

双曲正切函数

  • $z = wx + b$
  • $\frac{\partial tanh(z)}{\partial z} = (1 - tanh(z))^2$ :非常类似普通正切函数,可以简化梯度计算

线性类

Softsign

ReLU

Rectfied Linear Units:修正线性单元

LeakyReLU

Leaky ReLU:带泄露的修正线性

  • $\alpha$:超参,建议取0.01
  • 解决了$z < 0$时进入死区问题,同时保留了ReLU的非线性特性

Parametric ReLU

PReLU:参数化的修正线性

  • $\alpha$:自学习参数(向量),初始值常设置为0.25,通过 momentum方法更新

ThreshholdReLU

带阈值的修正线性

Linear

线性激活函数:不做任何改变

线性指数类

Exponential Linear Unit

Elu:线性指数

  • $\alpha$:超参
  • $x \leq 0$时,$f(x)$随$x$变小而饱和
    • ELU对输入中存在的特性进行了表示,对缺失特性未作定量 表示
  • 网络深度超超过5层时,ELU相较ReLU、LReLU学习速度更快、 泛化能力更好

Gausssion Error Liear Unit

GELU:ReLU的可导版本

Selu

可伸缩指数线性激活:可以两个连续层之间保留输入均值、方差

  • 正确初始化权重:lecun_normal初始化
  • 输入数量足够大:AlphaDropout
  • 选择合适的$\alpha, scale$值

梯度消失

激活函数导数太小($<1$),压缩误差(梯度)变化

距离函数

距离

  • 距离可认为是两个对象 $x,y$ 之间的 相似程度
    • 距离和相似度是互补的
    • 可以根据处理问题的情况,自定义距离

Bregman Divergence

  • $Phi(x)$:凸函数
  • 布雷格曼散度:穷尽所有关于“正常距离”的定义

    • 给定 $R^n * R^n \rightarrow R$ 上的正常距离 $D(x,y)$,一定可以表示成布雷格曼散度形式
    • 直观上:$x$处函数、函数过$y$点切线(线性近似)之差
      • 可以视为是损失、失真函数:$x$由$y$失真、近似、添加噪声得到
  • 特点

    • 非对称:$D(x, y) = D(y, x)$
    • 不满足三角不等式:$D(x, z) \leq D(x, y) + D(y, z)$
    • 对凸集作 Bregman Projection 唯一
      • 即寻找凸集中与给定点Bregman散度最小点
      • 一般的投影指欧式距离最小
Domain $\Phi(x)$ $D_{\Phi}(x,y)$ Divergence
$R$ $x^2$ $(x-y)^2$ Squared Loss
$R_{+}$ $xlogx$ $xlog(\frac x y) - (x-y)$
$[0,1]$ $xlogx + (1-x)log(1-x)$ $xlog(\frac x y) + (1-x)log(\frac {1-x} {1-y})$ Logistic Loss
$R_{++}$ $-logx$ $\frac x y - log(\frac x y) - 1$ Itakura-Saito Distance
$R$ $e^x$ $e^x - e^y - (x-y)e^y$
$R^d$ $\ x\ $ $\ x-y\ $ Squared Euclidean Distance
$R^d$ $x^TAx$ $(x-y)^T A (x-y)$ Mahalanobis Distance
d-Simplex $\sum_{j=1}^d x_j log_2 x_j$ $\sum_{j=1}^d x_j log_2 log(\frac {x_j} {y_j})$ KL-divergence
$R_{+}^d$ $\sum_{j=1}^d x_j log x_j$ $\sum{j=1}^d x_j log(\frac {x_j} {y_j}) - \sum{j=1}^d (x_j - y_j)$ Genelized I-divergence
  • 正常距离:对满足任意概率分布的点,点平均值点(期望点)应该是空间中距离所有点平均距离最小的点
  • 布雷格曼散度对一般概率分布均成立,而其本身限定由凸函数生成
    • Jensen 不等式有关?凸函数隐含部分对期望的度量
  • http://www.jmlr.org/papers/volume6/banerjee05b/banerjee05b.pdf

单点距离

Minkowski Distance

闵科夫斯基距离:向量空间 $\mathcal{L_p}$ 范数

  • 表示一组距离族

    • $p=1$:Manhattan Distance,曼哈顿距离
    • $p=2$:Euclidean Distance,欧式距离
    • $p \rightarrow \infty$:Chebychev Distance,切比雪夫距离
  • 闵氏距离缺陷

    • 将各个分量量纲视作相同
    • 未考虑各个分量的分布

Mahalanobis Distance

马氏距离:表示数据的协方差距离

  • $\Sigma$:总体协方差矩阵
  • 优点
    • 马氏距离和原始数据量纲无关
    • 考虑变量相关性
  • 缺点
    • 需要知道总体协方差矩阵,使用样本估计效果不好

LW Distance

兰氏距离:Lance and Williams Distance,堪培拉距离

  • 特点
    • 对接近0的值非常敏感
    • 对量纲不敏感
    • 未考虑变量直接相关性,认为变量之间相互独立

Hamming Distance

汉明距离:差别

  • $v_i \in {0, 1}$:虚拟变量
  • $p$:虚拟变量数量
  • 可以衡量定性变量之间的距离

Embedding

  • 找到所有点、所有维度坐标值中最大值 $C$
  • 对每个点 $P=(x_1, x_2, \cdots, x_d)$
    • 将每维 $x_i$ 转换为长度为 $C$ 的 0、1 序列
    • 其中前 $x_i$ 个值为 1,之后为 0
  • 将 $d$ 个长度为 $C$ 的序列连接,形成长度为 $d * C$ 的序列
  • 以上汉明距离空间嵌入对曼哈顿距离是保距的

Jaccard 系数

Jaccard 系数:度量两个集合的相似度,值越大相似度越高

  • $S_1, S_2$:待度量相似度的两个集合

Consine Similarity

余弦相似度

  • $x_1, x_2$:向量

欧式距离

点到平面

  • $T={(x_1,y_1),(x_2,y_2),\cdots,(x_n,y_n)}$:样本点集
  • $wx + b = 0$:超平面
Functional Margin 函数间隔
  • 函数间隔可以表示分类的正确性、确信度

    • 正值表示正确
    • 间隔越大确信度越高
  • 点集与超平面的函数间隔取点间隔最小值 $\hat{T} = \min_{i=1,2,\cdots,n} \hat{\gamma_i}$

  • 超平面参数 $w, b$ 成比例改变时,平面未变化,但是函数间隔成比例变化

Geometric Margin 几何间隔
  • 几何间隔一般是样本点到超平面的 signed distance

    • 点正确分类时,几何间隔就是点到直线的距离
  • 几何间隔相当于使用 $|w|$ 对函数间隔作规范化

    • $|w|=1$ 时,两者相等
    • 几何间隔对确定超平面、样本点是确定的,不会因为超平面表示形式改变而改变
  • 点集与超平面的几何间隔取点间隔最小值 $\hat{T} = \min_{i=1,2,\cdots,n} \hat{\gamma_i}$

Levenshtein/Edit Distance

(字符串)编辑距离:两个字符串转换需要进行插入、删除、替换操作的次数

组间距离

Single Linkage

Average Linkage

Complete Linkage

函数

齐次函数

齐次函数:有倍数性质的函数,若变量乘以系数 $\alpha$,则新函数为原函数乘上 $\alpha^k$ 倍

  • $\alpha \neq 0 \in F, x \in X$
  • $f: X \rightarrow W$:域 $F$ 内两个向量空间之间的 $k$ 次齐次函数
  • 线性函数 $f: X \rightarrow W$ 是一次齐次函数
  • 多线性函数 $f: x_1 x_2 \cdots * x_n \rightarrow W$ 是 $n$ 次齐次函数

基本定理

  • 欧拉定理:函数 $f: R^n \rightarrow R$ 可导、$k$ 次齐次函数,则有 $x \nabla f(x) = kf(x)$

数据模型--函数决定对象

函数

用户定义函数

用户定义函数:通过函数定义创建,调用时附带参数列表

  • 函数对象支持获取、设置任意属性
    • 用于给函数附加元数据
    • 使用属性点号.获取、设置此类属性

特殊属性

  • __defaults__:有默认值参数的默认值组成元组
    • 没有具有默认值参数则为None
  • __code__编译后函数体代码对象
  • __globals__:存放函数中全局变量的字典的引用
    • 即引用函数所属模块的全局命名空间
    • 只读
  • __closure__:包含函数自由变量绑定单元的元组
    • 没有则为None
    • 只读
  • __annotations__:包含参数注释的字典
    • 字典键为参数名、return(若包含返回值)
    • 将变量名称(非私有)映射到标注值的特殊字典
    • 若该属性可写、在类或模块体开始执行时,若静态地发现 标注则被自动创建
  • __kwdefaults__keyword-only参数的默认值字典
  • 大部分可写属性会检查赋值类型
  • 自由变量:上层命名空间中变量,不包括顶级命名空间,即 全局变量不是自由变量

实例/绑定方法

实例/绑定方法:使用属性表示法调用定义在类命名空间 中的函数

  • 实例方法用于将类、类实例同可调用对象结合起来

    • 可调用对象:须为类属性,即定义在类命名空间中, 通常为用户定义函数、类方法对象
  • 通过实例访问类命名空间定义函数时创建实例/绑定方法

    • 通过示例、类访问的命名空间定义函数类型不同
      • wrapper_descriptor转换为method-wrapper
      • function转换为method
      • 通过类访问方法,得到是普通函数
    • 绑定:此时会将self作为首个参数添加到参数列表
    • 调用实例方法时,调用相应下层函数
  • 函数对象到实例方法对象的转换每次获取实例该属性时都会发生

    • 有时可将属性赋值给本地变量、调用实现性能优化
    • 非用户定义函数、不可调用对象在被获取时不会发生转换
    • 实例属性的用户定义函数不会被转换为绑定方法,仅在函数 是类的属性时才会发生
  • Py3中以上特性依赖于___getattribute__实现

属性

  • 绑定方法对象支持只读获取底层函数对象任意属性

    • 但方法属性实际保存在下层函数对象中
    • 所以不能直接设置绑定方法的方法属性,必须在下层函数 对象中显式设置,否则raise AttributeError
  • 绑定方法有两个特殊只读属性

    • m.__self__:操作该方法的类实例
    • m.__func__:底层实现该方法的函数
  • m(args,...)完全等价于m.__func__(m.__self__,args,...)

特殊元属性

  • __self__:类对象实例
  • __func__:函数对象实例
  • __doc__:方法文档,等同于__func__.__doc__
  • __name__:方法名,等同于__func__.__name__
  • __module__:定义方法所在模块名

Class Method Objects

类方法:提供了始终将类绑定为函数对象首个参数的方式

  • 对其他对象的封装,通常用于封装用户定义方法对象
  • 会改变从类、类实例获取该对象的方式
  • 用途
    • 实现自定义、多个构造器,如
      • 只调用__new__()、绕过__init__,创建未初始化 的实例
      • 反序列化对象:从字节串反序列构造符合要求的对象
  • 通过classmethod()构造器创建
  • Py3中以上特性依赖于___getattribute__实现

Static Method Objects

静态方法:提供了避免将函数对象转换为方法对象的方式

  • 对任意其他对象的封装,通常用于封装用户定义方法对象
  • 从类、类实例获取静态方法对象时,实际返回的是封装的对象, 不会被进一步转换
  • 静态方法对象自身不是可调用的,但其封装的对象通常可调用
  • 通过内置staticmethod()构造器创建
  • Py3中以上特性依赖于___getattribute__实现

内置函数、方法

  • 内置函数:对C函数的外部封装
  • 内置方法:内置函数另一种形式,(类似实例方法)隐式传入 当前实例作为C函数额外参数
  • 包括以下两种类型
    • builtin_function_or_method
    • wrapper_descriptor
  • 参数数量、类型由C函数决定
  • 内置方法由支持其的类型描述

特殊元属性

  • __self__<module 'builtins' (built-in)>
  • __doc__:函数/方法文档
  • __name__:函数/方法名
  • __module__:所在模块名

说明

  • 函数是描述器,函数类function实现有__get__方法
  • function.__get__即将函数首个参数进行绑定,返回绑定方法
  • 参见cs_python/py3ref/cls_special_methods

Generator Functions

生成器函数:使用yield语句的函数、方法称为生成器函数

  • 生成器函数调用时返回生成器迭代器对象,控制执行函数体
  • yield表达式参见cs_python/py3ref/expressions

Generator-Iterator

生成器迭代器:生成器函数执行得到的迭代器

  • 实现有迭代器协议__next__的迭代器类实例不同于 生成器迭代器,其仅实现迭代

    • 迭代执行过程即__next__函数调用,重入(状态维护) 由类负责
    • 无不同迭代状态区别
    • 不会自动获得.send.throw.close等方法
  • 或者说生成器迭代器是:利用yield表达式对迭代器的的简化 实现,并预定义.send.throw.close方法

  • 迭代器协议参见cs_python/py3ref/cls_special_methods

执行过程

  • 其某方法被调用时,生成器函数开始执行
  • 执行到第一个yield表达式被挂起,保留所有局部状态
    • 局部变量当前绑定
    • 指令指针
    • 内部求值栈
    • 任何异常处理状态
  • 返回expression_list
  • 调用生成器某方法,生成函数继续执行
  • 执行return、函数体执行完毕将raise StopIteration
  • 生成器表达式、yield表达式参见 cs_python/py3ref/expressions

生成器迭代器状态

生成器在声明周期中有如下4中状态

  • "GEN_CREATED":等待执行
  • "GEN_RUNNING":正在执行,只有多线程中才能看到
  • "GEN_SUSPENDED":在yield表达式处挂起状态
  • "GEN_CLOSED":关闭状态
  • 可通过inspect.getgeneratorstate()方法查看

__next__

1
2
def(pre) generator.__next__():
pass
  • 用途:开始生成器函数执行、或从上次执行yield表达式处恢复 执行

    • 生成器函数通过__next__方法恢复执行时,yield表达式 始终取值为None
    • 执行至下个yield表达式
  • 返回值:生成器迭代器产生的下个值

    • yield表达式中expression_list
    • 若生成器没有产生下个值就退出,raise StopIteration
  • 此方法通常隐式通过for循环、next()函数调用

send

1
2
def(pre) generator.send(value):
pass
  • 用途:恢复生成器函数执行并向其“发送”值value

    • value参数作为yield表达式的结果
    • 执行至下个yield表达式
  • 返回值:生成器迭代器产生的下个值

    • yield表达式中expression_list
    • 若生成器没有产生下个值就退出,raise StopIteration
  • 说明

    • 若生成器中包含子迭代器,send传参数值将被传递给 下层迭代器,若子迭代器没有合适接收方法、处理,将 raise AttributeErrorraise TypeError
    • .send方法参数为None时实际不会有传递参数行为
      • 调用.send()启动生成器时,必须以None作为调用 参数,因为此时没有可以接收值的yield表达式
      • 子迭代器中没有处理参数时,.send(None)也能正常 工作

throw

1
2
def(pre) generator.throw(type[, value[, traceback]]):
pass
  • 用途:在生成器暂停位置处引发type类型异常

    • 若异常被处理:则执行直至下个yield表达式
    • 若生成器函数没有捕获传入异常、或引发另一个异常:异常 会被传播给调用者
  • 返回值:返回生成器产生的下个值(若异常被处理)

    • 若生成器没有产生下个值就退出,raise StopIteration
  • 说明

    • 若生成器中包含子迭代器,throw传入异常将被传递给 子迭代器
    • 调用throw启动生成器,会在生成器函数开头引发错误

close

1
2
def(pre) generator.close():
pass
  • 用途:在生成器函数暂停处raise GeneratorExit
    • 若之后生成器函数正常退出、关闭、引发GeneratorExit (生成器中未捕获该异常):则关闭生成器并返回调用者
    • 若生成器继续产生值:则raise RuntimeError
    • 若生成器引发其他异常:则传播给调用者
    • 若生成器由于异常、或正常而退出:则无操作

Yield表达式—调用外部函数

  • 生成器函数执行yield表达式类似调用外部函数
  • 当前函数栈保留当前状态、挂起
  • 执行yield表达式“完毕”后,“返回”到调用处
  • yield表达式“返回值”取决于生成器恢复执行所调用方法
    • .__next__fornext()调用,返回None
    • .send():返回.send()参数
  • 此时整体类似于
    • 主调函数调用生成器函数
    • 生成器函数调用yield表达式

生成器函数—协程

  • 生成器函数类似协程,也被称为semi-coroutine,是协程子集
  • 相似点
    • yield多次,有多个入口点
    • 执行可以被挂起
    • 可在恢复执行时传递参数控制执行
  • 不同点
    • yield后控制权总是转移给生成器迭代器调用者,生成器 函数不能控制yield后继续执行的位置 (yield表达式仅仅传递值给父程序,并不是指定需要 跳转的、平等的协程)

生成器函数—try

  • try结构中任何位置都允许yield表达式
  • 若生成器在销毁之前没有恢复执行(引用计数为0、被垃圾 回收),try结构中的yield表达式挂起可能导致finally 子句执行失败

  • 此时应由运行该异步生成器的事件循环、或任务调度器负责调用 生成器-迭代器close()方法,从而允许任何挂起的finally 子句得以执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def echo(value=None):
print("execution start")
try:
while True:
try:
value = (yield value)
except Exception as e:
value = e
finally:
print("clean up when gen.close() is called")

if __name__ == "__main__":
gen = echo(1)
print(next(gen)) # 1
print(next(gen)) # None
print(gen.send(2)) # 2
print(gen.throw(TypeError, "spam")) # TypeError('spam',)
gen.close() # clean up...

Coroutine Function

协程函数:使用async def定义的函数、方法

  • 调用时返回一个coroutine对象
  • 可能包含await表达式、async withasync for语句
    • 即协程函数能在执行过程中挂起、恢复
  • 协程参见cs_program/#todo

Coroutine Objects

协程对象:属于awaitable对象

  • 协程执行:调用__await__,迭代其返回值进行控制

    • 协程结束执行、返回时,迭代器raise StopIteration, 异常的value属性将指向返回值
    • 等待协程超过一次将raise RuntimeError
  • 若协程引发异常,其会被迭代器传播

    • 协程不应该直接引发未处理的StopIteration
  • 可等待对象参见cs_python/py3ref/cls_special_methods

send

1
2
def(pre) coroutine.send(value):
pass
  • 用途:开始、恢复协程执行

    • value is None:相当于等待__await__()返回迭代器 结果下一项
    • value is not None:此方法将委托给导致协程挂起的 迭代器的send()方法
  • 返回值:返回值、异常等同对__await__()返回值迭代结果

throw

1
2
def(pre) coroutine.throw(type[, value[, traceback]]):
pass
  • 用途:在协程内引发指定异常

    • 此方法将委托给导致协程内挂起的迭代器的throw()方法 ,若其存在
    • 否则异常在挂起点被引发
  • 返回值:返回值、异常等同对__await__()返回值迭代结果

    • 若异常未在协程内被捕获,则传回给主调者

close

1
2
def(pre) coroutine.close():
pass
  • 用途:清理自身并退出
    • 若协程被挂起,此方法先被委托给导致该协程挂起的迭代器 的.close()方法,若其存在
    • 然后在挂起点raise GeneratorExit,使得协程立即清理 自身
    • 最后协程被标记为已结束执行,即使未被启动
    • 协程对象将被销毁时,会被自动调用

Asynchronous Generator Functions

异步生成器函数:包含yield语句、使用async def定义函数、 方法(即在协程中)

  • 返回异步生成器迭代器对象,控制执行函数体
  • yield from表达式在异步生成器函数中使用将引发语法错误

Asynchronous Generator-Iterator

异步生成器迭代器

  • 异步体现:async def定义协程函数中可以使用异步代码

  • 异步生成器迭代器方法返回的可等待对象执行同一函数栈

    • 可等待对象被await运行时才会执行异步函数体
    • 类似普通生成器迭代器,执行到yield表达式挂起
    • 则连续返回多个可等待对象可以乱序await
    • 可以视为返回待执行函数体
  • 异步生成器迭代器执行过程、yield表达式、try结构、同异步 迭代器协议关联, 均参见普通生成器迭代器

  • 异步迭代器协议参见cs_python/py3ref/cls_special_methods

  • 异步生成器函数使用yield from语句将引发语法错误

最终化处理#todo

处理最终化:事件循环应该定义终结器函数

  • 其接收异步生成器迭代器、且可能调用aclose()方法并执行 协程
  • 终结器可以通过调用sys.set_asyncgen_hooks()注册
  • 首次迭代时,异步生成器迭代器将保存已注册终结器以便最终化 时调用

__anext__

1
2
async def(pre) coroutine agen.__anext__():
pass
  • 用途:返回可等待对象,可等待对象运行时开始、恢复异步 生成器函数执行

    • 异步生成器函数通过__anext__方法恢复执行时,返回的 可等待对象中yield表达式始终取值为None
  • 可等待对象返回值:返回异步生成器的下个值

    • 执行至下个yield表达式,返回expression_list
    • 若异步生成器没有产生下个值就退出,则 raise StopAsyncIteration
  • 此方法通常由async for异步循环隐式调用

asend

1
2
async def(pre) coroutine agen.asend(value):
pass
  • 用途:返回可等待对象,可等待对象运行时开始、恢复异步 生成器函数执行,并向其“发送”值value

    • value参数作为yield表达式的结果
    • 执行至下个yield表达式
  • 返回值:异步生成器产生的下个值

    • yield表达式中expression_list
    • 若生成器没有产生下个值就退出,则 raise StopAsyncIteration
  • 说明

    • 调用.asend()启动生成器时,必须以None作为调用参数

athrow

1
2
async def(pre) coroutine agen.athrow(type[, value[, traceback]]):
pass
  • 用途:返回可等待对象,可等待对象运行时将在异步生成器函数 暂停位置处引发type类型异常

    • 若异常被处理:则执行直至下个yield表达式
    • 若异步生成器函数没有捕获传入异常、或引发另一个异常: 可等待对象运行时异常会被传播给调用者
  • 返回值:返回生成器产生的下个值(若异常被处理)

    • 若生成器没有产生下个值就退出,则 raise StopAsyncIteration
  • 说明

    • 调用athrow启动异步生成器,会在函数开头引发错误

aclose

1
2
async def(pre) coroutine agen.aclose():
pass
  • 用途:返回可等待对象,可等待对象运行时在异步生成器函数 暂停处raise GeneratorExit
    • 若异步生成器函数正常退出、关闭、引发GeneratorExit (生成器中未捕获该异常):则运行可等待对象将 raise StopIteration???
    • 后续调用异步生成器迭代器方法返回其他可等待对象:则 运行可等待对象将raise StopAsyncIteration
    • 若异步生成器函数产生值:则运行可等待对象将 raise RuntimeError
    • 若异步生成器迭代器已经异常、正常退出,则后续调用 aclose方法将返回无行为可等待对象

C++函数

函数定义、声明

函数:被组织成具有特定名称的独立单元的代码块

  • 将某段操作代码组织起来,编写一次、多次使用,可以显著降低 程序规模,而且使程序更易于维护

  • 将大型程序分解成多个易于管理的小部分

    • 好的、独特的分解分解方法,会使得每个函数都是紧密的 单元,使得问题整体更加易于理解
    • top-down design:过程一般从主程序开始分解问题, 逐步求精
    • 即使函数只在程序中使用一次,定义函数依然值得
1
2
3
type name(parameters){
body
}
  • name:函数名
  • parameters:逗号分隔的函数形参列表

Parameters

形参:调用函数时用以传递实参的占位符

  • 类似局部变量,但是在调用时使用实参进行初始化
  • 如果需要使用形参值,可以忽略其形参名,即使是在 函数实现中也可以忽略,如:++后缀重载

Default Parameter

默认形参:具有默认值的形参,调用时可以不给其传递实参值

  • 默认形参只能出现在函数声明中,不能出现在函数定义中
  • 默认形参只能出现在形参列表末尾
  • 默认形参在C++中有滥用的问题,更倾向使用函数重载完成默认 形参的功能
  • 默认形参、函数重载同时使用可能导致编译器无法识别应该调用 何者而报错

Value Parameter

值参数:函数调用时,被调函数中值形参将获得主调函数的实参的 值拷贝

  • 被调函数中传入的实参变量值仅改变被调用函数局部形参 的值,对主调函数中实参变量的值没有影响

Reference Parameter

引用参数&:函数调用时,被调函数中引用形参获得主调函数中实参 引用

  • 主调函数、被调函数共享实参变量的统一存储空间,不需要复制 实参变量中的值,有时更高效

  • 对应引用形参的实参必须是可赋值的量,如:变量名

    • 实参可以是指针,即参数可以同时有* &

      1
      int insertAVL(BSTNode * & t, const string & key);
  • 常用于

    • 需要保存函数对参数值的修改
    • 函数需要返回多个值:并通过实参列表向函数传递、获取值

Constant Reference Parameter

常量引用调用const &:常量引用作为函数参数调用

  • 传递对象时,常量引用调用通常优于传统引用调用、值调用, 提供了引用传递的高效性、值传递的安全性

  • 需要注意参数中const关键字的位置

    1
    2
    3
    4
    5
    int strlen(const char * cptr);
    // `const`后是类型名,表明`cptr`是指向`const char`指针
    // 不能改变`cptr`指向的支付串内容
    int strlen(char * const cptr);
    // `const`后是形参名,表明`cptr`常量,其值不能改变
  • 使用常量引用需要参数类是constant correct,能够提供更多 的关于类中定义的方法的信息

Prototype

函数原型/函数声明:函数定义的首行加上分号结尾组成

  • 提供编译器大部分情况下仅仅需要的形参、返回值类型

  • 函数原型中形参名可选,但是好的形参名有助于可读性

  • 如果函数先定义后调用,可以不需要编写函数原型,但这种代码 风格和自顶向下的程序设计风格相悖

Signature

函数签名:函数的形参模型

  • 和函数原型相比,不包括返回值类型

Overloading

函数名重载:使用相同名字的不同版本函数

  • 函数名相同、函数参数列表不同是合法的,即函数签名不同即 合法(函数原型不同不一定合法,返回值类型不同)

    • 形参数量
    • 形参类型
  • 编译器遇到调用函数的函数名指代不唯一时,编译器会检查所传 实参,选择最合适的函数版本

Calling

使用函数名调用函数代码(块)的行为

  • 函数被调用后将会获得函数argument提供的值,执行函数功能

  • 返回函数调用点:记忆主调程序工作情况,以便程序 返回函数调用的确切位置是函数调用机制的主要特性之一

  • argument:实参,调用函数时的表达式,用于向函数传递信息
  • 调用函数前必须对函数提供声明或定义,以使编译器可以判断 函数调用是否与其定义兼容

函数调用步骤

  • 主调函数将实参与自己上下文中的若干局部变量绑定来 计算每个参数值

    • 实参通常为表达式,计算其值时可能涉及操作符、其他函数 调用
    • 新的函数开始执行前,主调函数会对传如的实参合法性进行 验证
  • 系统为新函数所需的所有局部变量(包括形参)创建新的存储 空间

    • 这些变量将被分配在内存中stack frame区域中
  • 每个实参值将被传入到函数相应的形参变量中

    • 对于包含多个形参的函数,实参对形参的值拷贝将按照 对应函数形参顺序执行
    • 如有必要,编译器将像变量赋值一样,执行从实参到 形参的类型转换
    • 对引用参数,栈帧会存储一个指针指向该值内存单元
  • 执被调函数体中语句,直到遇到return语句或没有多余语句

  • 如果函数有返回值,函数体内return语句表达式的值将被计算 ,作为返回值返回给主调函数

    • 如有必要,编译器将执行数值的类型转换,确保返回值 符合被调函数值的类型要求(被调函数返回之前转换)
      1
      2
      3
      4
      5
      6
      7
      8
      int rint(){
      return 9.8;
      // 返回整形
      }
      int main(){
      double j = rint();
      // `j`被初始化为`9.8`
      }
  • 删除为函数调用创建的栈帧,其中所有局部变量被系统清理

  • 将函数返回值(若存在)代入到调用函数调用点的位置

Pointer to a Function

函数指针:函数的第一条指令地址

1
2
3
4
double *g(double);
// 返回double类型指针的函数g
double (*fn)(double);
// 返回double类型的函数指针fn
  • 将函数作为数据值使用:使设计有效的接口变得容易,允许用户 像指定数据一样指定操作,即作为回调函数

    1
    2
    void mapAll(double (*fn)(double));
    // 声明使用函数指针做参数
  • C++对函数指针自动解析引用

  • 早期计算中,程序以代码、数据完全分开形式表示
  • 现代计算机,内存同时存储的数据值、硬件执行的机器指令
  • von Neumann Architecture:冯诺伊曼体系结构,将指令存储 在内存地址中作为数据值使用,使得创建函数指针成为可能

Closure

C++需要使用创建必要数据结构实现闭包

  • 需要将数据、代码封装在一个单独实体,即对象/类

  • 为使得闭包函数一般化,最好的方法是使用function class 实现,直接将实例作为“函数” (当然可以随便实现一个函数,不影响)

  • 函数类参见cppc/basics/class
  • 闭包参见program/program_design/function_design

函数类作为参数

  • 使用函数类作为参数时,没有任何明确方法声明类型,因为任何 重载()操作符都可以作为参数

  • C++使用模板函数实现,任何以函数对象作为参数的函数

    1
    2
    template <typename FunctionClass>
    void mapAll(FunctionClass fn);
    • 传给模板函数mapAll值可以是任意类型
    • 但当编译器展开其时,若参数类型不能获得期望参数, 编译器报错