头等函数
✍ dations ◷ 2025-11-08 19:18:29 #数据类型,函数式编程,程序设计语言理论,子程序
头等函数(first-class function)是指在程序设计语言中,函数被当作头等公民。这意味着,函数可以作为别的函数的参数、函数的返回值,赋值给变量或存储在数据结构中。 有人主张应包括支持匿名函数(函数字面量,function literals)。在这样的语言中,函数的名字没有特殊含义,它们被当作具有函数类型的普通的变量对待。1960年代中期,克里斯托弗·斯特雷奇在“functions as first-class citizens”中提出这一概念。
头等函数是函数式程序设计所必须的。通常要使用高阶函数。就是一个高阶函数,其实参是一个函数及一个list,返回结果是把作为参数的函数作用于list的每个元素后的结果形成的list。
把函数作为函数参数与函数返回值会遇到特别的困难。特别是存在非局部变量(英语:non-local variable)与嵌套函数(英语:nested function)、匿名函数。历史上,这被称作函数参数问题(英语:funarg problem)。 早期的命令式编程语言,或者不支持函数作为结果类型(如ALGOL 60, Pascal),或者忽略嵌套函数与非局部变量(如C语言)。早期的函数式语言Lisp采取了动态作用域方法,把非局部变量绑定到函数执行点最近的变量定义。Scheme语言支持词法作用域的头等函数,把对函数的引用绑定到闭包(closure)而不是函数指针,这使得垃圾收集成为必须。
在这一节,比较把函数视作头等公民的典型的函数式语言Haskell与把函数视作二等公民的命令式编程的C语言的有关概念。
具有函数参数的函数,称为高阶函数。函数式语言如Haskell:
map :: (a -> b) -> -> map f = map f (x:xs) = f x : map f xs
函数不是头等公民的程序设计语言可以使用函数指针或delegate,实现函数作为参数。C语言例子:
void map(int (*f)(int), int x, size_t n) { for (int i = 0; i < n; i++) x = f(x);} 匿名与嵌套函数
更多信息:匿名函数和嵌套函数
对于支持匿名函数的语言:
main = map (\x -> 3 * x + 1)
对于不支持匿名函数的语言,必须把函数绑定到一个名字上:
int f(int x) { return 3 * x + 1;}int main() { int list = {1, 2, 3, 4, 5}; map(f, list, 5);} 非局部变量与闭包
更多信息:非局部变量和闭包 (计算机科学)
一旦有了匿名函数与嵌套函数,引用函数体之外的变量(非局部变量)就很自然了:
main = let a = 3 b = 1 in map (\x -> a * x + b)
如果函数只能用函数指针表示,如何把函数体之外的值传递给函数就是个问题。可以手工建立一个闭包,但显然这不能算作头等函数:
typedef struct { int (*f)(int, int, int); int *a; int *b;} closure_t;void map(closure_t *closure, int x, size_t n) { for (int i = 0; i < n; ++i) x = (*closure->f)(*closure->a, *closure->b, x);}int f(int a, int b, int x) { return a * x + b;}void main() { int l = {1, 2, 3, 4, 5}; int a = 3; int b = 1; closure_t closure = {f, &a, &b}; map(&closure, l, 5);}注意这里的map是特化为使用当前环境外的两个int。即使f是个嵌套函数,仍然要面对同样问题,这也是C语言不支持嵌套函数的理由。
返回结果为函数时,实际上返回的是该函数的闭包。对于C语言,函数退出时其局部变量也退出了各自的作用域,这使得构建闭包变得困难。这被称为向上的函数参数问题(英语:upwards funarg problem)。
把函数赋值给变量面临着把函数当作返回结果一样的困难:构建该函数的闭包:
f :: -> ]f = let a = 3 b = 1 in
函数的相等
判断两个函数是否相等,有不同的判据:
对于类型论,函数类型接受值类型并返回值类型可写为 → 或。根据柯里-霍华德对应,函数类型可对应于逻辑蕴涵,lambda抽象对应于discharging hypothetical assumptions,函数调用对应于肯定前件推理规则。类型论还使用头等函数建模关联数组与类似的数据结构。
对于范畴论,头等函数对应于closed category(英语:closed category)设置。例如,简单类型λ演算 对应于笛卡儿闭范畴(CCC)的内部语言。
函数式程序设计语言,如Scheme、ML、Haskell、F#、Scala,都具有完整的头等函数。Lisp作为最早的函数式语言在当初设计时对头等函数各方面还没有适当的理解,导致了采用动态作用域。后来的Common Lisp已经改为使用词法作用域的头等函数。
许多脚本语言,如Perl、Python、PHP、Lua、Tcl/Tk、JavaScript、Io,有头等函数。
指令式程序设计语言,Algol及Pascal族系、C族系,与现代有垃圾收集的语言非常不同。Algol族系允许嵌套函数与高阶函数作为参数,但不允许函数作为返回值(除了Algol 68)。因为当时还不清楚如何处理内嵌函数作为返回值时的非局部变量问题(Algol 68对此会产生运行期错误)。
C族系允许函数作为参数与函数作为返回值,但由于不支持嵌套函数而避开了相关问题。因为返回嵌套函数并捕获所使用的非局部变量被认为才是真正有用,因此C族系不被认为有头等函数。
现代指令式编程语言由于有垃圾收集功能而使得头等函数成为可能。很多语言的后续版本开始支持头等函数,如C# 2.0,Apple公司的C、C++与Objective-C的Block扩展。C++11开始支持了匿名函数与闭包。
相关
- 炸玉米饼墨西哥薄饼(英语:tortilla)是在墨西哥、美国等地流行的用玉米粉或小麦面粉烙制的薄饼。使用小麦制成的薄饼称之为面粉薄饼(flour tortilla),使用玉米制成的薄饼则称为墨西哥玉米饼
- 台北植物园坐标:25°1′56.23″N 121°30′34.27″E / 25.0322861°N 121.5095194°E / 25.0322861; 121.5095194台北植物园,位于中华民国台北市中正区龙安里南海路53号,占地约8.2公顷,隶
- 九泉黄泉,出自儒教经典《左传》中郑伯克段于鄢的故事,郑庄公说待其死后将与母亲在黄泉相见。后在汉字文化圈中用于指人死后所居住的地方。因打井至深时地下水呈黄色,又人死后埋于地
- 机器织布局机器织布局为中国第一家机器棉纺织工厂,于1878年由四川候补道彭汝琮主持筹建,1880年,郑观应接手。1883年,上海金融风潮后,郑观应离局。1890年,开始投产。投产后,织布局营业兴旺,由于
- 奥伯特·弗里德里希·卑尔讷 (刑法学者)奥伯特·弗里德里希·卑尔讷(德语:Albert Friedrich Berner,1818年11月30日-1907年1月13日),是德国 19 世纪最重要的刑法学家之一,曾任柏林大学(今日的柏林洪堡大学)法学教授。他的学
- 伊斯兰共和报《伊斯兰共和报》(波斯语:جمهوری اسلامی)是伊朗官方报纸,创办于1979年5月30日,隶属于伊斯兰共和党。创办之初其社长为阿里·哈梅内伊,总编辑为穆萨维。在伊斯兰共和
- 梁伟铿梁伟铿(2000年11月30日-),广东广州人,中国男子羽毛球运动员。2018年7月,梁伟铿代表中国出战在印尼雅加达举行的亚洲青年羽毛球锦标赛,一路打进男双决赛,最终不敌队友邸子健/王昶组合
- 玛丽亚·路易莎 (保加利亚)保加利亚的玛丽亚·路易莎(保加利亚语:Мария Луиза Българска,1933年1月13日-),保加利亚沙皇鲍里斯三世的女儿。1957年,玛丽亚·路易莎与莱宁根的卡尔王子(英语:P
- 隆之里俊英隆之里俊英,(1952年9月29日-2011年11月7日),原名高谷俊英,日本青森县南津轻郡浪冈町(现在青森市)出身的前大相扑力士,第59代横纲。身高181cm、体重158kg,血型O型。引退后出任年寄,袭
- 顶生海百合待补充顶生海百合(学名:)是一属已灭绝的海百合,其化石主要分布在亚洲及欧洲。它们的结构结实,长有狭窄的、圆柱形并且呈锥状缩小的茎,以及附着于基座上呈不规则锥形的根。鳞茎状的