你写代码这么多年,有没有遇到过这样一种函数:它什么都不做,只是把输入原样返回,但你就是躲不开它,甚至越到后面越发现它重要。恒等函数(Identity function)就是这个东西,数学上写作 f(x) = x,形式上简单到像一句废话,可它偏偏在数学、编程范式、框架设计、神经网络里都占着一个不可替代的位置。这篇文章我想好好聊聊它,从数学定义讲到工程实战,再谈到我踩过的坑和琢磨明白的道理,尽量把“恒等函数”这个小话题一次讲透。
如果你是准备算法面试的开发者,或者正在学函数式编程、看神经网络的源码,又或者只是好奇“一个返回自身的函数有什么用”,这篇文章都值得你看下去。我会给大量可以直接抄进项目里的代码和思路,也会解释背后的设计逻辑,而不是停留在概念复述上。
1. 恒等函数是什么:听起来像废话的映射
1.1 数学定义再简单,也有值得抠的细节
先从定义入手。给定一个非空集合 A,恒等函数是指这样的一个映射:
f: A → A,f(x) = x,对任意 x ∈ A 成立。
意思就是定义域和陪域是同一个集合,每个元素都映射到它自己身上。这个定义看起来没有任何信息量,但细想一下有几个点很关键:
第一,它的定义域和陪域必须是同一个集合。你要说“把输入的 3 返回成 3”,这个 3 得属于某个数集,返回值也得落在同一个数集里。跨集合的映射即使形式上也是“返回原值”,也不能叫恒等函数。
第二,它的函数图像是一条 45 度的直线 y = x。如果你在坐标系里画出实数域上的恒等函数,就是那条过原点、斜率为 1 的直线。这个几何直觉可以帮助理解后面很多延伸概念。
第三,它是最“纯”的纯函数。没有任何副作用,不读外部状态,不修改参数,不产生随机数,只是把输入映射出来。所有编程语言里它都容易实现,但要在多线程、异步环境里保证“行为完全可预测”,恒等函数是天然满足的。
第四,它满足一些非常优雅的代数性质。任意函数 g 和恒等函数复合,结果不变:
id ∘ g = g
g ∘ id = g
这里的 id 就是恒等函数。无论是先执行 g 再做恒等变换,还是先做恒等变换再执行 g,结果都等于 g。这直接让恒等函数成为函数复合运算里的“单位元”。
第五,它是可逆的,而且它的反函数就是它自己。因为 f(x) = x,那么 f⁻¹(y) = y,也是恒等函数。对偶地看,如果两个函数互为反函数,那它们的复合就是一个恒等函数。这一点在密码学、数据变换、编解码器的设计里非常有用,后面我会再提到。
还有一个容易忽略但很关键的点:恒等函数在集合范畴里是每个对象都必须存在的态射。这个高度抽象的观点我放到 2.3 节展开。
1.2 一个“什么也不做”的函数凭什么值得写
我刚接触恒等函数时,和大多数人一样觉得它多余:我调用一个函数,它把输入原样返回,那我不如直接用原来的变量?为什么要绕一圈?
后来我想明白一个类比:加法的单位元是 0,乘法的单位元是 1。没有任何数字因为“加 0 等于没加”“乘 1 等于没乘”而被认为多余,0 和 1 反而是整个数系不可替代的基石。同理,恒等函数就是函数复合运算里的那个“0”或“1”,它让复合运算有了起点、边界和合法性。
举个例子。你实现一个函数组合工具 compose,如果没有任何函数要执行,那 compose() 应该返回什么?如果把所有要执行的函数看成一个数组,用 reduce 从左到右组合,初始值该给什么?这时候恒等函数就是唯一的天然选择。函数式编程里提倡“用值代替控制流”,恒等函数就是那个让代码逻辑闭环的“值”。
还有一层原因:恒等函数是很多理论上的“空操作”在函数层面的标准抽象。业务代码里你会写 if (condition) { return data; } else { return transform(data); },你本来想表达“分支一不做任何处理”。但如果用策略模式、管线模式,把处理逻辑抽象成一个个函数,那么“不做任何处理”就应该被显式地表达成一个函数,否则分支逻辑会遍布整个代码。恒等函数就是这种“显式空操作”的载体。
所以它看似废话,实际上是工程抽象能力的试金石。你写不写得出、能不能在合适的地方用上恒等函数,直接反映了你对“函数是一等公民”和“组合优于继承”这两件事的理解深度。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从工程视角重新认识恒等函数
2.1 函数式编程里的组合子与默认实现
在函数式语言和函数式风格代码里,恒等函数是最基础的一个组合子(combinator)。它通常命名为 id 或 identity。它的经典价值体现在函数组合中。
举个栗子:
javascript复制// Node.js / JavaScript
const identity = (x) => x;
const compose = (...fns) =>
fns.reduce((f, g) => (...args) => f(g(...args)), identity);
// 用法:
const add1 = (x) => x + 1;
const double
