﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-(define (cuigang) (coding))-文章分类-转帖</title><link>http://www.cppblog.com/cuigang/category/5861.html</link><description>(define (coding) (coding))
</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 12:56:50 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 12:56:50 GMT</pubDate><ttl>60</ttl><item><title>计算科学数学理论浅谈（转）</title><link>http://www.cppblog.com/cuigang/articles/46229.html</link><dc:creator>cuigang</dc:creator><author>cuigang</author><pubDate>Thu, 03 Apr 2008 17:12:00 GMT</pubDate><guid>http://www.cppblog.com/cuigang/articles/46229.html</guid><wfw:comment>http://www.cppblog.com/cuigang/comments/46229.html</wfw:comment><comments>http://www.cppblog.com/cuigang/articles/46229.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cuigang/comments/commentRss/46229.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cuigang/services/trackbacks/46229.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt; font-family: Tahoma;">
计算机自从其诞生之日起，它的主要任务就是进行各种各样的科学计算。文档处理， <br>
数据处理，图像处理，硬件设计，软件设计等等，都可以抽象为两大类：数值计算与非 <br>
数值计算。作为研究计算机科学技术的人员，我们大都对计算数学对整个计算机科学的 <br>
重要性有一些了解。但是数学对我们这些专业的研究和应用人员究竟有多大的用处呢？ <br>
我们先来看一下下面的一个流程图： <br>
<br>
─&#8594;数学模型─┬&#8594;数值计算方法──┐　　　　　　　　　 <br>
│　　　　　　　　　├&#8594;程序设计 <br>
└&#8594;非数值计算方法─┘&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; │ <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8595; <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编译程序，求计算结果 <br>
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上图揭示了利用计算机解决科学计算的步骤，实际问题转换为程序，要经过一个对 <br>
问题抽象的过程，建立起完善的数学模型，只有这样，我们才能建立一个设计良好的程 <br>
序。从中我们不难看出计算数学理论对用计算机解决问题的重要性。下面我们将逐步展 <br>
开对这个问题的讨论。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 计算机科学的数学理论体系是相当庞杂的，笔者不敢随意划分，参考计算机科学理 <br>
论的学科体系，我们谈及的问题主要涉及：数值计算，离散数学，数论，计算理论四大 <br>
方向。 <br>
<br>
[一]数值计算（Numerical Computation） <br>
<br>
主要包括数值分析学、数学分析学、线性代数、计算几何学、概率论与数理统计学。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 数值分析学又常被称为计算方法学，是计算理论数学非常重要的一个分支，主要研 <br>
究数值型计算。研究的内容中首先要谈谈数值计算的误差分析，误差是衡量我们的计算 <br>
有效与否的标准，我们的算法解决问题如果在误差允许的范围内，则算法是有效的，否 <br>
则就是一个无效的问题求解。另外就是数值逼近，它研究关于如何使用容易数值计算的 <br>
函数来近似地代替任意函数的方法与过程。感觉应用比较广的不得不提切雪比夫逼近和 <br>
平方逼近了。笔者曾经尝试过的就是通过最佳平方逼近进行曲线的拟合，开发工具可以 <br>
选择VC++或者Matlab。插值函数是另外一个非常重要的方面，现代的计算机程序控制加 <br>
工机械零件，根据设计可给出零件外形曲线的某些型值点，加工时走刀方向及步数，就 <br>
要通过插值函数计算零件外形曲线及其他点函数值。至于方程求根、线性方程组求解， <br>
一般的计算性程序设计问题都会多多少少的涉及一些，我们这里就不赘述了。关于数值 <br>
分析学的一个学习误区就是仅仅学习理论知识，而很难和程序设计结合起来，实际上通 <br>
过上面的论述，大家已经能够初步地认识到这个学科是应当与程序设计紧密联系才能够 <br>
体现它的重要性的。关于理论的学习，推荐华中科技大学李庆扬老师的《数值分析》。 <br>
然而理论学习毕竟是个过程，最终的目标还是要用于程序设计解决实际的计算问题，向 <br>
这个方向努力的书籍还是挺多的，这里推荐大家高等教育出版社（CHEP）和施普林格出 <br>
版社(Springer)联合出版的《计算方法（Computational Methods）》,华中理工大学数 <br>
学系写的（现华中科技大学），这方面华科大做的工作在国内应算是比较多的，而个人 <br>
认为以这本最好，至少程序设计方面涉及了：任意数学函数的求值，方程求根，线性方 <br>
程组求解，插值方法，数值积分，场微分方程数值求解。 <br>
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp; 数学分析学很多学校在近些年已经替代高等数学被安排到了本科教学当中。原因是 <br>
很简单的，高等数学虽然也是非常有用的工程数学，介绍的问题方法也被广泛的应用， <br>
但是正如大家所知道的，高等数学不太严格的说，基本上就是偏向于计算的数学分析， <br>
当然省去了数学分析非常看重的推理证明，然而我们认为这一部分正是我们最需要的。 <br>
这对我们培养良好的分析能力和推理能力极有帮助。我的软件工程学导师北工大数理学 <br>
院的王仪华先生就曾经教导过我们，数学系的学生到软件企业中大多作软件设计与分析 <br>
工作，而计算机系的学生做初级程序员的居多，原因就在于数学系的学生分析推理能力 <br>
，从所受训练的角度上要远远在我们平均水平之上。谈到这方面的书籍，公认北京大学 <br>
张筑生老师的《数学分析新讲》为最好。张筑生教授一生写的书并不太多，但是只要是 <br>
写出来的每一本都是本领域内的杰作，这本当然更显突出些。这种老书看起来不仅是在 <br>
传授你知识，而是在让你体会科学的方法与对事物的认识方法。现在多用的似乎是复旦 <br>
大学的《数学分析》，高等教育出版社的，也是很好的教材。但关于如何去利用从中获 <br>
得的推理证明能力，我们在遇到具体问题的时候，可以在今后的文章详细讨论。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 线性代数是我们在工科本科学习的必修课程，似乎大家找不到到底这个有什么用， <br>
其实很明显，线性代数作为工程数学的重要分支，在计算机领域的研究有相当广泛的应 <br>
用。最为突出的可以谈谈数组和矩阵的相关知识： <br>
<br>
①&#8592;—④ <br>
&#8593;\&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; │ <br>
&#8595;&nbsp;&nbsp;&nbsp;&nbsp; \,&#8595; <br>
②&#8592;—③ <br>
<br>
令aij=1,表示从i市到j市有1条航线 <br>
令aij=0，表示从i市到j市没有单项航线 <br>
则图可用矩阵表示： <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ┌&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ┐ <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; │0 1 1 0 │ <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; │1 0 0 0 │ <br>
A= (aij) = │0 1 0 0 │ <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; │1 0 0 0 │ <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; │1 0 1 0 │ <br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; └&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ┘ <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们可以采用程序设计实现这个问题，如果辅以权值，可以转化为最短路径的问题 <br>
，再复杂化一点还可以转化为具有障碍物的最短路径问题，这就会涉及一些如Dijkstra <br>
算法等高级程序设计算法话题。这些都依靠着数组、矩阵的基本知识。数组的应用主要 <br>
在图像处理以及一些程序设计理论。矩阵的运算领域极为广泛，比如在计算机图形学当 <br>
中曲线曲面的构造，图像的几何变换，包括平移、镜像、转置、缩放。在高级图像问题 <br>
更有广泛应用，例如在图像增强技术，投影技术中的应用。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 计算几何学研究的是几何外形信息的计算机表示。包括几何查找、多边形、凸包问 <br>
题、交与并、几何体的排列、几何拓扑网络设计、随机几何算法与并行几何算法。它构 <br>
成了计算机图形学中的基本算法，是动画设计，制造业计算机辅助设计的基础。如果从 <br>
事这方面的深入研究，可以参考中国计算机学会周培德先生的《计算几何——算法分析 <br>
与设计》。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 概率论与数理统计学是这个领域最后一门关键的课程。概率论部分提供了很多问题 <br>
的基本知识描述，比如模式识别当中的概率计算，参数估计等等。数理统计部分有很多 <br>
非常经典的内容，比如伪随机数、蒙特卡罗法、回归分析、排队论、假设检验、以及经 <br>
典的马科夫过程。尤其是随机过程部分，是分析网络和分布式系统，设计随机化算法和 <br>
协议非常重要的基础。 <br>
<br>
二]离散数学（Discrete Mathematics） <br>
<br>
随着计算机科学的出现与广泛应用,人们发现利用计算机处理的数学对象与传统的分析 <br>
有明显的区别：分析研究的问题解决方案是连续的，因而微分，积分成为基本的运算； <br>
而这些分支研究的对象是离散的，因而很少有机会进行此类的计算。人们从而称这些分 <br>
支为"离散数学"。离散数学经过几十年发展，方向上基本上稳定下来。当然不同时期还 <br>
有很多新内容补充进来。就学科方向而言，一般认为，离散数学包含：集合论、逻辑学 <br>
、代数学、图论、组合学。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 逻辑学（Logics）我们主要指数理逻辑，形式逻辑在推理问题中也有比较广泛的应 <br>
用。（比如我们学校还为此专门开设了选修课程）这方面的参考推荐中科院软件所陆钟 <br>
万教授的《面向计算机科学的数理逻辑》。现在可以找到陆钟万教授的讲课录像，http <br>
://www.cas.ac.cn/html/Dir/2001/11/06/3391.htm。总的来说，学集合/逻辑一定要站 <br>
在理解的高度上去思考相关的问题。集合论（Set Theory）和逻辑学构成了计算机科学 <br>
最重要的数学问题描述方式。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 代数学（Algebra）包括：抽象代数、布尔代数、关系代数、计算机代数 <br>
<br>
（1）抽象代数（Abstract Algebra）研究的主要内容涵盖群、环、域。抽象代表的是 <br>
将研究对象的本质提炼出来，加以高度概括，来描述其形象。&#8220;欧式环&#8221;就是在将整数 <br>
和多项式的一些相同的特点加以综合提炼引入的。抽象代数提供的一些结论为我们研究 <br>
一些具体问题时所需使用的一些性质提供了依据。推荐一个最简单的，最容易学的材料 <br>
：<a href="http://www.math.miami.edu/%7Eec/book/%E8%BF%99%E6%9C%AC%E3%80%8AIntroduction" target="_blank">http://www.math.miami.edu/~ec/book/这本《Introduction</a> to Linear and Abstra <br>
ct Algebra》非常通俗易懂，而且把抽象代数和线性代数结合起来，对初学者来说非常 <br>
理想。 <br>
<br>
（2）布尔代数（Boolean Algebra）是代数系统中最为基础的部分，也是最核心的基本 <br>
理论。主要包括了集合的基本概念与运算，自对偶的公理系统。是数据表示的重要基础 <br>
。相信大家都很清楚它的重要性。 <br>
<br>
（3）关系代数（Relational Algebra）应用也是极为广泛，比如数据库技术中的关系数 <br>
据库的构建就要用到关系代数的相关理论。 <br>
<br>
（4）计算机代数（Computer Algebra）大家可能比较生疏，其实它研究的主要内容即是 <br>
围绕符号计算与公式演算展开的。是研究代数算法的设计、分析、实现及其应用的学科 <br>
。主要求解非数值计算，输入输出用代数符号表示。计算机代数的开发语言主要有：AL <br>
TRAN,CAMAL,FORMAL。主要应用于：射影几何，工业设计，机器人手臂运动设计。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图论（Graph Theory）主要研究的内容包括：图的基本概念、基本运算、矩阵表示 <br>
，路径、回路和连通性，二部图、平面图，树，以及网络流。图论的应用领域太过广泛 <br>
，仅举两个例子：比如在计算机网络拓扑图的设计与结构描述中，就必须用到相当多的 <br>
图的结构和基本概念。关于网络流更是在电流网络与信息网络的流量计算当中广泛应用 <br>
。树的相关应用则无须多言了。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 组合学（Combinatorics）有两部分单独的研究领域：组合数学与组合算法。组合学 <br>
问题的算法，计算对象是离散的、有限的数学结构。从方法学的角度，组合算法包括算 <br>
法设计和算法分析两个方面。关于算法设计，历史上已经总结出了若干带有普遍意义的 <br>
方法和技术，包括动态规划、回溯法、分支限界法、分治法、贪心法等。应用是相当广 <br>
泛的,比如旅行商问题、图着色问题、整数规划问题。关于组合数学，主要研究的内容有 <br>
：鸽巢原理、排列与组合、二项式系数容斥原理及应用，递推关系和生成函数、特殊计 <br>
数序列、二分图中的匹配、组合设计。推荐Richard A.Brualdi的《Introductory Comb <br>
inatorics》作为参考。 <br>
<br>
[三]数论（Number Theory） <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp; 数论这门学科最初是从研究整数开始的，所以叫做整数论。后来更名为数论。它包括 <br>
以下几个分支： <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp; 初等数论是不求助于其他数学学科的帮助，只依靠初等方法来研究整数性质的数论分 <br>
支。比如在数论界非常著名的&#8220;中国剩余定理&#8221;，就是初等数论中很重要的内容。对于 <br>
程序设计来说这部分也是相当有价值的，如果你对中国剩余定理比较清楚，利用它，你 <br>
可以将一种表达式经过简单的转换后得出另一种表达式，从而完成对问题分析视角的转 <br>
换。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp; 解析数论是使用数学分析作为工具来解决数论问题的分支。是解决数论中比较深刻问 <br>
题的强有力的工具。我国数学家陈景润在尝试解决&#8220;哥德巴赫猜想&#8221;问题中使用的就是 <br>
解析数论的方法。以素数定理为基础解决计算素数的问题及其算法实现应是我们多多关 <br>
注的。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 代数数论是把整数的概念推广到一般代数数域上去，建立了素整数、可除性等概念 <br>
。程序设计方面涉及的比较多的是代数曲线的研究，比如说椭圆曲线理论的实现。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 几何数论研究的基本对象是&#8220;空间格网&#8221;。空间格网就是指在给定的直角坐标系上 <br>
，坐标全是整数的点，叫做整点；全部整点构成的组就叫做空间格网。空间格网对计算 <br>
几何学的研究有着重大的意义。几何数论涉及的问题比较复杂，必须具有相当的数学基 <br>
础才能深入研究。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 总的说来，由于近代计算机科学的发展，数论得到了广泛的应用。比如在计算方法 <br>
、代数编码、组合学理论等方面都广泛使用了初等数论范围内的许多研究成果；现在有 <br>
些国家应用&#8220;孙子定理&#8221;来进行测距，用原根和指数来计算离散傅里叶变换等。如果你 <br>
曾经系统的学习过数论算法，你会发现这个分支学科研究的一些基本问题对程序设计是 <br>
相当有用的，比如说素数问题、素性测试、因子分解、最大公约数、模取幂运算、求解 <br>
同余线性方程。其中的很多问题都是程序设计的基本问题。但这些问题都不能小视，举 <br>
个例子来说吧，关于求最大公约数的程序，笔者曾经尝试的就可以采用循环语句结构和 <br>
递归结构。另外，以大素数为基础的密码体系的建立是近些年数论算法广泛应用的一个 <br>
重要的原因。原理是大素数的乘积重新分解因数十分困难。RSA公钥加密系统的构建就是 <br>
基于这个原理的（三位发明人因此也获得了2002年美国计算机协会颁发的图灵奖）。 <br>
<br>
<br>
四]计算理论（Theory of Computation） <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 涉及的内容是科学计算非常重要的一部分分支，也是大家研究相当多的一部分。主 <br>
要包括：算法学，计算复杂性，程序理论。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp; 算法学（Algorithms）在计算机科学理论中有着举足轻重的地位。是解决很多数值 <br>
型，非数值型问题的基础。记得一次学校接收招标项目，很多中小型软件厂商都无法完 <br>
成一个软件的功能模块，就是因为当时他们对一个具体问题的算法不能做出正确的抽象 <br>
，最后由我们学校数理学院的一支软件团队承担了这项任务，他们的最终报告体现出来 <br>
，问题的解决策略只有通过人工神经元网络的反向传播算法。可见在比较有深度的程序 <br>
设计中，算法的重要性更为突出。学习算法学要有一个长期的理论和实践的过程。遇到 <br>
一个具体算法问题时,首先要通过自己描述的数学抽象步骤，看看自己以前有没有处理过 <br>
这种问题。如果没有，很可能这个问题是多个算法的综合，或者是需要我们自己去构造 <br>
算法。这就需要我们有扎实的算法功底，为了打好这个功底，推荐两套圣经级的书籍首 <br>
先是Thomas H.Cormen等著的《Introduction to Algorithms》。对算法学习而言，这一 <br>
本内容相当的全面。再深一点的就是大家作为常识都知道的《The Art of Computer Pr <br>
ogramming》，目前已经出版3册。两本书的价值大家应当都是清楚的。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 计算复杂性研究的内容很广，其中包括NP完全性理论，可计算性理论，自动机理论 <br>
，形式语言理论（包括广泛应用于编译原理领域的文法，还包括Petri网论的相关内容） <br>
以及大家熟知的复杂性度量。时间复杂度、空间复杂度的计算是度量算法非常重要的参 <br>
数，也是我们衡量程序优劣程度的重要依据。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 程序理论（Theory of programs）包含了形式语义学，程序验证和并发模型的研究 <br>
。关于程序验证学习的重要性大家都很清楚，学习的方法自然也是多多结合具体的问题 <br>
去分析。关于并发模型，主要研究的就是进程代数，通信系统演算，通信顺序进程。这 <br>
部分是研究操作系统理论与实现的重要基础。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 按照计算机科学数学理论的架构来谈了各方面的内容和一些应用，下面我们再单独 <br>
来看一些上面没有涉及到的学科与这些理论的具体结合情况： <br>
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 设计方面的应用刚才谈的很多，我只再说说数据库原理与技术，这方面用到的重要 <br>
数学基础主要包括：集合论，二元关系及其推理（尤其是研究关系数据库），研究数据 <br>
分布与数据库结构又涉及相当多的图论知识。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 计算机科学的发展有赖于硬件技术和软件技术的综合。在设计硬件的时候应当充分 <br>
融入软件的设计思想，才能使硬件在程序的指挥下发挥极致的性能。在软件设计的时候 <br>
也要充分考虑硬件的特点，才能冲破软件效率的瓶颈。达到硬件和软件设计的统一，严 <br>
格的说这并不轻松，一般的程序设计者很难将这样的思想贯穿在其程序设计当中。仅举 <br>
个简单的例子：我们在写一些C语言的程序，必要的时候都会采取内嵌一段汇编指令，这 <br>
就是比较充分地考虑了硬件的工作情况，从而能够提高程序运行的效率。所以我们也有 <br>
必要了解一些硬件的基础知识。关于学习硬件的时候常会用到的基本数学思想也是相当 <br>
多的，拿电路基础与模拟电路来说，我们就经常要利用多元函数，不等式计算进行电流 <br>
电压的计算。能量的计算还常常涉及微积分学的很多计算。在数字电子技术当中（有时 <br>
也称数字逻辑学）数理逻辑，尤其是逻辑演算部分运用相当广泛，数制转换更是非常重 <br>
要的基础，各种数字电路参数的计算则是多元函数，不等式的计算解决的问题。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 从事计算机硬件程序设计的程序员，则不可回避的就是数字信号处理。这门科学所 <br>
用到的数学基础主要有：三角函数、微积分、高次方程求解、数值逼近，傅里叶变换。 <br>
在滤波器的设计当中还会用到矩阵运算。笔者曾经研究过一个VC++环境下开发的滤波器 <br>
的模拟软件，就是利用莱文逊-杜宾递推算法，在较大规模的矩阵运算基础上进行的。当 <br>
然，开发的环境不一定是这个，你也可以选择MATLAB或者纯C语言编译器。如果我们不了 <br>
解相关的数学基础，不要说程序设计，就算是建立运算模型都是相当困难的。 <br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一些周围的同学和一些在职的程序员，大家经过一段时间的学习，普遍都觉得数学 <br>
对学习计算机和研究计算机程序设计等问题来说非常重要，但是又苦于无从下手。上面 <br>
比较全面地谈及了计算机科学数学理论的相关内容。需要特别指明的是，我们研究问题 <br>
的精力是有限的，如果您是在校的计算机系学生，则可以对上面的方方面面都有所涉及 <br>
，以尝试计算数学这个强大的理论工具。为今后的工作奠定一个坚实的基础。但是如果 <br>
您研究的是比较具体的工作，我们并不推荐您研究所有的内容，最好的方法就是对上面 <br>
的数学基础都有些了解，然后遇到具体工作，需要哪部分内容，再进行深入的学习与研 <br>
究。这样针对性比较强的学习效果是会比较显著的。对于上面推荐的一些参考材料，除 <br>
非你要花相当长的一段时间来提高你的计算机数学理论。否则也没必要每一页，每一本 <br>
都字字精读，还是那个原则，按需索取其中的内容。学习的方法描述起来就一句话：结 <br>
合具体的问题，深入的理解数学理论知识，将理论程序化，尝试用程序设计实现理论原 <br>
理。达到这样的程度，问题基本上都可以解决的
<br></span><img src ="http://www.cppblog.com/cuigang/aggbug/46229.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cuigang/" target="_blank">cuigang</a> 2008-04-04 01:12 <a href="http://www.cppblog.com/cuigang/articles/46229.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(ZT) Scheme 语言概要</title><link>http://www.cppblog.com/cuigang/articles/39398.html</link><dc:creator>cuigang</dc:creator><author>cuigang</author><pubDate>Sun, 23 Dec 2007 12:43:00 GMT</pubDate><guid>http://www.cppblog.com/cuigang/articles/39398.html</guid><wfw:comment>http://www.cppblog.com/cuigang/comments/39398.html</wfw:comment><comments>http://www.cppblog.com/cuigang/articles/39398.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/cuigang/comments/commentRss/39398.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/cuigang/services/trackbacks/39398.html</trackback:ping><description><![CDATA[<table style="width: 225px; height: 53px;" class="docutils field-list" frame="void" rules="none">
    <tbody valign="top">
        <tr class="field">
        </tr>
        <tr class="field">
        </tr>
        <tr class="field">
        </tr>
    </tbody>
