first-class functions:函数是一等公民

  前些阵子在读《javascript编程模式》一书时,发现了一个比较让我疑惑的说法:

图片

  什么叫函数是一等“对象”?
  针对js这个语言,我深受“万物皆对象”这句话的影响,或许仅仅是对字面意思的理解,在js中,根据原型继承的机制,几乎所有的对象都是继承自Object构造函数所指向的原型对象(即Object.prototype),照这么说,应该是“始祖对象”Object.prototype才是这门语言的一等对象啊,于是百度一下"函数是一等对象",根本找不到这个说法,唯一的关键字,匹配也是我看的这本书,偶尔发现有人提出这个问题,但是直接被pass了,原因是连回答问题的“大神”们,也压根不知道这个玩意。
  难道是是这本书说错了,或者说是这本书作者根本不出名?
  呵呵,其实都不是,今天我在wiki里面意外地读到这么一个短语:“ first-class functions”,怎么翻译,萝卜青菜,各有所爱,大致的意思也就是那本书里面提及到的“函数是一等对象”,查看英文版原著:
  “Functions As First-Class Objects In JavaScript, functions are first-class objects. They can be stored in variables, passed into other functions as arguments, passed out of functions as return values, and constructed at run-time. These features provide a great deal of flexibility and expressiveness when dealing with functions.As you will see throughout the book, these features are the foundation around which you will build a classically object-oriented framework.”
  显然,关键词语first-class是对Function最确切的形容,既然确定了这句话的存在性,那么存在即合理,接下来,我就只需要解决最根本的问题了:为什么函数是一等“对象”?
  同样,针对这个名词在国内这方面的非同一性,我必须得追溯英文了,既然这样,那就查wiki吧:
  于是,我在wiki百科全书里面查找到这样的原文描述:
 "In  computer science, a  programming language is said to have  first-class functions if it treats  functions as  first-class citizens."
  显然,对于函数是一等“对象”的说法,本身就是源于程序本身的,也就是说当一个编程语言本身就是把函数当成一等公民的时候,那么我们就称呼这个语言拥有“first-class function”,咋一看,这尼玛不就等于没说么?其实不然,间接地,它是在告诉我们:在不同编程语言里面,函数(对象)是处于不同地位的,怎么去理解这句话,当然要继续阅读下去,我们继续看原文:
  "Specifically, this means that the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures. [1] [2] Some programming language theorists require support for  anonymous functions as well. [3] In languages with first-class functions, the  names of functions do not have any special status, they are treated like ordinary  variables with a  function type. [4] The term was coined by  Christopher Strachey in the context of “functions as first-class citizens” in the mid-1960s. [5]"
  从上面语句中,我们大体可以获得这样的信息:
  1.这种语言(即把函数当做一等对象的编程语言),能够实现将函数作为参数传递给另外一个函数。
  2.它函数作为一种返回值(或者对象结构),在另外一个函数执行时return。
  3.分派函数成为变量,或者将函数作为一种数据结构进行存储。
  4.在一般语言里面,函数是必须有名字的(例如C++,C等),但是这种语言甚至支持匿名函数的存在,主要原因就是这种语言把函数作为了一种最基本的数据类型(function type),就像1,2,3一样,这些整型数字,我们没有必要为它们取名字。
  5.我们知道了这个“functions as first-class citizens”是个专业的“term”(术语),是在60年代中期由 Christopher Strachey提出来的。
  既然这样,在这种语言里面,函数的作用甚至超过基本数据类型本身,但是它又拥有最基本数据类型同样的待遇,那么我们完全可以认为,这个函数在这种语言里面的身份与地位是相当重要的了。到此为止,我们大概也就了解了为什么称“函数是一等的”的说法,当然我们要注意这是针对某种语言来说的,而不是所有语言的共性。
  在wiki中,当谈及到函数式编程的时候,它又引入一个新名词“ higher-order function”,什么意思,我没有管他,英语的解释千奇百怪,当没有官方的解释的时候,英文本身就是最好的解释,对此,“ higher-order function”遵循两个最基本原则:
  1.take one or more functions as an input.
  2.output a function
  输入一个或多个函数(对象)和输出一个函数(对象)," higher-order function"实际上是函数式编程的一个约束范例,真实的例子,便是map function,参见原文的map函数的描述:
  "A simple example of a higher-ordered function is the  map function, which takes as its arguments a function and a list, and returns the list formed by applying the function to each member of the list. For a language to support map, it must support passing a function as an argument."
  很有意思的函数,就好像C语言的main函数似的(当然有本质的区别),它好像有一种把所有函数集合在一起,再综合调用的意思,但是它实现的功能是远远强大于C的main函数的,这里我也不便多说,需要自己去体会。
