`
javasee
  • 浏览: 960787 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论
阅读更多

本文刊发在《程序员》杂志09年第二期上。是讨论函数式语言基本性质和发展方向的一篇文章。

表面的简洁/strong>


一、把大象装进冰箱 =====   在命令式语言(当然我们可以确指为C、Delphi、Java或C#等等)中,初学者的第一 个疑难便是这样的代码(*注1):   X=X+1   为什么?因为在数学概念中,上述等式是不能成立的。这种表达式是计算机的思维逻 辑:当它运算上述表达式(或语句)时,X被作为暂存单元——例如冰箱。为了让冰箱产 生变化,比如解决“把大象装进冰箱”这样的问题,我们需要如下三步:   把冰箱门打开,把大象放进去,把冰箱门关上      (图1:“把大象装进冰箱”的问题)   因为我们有两只手来分别负责拉住冰箱门和大象,所以整个操作过程看起来很完美, 但接下来我们再加上点需求:我们要“把大象拿出来,把长颈鹿装进去”,怎么办?是的, 应该这样:   把冰箱门打开,把大象拿出来,把长颈鹿装进去,把冰箱门关上   可惜长颈鹿有腿有思想,所以问题将会出在我们把大象拿出来的那一时刻:长颈鹿跑 掉了。为此,我们必须做很多的防护措施,例如先锁住长颈鹿,再锁住大象,以及在整个 过程中,保证冰箱门不会自动关上或打开……而代码:   X=X+1 的执行过程与此类似:当CPU开始存取X这个位置时,它只能在“当前X”与“下一次X” 之间选择二者之一。当多个线程(或多个CPU)开始存取X这个位置时,如果我们希望得 到相同的X值,那么我们就得在X操作过程中采取象上面一样的防护措施:加一个锁。以 保证要求所有线程都取完了这个“当前X”值,才会被切换到“下一次X”值。   由于这个限制,一旦多个线程都排着队来看看这个X的美妙身形,整个队列就全都慢 下来了。   解决这个问题的办法其实很简单:只要X可见,我们就永远不修改这个X。而这,就 是函数式应对大象问题的方法:如果冰箱里放着大象,就永远不要试图放长颈鹿。所以在 函数式语言Erlang中的变量一旦赋值,就不能再修改。   云风曾在SD2C 2007大会上说:解决问题的最好方法,就是不解决它。这个观点深得 函数式的精髓。 二、帽子戏法的关键,在于至少多一顶帽子 =====   杂技中的一种常见帽子戏法,是很多人围成一圈或排成一排然后飞速地传送手中的帽 子。这如果是一个人来表演,那么应该是左右手各一顶帽子,而多出的一顶则总在头上。        (图2:帽子戏法)   所以,关键在于至少要多出一顶帽子。正因为多出来这个帽子,所以我们看到杂技师 在我们面前构建了一个往复不休的循环。事实上程序设计里的“循环”也存在完全相同的 问题:我们至少需要一个变量来保存迭代中的计数,而且这个变量必须是可以修改的(*注 2)。然而这一要求既是玩转“帽子戏法”的必要条件,却又与我们上面讲过的“不修改变 量”的原则相违背。   函数式如何解决这个问题呢?   其实答案还是相同的答案:至少多一顶帽子。只不过,帽子不一定要放在头上,我们 可以把它放在传递的过程中——例如空中——就可以了。要知道,让杂技师用同样的方法 来掷苹果,那多出来的一个就总是在空中了。   在函数式中,我们如果要构建一个循环,那么可以使用函数递归来实现。这上述控制 循环过程的变量,则可以把它放在函数形式参数表——这种类似“空中”的地方。与“空 中”相同的是,我们在静态看函数时,那是参数表;而运行中时,它传入实参。   然而帽子戏法的表演者并没有三只手或更多只手,被循环帽子增加的时候,杂技师除 了加快速度之外,保证一个简单的原则也是极其重要的:总是从帽子队列的最末端取到下 一只帽子。这一原则保证了可以容纳更多帽子,而又不会少处理任何一个。同样的,递归 是消耗栈的,为了使栈空间不爆炸,解决的方法就是在最后一行代码上调用递归,即尾递 归。因为尾递归的存在,函数最末的调用就可以被优先为一行不消耗栈的跳转指令,就像 帽子戏法的杂技师从帽子队列上直接取走轮转到手边的帽子一样。   最后,对于一个函数来说,如果它只返回值而不修改函数外的任何东西,那么这个函 数就是安全的,它等价于它返回的这个值——如前所述的,这个值一旦有效(运算出结果 并传出),就不再变更。所以函数式中的函数调用,可以等价于一个表达式中运算的值。   如同函数调用,函数的递归也只返回值,所以也等价于一个表达式中运算的值。再进 一步的推论,递归实际上等效于循环求值。   复杂的表象下,总会有一个简单的原则。万人的与一个人的帽子戏法,其原则是一样 地:至少多一顶帽子,放在头上,或是空中。 三、计算机其实不认得"hello" =====   32位的unicode,以及128位的GUID等等,都直接与我们现在或将来的存储单位以及 运算的通道大小有关。事实上即使我们有128位机器,我们也只打算在这样的通路上传送 一个字符"h"——而不是字符串"hello"。从更为准确的角度来说,事实上计算机也不认得字 符"h",而只认得数字0x68。同样的,它也不认得所谓的“真假(true/false)”,而只认得数字 的1/0。      (图3:图灵机的概念图)   我们编程的本质,其实不过是在求值一些数字而已。只是最终我们在自然语义上把这 些数字的一个连续或非连续的集合认为是布尔值、字符串、数组或对象。当我们认识到运 算求值的结果无非是数值,而表达形式又无非是连续或非连续时,我们就得到了基本的数 据抽象单元:值、值系列。再加上我们前面讲到的执行体(函数),我们就得到了整个函数 式语言的鼻祖——LISP——的基本运算模型:   (+ Xn)   其中,“( )”表明一个值系列(表),而“+”在这里指代某种运算,Xn表明值(或值 系列)。整个的表返回一个值,因此也可以将“整个的表(通常这里称为表达式)”等义为 一个值。任何的一个运算,最终输出的仍然只是一个或一系列数字,它被显示在屏幕上, 便成了文本;放在内存中,便成了数据。   当然,现实是这样的机器最终从科学领域走向了民用,在PC(个人计算机)普及的现 在,我们也需要让类似LISP这样的——绝对正确而又绝对非人性的——语言变得亲切一 些。于是稍微复杂而有用一点的函数式语言,例如Erlang,通过丰富了上述的基本运算模 型来使我们的视觉愉悦,或是在讨论它的代码时显得神经(略为)正常一些:   (表一:以Erlang为例的、简单的类型抽象)         而我们逆推一份具体的Erlang代码,其实仍然可以表达为上述的(+ Xn)。例如我们可 以在Erlang代码在编译阶段使用解析树(Abstract Form)中看到这样的抽象代码(abstract code):   [{abstract_code,    {raw_abstract_v1, [{attribute,1,file,{"./simplest.erl",1}}, {attribute,1,module,simplest}, {function,3,test,0,    [{clause,3,[],[],[{atom,4|...}]}]    },    {eof,1}]    }   }]   无论是从形式,还是从实质来看,这种解析树(在erlang执行中将会按照解析树来生 成语法树并执行)与LISP语言的基本原则都是一致的。 四、从“函数等义于值”到“函数是值” =====   现在,JavaScript语言被更为深入的挖掘并渐渐了解到它的函数式语言本质,而类似 Erlang这样的“天生伤害人的视力”的语言也移步前台。这些语言的努力,使我们终于看 到一个属于函数式语言的时代的曙光。在黎明之前的黑暗中,函数式以它诸多的、最不可 思议的特性迷惘着程序员的目光。连它最基本的概念说明,也如同玄学家的呓语:如同数 学函数是集合A(称为定义域)中成员到集合B(称为值域)中成员的映射,函数式语言 就是通过数学函数的定义、应用的说明和求值完成运算过程的。   类似于这种等同于“什么也没说”的解释,其实的确是在阐述函数式语言的精髓。为 了减轻你的痛苦(但绝非轻视你的智商),我通常换个说法来陈述它们:如果表达式“1+1=2” 中的“+”被理解为求值函数,那么所谓函数式语言,就是通过连续表达式运算求值的语言; 既然上面的表达式可以算出结果“=2”,那么函数式语言自然也可以通过不停地求值找到问 题的答案。   首先,在函数式语言中,函数只表明一个运算过程,并产生一个运算结果。这与表达 式中的运算符具有完全相同的性质——所以事实上一个函数式语言中,表达式的运算符被 实现为一个函数。例如erlang的核心模块中,可以导出类似这样的函数:   (表二:Erlang中的运算符其实对应于内核函数-部分举例)      所以,函数式语言的本质是表达式/函数的“连续求值”。既然我们的输出或存储最终 只是在关心“值”,那么显然连续求值的结果就可以直接作为他们的输入。如果把输出终端 或存储看成接受输入的设备,那么他也相当于一个函数;如果一台计算设备只对外界表达 为一个或一系列输出,并接受来自其他类似设备的输入,那么计算设备本身也可以视为一 个函数……   我们将这个过程放大,其实网络可以是函数式的。这个就是著名的语用网,它的理论 基础是petri网论(Petri nets theory)。而事实上,作为计算模型来理解,它与函数式语言是 相似、等价的(*注3)。      图4:perti网的“库所变换”   函数式语言通过函数实现了三个基本的运算逻辑(顺序、分支与循环),因此它与我们 常用的命令式语言是等价的(*注4)。但是由于存在存储问题,所以命令式语言是时序相关 的——即有存取某个存储单元的先后问题。而函数式语言由于“把大象装进冰箱”之后就 再也不可更改,因此变得时序无关。   以上述的petri网的例子来讲,由于时序无关,所以图左侧的“库所(圆圈)”中的两 个“消息(黑点)”分解成右侧的两个库所来处理时,其转换的代价为0(无锁),而这个 过程应用在多核机器或分布式网络上时,效率却提高了一倍。   更深层次地思考这个问题,由于在计算机系统中函数本身仍然是以数据形式存储的, 所以函数事实上也是“被运算的对象”和“运算结果”。函数的这种特性被称为高阶函数。 “函数等义于值”是函数式的基础,而“函数是值”,则是高阶函数的基础。      图5:JavaScript统一语言范型的基本模型(*注5)   当“函数是值”时,我们可以把一个函数传递到另一个地方去运算,而其运算结果仍 然是值,所以可以把一个等义的结果再传回来。注意这一过程,就是分布运算的实质,所 以,函数式在本质上、天生地就是支持分布运算的。无论我们是将“一段函数式代码”所 表达的整个运算过程分解成何种形式,并分布在何种复杂的运算环境或网络环境中,只要 最终在逻辑上它能得到一个值序列和一个运算,就能够成为更大范围的分布网络中的一个 结点。   而这,就是整个计算世界的全部(注6):(symbol)。
==========   注1:《Erlang程序设计》中,作者以这个例子为起始,来讲述Erlang变量的单一赋值。   注2:参见《结构程序设计》,讨论“如何刻画计算的进展”时,作者E.W.Dijkstra说: (程序)如果含有循环语句,仅用语法指示器就不能描述计算的进展了……(而应该)引 进一个“动态指示器”毫不含糊地累计相应的现行循环的序数……因为,语法指示器无法 充当这种坐标系统的一个组成成分。   注3:"Implementing Coloured Petri Nets Using a Functional Programming Language" at http://portal.acm.org/citation.cfm?id=993039,and Functional Nets at http://lamp.epfl.ch/fn/   注4:不同范型的计算机语言之间等价问题,可以归结到图灵等价这个命题上,这意 味着该运算系统或模型能够执行任何复杂程度的、图灵机可完成的数学运算。2007年,Alex Smith证明了Wolfram提出了最小的“2,3图灵机(两种颜色,三种状态)”模型是最小完 备的图灵等价系统。   注5:这个统一过程用到了多项与函数式相关的基本设定:函数是执行体、函数等义 于值、函数是值。   注6:与“(+ Xn)”比较,这个表达式认为“运算 +”——也就是某个函数——其实也 是值,因此它也是“值系列”Xn中的一个部分。于是,当由自然语义中的symbol来指代 Xn时,整个表达式就变成了:(symbol)。建议阅读作者的其它两篇文章:   《从表达式到变量:一行scheme代码之所见》   http://blog.csdn.net/aimingoo/archive/2007/02/12/1508118.aspx   《从表达式到函数:表面的简洁》   http://blog.csdn.net/aimingoo/archive/2007/10/08/1815379.aspx
分享到:
评论

相关推荐

    Python3.6伪装exe启动

    这样表面简洁,内部是python,整体复制就可以使用了,通过main.py去运行python。快捷方式的参数不写是用python.exe打开main.py,大于1的数字也是python.exe打开main.py,只有等1是pythonw.exe打开main.py。快捷方式...

    初识机织物与技术参数.pdf

    素织物表面简洁无明显花纹,常见的有染色和印花两种类型。小花纹织物利用变化组织形成细小的图案,而大花纹织物则通过基础组织和变化组织的结合,创造出立体的图案。机织物的生产流程可分免浆织物、白织物和色织物。...

    Ruby方面的英文翻译

    Matsumoto的目标是使Ruby自然而不简单,就像人类身体一样,表面简洁但内部复杂。 自公开发布以来,Ruby在全球范围内吸引了众多忠实的开发者。在2006年,Ruby获得了大众的认可,活跃用户群体遍布全球各大城市,相关...

    日本钢板表面处理技术基础研究的进步和展望.rar

    【描述解析】:“日本钢板表面处理技术基础研究的进步和展望rar,日本钢板表面处理技术基础研究的进步和展望”虽然描述较为简洁,但重复了标题的关键信息,进一步确认了文档内容将聚焦于日本在该领域的研究动态和未来...

    ppt简洁模板

    在探讨“PPT简洁模板”的知识点时,我们不仅限于表面的设计美学,更深入到其背后的逻辑、原则以及如何在实际应用中最大化简洁模板的优势。本文将从以下几个方面展开: ### 1. **理解简洁设计的基本原则** 简洁设计...

    大学物理实验教学中基于MATLAB的液体表面张力系数测定的实验数据处理.pdf

    这种方法不仅能有效避免手动计算可能产生的误差,而且可以将复杂的数学计算过程转换为简洁的可视化操作,极大地提高数据处理的效率和准确性。 在液体表面张力系数测定实验中,通过MATLAB编程处理,可以精确地标记...

    简洁客厅模型效果图

    3D模型设计的过程通常包括以下步骤:概念草图、建模(包括基础形状构建、细节添加和雕刻)、UV展开(为纹理映射准备模型表面)、贴图(应用颜色、纹理和光照信息)、灯光设置、摄像机定位以及最终的渲染。...

    最简洁的坐标转换

    坐标系统是用于定位地球表面上任何位置的数学框架。常见的有笛卡尔坐标、极坐标和地理坐标等。然而,由于历史原因和不同应用的需求,全球存在多种不同的坐标系统,如WGS84、CGCS2000、北京54、西安80等。在这些坐标...

    电气类64. 风力涡轮机表面损坏检测数据集(1万多张+yolo格式标签).txt

    - **基本概念**:YOLO (You Only Look Once) 是一种流行的实时物体检测框架,其标签格式简洁且易于处理。 - **格式介绍**:每个图像的标签文件为.txt格式,内容包含物体类别索引和物体边界框的位置信息。 - **具体...

    产品几何技术规定GPS表面结构的表示法.pptx

    4. **表面结构参数的表示方式发生变化**:参数代号改为大小写斜体形式,如Ra和Rz,且不再使用下角标,这使得参数表示更加简洁明了。 5. **表面结构参数的标准化**:新标准定义了三组新的表面结构参数,并且增加了...

    行业分类-设备装置-水溶性表面活性剂组合物、墨配制剂和纸涂布配制剂.zip

    描述部分简洁明了,直接重申了标题中的关键信息,暗示文档将深入探讨水溶性表面活性剂的组合物,以及它们在墨水和纸涂布配制剂中的应用。表面活性剂在化工中起着重要作用,它们能够降低水的表面张力,使得油水混合,...

    祝福话语简洁经典.docx

    【知识点解析】: 这篇文档主要是一系列简洁而经典...综上所述,虽然这些祝福话语表面上看似与IT无关,但实际上它们所蕴含的情感智慧和人际交往原则,对于构建良好的IT工作环境和提升个人职业素养具有重要的启示意义。

    ProtelDXP简洁教程.pdf

    为了保护线路,防止短路,PCB表面会涂抹阻焊油,常见颜色为绿色。根据设计需求,PCB可分为单面、双面及多层板,层数越多,复杂度和成本越高。 Protel DXP是Protel系列的升级版本,它在原有基础上增加了许多新特性,...

    行业分类-设备装置-板纸用表面施胶剂及其制备方法.zip

    标签“行业分类-设备装置-板纸用表面施”是对主题的简洁概括,突出了这是一项与板纸生产中表面施胶相关的技术和应用。 压缩包内的“板纸用表面施胶剂及其制备方法.pdf”很可能是详细的科研报告或专利文件,包含了...

    蓝色拉丝商务背景简洁线条工作总结报告ppt模板.rar

    拉丝效果往往通过图像处理软件(如Photoshop或PowerPoint内置工具)实现,通过调整颜色、光影和纹理参数来模拟金属表面的细腻纹理。 “简洁线条”则强调了模板的设计语言,线条作为设计元素在PPT中起到引导视线、...

    有关去除函数,计算机控制光学表面成型的英文论文

    论文的描述虽然简洁,但暗示了内容可能涵盖以下几个方面: 1. 去除函数的理论基础:深入探讨去除函数的数学模型和其在光学表面成型中的作用。 2. 实际应用:可能包含具体的案例研究,展示如何运用去除函数来改善...

    基于深度学习的连铸板坯表面缺陷检测系统.pdf

    文章还提到了Python,Python是一种广泛使用的高级编程语言,以其简洁的语法和强大的库支持而受到开发者的青睐。在深度学习和机器学习领域,Python是首选语言之一,大量的库如Tensorflow、Keras、PyTorch等都提供了...

    行业分类-设备装置-具有催化材料减少表面的聚晶金刚石.zip

    描述中的内容虽然简洁,但暗示了这是一个关于优化聚晶金刚石表面处理的技术,可能涉及的工艺包括化学气相沉积(CVD)、物理气相沉积(PVD)或者其他的表面处理技术。这些方法会根据具体的应用需求和工况条件,选择...

    OBLOG 表面

    同时,考虑到网页优化,设计师还会考虑代码的简洁性和加载速度,以确保网站在各种设备和网络环境下都能顺畅运行。 总之,OBLOG 表面是一个旨在帮助用户轻松创建美观博客的网页模版,包含了构成网站所需的各种元素。...

    声表面波带通滤波器设计仿真软件研究.pdf

    从软件开发的角度来看,Visual Basic因其简洁性和强大的编程能力而被选作开发语言,而MatrixVB插件则提供与Matlab语言的接口,使得软件能够在数据处理和图形绘制方面更为强大。这种混合编程的方式,让软件既拥有...

Global site tag (gtag.js) - Google Analytics