</table>
Scheme
语言是LISP语言的一个方言(或说成变种)，它诞生于1975年的MIT，对于这个有近三十年历史的编程语言来说，它并没有象C++，java，C#那
样受到商业领域的青睐，在国内更是显为人知。但它在国外的计算机教育领域内却是有着广泛应用的，有很多人学的第一门计算机语言就是Scheme语言。
<p>作为Lisp 变体，Scheme 是一门非常简洁的计算语言，使用它的编程人员可以摆脱语言本身的复杂性，把注意力集中到更重要的问题上，从而使语言真正成为解决问题的工具。本文分为上、 下两部分来介绍 scheme 语言。</p>
<!-- MORE -->
<div class="section" id="scheme">
<h2>一．Scheme语言的特点</h2>
<p>Scheme
语言是LISP语言的一个方言(或说成变种)，它诞生于1975年的MIT，对于这个有近三十年历史的编程语言来说，它并没有象C++，java，C#那
样受到商业领域的青睐，在国内更是显为人知。但它在国外的计算机教育领域内却是有着广泛应用的，有很多人学的第一门计算机语言就是Scheme语言。</p>
<p>它是一个小巧而又强大的语言，作为一个多用途的编程语言，它可以作为脚本语言使用，也可以作为应用软件的扩展语言来使用，它具有元语言特性，还有很多独到的特色,以致于它被称为编程语言中的"皇后"。</p>
<p>下面是洪峰对Scheme语言的编程特色的归纳：</p>
<blockquote>
<ul class="simple">
    <li>词法定界（Lexical Scoping）</li>
    <li>动态类型（Dynamic Typing）</li>
    <li>良好的可扩展性</li>
    <li>尾递归（Tail Recursive）</li>
    <li>函数可以作为值返回</li>
    <li>支持一流的计算连续</li>
    <li>传值调用（passing-by-value）</li>
    <li>算术运算相对独立</li>