当然,针对于C语言实现函数作为参数传递,唯一的实现办法估计也是指针吧,往下读的时候,便找到这样的代码:
  1. passing functions as arguments将函数作为参数传入的实现方式:
1 void map(int (*f)(int), int x[], size_t n) {
2     for (int i = 0; i < n; i++) 
3         x[i] = (*f)(x[i]);
4 }

注意:C语言采用指针类型来实现。

  2. Anonymous and nested functions匿名函数和内嵌函数的实现:
1 int f(int x) { 
2     return 3 * x + 1;
3 } 
4 int main() { 
5      int l[] = {1, 2, 3, 4, 5};
6      map(f, l, 5);
7 }
  注意:这里,C语言并没有匿名函数的支持,所以只能将函数名f,传入map函数里面。
  3. Non-local  variables and closures局部变量和闭包:
 1 typedef struct { 
 2     int (*f)(int, int, int);
 3     int *a; 
 4     int *b;
 5 } closure_t; 
 6 void map(closure_t *closure, int x[], size_t n) { 
 7     for (int i = 0; i < n; ++i) 
 8         x[i] = (*closure->f)(*closure->a, *closure->b, x[i]);
 9 } 
10 int f(int a, int b, int x) { 
11     return a * x + b;
12 }
13 void main() { 
14     int l[] = {1, 2, 3, 4, 5}; 
15     int a = 3; 
16     int b = 1; 
17     closure_t closure = {f, &a, &b};
18     map(&closure, l, 5);
19 }
  对于这里,稍微停顿一下,我以前曾经谈过闭包的一些定义,有句话叫做:“函数内部的函数即为闭包”,其实这我想估计只是针对javascript来说的,那么对于C语言来说,C语言里面是不能在函数内部定义函数的,顶多在函数内部调用函数(而不是嵌套定义),对此,我查了一下官方一点的闭包的描述:
    “在 计算机科学中, 闭包(Closure)是 词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。”
    回过头来,那么对于wiki上对这段程序的描述则是这样的:
    “Also note that the  map is now specialized to functions referring to two  ints outside of their environment. This can be set up more generally, but requires more  boilerplate code. If  f would have been a  nested function we would still have run into the same problem and this is the reason they are not supported in C.”
    由此本质上,C语言是不支持内嵌函数的,然而在 C语言中,存在一些模拟闭包的库,支持 回调函数callback的库有时在注册时需要两个参数:一个函数指针,一个独立的 void*指针用以保存用户数据。这样的做法允许回调函数恢复其调用时的状态。这样的惯用法在功能上类似于闭包,但语法上有所不同。
    4. returning functions as results将函数作为结果返回,这里没有C语言的实现方案,只有一段英文解释:   
  "When returning a function, we are in fact returning its closure. In the C example any local variables captured by the closure will go out of scope once we return from the function that builds the closure. Forcing the closure at a later point will result in undefined behaviour, possibly corrupting the stack. This is known as the  upwards funarg problem."   
  “当返回一个函数本身时,我们实际上是返回了这个闭包(因为闭包就是一种函数),在这个C语言示例里面,一旦我们返回那些闭包,那么那些闭包所约束的局部变量将会溢出它的作用域,强行返回一个‘later point’将会导致一个未定义行为,很有可能就是堆栈溢出,这就是所谓的‘ upwards funarg   problem
  这是我对这句话的拙劣的翻译,抛开各种专业性的问题,显然它是在告诉我们C语言对这种机制的实现是不完善的。换一个角度来说,C语言或许在构造的时候就天生不是实现闭包的最佳语言,它是不太适合进行函数式编程模式的。    这里,又有来自于wiki的一个总结:    
  "The C family allowed both passing functions as arguments and returning them as results, but avoided any problems by not supporting nested functions. (The gcc compiler does allows them as an extension.) As the usefulness of returning functions primarily lies in the ability to return nested functions that have captured non-local variables, instead of top-level functions, these language are generally not considered to have first-class functions."
  这里面大致的意思是这样的,C语言家族里面,它是可以实现函数的参数传入和函数类型的返回(大致都是用指针来模拟实现的),但是由于内嵌函数的不支持性(除了gcc编译器对其的扩展,ps:可能是一个特例吧),并且针对返回的函数一定要是有意义的这一层面来说,返回内嵌函数显然是意义斐然的,所以C语言是不具有一等函数(first-class function)的,这里我可以补充一个javacript的例子:
