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语言采用指针类型来实现。javascript

  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到浏览器的控制台里面试试结果。
  下面列出了一张对于该特性的语言支持状况的表格:
Language Higher-order functions Non-local variables Partial application Notes
Arguments Results Nested functions Anonymous functions Closures
Algol family ALGOL 60 Yes No Yes No No No Have function types.
ALGOL 68 Yes Yes[9] Yes Yes No No
Pascal Yes No Yes No No No
Oberon Yes Non-nested only Yes No No No
C family C Yes Yes No No No No Has function pointers.
C++ Yes Yes No C++11[10] C++11[10] No Has function pointers, function objects. (Also, see below.)
C# Yes Yes No 2.0 / 3.0 2.0 No Has delegates (2.0) and lambda expressions (3.0).
Objective-C Yes Yes No 2.0 + Blocks[11] 2.0 + Blocks No Has function pointers.
Java Partial Partial No No 8 No Has anonymous inner classes.
Functional languages Lisp Syntax Syntax Yes Yes Common Lisp No (see below)
Scheme Yes Yes Yes Yes Yes SRFI 26[12]  
Clojure Yes Yes Yes Yes Yes Yes  
ML Yes Yes Yes Yes Yes Yes  
Haskell Yes Yes Yes Yes Yes Yes  
Scala Yes Yes Yes Yes Yes Yes  
Scripting languages JavaScript Yes Yes Yes Yes Yes ECMAScript 5 Partial application possible with user-land code on ES3 [13]
PHP Yes Yes Unscoped 5.3 5.3 No (see below)
Perl Yes Yes 6 Yes Yes 6[14]  
Python Yes Yes Yes Partial Yes 2.5[15] (see below)
Ruby Syntax Syntax Unscoped Yes Yes 1.9 (see below)
Other languages Mathematica Yes Yes Yes Yes Yes No  
Smalltalk Yes Yes Yes Yes Yes Partial Partial application possible through library.
Fortran Yes Yes Yes No No No
  此外,咱们从中看见了java在这个方面表现并不出色,或许在之后的版本会改变,可是针对某种语言,我并非在说明java的很差,反而我以为这样作是对的,由于java 的先天性的面向对象和语言结构的严谨,当一个语言在某一方面表现优秀的时候,天然会专一某一个领域,就像python同样。每一种语言都有他本身魅力,不能轻视每一种语言的存在性价值,回到我所学习的javascript这门语言上来看,我觉的她是优美的,但也是年轻的,年轻天然是有活力的,固然也避免不了一些错误与糟粕。随着HTML5和Ajax技术的发展,她的地位也从当初的玩具语言上升到一种”跨平台式“(这个须要定义来考证)的主流语言,我之因此这样描述是由于浏览器的存在维持了她的生命,现在她又以一种藤蔓式的生长姿态在向各个方向蔓延,好比CommentJs,Node.js,甚至微软将JSscript做为了win8应用开发的一种主要语言,现在的人们,不得不认可她的地位愈来愈明显。爱也好,恨也罢,我以为不要为某种语言所绑定了,而是要升华出来,咱们仍然是本身,不要觉得本身天生是写什么语言的料,因而其余语言不屑一顾,我能够去热爱javascript,并不表明我要摒弃C#,java,C,C++这些一样优秀的语言。   现在个人学习宗旨就是一个,学习其余语言的机制与思想,借鉴过来,运用在我如今使用的语言上,个人学习目的便达到了,没有必要去担忧这个东西到时候面试会不会出,这个东西彻底没有必要了解?不要被别人的思想所左右,普遍阅读,咱们专精的不是语言,而是本身的兴趣与目的,相信你也认同个人。
相关文章
相关标签/搜索