</ul>
</blockquote>
<p>本文的目的是让有编程基础（那怕是一点点）的朋友能尽快的掌握Scheme语言的语法规则，如果您在读完本文后，发现自己已经会用Scheme语言了，那么我的目的就达到了。</p>
</div>
<div class="section" id="id2">
<h2>二．Scheme语言的标准与实现</h2>
</div>
<div class="section" id="r5rs-revised-5-report-on-the-algorithmic-language-scheme">
<h2>R5RS (Revised(5) Report on the Algorithmic Language Scheme)</h2>
<p>Scheme语言的语法规则的第5次修正稿，1998年制定，即Scheme语言的现行标准，目前大多数Scheme语言的实现都将达到或遵循此标准，并且几乎都加入了一些属于自己的扩展特色。</p>
</div>
<div class="section" id="guile-gnu-s-extension-language">
<h2>Guile (GNU's extension language)</h2>
<p>Guile是GNU工程的一个项目，它是GNU扩展语言库，它也是Scheme语言的一个具体实现；如果你将它作为一个库打包，可以把它链接到你的应用程序中去，使你的应用程序具有自己的脚本语言，这个脚本语言目前就是Scheme语言。</p>
<p>Guile可以在LINUX和一些UNIX系统上运行，下面是简单的安装过程：</p>
<p>下载guile-1.6.4版，文件名为guile-1.6.4.tar.gz，执行下面的命令：</p>
<pre class="literal-block">tar xvfz guile-1.6.4.tar.gz<br>cd guile-1.6.4<br>./configure<br>make<br>make install<br></pre>
<p>如此，即可以执行命令guile，进入guile&gt;提示符状态，输入调试Scheme程序代码了，本文的所有代码都是在guile下调试通过。</p>
</div>
<div class="section" id="id3">
<h2>其它实现</h2>
<p>除了Guile外，Scheme语言的实现还有很多，如：GNU/MIT-Scheme，
SCI，Scheme48，DrScheme等，它们大多是开源的，可以自由下载安装使用，并且跨平台的实现也很多。你会发现既有象basic的
Scheme语言解释器，也有将Scheme语言编译成C语言的编译器，也有象JAVA那样将Scheme语言代码编译成虚拟机代码的编译器。</p>
</div>
<div class="section" id="id4">
<h2>三．基本概念</h2>
</div>
<div class="section" id="id5">
<h2>注释</h2>
<p>Scheme语言中的注释是单行注释，以分号[;]开始一直到行尾结束，其中间的内容为注释，在程序运行时不做处理，如：</p>
<pre class="literal-block">; this is a scheme comment line.<br></pre>
<p>标准的Scheme语言定义中没有多行注释，不过在它的实现中几乎都有。在Guile中就有多行注释，以符号组合"#!"开始，以相反的另一符号组合"!#"结束，其中内容为注释，如：</p>
<pre class="literal-block">#!<br>there are scheme comment area.<br>you can write mulity lines here .<br>!#<br></pre>
<p>注意的是，符号组合"#!"和"!#"一定分做两行来写。</p>
</div>
<div class="section" id="id6">
<h2>Scheme用做脚本语言</h2>
<p>Scheme语言可以象sh，perl，python等语言那样作为一种脚本语言来使用，用它来编写可执行脚本，在Linux中如果通过Guile用Scheme语言写可执行脚本，它的第一行和第二行一般是类似下面的内容：</p>
<pre class="literal-block">#! /usr/local/bin/guile -s<br>!#<br></pre>
<p>这样的话代码在运行时会自动调用Guile来解释执行，标准的文件尾缀是".scm"。</p>
</div>
<div class="section" id="form">
<h2>块(form)</h2>
<p>块(form)是Scheme语言中的最小程序单元，一个Scheme语言程序是由一个或多个form构成。没有特殊说明的情况下 form 都由小括号括起来，形如：</p>
<pre class="literal-block">(define x 123)<br>(+ 1 2)<br>(* 4 5 6)<br>(display "hello world")<br></pre>
<p>一个 form 也可以是一个表达式，一个变量定义，也可以是一个过程。</p>
</div>
<div class="section" id="id7">
<h2>form嵌套</h2>
<p>Scheme语言中允许form的嵌套，这使它可以轻松的实现复杂的表达式，同时也是一种非常有自己特色的表达式。下图示意了嵌套的稍复杂一点的表达式的运算过程：</p>
<!-- IMAGE:png -->
<img src="http://www.oxsama.com/?k=data_image&amp;p=static.d/1189119482-19692-0.rst.images/0.png" alt="http://www.oxsama.com/?k=data_image&amp;p=static.d/1189119482-19692-0.rst.images/0.png">
</div>
<div class="section" id="id8">
<h2>变量定义</h2>
<p>可以用define来定义一个变量，形式如下：</p>
<pre class="literal-block">(define 变量名 值)<br></pre>
<p>如： (define x 123) ，定义一个变量x，其值为123。</p>
<p>更改变量的值</p>
<p>可以用set!来改变变量的值，格式如下：</p>
<pre class="literal-block">(set! 变量名 值)<br></pre>
<p>如： (set! x "hello") ，将变量x的值改为"hello" 。</p>
<p>Scheme语言是一种高级语言，和很多高级语言(如python，perl)一样，它的变量类型不是固定的，可以随时改变。</p>
</div>
<div class="section" id="id9">
<h2>四．数据类型</h2>
</div>
<div class="section" id="id10">
<h2>1. 简单数据类型</h2>
<div class="section" id="boolean">
<h3>逻辑型(boolean)</h3>
<p>最基本的数据类型，也是很多计算机语言中都支持的最简单的数据类型，只能取两个值：#t，相当于其它计算机语言中的 TRUE；#f，相当于其它计算机语言中的 FALSE。</p>
<p>Scheme语言中的boolean类型只有一种操作：not。其意为取相反的值，即：</p>
<pre class="literal-block">(not #f) =&gt; #t<br>(not #t) =&gt; #f<br></pre>
<p>not的引用，与逻辑非运算操作类似</p>
<pre class="literal-block">guile&gt; (not 1)<br>#f<br>guile&gt; (not (list 1 2 3))<br>#f<br>guile&gt; (not 'a)<br>#f<br></pre>
<p>从上面的操作中可以看出来，只要not后面的参数不是逻辑型，其返回值均为#f。</p>
</div>
<div class="section" id="number">
<h3>数字型(number)</h3>
<p>它又分为四种子类型：整型(integer)，有理数型(rational)，实型(real)，复数型(complex)；它们又被统一称为数字类型(number)。</p>
<p>如：复数型(complex) 可以定义为 (define c 3+2i)
实数型（real）可以定义为 (define f 22/7)
有理数型（rational）可以定义为 (define p 3.1415)
整数型(integer) 可以定义为 (define i 123)</p>
<p>Scheme 语言中，数字类型的数据还可以按照进制分类，即二进制，八进制，十进制和十六进制，在外观形式上它们分别以符号组合 #b、
#o、 #d、 #x 来作为表示数字进制类型的前缀，其中表示十进制的#d可以省略不写，如：二进制的 #b1010 ，八进制的
#o567，十进制的123或 #d123，十六进制的 #x1afc 。</p>
<p>Scheme语言的这种严格按照数学定理来为数字类型进行分类的方法可以看出Scheme语言里面渗透着很深的数学思想，Scheme语言是由数学家们创造出来的，在这方面表现得也比较鲜明。</p>
</div>
<div class="section" id="char">
<h3>字符型(char)</h3>
<p>Scheme语言中的字符型数据均以符号组合 "#\" 开始，表示单个字符，可以是字母、数字或"[ ! $ % &amp; * + - . / : &lt; = &gt; ? @ ^ _ ~ ]"等等其它字符，如：
#\A 表示大写字母A，#\0表示字符0，
其中特殊字符有：#\space 表示空格符和 #\newline 表示换行符。</p>
</div>
<div class="section" id="symbol">
<h3>符号型(symbol)</h3>
<p>符号类型是Scheme语言中有多种用途的符号名称，它可以是单词，用括号括起来的多个单词，也可以是无意义的字母组合或符号组合，它在某种意义上可以理解为C中的枚举类型。看下面的操作：</p>
<pre class="literal-block">guile&gt; (define a (quote xyz))  ;  定义变量a为符号类型，值为xyz<br>guile&gt; a<br>xyz<br>guile&gt; (define xyz 'a)  ; 定义变量xyz为符号类型，值为a<br>guile&gt; xyz<br>a<br></pre>
<p>此处也说明单引号' 与quote是等价的，并且更简单一些。符号类型与字符串不同的是符号类型不能象字符串那样可以取得长度或改变其中某一成员字符的值，但二者之间可以互相转换。</p>
</div>
</div>
<div class="section" id="id11">
<h2>2. 复合数据类型</h2>
<p>可以说复合数据类型是由基本的简单数据类型通过某种方式加以组合形成的数据类型，特点是可以容纳多种或多个单一的简单数据类型的数据，多数是基于某一种数学模型创建的。</p>
<p>字符串(string) 由多个字符组成的数据类型，可以直接写成由双引号括起的内容，如："hello" 。下面是Guile中的字符串定义和相关操作：</p>
<pre class="literal-block">guile&gt; (define name "tomson")<br>guile&gt; name<br>"tomson"<br>guile&gt; (string-length name)  ; 取字符串的长度<br>6<br>guile&gt; (string-set! name 0 #\g)  ; 更改字符串首字母(第0个字符)为小写字母g (#\g)<br>guile&gt; name<br>"gomson"<br>guile&gt; (string-ref name 3)  ; 取得字符串左侧第3个字符（从0开始）<br>#\s<br></pre>
<p>字符串还可以用下面的形式定义：</p>
<pre class="literal-block">guile&gt; (define other (string #\h #\e #\l #\l #\o ))<br>guile&gt; other<br>"hello"<br></pre>
<p>字符串中出现引号时用反斜线加引号代替，如："abc\"def" 。</p>
<div class="section" id="pair">
<h3>点对(pair)</h3>
<p>我把它译成"点对"，它是一种非常有趣的类型，也是一些其它类型的基础类型，它是由一个点和被它分隔开的两个所值组成的。形如： (1 . 2) 或 (a . b) ，注意的是点的两边有空格。</p>
<p>这是最简单的复合数据类型，同是它也是其它复合数据类型的基础类型，如列表类型（list）就是由它来实现的。</p>
<p>按照Scheme语言说明中的惯例，以下我们用符号组合 "=&gt;" 来表示表达式的值。</p>
<p>它用cons来定义，如： (cons 8 9) =&gt;(8 . 9)</p>
<p>其中在点前面的值被称为 car ，在点后面的值被称为 cdr ，car和cdr同时又成为取pair的这两个值的过程，如：</p>
<pre class="literal-block">(define p (cons 4 5))   =&gt; (4 . 5)<br>(car p)                 =&gt; 4<br>(cdr p)                 =&gt; 5<br></pre>
<p>还可以用set-car! 和 set-cdr! 来分别设定这两个值：</p>
<pre class="literal-block">(set-car! p "hello")<br>(set-cdr! p "good")<br></pre>
<p>如此，以前定义的 p 又变成了 ("hello" . "good") 这个样子了。</p>
</div>
<div class="section" id="list">
<h3>列表(list)</h3>
<p>列表是由多个相同或不同的数据连续组成的数据类型，它是编程中最常用的复合数据类型之一，很多过程操作都与它相关。下面是在Guile中列表的定义和相关操作：</p>
<pre class="literal-block">guile&gt; (define la (list 1 2 3 4 ))<br>guile&gt; la<br>(1 2 3 4)<br>guile&gt; (length la)  ; 取得列表的长度<br>4<br>guile&gt; (list-ref la 3)  ; 取得列表第3项的值（从0开始）<br>4<br>guile&gt; (list-set! la 2 99)  ; 设定列表第2项的值为99<br>99<br>guile&gt; la<br>(1 2 99 4)<br>guile&gt; (define y (make-list 5 6))  ;创建列表<br>guile&gt; y<br>(6 6 6 6 6)<br></pre>
<p>make-list用来创建列表，第一个参数是列表的长度，第二个参数是列表中添充的内容；还可以实现多重列表，即列表的元素也是列表，如：(list (list 1 2 3) (list 4 5 6))。</p>
</div>
<div class="section" id="id12">
<h3>列表与pair的关系</h3>
<p>回过头来，我们再看看下面的定义：</p>
<pre class="literal-block">guile&gt; (define a (cons 1 (cons 2 (cons 3 '()))))<br>guile&gt; a<br>(1 2 3)<br></pre>
<p>由上可见，a本来是我们上面定义的点对，最后形成的却是列表。事实上列表是在点对的基础上形成的一种特殊格式。</p>
<p>再看下面的代码：</p>
<pre class="literal-block">guile&gt; (define ls (list 1 2 3 4))<br>guile&gt; ls<br>(1 2 3 4)<br>guile&gt; (list? ls)<br>#t<br>guile&gt; (pair? ls)<br>#t<br></pre>
<p>由此可见，list是pair的子类型，list一定是一个pair，而pair不是list。</p>
<pre class="literal-block">guile&gt; (car ls)<br>1<br>guile&gt; (cdr ls)<br>(2 3 4)<br></pre>
<p>其cdr又是一个列表，可见用于pair的操作过程大多可以用于list。</p>
<pre class="literal-block">guile&gt; (cadr ls)   ; 此"点对"对象的cdr的car<br>2<br>guile&gt; (cddr ls)   ; 此"点对"对象的cdr的cdr<br>(3 4)<br>guile&gt; (caddr ls)   ; 此"点对"对象的cdr的cdr的car<br>3<br>guile&gt; (cdddr ls)   ; 此"点对"对象的cdr的cdr的cdr<br>(4)<br></pre>
<p>上在的操作中用到的cadr，cdddr等过程是专门对PAIR型数据再复合形成的数据操作的过程，最多可以支持在中间加四位a或d，如cdddr，caaddr等。</p>
<p>下图表示了由pairs定义形成的列表：</p>
<!-- IMAGE:png -->
<img src="http://www.oxsama.com/?k=data_image&amp;p=static.d/1189119482-19692-0.rst.images/1.png" alt="http://www.oxsama.com/?k=data_image&amp;p=static.d/1189119482-19692-0.rst.images/1.png">
<p>这个列表可以由pair定义为如下形式：</p>
<pre class="literal-block">(define x (cons 'a (cons 'b (cons 'c (cons 'd '())))))<br></pre>
<p>而列表的实际内容则为：(a b c d)</p>
<p>由pair类型还可以看出它可以轻松的表示树型结构，尤其是标准的二叉树。</p>
</div>
<div class="section" id="vector">
<h3>向量（vector）</h3>
<p>可以说是一个非常好用的类型 ，是一种元素按整数来索引的对象，异源的数据结构，在占用空间上比同样元素的列表要少，在外观上：</p>
<p>列表示为： (1 2 3 4)
VECTOR表示为： #(1 2 3 4)
可以正常定义：(define v (vector 3 4 5))
也可以直接定义：(define v #(3 4 5))</p>
<p>vector是一种比较常用的复合类型，它的元素索引从0开始，至第 n-1 结束，这一点有点类似C语言中的数组。</p>
<p>关于向量表（vector）的常用操作过程：</p>
<pre class="literal-block">guile&gt; (define v (vector 1 2 3 4 5))<br>guile&gt; v<br>#(1 2 3 4 5)<br>guile&gt; (vector-ref v 0) ; 求第n个变量的值<br>1<br>guile&gt; (vector-length v)  ; 求vector的长度<br>5<br>guile&gt; (vector-set! v 2 "abc")  ; 设定vector第n个元素的值<br>guile&gt; v<br>#(1 2 "abc" 4 5)<br>guile&gt; (define x (make-vector 5 6))  ; 创建向量表<br>guile&gt; x<br>#(6 6 6 6 6)<br></pre>
<p>make-vector用来创建一个向量表，第一个参数是数量，后一个参数是添充的值，这和列表中的make-list非常相似。</p>
<p>我们可以看出，在Scheme语言中，每种数据类型都有一些基本的和它相关的操作过程，如字符串，列表等相关的操作，这些操作过程都很有规律，过程名的单词之间都用-号隔开，很容易理解。对于学过C++的朋友来说，更类似于某个对象的方法，只不过表现的形式不同了。</p>
</div>
</div>
<div class="section" id="id13">
<h2>3. 类型的判断、比较、运算、转换与方法</h2>
<div class="section" id="id14">
<h3>类型判断</h3>
<p>Scheme语言中所有判断都是用类型名加问号再加相应的常量或变量构成，形如：</p>
<pre class="literal-block">(类型? 变量)<br></pre>
<p>Scheme语言在类型定义中有比较严格的界定，如在C语言等一些语言中数字0来代替逻辑类型数据False，在Scheme语言中是不允许的。</p>
<p>以下为常见的类型判断和附加说明：</p>
<p>逻辑型：</p>
<pre class="literal-block">(boolean? #t) =&gt; #t<br>(boolean? #f) =&gt; #t     因为#t和#f都是boolean类型，所以其值为#t<br>(boolean? 2)  =&gt; #f     因为2是数字类型，所以其值为 #f<br></pre>
<p>字符型：</p>
<pre class="literal-block">(char? #\space) =&gt; #t<br>(char? #\newline)       =&gt; #t   以上两个特殊字符：空格和换行<br>(char? #\f)             =&gt; #t   小写字母 f<br>(char? #\;)             =&gt; #t   分号 ;<br>(char? #\5)             =&gt; #t   字符 5    ，以上这些都是正确的，所以返回值都是 #t<br>(char? 5)               =&gt; #f   这是数字 5 ，不是字符类型，所以返回 #f<br></pre>
<p>数字型：</p>
<pre class="literal-block">(integer? 1)            =&gt; #t<br>(integer? 2345) =&gt; #t<br>(integer? -90)          =&gt; #t   以上三个数均为整数<br>(integer? 8.9)          =&gt; #f   8.9不整数<br>(rational? 22/7)        =&gt; #t<br>(rational? 2.3)         =&gt; #t<br>(real? 1.2)             =&gt; #t<br>(real? 3.14159) =&gt; #t<br>(real? -198.34)         =&gt; #t   以上三个数均为实数型<br>(real? 23)              =&gt; #t   因为整型属于实型<br>(number? 5)     =&gt; #t<br>(number? 2.345) =&gt; #t<br>(number? 22/7)  =&gt; #t<br></pre>
<p>其它型：</p>
<pre class="literal-block">(null? '())             =&gt; #t  ; null意为空类型，它表示为 '() ，即括号里什么都没有的符号<br>(null? 5)               =&gt; #f<br>(define x 123)          定义变量x其值为123<br>(symbol? x)             =&gt; #f<br>(symbol? 'x)            =&gt; #t  ; 此时 'x 为符号x，并不表示变量x的值<br></pre>
<p>在Scheme语言中如此众多的类型判断功能，使得Scheme语言有着非常好的自省功能。即在判断过程的参数是否附合过程的要求。</p>
</div>
<div class="section" id="id15">
<h3>比较运算</h3>
<p>Scheme语言中可以用&lt;，&gt;，&lt;=，&gt;=，= 来判断数字类型值或表达式的关系，如判断变量x是否等于零，它的形式是这样的：(= x 0) ，如x的值为0则表达式的值为#t，否则为#f。</p>
<p>还有下面的操作：</p>
<pre class="literal-block">(eqv? 34 34)   =&gt;  #t<br>(= 34 34)      =&gt;  #t<br></pre>
<p>以上两个form功能相同，说明 eqv? 也可以用于数字的判断。</p>
<p>在Scheme语言中有三种相等的定义，两个变量正好是同一个对象；两个对象具有相同的值；两个对象具有相同的结构并且结构中的内容相同。除了上面提到的符号判断过程和eqv?外，还有eq?和equal?也是判断是否相等的过程。</p>
</div>
<div class="section" id="eq-eqv-equal">
<h3>eq?，eqv?，equal?</h3>
<p>eq?，eqv?和equal?是三个判断两个参数是否相等的过程，其中eq?和eqv?的功能基本是相同的，只在不同的Scheme语言中表现不一样。</p>
<p>eq?是判断两个参数是否指向同一个对象，如果是才返回#t；equal?则是判断两个对象是否具有相同的结构并且结构中的内容是否相同，它用eq?来比较结构中成员的数量；equal?多用来判断点对，列表，向量表，字符串等复合结构数据类型。</p>
<pre class="literal-block">guile&gt; (define v (vector 3 4 5))<br>guile&gt; (define w #(3 4 5))  ; w和v都是vector类型，具有相同的值#(3 4 5)<br>guile&gt; (eq? v w)<br>#f                                      ; 此时w和v是两个对象<br>guile&gt; (equal? v w)<br>#t                  ; 符合equal?的判断要求<br></pre>
<p>以上操作说明了eq? 和equal? 的不同之处，下面的操作更是证明了这一点：</p>
<pre class="literal-block">guile&gt; (define x (make-vector 5 6))<br>guile&gt; x<br>#(6 6 6 6 6)<br>guile&gt; (eq? x x)    ; 是同一个对象，所以返回#t<br>#t<br>guile&gt; (define z (make-vector 5 6))<br>guile&gt; z<br>#(6 6 6 6 6)<br>guile&gt; (eq? x z)    ; 不是同一个对象<br>#f<br>guile&gt; (equal? x z)  ; 结构相同，内容相同，所以返回#t<br>#t<br></pre>
</div>
<div class="section" id="id16">
<h3>算术运算</h3>
<p>Scheme语言中的运算符有：
<tt class="docutils literal"><span class="pre">+</span> <span class="pre">,</span> <span class="pre">-</span> <span class="pre">,</span> <span class="pre">*</span> <span class="pre">,</span> <span class="pre">/</span></tt> 和 expt (指数运算)
其中 - 和 / 还可以用于单目运算，如：</p>
<pre class="literal-block">(- 4)  =&gt; -4<br>(/ 4)  =&gt; 1/4<br></pre>
<p>此外还有许多扩展的库提供了很多有用的过程，</p>
<pre class="literal-block">max 求最大 (max 8 89 90 213)  =&gt; 213<br>min 求最小 (min 3 4 5 6 7)  =&gt; 3<br>abs 求绝对值 (abs -7) ==&gt; 7<br></pre>
<p>除了max，min，abs外，还有很多数学运算过程，这要根据你用的Scheme语言的运行环境有关，不过它们大多是相同的。在R5RS中规定了很多运算过程，在R5RS的参考资料中可以很容易找到。</p>
</div>
<div class="section" id="id17">
<h3>转换</h3>
<p>Scheme语言中用符号组合"-&gt;"来标明类型间的转换（很象C语言中的指针）的过程，就象用问号来标明类型判断过程一样。下面是一些常见的类型转换过程：</p>
<pre class="literal-block">guile&gt; (number-&gt;string 123)  ; 数字转换为字符串<br>"123"<br>guile&gt; (string-&gt;number "456")  ; 字符串转换为数字<br>456<br>guile&gt; (char-&gt;integer #\a)   ;字符转换为整型数，小写字母a的ASCII码值为96<br>97<br>guile&gt; (char-&gt;integer #\A)  ;大写字母A的值为65<br>65<br>guile&gt; (integer-&gt;char 97)  ;整型数转换为字符<br>#\a<br>guile&gt; (string-&gt;list "hello")   ;字符串转换为列表<br>(#\h #\e #\l #\l #\o)<br>guile&gt; (list-&gt;string (make-list 4 #\a)) ; 列表转换为字符串<br>"aaaa"<br>guile&gt; (string-&gt;symbol "good")  ;字符串转换为符号类型<br>good<br>guile&gt; (symbol-&gt;string 'better)  ;符号类型转换为字符串<br>"better"<br></pre>
</div>
</div>
<div class="section" id="id18">
<h2>五．过程定义</h2>
</div>
<div class="section" id="procedure">
<h2>过程（Procedure）</h2>
<p>在Scheme语言中，过程相当于C语言中的函数，不同的是Scheme语言过程是一种数据类型，这也是为什么Scheme语言将程序和数据作为同一对象处理的原因。如果我们在Guile提示符下输入加号然后回车，会出现下面的情况：</p>
<pre class="literal-block">guile&gt; +<br>#&lt;primitive-procedure +&gt;<br></pre>
<p>这告诉我们"+"是一个过程，而且是一个原始的过程，即Scheme语言中最基础的过程，在GUILE中内部已经实现的过程，这和类型判断一样，如
boolean?等，它们都是Scheme语言中最基本的定义。注意：不同的Scheme语言实现环境，出现的提示信息可能不尽相同，但意义是一样的。</p>
<p>define不仅可以定义变量，还可以定义过程，因在Scheme语言中过程（或函数）都是一种数据类型，所以都可以通过define来定义。不同的是标准的过程定义要使用lambda这一关键字来标识。</p>
</div>
<div class="section" id="lambda">
<h2>Lambda关键字</h2>
<p>Scheme语言中可以用lambda来定义过程，其格式如下：
(define 过程名 ( lambda (参数 ...) (操作过程 ...)))</p>
<p>我们可以自定义一个简单的过程，如下：</p>
<dl class="docutils"><dt>::</dt><dd>(define add5 (lambda (x) (+ x 5)))</dd></dl>
<p>此过程需要一个参数，其功能为返回此参数加5 的值，如：</p>
<pre class="literal-block">(add5 11) =&gt; 16<br></pre>
<p>下面是简单的求平方过程square的定义：</p>
<pre class="literal-block">(define square (lambda (x)  (* x x)))<br></pre>
</div>
<div class="section" id="id19">
<h2>与lambda相同的另一种方式</h2>
<p>在Scheme语言中，也可以不用lambda，而直接用define来定义过程，它的格式为：
(define (过程名 参数) (过程内容 &#8230;))</p>
<p>如下面操作：</p>
<pre class="literal-block">(define (add6 x) (+ x 6))<br>add6<br>#&lt;procedure: add6 (x)&gt; 说明add6是一个过程，它有一个参数x<br>(add6 23)    =&gt; 29<br></pre>
<p>再看下面的操作：</p>
<pre class="literal-block">guile&gt; (define fun<br>                (lambda(proc x y)<br>                        (proc x y)))<br>guile&gt; fun<br>#&lt;procedure fun (proc x y)&gt;<br>guile&gt; (fun * 5 6)<br>30<br>guile&gt; (fun / 30 3)<br>10<br></pre>
</div>
<div class="section" id="id20">
<h2>更多的过程定义</h2>
<p>上面定义的过程fun有三个参数，其中第一个参数proc也是一个操作过程（因为在Scheme语言中过程也是一种数据，可以作为过程的参数），另外两个参数是数值，所以会出现上面的调用结果。</p>
<pre class="literal-block">guile&gt; (define add<br>                (lambda (x y)<br>                        (+ x y)))<br>guile&gt; add<br>#&lt;procedure add (x y)&gt;<br>guile&gt; (fun add 100 200)<br>300<br></pre>
<p>继续上面操作，我们定义一个过程add，将add作为参数传递给fun过程，得出和(fun + 100 200)相同的结果。</p>
<pre class="literal-block">guile&gt; ((lambda (x) (+ x x)) 5)<br>10<br></pre>
<p>上面的 (lambda(x) (+ x x)) 事实上是简单的过程定义，在后面直接加上操作参数5，得出结果10，这样实现了匿名过程，直接用过程定义来操作参数，得出运算结果。</p>
<p>通过上面的操作，相信你已初步了解了过程的用法。既然过程是一种数据类型，所以将过程作为过程的参数是完全可以的。以下过程为判断参数是否为过程，给出一个参数，用 procedure? 来判断参数是否为过程，采用if结构（关于if结构见下面的介绍）：</p>
<pre class="literal-block">guile&gt; (define isp<br>            (lambda (x)<br>                     (if (procedure? x) 'isaprocedure 'notaprocedure)))<br>guile&gt; isp<br>#&lt;procedure isp (x)&gt;<br>guile&gt; (isp 0)<br>notaprocedure<br>guile&gt; (isp +)<br>isaprocedure<br></pre>
<p>上面的过程就体现了Scheme语言的参数自省（辨别）能力，'0'是数字型，所以返回notaprocedure；而'+'是一个最基础的操作过程，所以返回isaprocedure。</p>
</div>
<div class="section" id="id21">
<h2>过程的嵌套定义</h2>
<p>在Scheme语言中，过程定义也可以嵌套，一般情况下，过程的内部过程定义只有在过程内部才有效，相当C语言中的局部变量。</p>
<p>如下面的代码的最终结果是50：</p>
<pre class="literal-block">(define fix<br>        (lambda (x y z)<br>                (define add<br>                        (lambda (a b) (+ a b)))<br>                (- x (add y z))))<br>(display (fix 100 20 30))<br></pre>
<p>此时过程add只在fix过程内部起做用，这事实上涉及了过程和变量的绑定，可以参考下面的关于过程绑定（let，let* 和letrec）的介绍。</p>
<p>过程是初学者难理解的一个关键，随着过程参数的增加和功能的增强，过程的内容变得越来越复杂，小括号也会更多，如果不写出清晰的代码的话，读代码也会成为一个难题。</p>
<p>熟悉了 scheme 基本概念、数据类型和过程（函数）后， 下一部分我们来学习 scheme 的结构、递归调用和其他扩展功能。</p>
</div>
<div class="section" id="id22">
<h2>六．常用结构</h2>
</div>
<div class="section" id="id23">
<h2>顺序结构</h2>
<p>也可以说成由多个form组成的form，用begin来将多个form放在一对小括号内，最终形成一个form。格式为：(begin form1 form2 &#8230;)</p>
<p>如用Scheme语言写成的经典的helloworld程序是如下样子的：</p>
<pre class="literal-block">(begin<br>        (display "Hello world!")  ; 输出"Hello world!"<br>        (newline))                              ; 换行<br></pre>
</div>
<div class="section" id="if">
<h2>if结构</h2>
<p>Scheme语言的if结构有两种格式，一种格式为：(if 测试 过程1 过程2)，即测试条件成立则执行过程1，否则执行过程2。例如下面代码：</p>
<pre class="literal-block">(if (= x 0)<br>(display "is zero")<br>(display "not zero"))<br></pre>
<p>还有另一种格式：(if 测试 过程) ，即测试条件成立则执行过程。例如下面代码：</p>
<pre class="literal-block">(if (&lt; x 100) (display "lower than 100"))<br></pre>
<p>根据类型判断来实现自省功能，下面代码判断给定的参数是否为字符串：</p>
<pre class="literal-block">(define fun<br>        (lambda ( x )<br>                (if (string? x)<br>                        (display "is a string")<br>                        (display "not a string"))))<br></pre>
<p>如执行 (fun 123) 则返回值为"not a string"，这样的功能在C++或JAVA中实现的话可能会很费力气。</p>
</div>
<div class="section" id="cond">
<h2>cond结构</h2>
<p>Scheme语言中的cond结构类似于C语言中的switch结构，cond的格式为：</p>
<pre class="literal-block">(cond ((测试) 操作) &#8230; (else 操作))<br></pre>
<p>如下是在Guile中的操作：</p>
<pre class="literal-block">guile&gt; (define w (lambda (x)<br>      (cond ((&lt; x 0) 'lower)<br>           ((&gt; x 0) 'upper)<br>           (else 'equal))))<br>guile&gt; w<br>#&lt;procedure w (x)&gt;<br>guile&gt; (w 9)<br>upper<br>guile&gt; (w -8)<br>lower<br>guile&gt; (w 0)<br>equal<br></pre>
<p>上面程序代码中，我们定义了过程w，它有一个参数x，如果x的值大于0，则返回符号upper，如x的值小于0则返回符号lower，如x 的值为0则返回符号equal。</p>
<p>下载已做成可执行脚本的 例程。</p>
<p>cond可以用if形式来写，上面的过程可以如下定义：</p>
<pre class="literal-block">guile&gt; (define ff<br>                     (lambda (x)<br>                    (if (&lt; x 0) 'lower<br>                            (if (&gt; x 0) 'upper 'zero))))<br>guile&gt; ff<br>#&lt;procedure ff (x)&gt;<br>guile&gt; (ff 9)<br>upper<br>guile&gt; (ff -9)<br>lower<br>guile&gt; (ff 0)<br>zero<br></pre>
<p>这在功能上是和cond一样的，可以看出cond实际上是实现了if的一种多重嵌套。</p>
</div>
<div class="section" id="case">
<h2>case结构</h2>
<p>case结构和cond结构有点类似，它的格式为：</p>
<pre class="literal-block">(case (表达式) ((值) 操作)) ... (else 操作)))<br></pre>
<p>case结构中的值可以是复合类型数据，如列表，向量表等，只要列表中含有表达式的这个结果，则进行相应的操作，如下面的代码：</p>
<pre class="literal-block">(case (* 2 3)<br> ((2 3 5 7) 'prime)<br> ((1 4 6 8 9) 'composite))<br></pre>
<p>上面的例子返回结果是composite，因为列表(1 4 6 8 9)中含有表达式(* 2 3)的结果6；下面是在Guile中定义的func过程，用到了case结构：</p>
<pre class="literal-block">guile&gt; (define func<br>                (lambda (x y)<br>                        (case (* x y)<br>                                ((0) 'zero)<br>                                (else 'nozero))))<br>guile&gt; func<br>#&lt;procedure func (x y)&gt;<br>guile&gt; (func 2 3)<br>nozero<br>guile&gt; (func 2 0)<br>zero<br>guile&gt; (func 0 9)<br>zero<br>guile&gt; (func 2 9)<br>nozero<br></pre>
<p>可以下载另一个脚本文件 te.scm，参考一下。</p>
</div>
<div class="section" id="and">
<h2>and结构</h2>
<p>and结构与逻辑与运算操作类似，and后可以有多个参数，只有它后面的参数的表达式的值都为#t时，它的返回值才为#t，否则为#f。看下面的操作：</p>
<pre class="literal-block">guile&gt; (and (boolean? #f) (&lt; 8 12))<br>#t<br>guile&gt; (and (boolean? 2) (&lt; 8 12))<br>#f<br>guile&gt; (and (boolean? 2) (&gt; 8 12))<br>#f<br></pre>
<p>如果表达式的值都不是boolean型的话，返回最后一个表达式的值，如下面的操作：</p>
<pre class="literal-block">guile&gt; (and (list 1 2 3) (vector 'a 'b 'c))<br>#(a b c)<br>guile&gt; (and 1 2 3 4 )<br>4<br>guile&gt; (and 'e 'd 'c 'b 'a)<br>a<br></pre>
</div>
<div class="section" id="or">
<h2>or结构</h2>
<p>or结构与逻辑或运算操作类似，or后可以有多个参数，只要其中有一个参数的表达式值为#t，其结果就为#t，只有全为#f时其结果才为#f。如下面的操作：</p>
<pre class="literal-block">guile&gt; (or #f #t)<br>#t<br>guile&gt; (or #f #f)<br>#f<br>guile&gt; (or (rational? 22/7) (&lt; 8 12))<br>#t<br>guile&gt; (rational? 22/7)<br>#t<br>guile&gt; (real? 22/7)<br>#t<br>guile&gt; (or (real? 4+5i) (integer? 3.22))<br>#f<br></pre>
<p>我们还可以用and和or结构来实现较复杂的判断表达式，如在C语言中的表达式：</p>
<pre class="literal-block">((x &gt; 100) &amp;&amp; (y &lt; 100)) 和 ((x &gt; 100) || (y &gt; 100))<br></pre>
<p>在Scheme中可以表示为：</p>
<pre class="literal-block">guile&gt; (define x 123)<br>guile&gt; (define y 80)<br>guile&gt; (and (&gt; x 100) (&lt; y 100))<br>#t<br>guile&gt; (or (&gt; x 100) (&gt; y 100))<br>#t<br></pre>
<p>Scheme语言中只有if结构是系统原始提供的，其它的cond，case，and，or，另外还有do，when，unless等都是可以用宏定义的方式来定义的，这一点充分体现了Scheme的元语言特性，关于do，when等结构的使用可以参考R5RS。</p>
</div>
<div class="section" id="id24">
<h2>七．递归调用</h2>
</div>
<div class="section" id="id25">
<h2>用递归实现阶乘</h2>
<p>在Scheme语言中，递归是一个非常重要的概念，可以编写简单的代码很轻松的实现递归调用，如下面的阶乘过程定义：</p>
<pre class="literal-block">(define  factoral (lambda (x)<br>                  (if (&lt;= x 1) 1<br>                      (* x (factoral (- x 1))))))<br></pre>
<p>我们可以将下面的调用(factoral 4)，即4的阶乘的运算过程图示如下：</p>
<!-- IMAGE:jpg -->
<img src="http://www.oxsama.com/?k=data_image&amp;p=static.d/1189119482-19692-0.rst.images/2.jpg" alt="http://www.oxsama.com/?k=data_image&amp;p=static.d/1189119482-19692-0.rst.images/2.jpg">
<p>以下为factoral过程在Guile中的运行情况：</p>
<pre class="literal-block">guile&gt; (define factoral (lambda (x) (if (&lt;= x 1) 1 (* x (factoral (- x 1))))))<br>guile&gt; factoral<br>#&lt;procedure factoral (x)&gt;<br>guile&gt; (factoral 4)<br>24<br></pre>
</div>
<div class="section" id="id26">
<h2>另一种递归方式</h2>
<p>下面是一另一种递归方式的定义：</p>
<pre class="literal-block">(define (factoral n)<br>        (define (iter product counter)<br>                (if (&gt; counter n)<br>                        product<br>                        (iter (* counter product) (+ counter 1))))<br>        (iter 1 1))<br>(display (factoral 4))<br></pre>
<p>这个定义的功能和上面的完全相同，只是实现的方法不一样了，我们在过程内部实现了一个过程iter，它用counter参数来计数，调用时从1开始累计，这样它的展开过程正好和我们上面的递归过程的从4到1相反，而是从1到4。</p>
</div>
<div class="section" id="id27">
<h2>循环的实现</h2>
<p>在Scheme语言中没有循环结构，不过循环结构可以用递归来很轻松的实现（在Scheme语言中只有通过递归才能实现循环）。对于用惯了C语言循环的朋友，在Scheme中可以用递归简单实现：</p>
<pre class="literal-block">guile&gt; (define loop<br>             (lambda(x y)<br>             (if (&lt;= x y)<br>                (begin (display x) (display #\\space) (set! x (+ x 1))<br>                        (loop x y)))))<br>guile&gt; loop<br>#&lt;procedure loop (x y)&gt;<br>guile&gt; (loop 1 10)<br>1 2 3 4 5 6 7 8 9 10<br></pre>
<p>这只是一种简单的循环定义，过程有两个参数，第一个参数是循环的初始值，第二个参数是循环终止值，每次增加1。相信读者朋友一定会写出更漂亮更实用的循环操作来的。</p>
</div>
<div class="section" id="id28">
<h2>八．变量和过程的绑定</h2>
</div>
<div class="section" id="let-let-letrec">
<h2>let，let*，letrec</h2>
<p>在多数编程语言中都有关于变量的存在的时限问题，Scheme语言中用let，let*和letrec来确定变量的存在的时限问题，即局部变量和全局变量，一般情况下，全局变量都用define来定义，并放在过程代码的外部；而局部变量则用let等绑定到过程内部使用。</p>
<p>用let可以将变量或过程绑定在过程的内部，即实现局部变量：</p>
<pre class="literal-block">guile&gt; let<br>#&lt;primitive-macro! let&gt;<br></pre>
<p>从上面的操作可以看出let是一个原始的宏，即guile内部已经实现的宏定义。</p>
<p>下面的代码显示了let的用法（注意多了一层括号）：</p>
<pre class="literal-block">guile&gt; (let ((x 2) (y 5)) (* x y))<br>10<br></pre>
<p>它的格式是：(let ((&#8230;)&#8230;) &#8230;)，下面是稍复杂的用法：</p>
<pre class="literal-block">guile&gt; (let ((x 5))<br>         (define foo (lambda (y) (bar x y)))<br>         (define bar (lambda (a b) (+ (* a b) a)))<br>         (foo (+ x 3)))<br>45<br></pre>
<p>以上是Guile中的代码实现情况。它的实现过程大致是：(foo 8) 展开后形成 (bar 5 8)，再展开后形成 (+ (* 5 8) 5) ，最后其值为45。</p>
<p>再看下面的操作：</p>
<pre class="literal-block">guile&gt; (let ((iszero?<br>                (lambda(x)<br>                (if (= x 0) #t #f))))<br>        (iszero? 9))<br>#f<br>guile&gt; (iszero? 0)  ;此时会显示出错信息<br></pre>
<p>let的绑定在过程内有效，过程外则无效，这和上面提到的过程的嵌套定是一样的，上面的iszero?过程在操作过程内定义并使用的，操作结束后再另行引用则无效，显示过程未定义出错信息。</p>
<p>下面操作演示了let*的用法：</p>
<pre class="literal-block">guile&gt; (let ((x 2) (y 5))<br>       (let* ((x 6)(z (+ x y)))  ;此时x的值已为6，所以z的值应为11，如此最后的值为66<br>       (* z x)))<br>66<br></pre>
<p>还有letrec，看下面的操作过程：</p>
<pre class="literal-block">guile&gt; (letrec ((even?<br>                (lambda(x)<br>                (if (= x 0) #t<br>                        (odd? (- x 1)))))<br>            (odd?<br>                (lambda(x)<br>                (if (= x 0) #f<br>                        (even? (- x 1))))))<br>        (even? 88))<br>#t<br></pre>
<p>上面的操作过程中，内部定义了两个判断过程even?和odd?，这两个过程是互相递归引用的，如果将letrec换成let或let*都会不正常，因为letrec是将内部定义的过程或变量间进行相互引用的。看下面的操作：</p>
<pre class="literal-block">guile&gt; (letrec ((countdown<br>                (lambda (i)<br>                (if (= i 0) 'listoff<br>                        (begin (display i) (display ",")<br>                                (countdown (- i 1)))))))<br>        (countdown 10))<br>10,9,8,7,6,5,4,3,2,1,listoff<br></pre>
<p>letrec帮助局部过程实现递归的操作，这不仅在letrec绑定的过程内，而且还包括所有初始化的东西，这使得在编写较复杂的过程中经常用到letrec，也成了理解它的一个难点。</p>
</div>
<div class="section" id="apply">
<h2>apply</h2>
<p>apply的功能是为数据赋予某一操作过程，它的第一个参数必需是一个过程，随后的其它参数必需是列表，如：</p>
<pre class="literal-block">guile&gt; (apply + (list 2 3 4))<br>9<br>guile&gt; (define sum<br>           (lambda (x )<br>                  (apply + x)))  ; 定义求和过程<br>guile&gt; sum<br>#&lt;procedure sum (x)&gt;<br>guile&gt; (define ls (list 2 3 4 5 6))<br>guile&gt; ls<br>(2 3 4 5 6)<br>guile&gt; (sum ls)<br>20<br>guile&gt; (define avg<br>          (lambda(x)<br>                 (/ (sum x) (length x))))   ; 定义求平均过程<br>guile&gt; avg<br>#&lt;procedure avg (x)&gt;<br>guile&gt; (avg ls)<br>4<br></pre>
<p>以上定义了求和过程sum和求平均的过程avg，其中求和的过程sum中用到了apply来绑定"+"过程操作到列表，结果返回列表中所有数的总和。</p>
</div>
<div class="section" id="map">
<h2>map</h2>
<p>map的功能和apply有些相似，它的第一个参数也必需是一个过程，随后的参数必需是多个列表，返回的结果是此过程来操作列表后的值，如下面的操作：</p>
<pre class="literal-block">guile&gt; (map + (list 1 2 3) (list 4 5 6))<br>(5 7 9)<br>guile&gt; (map car '((a . b)(c . d)(e . f)))<br>(a c e)<br></pre>
<p>除了apply，map以外，Scheme语言中还有很多，诸如：eval，delay，for-each，force，call-with-
current-
continuation等过程绑定的操作定义，它们都无一例外的提供了相当灵活的数据处理能力，也就是另初学者望而生畏的算法，当你仔细的体会了运算过
程中用到的简直妙不可言的算法后，你就会发现Scheme语言设计者的思想是多么伟大。</p>
</div>
<div class="section" id="id29">
<h2>九．输入输出</h2>
<p>Scheme语言中也提供了相应的输入输出功能，是在C基础上的一种封装。</p>
</div>
<div class="section" id="id30">
<h2>端口</h2>
<p>Scheme语言中输入输出中用到了端口的概念，相当于C中的文件指针，也就是Linux中的设备文件，请看下面的操作：</p>
<pre class="literal-block">guile&gt; (current-input-port)<br>#&lt;input: standard input /dev/pts/0&gt; ;当前的输入端口<br>guile&gt; (current-output-port)<br>#&lt;output: standard output /dev/pts/0&gt; ;当前的输出端口<br></pre>
<p>判断是否为输入输出端口，可以用下面两个过程：input-port? 和output-port? ，其中input-port?用来判断是否为输入端口，output-port?用来判断是否为输出端口。</p>
<p>open-input-file，open-output-file，close-input-port，close-output-port这四个过程用来打开和关闭输入输出文件，其中打开文件的参数是文件名字符串，关闭文件的参数是打开的端口。</p>
</div>
<div class="section" id="id31">
<h2>输入</h2>
<p>打开一个输入文件后，返回的是输入端口，可以用read过程来输入文件的内容：</p>
<pre class="literal-block">guile&gt; (define port (open-input-file "readme"))<br>guile&gt; port<br>#&lt;input: readme 4&gt;<br>guile&gt; (read port)<br>GUILE语言<br></pre>
<p>上面的操作打开了readme文件，并读出了它的第一行内容。此外还可以直接用read过程来接收键盘输入，如下面的操作：</p>
<pre class="literal-block">guile&gt; (read)  ; 执行后即等待键盘输入<br>12345<br>12345<br>guile&gt; (define x (read))  ; 等待键盘输入并赋值给x<br>12345<br>guile&gt; x<br>12345<br></pre>
<p>以上为用read来读取键入的数字，还可以输入字符串等其它类型数据：</p>
<pre class="literal-block">guile&gt; (define name (read))<br>tomson<br>guile&gt; name<br>tomson<br>guile&gt; (string? name)<br>#f<br>guile&gt; (symbol? name)<br>#t<br></pre>
<p>此时输入的tomson是一个符号类型，因为字符串是用引号引起来的，所以出现上面的情况。下面因为用引号了，所以(string? str)返回值为#t 。</p>
<pre class="literal-block">guile&gt; (define str (read))<br>"Johnson"<br>guile&gt; str<br>"Johnson"<br>guile&gt; (string? str)<br>#t<br></pre>
<p>还可以用load过程来直接调用Scheme语言源文件并执行它，格式为：(load "filename")，还有read-char过程来读单个字符等等。</p>
</div>
<div class="section" id="id32">
<h2>输出</h2>
<p>常用的输出过程是display，还有write，它的格式是：(write 对象 端口)，这里的对象是指字符串等常量或变量，端口是指输出端口或打开的文件。下面的操作过程演示了向输出文件temp中写入字符串"helloworld"，并分行的实现。</p>
<pre class="literal-block">[root@toymouse test]# guile<br>guile&gt; (define port1 (open-output-file "temp"))  ; 打开文件端口赋于port1<br>guile&gt; port1<br>#&lt;output: temp 3&gt;<br>guile&gt; (output-port? port1)<br>#t                     ; 此时证明port1为输出端口<br>guile&gt; (write "hello\\nworld" port1)<br>guile&gt; (close-output-port port1)<br>guile&gt; (exit)               ; 写入数据并关闭退出<br>[root@toymouse test]# more temp          显示文件的内容，达到测试目的<br>"hello<br>world"<br></pre>
<p>在输入输出操作方面，还有很多相关操作，读者可以参考R5RS的文档。</p>
</div>
<div class="section" id="id33">
<h2>十．语法扩展</h2>
<p>Scheme语言可以自己定义象cond，let等功能一样的宏关键字。标准的Scheme语言定义中用define-syntax和syntax-rules来定义，它的格式如下：</p>
<pre class="literal-block">(define-syntax 宏名<br>        (syntax-rules()<br>                ((模板) 操作))<br>                . . . ))<br></pre>
<p>下面定义的宏start的功能和begin相同，可以用它来开始多个块的组合：</p>
<pre class="literal-block">(define-syntax start<br>        (syntax-rules ()<br>                ((start exp1)<br>                        exp1)<br>                ((start exp1 exp2 ...)<br>                        (let ((temp exp1)) (start exp2 ...))) ))<br></pre>
<p>这是一个比较简单的宏定义，但对理解宏定义来说是比较重要的，理解了他你才会进一步应用宏定义。在规则 ((start exp1) exp1)
中，(start exp1) 是一个参数时的模板，exp1是如何处理，也就是原样搬出，不做处理。这样 (start form1) 和
(form1) 的功能就相同了。</p>
<p>在规则 ((start exp1 exp2 ...) (let ((temp exp1)) (start exp2 ...)))
中，(start exp1 exp2 &#8230;)
是多个参数时的模板，首先用let来绑定局部变量temp为exp1，然后用递归实现处理多个参数，注意这里说的是宏定义中的递归，并不是过程调用中的递
归。另外在宏定义中可以用省略号（三个点）来代表多个参数。</p>
<p>在Scheme的规范当中，将表达式分为原始表达式和有源表达式，Scheme语言的标准定义中只有原始的if分支结构，其它均为有源型，即是用后来的宏定义成的，由此可见宏定义的重要性。附上面的定义在GUILE中实现的 代码。</p>
</div>
<div class="section" id="id34">
<h2>十一. 其它功能</h2>
</div>
<div class="section" id="id35">
<h2>1. 模块扩展</h2>
<p>在R5RS
中并未对如何编写模块进行说明，在诸多的Scheme语言的实现当中，几乎无一例外的实现了模块的加载功能。所谓模块，实际就是一些变量、宏定义和已命名
的过程的集合，多数情况下它都绑定在一个Scheme语言的符号下（也就是名称）。在Guile中提供了基础的ice-9模块，其中包括POSIX系统调
用和网络操作、正则表达式、线程支持等等众多功能，此外还有著名的SFRI模块。引用模块用use-modules过程，它后面的参数指定了模块名和我们
要调用的功能名，如：(use-modules (ice-9
popen))，如此后，就可以应用popen这一系统调用了。如果你想要定义自己的模块，最好看看ice-9目录中的那些tcm文件，它们是最原始的定
义。</p>
<p>另外Guile在面向对象编程方面，开发了GOOPS（Guile Object-Oriented Programming System），对于喜欢OO朋友可以研究一下它，从中可能会有新的发现。</p>
</div>
<div class="section" id="id36">
<h2>2. 如何输出漂亮的代码</h2>
<p>如何编写输出漂亮的Scheme语言代码应该是初学者的第一个问题，这在Guile中可以用ice-9扩展包中提供的pretty-print过程来实现，看下面的操作：</p>
<pre class="literal-block">guile&gt; (use-modules (ice-9 pretty-print))    ; 引用漂亮输出模块<br>guile&gt; (pretty-print '(define fix (lambda (n)<br>        (cond ((= n 0) 'iszero)<br>                ((&lt; n 0) 'lower)<br>                (else 'upper)))))   ; 此处是我们输入的不规则代码<br>(define fix<br>  (lambda (n)<br>    (cond ((= n 0) 'iszero)<br>          ((&lt; n 0) 'lower)<br>          (else 'upper))))     ; 输出的规则代码<br></pre>
</div>
<div class="section" id="id37">
<h2>3. 命令行参数的实现</h2>
<p>在把Scheme用做shell语言时，经常用到命令行参数的处理，下面是关于命令行参数的一种处理方法：</p>
<pre class="literal-block">#! /usr/local/bin/guile -s<br>!#<br>(define cmm (command-line))<br>(display "应用程序名称：")<br>(display (car cmm))<br>(newline)<br>(define args (cdr cmm))<br>(define long (length args))<br>(define loop (lambda (count len obj)<br>        (if (&lt;= count len)<br>                (begin<br>                        (display "参数 ")<br>                        (display count)<br>                        (display " 是：")<br>                        (display (list-ref obj (- count 1)))<br>                        (newline)<br>                        (set! count (+ count 1))<br>                        (loop count len obj)))))<br>(loop 1 long args)<br></pre>
<p>下面是运行后的输出结果：</p>
<pre class="literal-block">[root@toymouse doc]# ./tz.scm abc 123 ghi<br>应用程序名称：./tz.scm<br>参数 1 是：abc<br>参数 2 是：123<br>参数 3 是：ghi<br></pre>
<p>其中最主要的是用到了command-line过程，它的返回结果是命令参数的列表，列表的第一个成员是程序名称，其后为我们要的参数，定义loop递归调用形成读参数的循环，显示出参数值，达到我们要的结果。</p>
</div>
<div class="section" id="id38">
<h2>4. 特殊之处</h2>
<p>一些精确的自己计算自己的符号</p>
<pre class="literal-block">数字      Numbers         2 ==&gt; 2<br>字符串     Strings         "hello" ==&gt; "hello"<br>字符      Charactors      #\\g ==&gt; #\\g<br>辑值    Booleans  #t ==&gt; #t<br>量表      Vectors         #(a 2 5/2) ==&gt; #(a 2 5/2)<br></pre>
<p>通过变量计算来求值的符号</p>
<p>如：</p>
<pre class="literal-block">x ==&gt; 9<br>   -list ==&gt; ("tom" "bob" "jim")<br>factoral ==&gt; #&lt;procedure: factoral&gt;<br>         ==&gt; #&lt;primitive: +&gt;<br></pre>
</div>
<div class="section" id="define-form">
<h2>define 特殊的form</h2>
<p>(define x 9) ，define不是一个过程， 它是一个不用求所有参数值的特殊的form，它的操作步骤是，初始化空间，绑定符号x到此空间，然后初始此变量。</p>
</div>
<div class="section" id="id39">
<h2>必须记住的东西</h2>
<p>下面的这些定义、过程和宏等是必须记住的：</p>
<p>define，lambda，let，lets，letrec，quote，set!，if，case，cond，begin，and，or等等，当然还有其它宏，必需学习，还有一些未介绍，可参考有关资料。</p>
<p>走进Scheme语言的世界，你就发现算法和数据结构的妙用随处可见，可以充分的检验你对算法和数据结构的理解。Scheme语言虽然是古老的函数型语言的继续，但是它的里面有很多是在其它语言中学不到的东西，我想这也是为什么用它作为计算机语言教学的首选的原因吧。</p>
</div>
<div class="section" id="id40">
<h2>参考资料</h2>
<blockquote>
<ul class="simple">
    <li><a href="http://learn.tsinghua.edu.cn/homepage/015450/" class="reference external">王垠的个人主页</a> ，此篇文章的很多东西都是从他哪里受到启发而写出来的。</li>
    <li><a href="http://www.rons.net.cn/" class="reference external">自由软件</a> 杂志编辑洪峰在他的文章中有很多对Scheme语言的评价，在他开创的工程中也有很多应用。</li>
    <li>在此网站的Linux专区中，赵蔚对 <cite>Scheme的介绍 &lt;http://www-128.ibm.com/developerworks/cn/linux/l-scheme/part2/index.html&gt;</cite> 还是比较精彩的。</li>
    <li>R5RS，Scheme语言的标准定义： <a href="http://www.swiss.ai.mit.edu/%7Ejaffer/r5rs_toc.html" class="reference external">http://www.swiss.ai.mit.edu/~jaffer/r5rs_toc.html</a></li>
    <li>Guile，GNU的扩展语言： <a href="http://www.gnu.org/software/guile" class="reference external">http://www.gnu.org/software/guile</a>/和 <a href="http://www.delorie.com/gnu/docs/guile/guile_toc.html" class="reference external">Guile Reference Manual</a></li>
    <li>Scheme语言在MIT的6.0001课程中广泛应用，即有名的 <a href="http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-4.html#%_toc_start" class="reference external">SICP</a> ，这是该校计算机科学专业的必修课之一。</li>
    <li>Scheme语言在学校中的应用， <a href="http://www.schemers.com/schools.html" class="reference external">http://www.schemers.com/schools.html</a> ，这是一个不完全列表，从这里可以看出SCHEME语言在教学中的重要性。</li>
</ul>
</blockquote>
</div>
<h2>关于作者</h2>
<p>宋国伟（ <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#103;&#119;&#115;&#111;&#110;&#103;&#53;&#50;&#64;&#115;&#111;&#104;&#117;&#46;&#99;&#111;&#109;" class="reference external">mailto:gwsong52@sohu.com</a> ），目前在 <a href="http://www.dehui.gov.cn/" class="reference external">吉林省德惠市信息中心</a> 从事网络维护工作，著有 <a href="http://www.china-pub.com/computers/common/info.asp?id=8419" class="reference external">《GTK+2.0编程范例》</a> 一书，热衷于Linux系统上的编程及相关的研究。</p><img src ="http://www.cppblog.com/cuigang/aggbug/39398.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/cuigang/" target="_blank">cuigang</a> 2007-12-23 20:43 <a href="http://www.cppblog.com/cuigang/articles/39398.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>