1 var outer = function(){
2     var innerNum = 123;
3     return function(){
4         console.log(++innerNum);
5     };
6 }();
7 outer();
  这里面外层的匿名函数的内部便有一个闭包函数,并且它引用了innerNum,我们在外层的函数定义完成之后便即时调用它自身,此时便返回了该结构里面的匿名函数,那么最终outer实际上是指向了内部的匿名函数,并且它有一个很厉害的功能——使用局部变量innerNum(这个变量,外部是无法访问的),这便是C语言不可能,或者说尚未做到的东西,也是javascript最优美的特性之一,我这里讲的很肤浅,如果你对这个程序有兴趣,可以copy到浏览器的控制台里面试试结果。
  下面列出了一张对于该特性的语言支持情况的表格:
LanguageHigher-order functionsNon-local variablesPartial applicationNotes
ArgumentsResultsNested functionsAnonymous functionsClosures
Algol familyALGOL 60YesNoYesNoNoNoHave function types.
ALGOL 68YesYes[9]YesYesNoNo
PascalYesNoYesNoNoNo
OberonYesNon-nested onlyYesNoNoNo
C familyCYesYesNoNoNoNoHas function pointers.
C++YesYesNoC++11[10]C++11[10]NoHas function pointers, function objects. (Also, see below.)
C#YesYesNo2.0 / 3.02.0NoHas delegates (2.0) and lambda expressions (3.0).
Objective-CYesYesNo2.0 + Blocks[11]2.0 + BlocksNoHas function pointers.
JavaPartialPartialNoNo8NoHas anonymous inner classes.
Functional languagesLispSyntaxSyntaxYesYesCommon LispNo(see below)
SchemeYesYesYesYesYesSRFI 26[12] 
ClojureYesYesYesYesYesYes 
MLYesYesYesYesYesYes 
HaskellYesYesYesYesYesYes 
ScalaYesYesYesYesYesYes 
Scripting languagesJavaScriptYesYesYesYesYesECMAScript 5Partial application possible with user-land code on ES3 [13]
PHPYesYesUnscoped5.35.3No(see below)
PerlYesYes6YesYes6[14] 
PythonYesYesYesPartialYes2.5[15](see below)
RubySyntaxSyntaxUnscopedYesYes1.9(see below)
Other languagesMathematicaYesYesYesYesYesNo 
SmalltalkYesYesYesYesYesPartialPartial application possible through library.
FortranYesYesYesNoNoNo 
  此外,我们从中看见了java在这个方面表现并不出色,或许在以后的版本会改变,但是针对某种语言,我并不是在说明java的不好,反而我觉得这样做是对的,因为java 的先天性的面向对象和语言结构的严谨,当一个语言在某一方面表现优秀的时候,自然会专注某一个领域,就像python一样。每一种语言都有他自己魅力,不能轻视每一种语言的存在性价值,回到我所学习的javascript这门语言上来看,我觉的她是优美的,但也是年轻的,年轻自然是有活力的,当然也避免不了一些错误与糟粕。随着HTML5和Ajax技术的发展,她的地位也从当初的玩具语言上升到一种”跨平台式“(这个需要定义来考证)的主流语言,我之所以这样描述是因为浏览器的存在维持了她的生命,如今她又以一种藤蔓式的生长姿态在向各个方向蔓延,比如CommentJs,Node.js,甚至微软将JSscript作为了win8应用开发的一种主要语言,如今的人们,不得不承认她的地位越来越明显。爱也好,恨也罢,我觉得不要为某种语言所绑定了,而是要升华出来,我们仍然是自己,不要以为自己天生是写什么语言的料,于是其他语言不屑一顾,我可以去热爱javascript,并不代表我要摒弃C#,java,C,C++这些同样优秀的语言。
  如今我的学习宗旨就是一个,学习其他语言的机制与思想,借鉴过来,运用在我现在使用的语言上,我的学习目的便达到了,没有必要去担心这个东西到时候面试会不会出,这个东西完全没有必要了解?不要被别人的思想所左右,广泛阅读,我们专精的不是语言,而是自己的兴趣与目的,相信你也认同我的。

 

转载于:https://www.cnblogs.com/wuchu/p/3404638.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值