`
琉璃月
  • 浏览: 44137 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

函数式语言

阅读更多
   这几天都在看函数式语言。今年以来发现这东西应用的越来越多。

   函数式语言,定义有广义和狭义。最简单的概念就是函数是元数据,具有和整数,字符串同等的地位。函数可以做函数的参数,是啊,c语言可以用函数指针来模拟。但是函数式语言里面函数能作为返回值!c里面可不能返回一个函数指针,这个指针指向一个新构建的没有定义过的函数。

    在最严格的函数式语言里面,比如haskell,没有变量。赋值语句就是公式定义。x=x+1是绝对不成立的。数组或者列表处理用的是递归。阶乘的函数定义,f( x+1)=x*f(xs),f(1)=1;你看,就是数学定义翻译一下。我们写的程序其实包括两个部分:做什么和如何做。函数式语言就是尽量去掉如何做的描写,只写做什么的部分。

    我们现在用的语言里面,除了最古老的lisp以外,python,ruby等新一点的脚本语言基本都支持函数式编程。javascript也支持,而且是现在新一点的框架里面必用的模式。
    “將Object為主的程式碼轉回Procedure的方式:將Object化成Record,將Method轉成Function,將this(或self)當作Function的參數,把Function集中放到Module(模組)。將程式中用到迴圈的地方,盡量轉成遞迴(Recursion)。先不要管執行效率的問題。將程式中用到if/else或switch/case的地方,改用Pattern Matching(模式比對)。只要做到上述這三件事,你的F#程式會具有濃濃的FP風味。”

    1:速度大家可以看到,函数式语言目前基本都是脚本语言,动态语言,很少有编译类型的。这就带来一个过去函数式语言一直发展不起来的原因:太慢。lisp的一些方言,比如scheme,加上haskell这个比较纯的函数式语言,可以编译为c语言进而变成本地代码。但是现在由于jit(即时编译)技术的发展,这个已经不是问题。最简单的c++比起c来多了很多包装和抽象,但是c++解决常见问题的程序反而比c快。比如java是在虚拟机里面跑的,理论上效率比C低。但是jit可以即时翻译为本地代码,所以在数值计算上可以比c还要高。而lisp在数值计算上如果是某些特殊的编译器可以比c快很多。这是因为C的实现太过古老,要保持兼容性,而很多算法实际上是很低效的但是又不得不用。类型问题和函数副作用的问题又阻止了很多比较激进的编译器优化:你不知道简化会不会造成计算结果和原来不一样,那么你就不敢做这个优化。C++到java因为数据类型检查更严格,面向对象也约束了很多变量的作用范围,所以优化可以更彻底。如果是lisp,因为只定义了做什么,而没有定义怎么做,机器就可以尽情的找最优化的方案。当然,在数值计算方面,数学公式的简化推导已经有成熟理论,所以优化是巨大的,但在其他领域,java和lisp还是比不上c或者c++的。而c的存在也不是没有意义:c很灵活(这也是无法深度优化的原因),c不需要额外的一些运行时支持(c++需要维护一些虚拟函数表格之类)使得c在写操作系统的时候无可替代的作用。而且c++过于复杂,很多特征容易被人滥用。

    有个测试结果是这样的,跑一个程序,c++需要的时间是1,那么c在1.05左右,java约1.6,javascript大约是4,编译过的scheme或者haskell小于3-4,go语言居然有5(这可是没有虚拟机的编译语言),lua是2(编译后)到32(编译前),python50左右,php过百,perl64,ruby接近60。
    里面值得注意的是javascript在google的v8引擎下居然能跑到仅仅3倍时间,可见jit的威力。而比较晚的语言在设计的时候都考虑了虚拟机和编译的需求,速度并不慢。ruby是个特例,应该是开始的时候并没有考虑到执行效率,而是以写起来方便为主要目标吧,毕竟实现的特征和类型反射做的是最方便的。

    可见动态语言是可以将灵活性,严谨的数据类型和速度兼顾的。

    2:无关的东西 基础数据类型
    脚本语言里面大多实现了列表,map数组(也就是可以用任意数据类型做数组下标)等数据类型。这些数据类型很有必要,一个是到了比较复杂的数据情况,建立这些数据类型几乎是必然的,而系统自己实现因为算法很成熟,比起我们自己临时写一些总要好一些。比如map数组用的哈希表,比起普通数组在时间上都是O(1)的,如果我们自己用结构数组去实现,在进行查找和添加等操作时如果不想麻烦的话,用的肯定是O(n)的算法,反而比系统自己实现的慢。
    C++里面实现这些可以用标准泛型库。
    这些东西在写程序的时候大大增加了便利,以往很多写起来很麻烦的算法现在写起来就很简单了。而动用函数式编程,写程序就更只管快捷了。比如要对数组进行过滤,两个数组里面的数字逐对叠加,传统需要写一个循环。现在只要用一个map或者filter函数就可以了,具体操作可以写在一个函数内的匿名函数(用lambda表达式)。但是函数式语言基本肯定要用到表,其他就不一定了。

    3:函数式编程那么多函数调用,会不会带来大量消耗?
    函数式语言里面函数是基本数据,所以很多我们觉得不必用函数的地方也用到了。比如说一个表达式里面有一些类似项,我们想用一个中间变量来简化表达式。函数式编程的办法就是写一个匿名函数,这个函数调用的参数就是前面的中间表达式。这样来避免中间表达式多次求值。而且函数式语言里面可以返回函数。在数学的函数理论里面,每个函数只有一个参量,那么多个参数的表达式怎么办?就是F(a,b)=(f(g(b)))(a),先生成一个使用参量b的g子函数,然后用这个函数制作出函数ff,这个函数ff调用参数a。
比如
func plus(int n){return func(x){return x+n}}
a=plus(3) //a是一个函数
a(5) //返回8
b=plus(6)
b(5) //返回11,我们把plus'(5,6)变成了b(5),也就是(plus(6))(5)
也就是函数实例是带有它产生时候的环境变量的。那么函数调用必然要记住这些东西,比起普通编程里面的函数调用需要更多步骤。但是函数式编程其实是可以通过归纳把不必要的部分去掉的。比如尾递归,如果一个函数递归调用自己,而最后的返回语句就是一个光秃秃的函数调用,而且是调用自己,那么就可以把这个调用转换为goto语句。所以合理的函数式编程并不过分消耗计算资源。

    4:函数式计算的副作用
    像haskell这样的严格语言里面函数调用是没有副作用的,也就是说只要参数一样,结果肯定一样。lisp大部分时间是没有副作用的,但是允许它存在。没有副作用有好几个很大的用处
a 优化编译的作用 因为不必担心副作用,所以编译优化的时候可以大胆合并类似项,去掉不必要的计算步骤,计算顺序可以随意改变,记忆函数调用求值,像a+f(x+2)+f(2+x)就会自动简化为a+2*f(x+2)等等。像自动展开循环,把数列和直接归纳计算等等都不在话下。
b 函数可以随时停下来,返回,然后需要的时候从断点重新开始。最常见的是yield
func fabbi(){
a,b=1,1
while(1){  
  yield a 
  a,b=b,a+b
}}
fabbi() //return 1
fabbi() //return 2
fabbi() //return 3,5,8,11.....
这里违反了同样输入带来同样输出的规定,不过大家可以看到在写一些循环的时候这种写法的威力,如果不用yield基本上不可能把这个函数独立写到循环外面了。其实肯定可以做到,但是远远没有这么直观和简单。
也就是说,无需线程等操作系统支持,函数式语言可以轻易实现模拟多线程。go语言里面就大量使用这种手法。现代很多脚本语言也用来写异步语法。比如我们写界面语言,经常要等待键盘输入,或者等待某个操作完成(比如读盘或者下载)。过去我们这个时候只能写一个无限循环不停等,其他部分的操作就只能等了。现在只要马上返回一个触发函数,这个触发函数保留了当前运行状态然后去干别的事情。等我们要的操作完成的时候,去调用那个触发函数,我们就继续从刚才的断点往下跑了。使用函数式编程的匿名函数还可以把这样一个阻塞式函数自动变成非阻塞式的。传统方法不是不能做到,只是很难在简单写程序的情况下又保证跑下去和当时一样。

现代程序的一个特点是要做大量的并行操作。网页程序同时在刷新界面元素,等待后台读取数据,还在监视用户输入,同时还在播放背景音乐。使用函数式编程可以大大简化。传统这些用的是回调函数,函数式编程可以直接把回调函数写成匿名函数包在调用语句里面,十分直观。更别谈如果要用到外部变量的时候非函数语言如何正确传播变量了。

5 函数式语言的其他
a 延时求值 可以构造无穷列表
b 模糊求值 可以自动搜索匹配
c 抽象计算 可以简化很多算法,而不用多态和泛型
面向对象是数据自带方法,函数式编程是方法自带数据。前者在数据类型基本不变时有着计算简单快速的优势,后者则可以套用到多种数据类型而有编程简单灵活的优势。

函数式语言的抽象性是其主要优点。很多编程技巧都用不上了。有人归纳说一个足够复杂的系统,迟早会发明出一个类似lisp的内部语言,而且还是低效和不完善的。因为系统过于复杂,就必然需要一个抽象层来简化。MS office有一个vba。很多游戏场景用脚本描述。现在连界面都用lua脚本描述。所以觉得脚本语言慢是不必要的,大部分计算不是消耗在脚本,而是消耗在IO绘图数据库等方面。而lisp等抽象语言带来的垃圾收集,列表和map数组,异步操作,自动类型推断等,极大的简化了编程。所以在计算机速度越来越快,计算机比人工便宜得多的时代,使用脚本语言是划算的。而且,现代编译技术在一个良好设计的脚本语言里面,根本就不慢。go语言等甚至根本就不是脚本,直接就是比c编译速度快得多的强类型编译语言(当然是以执行速度为代价,但是go语言目前的编译器还远没有优化)。这样一个简化编写又大大减少潜在bug的语言(类型错误,逻辑错误,指针分配/回收错误),c语言是该从非操作系统的编程语言中退役了。java和c++都属于过度设计+委员会扯皮,所以反而不如c#的快速迭代和清爽(背后的主持人功力也是原因),总之类似go这样的重新触发设计c的编译型语言和新时代代替lisp做日常编程的脚本语言都是很需要的。

新语言的发明在2000年后陷入低潮,但是最近又有抬头的趋势。因为网络编程和新的jit编译理论,以及脚本语言的实践带来了大量新的方法。可见除了c/lisp这一抽象一机器的两个极端,中间的语言都需要大洗牌一次了。c++,java以及大量的脚本语言这些可能除了名字没变,编程风格等可能都会大变样,迎接“简单就是美”的新一轮抽象化。
分享到:
评论

相关推荐

    《JavaScript语言精髓与编程实践》精选版--动态函数式语言精粹

    动态函数式语言精粹 作者 周爱民 《JavaScript语言精髓与编程实践》这本书,最初的名字是叫《动态函数式语言精髓与编程实践》,这是作者写本书的原意。确切地说,作者并非是想讨论JavaScript作为一种语言工具的...

    books 动态函数式语言精髓

    标题中的“动态函数式语言精髓”指的是编程领域中关于动态函数式编程的一种深入探讨。动态函数式编程是一种编程范式,它结合了动态类型和函数式编程的特点,强调代码的简洁性、表达性和可读性。这类语言通常包括Lisp...

    [本科毕业设计] 简单的函数式语言实现(编译器+解释器).zip

    【本科毕业设计】“简单的函数式语言实现(编译器+解释器)”是一个典型的计算机科学项目,旨在让学生深入了解编程语言的内部工作原理,特别是函数式编程语言。在本设计中,学生将面临编译器与解释器的设计与实现,这...

    函数式语言并行化的方法_古志民.pdf

    古志民和郑守淇的研究重点在于探讨函数式语言并行化的方法,尤其关注的是如何在函数式编程语言中实现并行和分布式计算。并行化语言可以分为两类,一类是传统的过程语言,如C和Fortran,另一类是函数语言,比如LISP。...

    毕业设计 简单的函数式语言实现(编译器+解释器)源码+详细文档+全部数据资料 高分项目.zip

    毕业设计 简单的函数式语言实现(编译器+解释器)源码+详细文档+全部数据资料 高分项目.zip毕业设计 简单的函数式语言实现(编译器+解释器)源码+详细文档+全部数据资料 高分项目.zip 【备注】 1、该项目是高分毕业设计...

    函数式编程思维.pdf_函数式编程_函数式编程思维_

    在函数式编程中,递归经常用来替代循环,因为它能更好地与函数式语言的特性结合。 总之,函数式编程是一种强大的思维方式,它可以帮助程序员写出更简洁、更易于理解和维护的代码。随着并行计算和大数据处理的需求...

    小而全的Java工具类库,使Java拥有函数式语言般的优雅.zip

    小而全的Java工具类库,使Java拥有函数式语言般的优雅.zip

    函数式编程语言编程和程序验证PPT课件.pptx

    更复杂的情况下,如高阶函数应用,如Curry化,展示了函数式语言如何将函数作为第一类公民,允许动态构造新函数,这在命令式语言中通常是受限的。 总之,函数式编程语言以其独特的理论基础,λ演算,提供了构建无副...

    用C++进行函数式编程

    Carmack的观点认为,虽然完全转向如Lisp或Haskell这样的函数式语言可能是不切实际的,但在现有的C++环境中采用函数式编程的思想仍然是非常有价值的。 #### 函数式编程的价值 函数式编程的核心理念在于减少代码的副...

    Scala函数式编程

    所以,当你读完本书,做完习题后,虽然你的应用开发能力并不会直接提升,但你会体会到构建函数式语言和框架时的难点和取舍,从而增进你的框架开发和语言设计的能力。  ——ThoughtWorks Lead Consultant 杨博  这...

    函数式编程中的Swift与Swift中的函数式编程

    Swift的函数式特性不仅仅是为了在技术上与Haskell、Erlang等函数式语言看齐,更是为了提高软件开发的效率、可靠性和维护性。同时,Swift社区也提供了许多便于实现函数式编程思想的工具和库,比如Swift的函数式扩展和...

    Python函数式编程(第2版)1

    Python虽不是纯函数式语言,但它具备函数式编程的许多关键特性,如函数作为一等公民,以及丰富的高阶函数支持。 书中首先介绍了函数式编程的基本概念和特点,包括函数的不可变性、纯函数、柯里化、高阶函数等。作者...

    函数式编程另类指南.pdf

    - **自动优化**:现代函数式语言通常能够进行自动化的内存管理和优化。 #### 五、函数式编程面临的挑战 - **学习曲线陡峭**:对于习惯了命令式编程的开发者来说,理解和应用函数式编程概念需要一定的时间。 - **...

    函数式编程

    函数式编程是一种编程范式,其核心理念是将计算视为数学函数的...通过学习函数式编程,开发者可以掌握一门编程语言的核心特性,并将这些知识应用到其他强类型函数式语言中,如 Swift、Scala、Haskell 和 PureScript。

    函数式数据结构

    这主要是因为大多数为命令式语言设计的数据结构都依赖于赋值操作——这种操作在函数式语言中要么被禁止,要么至少是不推荐使用的。 #### 核心概念 ##### 1. 函数式编程 函数式编程是一种编程范式,它将计算过程视...

    javaScript函数式编程

    内容简介 · · · · · · JavaScript 是近年来非常受瞩目的一门编程语言,它既...A.2 能编译成JavaScript的函数式语言196 A.2.1 ClojureScript196 A.2.2 CoffeeScript197 A.2.3 Roy198 A.2.4 Elm198 附录B推荐书目

    JS 函数式编程指南

    以上说明,强类型的函数式语言毫无疑问将会成为本书所示范式的最佳试验场。JavaScript 是我们学习这种范式的一种手段,将它应用于什么地方则完全取决于你自己。幸运的是,所有的接口都是数学的,因而也是普适的。...

    函数式编程语言编程和程序验证PPT学习教案.pptx

    在实际编程中,函数式编程的风格也可以应用于非专门设计为函数式语言的环境中,如JavaScript和Perl,这些语言都引入了函数式编程的概念。此外,通过领域特定语言(DSL),函数式编程可以更有效地应用于特定领域的...

Global site tag (gtag.js) - Google Analytics