<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>冒号空间 &#187; 函数式编程</title>
	<atom:link href="http://blog.zhenghui.org/tag/functional-programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhenghui.org</link>
	<description>自然、人类、机器</description>
	<lastBuildDate>Fri, 30 Dec 2011 03:14:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>冒号课堂§4.1：函数范式</title>
		<link>http://blog.zhenghui.org/2009/09/13/colon-class-4_1/</link>
		<comments>http://blog.zhenghui.org/2009/09/13/colon-class-4_1/#comments</comments>
		<pubDate>Sun, 13 Sep 2009 12:50:36 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[函数式编程]]></category>
		<category><![CDATA[惰性求值]]></category>
		<category><![CDATA[知识]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[高阶函数]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=401</guid>
		<description><![CDATA[<b>函数范式</b>——精巧的数学思维（<em>再谈函数式编程</em>）<br/> • 单靠记忆只能触及知识之表，单靠练习只能深入知识之里，唯有培养方能渗透知识之根<br/> • 学会适度地容忍无知<br/> • 不仅需要强调钻劲和深度的“钉子精神”，还需要强调磨功和广度的“刨子精神”<br/> • 编程语言的语法、语义等都是从编程范式的树根衍生而出的枝叶，把握了这种脉络和节奏，代码才会如音乐舞蹈般韵律有致<br/> • 每种范式擅长的问题领域不尽相同，只有博闻广识，方可扬长避短，程序才会如行云流水般流畅自然<br/> • 程序员更习惯机器风格的过程式思维和现实风格的OOP思维，不容易接纳数学风格的函数式思维 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center;"><span style="font-family: 宋体;">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体;">第四课 重温范式（1）</span></strong>


<p><strong><span style="font-family: 宋体;">课前导读</span></strong></p>
<p style="text-indent: 18pt;"><span style="font-family: 宋体;">本课对函数式编程与逻辑式编程作了更详细的展开，并对前面介绍的范式进行了汇总分析，最后用情景式编程贯穿所学范式。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 18pt;"><span style="font-family: 宋体;">本课共分四节——</span></p>
<p style="text-indent: 18pt;"><em>1.</em><em><span style="font-family: 宋体;">函数范式——一精巧的数学思维</span></em></p>
<p style="text-indent: 18pt;"><em>2.</em><em><span style="font-family: 宋体;">逻辑范式——当算法失去了控制</span></em></p>
<p style="margin-left: 18pt;"><em>3.</em><em><span style="font-family: 宋体;">汇总范式——一张五味俱全的大烙饼</span></em></p>
<p style="margin-left: 18pt;"><em>4.</em><em><span style="font-family: 宋体;">情景范式——餐馆里的编程范式</span></em></p>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>
 
<div lang="zh-CN" class="article" title="函数范式"><div class="titlepage"><div><div><h1 class="title"><a name="id637341"></a>4.1 函数范式——精巧的数学思维</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>再谈函数式编程</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>知不知，上；不知不知，病</p></div><div class="attribution"><span>—<span class="attribution">《老子•德经》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    单靠记忆只能触及知识之表，单靠练习只能深入知识之里，唯有培养方能渗透知识之根
                </p></li><li class="listitem"><p>
                    学会适度地容忍无知
                </p></li><li class="listitem"><p>
                    不仅需要强调钻劲和深度的“钉子精神”，还需要强调磨功和广度的“刨子精神”
                </p></li><li class="listitem"><p>
                    编程语言的语法、语义等都是从编程范式的树根衍生而出的枝叶，把握了这种脉络和节奏，代码才会如音乐舞蹈般韵律有致
                </p></li><li class="listitem"><p>
                    每种范式擅长的问题领域不尽相同，只有博闻广识，方可扬长避短，程序才会如行云流水般流畅自然
                </p></li><li class="listitem"><p>
                    程序员更习惯机器风格的过程式思维和现实风格的OOP思维，不容易接纳数学风格的函数式思维
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>掌握编程范式对编程语感的提高有什么作用？</p></li><li class="listitem"><p>为什么声明式程序一般比命令式程序更简洁？</p></li><li class="listitem"><p>函数式编程有哪些特征？为何简洁而不失强大？</p></li><li class="listitem"><p>函数的无副作用性的意义何在？</p></li><li class="listitem"><p>相比过程式和OOP，函数式编程的弱点是什么？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            众人落座之后，冒号鸣锣开场：“上两节课为大家介绍了多种编程范式，虽未将所有类型尽囊其中，但最具代表性的均在其列。我们也不必贪多求全，俗话说得好：贪多嚼不烂啊。现在给大家一个知识反刍的机会。”
        </p><p>
            问号正感求之不得：“总算可以喘口气了。我们就像观光客，被导游带着从一个景点赶往另一景点。一天下来，虽然大开眼界，但都是走马观花，无法充分领略各地的风光。”
        </p><p>
            “你说得没错，我就是那个不近情理的导游。”冒号哈哈一笑，“类似时下流行的欧洲N国M日游，大部分人的收获就是一堆照片和日渐模糊的记忆。不出多日，如果不看标注，八成连照片上的背景是在法国还是在意大利都分不清了。”
        </p><p>
            逗号颇有同感：“差不多，目前我的收获就是一堆幻灯片和似懂非懂的概念。”
        </p><p>
            冒号料有此果：“这一点也不奇怪。别说几天游一个国家，单一个罗马，没有一个月是不可能深入了解的。至于编程范式，单一个OOP，没有两年以上的实践和思考，是难以真正领会其精髓的。”
        </p><p>
            叹号深表怀疑：“OOP需要两年以上才能领会？！”
        </p><p>
            “那还得看你是否有足够的勤奋和悟性。”冒号加强了语气，“前面说过，<span class="strong"><strong>单靠记忆只能触及知识之表，单靠练习只能深入知识之里，唯有培养方能渗透知识之根</strong></span>。编程范式正处知识的根部，你们又怎能奢望只听几堂课即豁然贯通呢？”
        </p><p>
            引号表达自己的感受：“虽然学了不少东西，但也存了不少疑惑，搁在心里有点不舒服。”
        </p><p>
            “我明白你的意思。凡事追根究底是一种良好的学习习惯，也是一种可贵的学习精神。” 冒号表示理解和肯定，“但学习如打仗，除了要有直线式的纵深攻击，还要有曲线式的迂回包抄。回顾我们中学的课堂，往往是每引入一个概念或理论，便围绕其作深入的学习和反复的练习。在此过程中的种种疑惑，随着学习的深入都会烟消云散。这样稳扎稳打、层层推进，学得扎实，心里也踏实。但这种方法并不总是最好的，尤其在面临动态的、开放的知识体系时，难免左支右绌。为此，我们必须学会<span class="strong"><strong>适度地容忍无知</strong></span>。请注意，容忍无知不是放任无知，而是一种学习的技巧，让无知成为求知的动力而不是障碍。容忍无知能使我们既不沮丧气馁，也不急于求成。在学习时不妨略过一些细节或难点，先概览全貌以获取感性认识，然后在逐步积累中升华为理性认识。要而言之，我们不仅需要强调钻劲和深度的‘<span class="strong"><strong>钉子精神</strong></span>’，还需要强调磨功和广度的‘<span class="strong"><strong>刨子精神</strong></span>’。我一口气兜售这么多编程范式，就是为了刺激大家求知欲，同时为大家进行第一道刨磨。”
        </p><p>
            引号得到一些安慰：“看来今后我们还会故地重游的。”
        </p><p>
            “不仅会重游，而且会‘深度游’。” 冒号肯定地说，“此番我们一路行色匆匆，若能感受到途中景色带来的感官冲击，便算是不枉此行了。其实，把编程范式类比旅游景点并不十分准确，或许比作当地的风俗文化更确切些。”
        </p><p>
            句号立刻会意：“景点是具体的，背后的风俗文化是抽象的；编程语言是具体的，背后的编程范式是抽象的。”
        </p><p>
            “此乃其一。”冒号右手伸出食指，“其二，如果不了解景点的历史文化和风俗人情，仅仅满足于表面的奇观异景，一切很快便会淡忘。比如说，你若不了解圣经文化、不了解文艺复兴史，则欧洲之行至多只是视觉的盛宴，而非文化的洗礼，收获将是有限的，印象将是肤浅的。同样，如果你不了解编程范式，那么眼中的编程语言只是语法、语义、核心库、规范等组成的集合，写出的代码虽能编译、能工作，却会显得生硬、别扭。就像中式英语，语法正确、表达也正确，可就是不正宗、不地道。其症结我们在第一节课中已经提过了，即所谓的<span class="strong"><strong>语感缺失</strong></span>。”
        </p><p>
            问号实话实说：“可我还是不明白编程范式如何提高我们的编程语感。”
        </p><p>
            “那就让我们再说说范式吧。”冒号并不着急，“范式可以粗略理解为模范、模型、模式、风格、流派等等。软件中的范式除了编程范式外，还有架构范式<a class="link" href="#note1"><sup>[1]</sup></a>、数据库范式<a class="link" href="#note2"><sup>[2]</sup></a>等。比如，对象导向式（Object-Oriented）可以应用于编程、架构和数据库中，分别成为OOP（Object-Oriented Programming）、OOA（Object-Oriented Architecture）和OODB（object-oriented database）；类似地，事件驱动式（Event-Driven）可以是一种编程范式，可以是一种架构模型，也可以是一种设计模式。总之，每种范式都代表着<span class="strong"><strong>一套独特而有效的解决问题的思想和方法</strong></span>。掌握范式对编程语感的提高至少有两层作用：首先，编程语言的语法、语义等都是从编程范式的树根衍生而出的枝叶，把握了这种脉络和节奏，代码才会如音乐舞蹈般韵律有致；其次，每种范式擅长的问题领域不尽相同，只有博闻广识，方可扬长避短，程序才会如行云流水般流畅自然。”
        </p><p>
            逗号添油加醋：“武功练至化境，一定是博采众长，就像杨过融合了东邪、西毒、南帝、北丐、中神通等各派武功，才能随心所欲地打出黯然销魂掌来。”
        </p><p>
            提起武侠人物，众人俱是逸兴遄飞，哪能体会到半点黯然消魂之伤？
        </p><p>
            冒号道：“天下之理，殊途同归。我们停止玄玄之论，用实例来说明吧。谁来介绍一下快速排序法（quicksort）？”
        </p><p>
            众人飞舞的思绪渐渐收敛，终于由引号作答：“快速排序法的思想是：在列表中找一个基准元素，将所有小于它的元素划归一个子列，置于其前；将所有大于等于它的元素划归另一子列，置于其后。然后递归地对前后两个子列作同样处理，直至最终。”
        </p><p>
            “很好，让我们用Java来实现一下该算法。”冒号显示出一段代码——
        </p><div class="informalexample"><pre class="programlisting">
/** 快速排序法的Java实现 */
public class Sorter
{
    public static &lt;T extends Comparable&lt;? super T&gt;&gt; void qsort(T[] list)
    {
        qsort(list, 0, list.length - 1);
    }

    private static &lt;T extends Comparable&lt;? super T&gt;&gt; void qsort(T[] list, int low, int high)
    {
        if (low &gt;= high) return;

        int i = low - 1, j = high + 1;
        T pivot = list[low]; // 基准元素

        for ( ; ; )
        {
            do { ++i; } while (list[i].compareTo(pivot) &lt; 0);
            do { --j; } while (list[j].compareTo(pivot) &gt; 0);

            if (i &gt;= j) break;

            // 交换
            T tmp = list[i]; list[i] = list[j]; list[j] = tmp;
        }

        // 找到分割点j，递归
        qsort(list, low, j);
        qsort(list, j + 1, high);
    }
}</pre></div><p>
            “请问这里用到了哪些编程范式？”冒号提问。
        </p><p>
            叹号心想，有何难哉？遂答：“既然是用Java实现的，自然少不了OOP。同时为了使算法更具普适性，还用到了泛型编程。”
        </p><p>
            “你好像忘记了最重要的过程式，反倒是OOP的色彩极淡。”冒号显然不满意他的答案。
        </p><p>
            叹号不解：“不是说Java是100%的OOP语言吗？”
        </p><p>
            冒号颇为不屑：“不要轻信这种浮浅之论。且不说Java的<span class="term">基本类型</span>（primitive type）不属于<span class="term">类</span>（class），本就不是100%的OOP，即使是100%的OOP，那与过程式也不矛盾啊。此例中的Sorter类连一个<span class="term">实例成员</span>（instance member）也没有，唯一与OOP沾边的是作为interface的Comparable，在C中也可用函数指针代替。如果不考虑泛型式的特征，本例无论用Java还是用C，并没有本质差别。事实上，对于这类纯算法的问题，OOP范式本无太多用武之地。换句话说，quicksort虽然是通过以OOP著称的Java来实现的，但用的主要还是过程式的思想和方法。”
        </p><p>
            问号赶紧问道：“还能用其他范式来实现吗？”
        </p><p>
            此问正合冒号之意：“我们改用纯函数式语言Haskell来试试——”
        </p><div class="informalexample"><pre class="programlisting">
-- 快速排序法的Haskell实现 
qsort :: (Ord a) =&gt; [a] -&gt; [a]  --函数声明
qsort[] = []                    --递归终点 
qsort(pivot : rest) = qsort[x| x &lt;- rest, x &lt; pivot]         --对前面的子列递归
                          ++ [pivot]
                          ++ qsort[x| x &lt;- rest, x &gt;= pivot] --对后面的子列递归</pre></div><p>
            叹号几不能信：“竟然可以如此精炼？”
        </p><p>
            “上面的Java代码很难再精简了，但与Haskell代码相比还是太冗长了。后者省去了所有的赋值、迭代和流程控制，只有单纯的递归，反映了典型的函数式特征。”冒号解说着，“鉴于你们对Haskell不太熟悉，我稍微解释一下。第一步，声明函数类型<a class="link" href="#note3"><sup>[3]</sup></a>：同类型列表之间的变换，其中Ord可类比Java中的Comparable，以保证列表元素之间能进行比较；第二步，声明递归终点：空列排序后仍是空列；第三步，描述递归原则：基准元素pivot与剩余子列rest进行排序后的列表，正是将小于基准的子列和超过基准的子列分别排序，中间插入基准元素后的结果。”
        </p><p>
            句号思有所得，不禁喜形于色：“我明白了，这两段代码生动地反映了命令式编程与声明式编程之间的差别：前者需要指定计算的过程，后者只需指定计算的原则。一个着重微观的细节，一个着重宏观的方向，自有繁简之别。”
        </p><p>
            冒号亦有所慰：“非常好！类似的话我以前也说过，但你们自己说的才是真正的收获啊。我们还提过，过程式与函数式的差别同时也是机器思维与数学思维的差别。不妨对比Haskell表达式与数学中的集合表达式，它们是多么地相近！”
        </p><p>
            黑板上出现两行式子——
        </p><div class="informalexample"><pre class="programlisting">
数学表达式：   {x| x ∈ rest, x &lt; pivot} 
Haskell表达式：[x| x &lt;- rest, x &lt; pivot] </pre></div><p>
            逗号仔细打量着：“嗯，的确像，跟哥俩似的，连符号&lt;-都是仿照集合属于符（∈）的。”
        </p><p>
             “还有另一种表达方法。”冒号又添加了一行——
        </p><div class="informalexample"><pre class="programlisting">
Haskell表达式2 ：(filter (&lt; pivot) rest) </pre></div><p>
            “虽然与前一表达式的简洁度相差无几，但可读性更强。filter即是过滤，将列表rest中的元素进行筛选，条件是小于基准元素。”冒号讲解道。
        </p><p>
            问号略感迷惑：“（&lt; pivot）的用法看起来有点怪异。”
        </p><p>
            “它是一个函数，也是filter的第一个参数，用来判断第二个参数rest的元素是否合格，即 &lt; pivot。这体现了函数式的一个重要特征：函数是头等公民（first-class citizen）——可作为传递参数、可作为表达式的值、可嵌入数据结构、也可与某变量绑定，与普通的基本数据类型毫无二致。这类函数更数学化的叫法是<span class="term">高阶函数</span>（higher-order function），它是函数式编程<span class="emphasis"><em>简洁而强大</em></span>的重要根源。”冒号细加解释，“大家还记得上节课谈到的回调函数吧？callback无非是将函数作为参数来传递，本质上是将<span class="emphasis"><em>代码</em></span>当<span class="emphasis"><em>数据</em></span>来使用，回调机制的巨大威力均拜此高级用法所赐。”
        </p><p>
            众人又一段经脉被打通了。
        </p><p>
            引号提出一个很实际的问题：“函数式编程的确很酷，可Java并不支持。如果采用Haskell之类的函数式语言，会不会带来系统集成上的困难？”
        </p><p>
            冒号打消了他的疑虑：“Java平台下已经集成了不少的支持函数式编程的语言，如JRuby、Jython、Groovy、Scala等，甚至Haskell在JVM下也有相应的Jaskell。其中，Groovy与Java的结合最为自然。此外，C#3.0引入了lambda表达式，也能方便地支持函数式。我们看一下它们是如何实现quicksort的——”
        </p><div class="informalexample"><pre class="programlisting">
/** 快速排序法的Groovy实现 */
def qsort(list) {
    if (list.size() &lt;= 1) return list

    def pivot = list[0]
    return (qsort(list.findAll{x -&gt; x &lt; pivot})
          +             list.findAll{x -&gt; x == pivot}
          +    qsort(list.findAll{x -&gt; x &gt; pivot}))
} </pre></div><p/><div class="informalexample"><pre class="programlisting">
/** 快速排序法的C#3.0实现 */
IEnumerable&lt;T&gt; qsort&lt;T&gt;(IEnumerable&lt;T&gt; list) where T : IComparable&lt;T&gt;
{
    if (list.Count() &lt;= 1) return list;

    var pivot = list.First();
    return qsort(list.Where(x =&gt; x.CompareTo(pivot) &lt; 0))
                .Concat(list.Where(x =&gt; x.CompareTo(pivot) == 0))
                .Concat(qsort(list.Where(x =&gt; x.CompareTo(pivot) &gt; 0)));
} </pre></div><p>
            “以上两种方法如出一辙，虽然比Haskell的代码略长了些，并且还带着过程式的烙印，但总体思想还是函数式的。”冒号紧扣本质，“函数式还有一个重要特征：无副作用或尽量减少副作用<a class="link" href="#note4"><sup>[4]</sup></a>。所谓无副作用，是指一个函数在被调用前后保持程序的状态不变。无副作用的函数不会改变非局部变量的值，不会改变传入的参数，也没有I/O操作。”
        </p><p>
            逗号脱口而出：“什么状态都不变，那这样的函数有什么用？”
        </p><p>
            冒号不以为奇：“你的这种想法源自根深蒂固的命令式思维。我们曾把命令式程序比作状态自动机，其运行过程就是在不断地修改机器的状态。而函数式程序则是进行表达式变换，一般不会改变变量的值。其实函数式并非完全不改变内存，只不过改变的是<span class="term">栈内存</span>（stack）罢了。换言之，无副作用函数的作用关键在于其估值结果，按过程式的说法是返回值。刚才的quicksort不正是如此吗？”
        </p><p>
            逗号如梦初醒。
        </p><p>
            冒号旋即补充：“当然，在涉及流程控制、顺序操作、状态维护、I/O运算等问题时，没有副作用是很不方便的。函数式语言一般也会提供变通的方式，比如Haskell、Ruby、Python、Scala等语言支持的monad便是这样一种机制。”
        </p><p>
            问号仍有疑问：“药物最好没有副作用，函数没有副作用的好处是什么？”
        </p><p>
            冒号嘴一咧：“好处太多了。首先，没有副作用的函数易于重构、调试和单元测试。其次，代码有效性与函数顺序无关，方便并发处理和优化处理。举个简单的例子，计算两个函数的乘积：f(x) * g(y)。由于无副作用，f(x) 和g(y)的估值过程是独立的，估值顺序也不重要，因此理论上可以对二者并行计算。另外，还可利用<span class="term">惰性求值</span>（lazy evaluation）<a class="link" href="#note5"><sup>[5]</sup></a>：如果算出f(x)为零，那么不用计算g(y)便可知乘积为零了。”
        </p><p>
            叹号忍不住赞叹：“听起来真不错！”
        </p><p>
            冒号突然扭头写下一行字，然后提问：“请问这个unix命令表示什么意思？”
        </p><div class="informalexample"><pre class="programlisting">
grep the BigFile.txt | head </pre></div><p>
            引号回答：“这是要打印出文件BigFile.txt中包含字符串‘the’的十行文字。”
        </p><p>
            冒号点头：“没错，这是unix<span class="term">管道</span>（pipe）的用法。它由两个命令组成：grep和head，前者的输出是后者的输入。一个有趣的事实是，后者不用等到前者执行完毕才启动。更有趣的是，只要后者获取了足够的数据，前者便会停止执行。故而当grep在给定文件中找到含有给定字符串的十行文字后，即可功成身退，因为那已是head的全部所需。假如没有管道机制，那就只能这样——”
        </p><p>
            
        </p><div class="informalexample"><pre class="programlisting">
grep the BigFile.txt &gt; tmpfile; head tmpfile </pre></div><p>
            “这样不仅产生了多余的临时文件，而且效率大大降低。这与惰性求值有关吗？”句号隐隐明白了冒号的用意。
        </p><p>
            “类似地，通常要计算f(g(x))的值，需要计算完g(x)后才能将所得值代入函数f。有了惰性求值机制，g(x)的计算完全由函数f的需求来驱动，避免做无用功。此乃其惰性之所在。”
        </p><p>
            逗号挑起大拇指：“这个懒偷得真聪明！”
        </p><p>
            “惰性求值不仅能节省有限的时间，还能超越无限的时间——g(x)甚至可以永不退出，从而可能产生无穷的输出结果集供函数f使用。这可是在过程式编程中想都不敢想的事啊。”冒号言犹未尽，“最后，没有副作用的函数是<span class="term">引用透明的</span>（referential transparency），即一个表达式随时可以用它的值来替换<a class="link" href="#note6"><sup>[6]</sup></a>，正如数学中的函数一样，保证了数学思维的贯彻和运用。”
        </p><p>
            引号自感获益颇丰：“前面介绍范式时，觉得函数式最为神秘。现在总算有了些感性认识了。”
        </p><p>
            冒号道出缘由：“函数式编程不仅有许多独特的概念和方法，还有很深的数学背景——<span class="term">λ-演算</span>（lambda calculus）。如果一开始便倾囊相授，你们必会望而却步，我岂不是打草惊蛇了？”
        </p><p>
            众人始觉：老冒狡猾狡猾的，原来在诱敌深入啊。
        </p><p>
            “尽管函数式有这么多优点，运算能力从理论上比诸过程式也毫不逊色<a class="link" href="#note7"><sup>[7]</sup></a>，但一直没有成为主流范式。”冒号话锋一转，“细究之，至少有两方面的原因：主观上，程序员更习惯<span class="emphasis"><em>机器风格</em></span>的过程式思维和<span class="emphasis"><em>现实风格</em></span>的OOP思维，不容易接纳<span class="emphasis"><em>数学风格</em></span>的函数式思维；客观上，函数式语言在表现力和运行效率<a class="link" href="#note8"><sup>[8]</sup></a>等方面与过程式和OOP语言也有一定的差距。饶是如此，支持它的语言还是越来越多，其简洁精巧的特性也为越来越多的人所青睐。它的整体应用虽然主要集中于数学计算、人工智能等领域，但局部应用早已遍地开花了。”
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    如OOA（Object-Oriented Architecture），COA（Component-Oriented Architecture），SOA（Service-Oriented Architecture）、EDA（Event-Driven Architecture）等。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    如关系数据库（relational database）、对象导向式数据库（object-oriented database）等。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    这一步可省略，但出于对代码的清晰度以及性能、调试等方面的考虑，最好保留。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    没有副作用的函数式语言被称为纯函数式（purely functional），如Haskell和SISAL；有副作用的被称为非纯函数式（impurely functional），如Lisp和ML。不过Haskell等语言也可通过monad来实现包括I/O在内的副作用。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    惰性求值又称为延迟求值（delayed evaluation）或非严格求值（non-strict evaluation）。参考文献【3】对惰性求值和高阶函数的意义作了深入的阐述。
                </p></li><li class="listitem"><p><a name="note6"></a>
                    例如，如果square是一个平方函数，那么square(3)总可用9来代替。这是因为函数square的无副作用性保证了相同的输入（此处是3）一定有相同的输出（此处是9）。
                </p></li><li class="listitem"><p><a name="note7"></a>
                    λ-演算被证明是图灵完备的。
                </p></li><li class="listitem"><p><a name="note8"></a>
                    但函数式语言中无副作用的特性和惰性求值等机制也可带来运行效率上的改善。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    学习知识之表须通过记忆，掌握知识之里须通过练习，渗透知识之根须通过培养。编程范式正是知识之根。
                </p></li><li class="listitem"><p>
                    适度地容忍无知也是一种学习技巧。
                </p></li><li class="listitem"><p>
                    “钉子精神”固然可贵，“刨子精神”也不可少。
                </p></li><li class="listitem"><p>
                    不同的编程范式代表不同的解决问题的思想和方法。不同的编程语言提倡不同的编程范式，并在语法上给予支持。只有掌握范式，才能更合理、更有效地运用编程语言的语法和语义特征，并能针对不同的问题领域使用不同的编程风格，编写的代码才会和谐自然、富于美感。
                </p></li><li class="listitem"><p>
                    命令式编程需要指定计算的过程，着重微观的细节；声明式编程只需指定计算的原则，着重宏观的方向。因此二者繁简有别。
                </p></li><li class="listitem"><p>
                    在函数式编程中，函数是程序的核心，是头等公民，一般没有或很少副作用，同时没有显式的内存管理。
                </p></li><li class="listitem"><p>
                    由于函数式编程中的高阶函数与基本数据类型平起平坐，故可将代码作数据用，这是程序既简洁又强大的原因之一。回调机制采用的正是函数式风格。
                </p></li><li class="listitem"><p>
                    无副作用的函数容易重构、调试和测试，便于并发和优化处理，并能贯彻和运用数学思维。
                </p></li><li class="listitem"><p>
                    惰性求值是需求驱动的，可以避免不必要的等待和计算。
                </p></li><li class="listitem"><p>
                    相比过程式和OOP，函数式编程思想过于数学化和抽象化，语言的表现力和运行效率也有所不足。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Michael Lee Scott．Programming Language Pragmatics．San Francisco：Morgan Kaufmann，2000．589-620
                </p></li><li class="listitem"><p>
                    Stephen H. Kaisler．SOFTWARE PARADIGMS．New Jersey：Wiley，2005．23-24
                </p></li><li class="listitem"><p>
                    John Hughes．Why Functional Programming Matters．The Computer Journal，1989，32(2)：98-107
                </p></li></ol></div></div></div><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F13%2Fcolon-class-4_1%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A74.1%EF%BC%9A%E5%87%BD%E6%95%B0%E8%8C%83%E5%BC%8F" id="wpa2a_2">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年09月17日 -- <a href="http://blog.zhenghui.org/2009/09/17/colon-class-4_3/" title="冒号课堂§4.3：汇总范式">冒号课堂§4.3：汇总范式</a> (5)</li><li>2009年09月7日 -- <a href="http://blog.zhenghui.org/2009/09/07/colon-class-2_4/" title="冒号课堂§2.4：并发范式">冒号课堂§2.4：并发范式</a> (2)</li><li>2009年09月4日 -- <a href="http://blog.zhenghui.org/2009/09/04/colon-class-2_2/" title="冒号课堂§2.2：声明范式">冒号课堂§2.2：声明范式</a> (1)</li><li>2009年08月29日 -- <a href="http://blog.zhenghui.org/2009/08/29/colon-class-1_4/" title="冒号课堂§1.4：初识范式">冒号课堂§1.4：初识范式</a> (2)</li><li>2009年09月27日 -- <a href="http://blog.zhenghui.org/2009/09/27/colon-class-5_4/" title="冒号课堂§5.4：语言误区">冒号课堂§5.4：语言误区</a> (0)</li><li>2009年09月21日 -- <a href="http://blog.zhenghui.org/2009/09/21/colon-class-5_1/" title="冒号课堂§5.1：教学计划">冒号课堂§5.1：教学计划</a> (1)</li><li>2009年09月19日 -- <a href="http://blog.zhenghui.org/2009/09/19/colon-class-4_4/" title="冒号课堂§4.4：情景范式">冒号课堂§4.4：情景范式</a> (0)</li><li>2009年09月15日 -- <a href="http://blog.zhenghui.org/2009/09/15/colon-class-4_2/" title="冒号课堂§4.2：逻辑范式">冒号课堂§4.2：逻辑范式</a> (0)</li><li>2009年09月11日 -- <a href="http://blog.zhenghui.org/2009/09/11/colon-class-3_4/" title="冒号课堂§3.4：事件驱动">冒号课堂§3.4：事件驱动</a> (2)</li><li>2009年09月10日 -- <a href="http://blog.zhenghui.org/2009/09/10/colon-class-3_3/" title="冒号课堂§3.3：切面范式">冒号课堂§3.3：切面范式</a> (4)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/13/colon-class-4_1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>冒号课堂§2.4：并发范式</title>
		<link>http://blog.zhenghui.org/2009/09/07/colon-class-2_4/</link>
		<comments>http://blog.zhenghui.org/2009/09/07/colon-class-2_4/#comments</comments>
		<pubDate>Mon, 07 Sep 2009 05:14:57 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[函数式编程]]></category>
		<category><![CDATA[对象式编程]]></category>
		<category><![CDATA[并发式编程]]></category>
		<category><![CDATA[线程]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[过程式编程]]></category>
		<category><![CDATA[进程]]></category>
		<category><![CDATA[逻辑式编程]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=361</guid>
		<description><![CDATA[<b>并发范式</b>——合作与竞争（<em>并发式编程简谈</em>）<br/> • 有谁愿意驾驶一辆启动后不能刹车、不能倒车、不能变速、油尽方停的汽车呢？<br/> • 并发式编程以进程为导向、以任务为中心将系统模块化<br/> • 并发式编程以资源共享与竞争为主线 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第二课 重要范式(4)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="并发范式"><div class="titlepage"><div><div><h1 class="title"><a name="id599705"></a>2.4 并发范式——合作与竞争</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>并发式编程简谈</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>在合作中竞争，在竞争中合作</p></div><div class="attribution"><span>—<span class="attribution">《竞合》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    有谁愿意驾驶一辆启动后不能刹车、不能倒车、不能变速、油尽方停的汽车呢？
                </p></li><li class="listitem"><p>
                    并发式编程以进程为导向、以任务为中心将系统模块化
                </p></li><li class="listitem"><p>
                    并发式编程以资源共享与竞争为主线
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>并发式编程有何特点？</p></li><li class="listitem"><p>并发式编程有哪些好处？</p></li><li class="listitem"><p>合理的并发式编程设计应满足哪些条件？ </p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            逗号好奇地问：“还有其他类型的编程范式吗？”
        </p><p>
            “不但有，而且有很多。”冒号喝了一口水，悠悠地说，“<span class="term">并发式编程</span>（concurrent programming）就是其中之一。”
        </p><p>
            叹号有些惊讶：“并发式编程也算一种范式？它似乎更像是提高运行效率的一种手段。” 
        </p><p>
            “大谬不然。”冒号摇摇头，“真正的并发式编程，绝不只是调用线程<a class="link" href="#note1"><sup>[1]</sup></a>API或使用synchronized、lock之类的关键字那么简单。从宏观的架构设计，到微观的数据结构、流程控制乃至算法，相比通常的串行式编程都可能发生变化。随着硬件性能和用户需求的双重提升，并发式编程已成为不可回避的主题。毫不夸张地说，并发式编程是继OOP之后又一场思想和技术上的革命。只是相比OOP，尽管年龄相仿，但语言上不够支持，标准上不够统一，理论上不够完善，因而这场革命更具<span class="emphasis"><em>破坏性</em></span>和<span class="emphasis"><em>建设性</em></span>。现在我们来看一个例子，比较两种烧水泡茶的方案。”
        </p><p>
            说着冒号在黑板上写下——
        </p><div class="informalexample"><p>方案一：洗茶杯；放茶叶；灌水壶；烧水；水开后泡茶。</p><p>方案二：灌水壶；在烧水的同时，洗茶杯；放茶叶；水开后泡茶。</p></div><p>
            引号见多识广：“我记得这好像是运筹学中的例子，显然方案二更佳。从编程的角度来看，方案一是串行式编程，方案二是并发式编程——烧水的线程与洗茶杯放茶叶的线程是同时进行的。”
        </p><p>
            “如果方案一也用并发式编程呢？”冒号追问。
        </p><p>
            引号一愣，随即道：“必须先洗茶杯后放茶叶，洗茶杯放茶叶的同时也没法烧水，至于泡茶，更得等水开之后了。”
        </p><p>
            句号明白了冒号的用意：“这就是说，单凭并发式编程并不能保证提高程序性能，还必须在程序设计上下功夫。” 
        </p><p>
            问号继续深究：“即便如此，如果硬件不支持，并发式编程也未必能提高效率啊。比如在一台单处理器的主机上，多个线程只能是模拟的、逻辑上的并行，而非真正物理上的并行。”
        </p><p>
            “这话有一定道理。”冒号有限度地表示赞同，“但设计者应该未雨绸缪，总不能等到有了多处理器才想到并发式编程吧？再者，没有多处理器，是不是可以利用多台单处理器的主机同时运算呢？退一步讲，即使在一台单处理器的情况下，并发式编程有时也是必不可少的。它能保证不同用户、不同程序之间的公平竞争，这对多用户、多任务的系统而言尤为重要。此外，采用并发式编程同样可能提高性能。最典型的一个例子：当一个线程因为等待某种资源而被堵塞时，可以切换到其他线程而不至让CPU闲置。更重要的一点是，难道除了缩短程序运行时间外，并发式编程就没有别的好处吗？譬如打开一个网页，你是希望浏览器边下载边显示网页呢，还是下载完成后再显示？”
        </p><p>
            “当然是边下载边显示啦。” 叹号毫不犹豫地说，“如果每个网页都是下载完成后再显示，那还不把人给憋坏了！”
        </p><p>
            逗号加了一句：“浏览器加载文字和加载图像也应分开在不同的线程，如果用户对文字部分不感兴趣，不等图像显示就可直接关闭网页了。”
        </p><p>
            “如果网页被提前关闭，那也是假用户之手变相缩短了程序运行时间。”冒号接过话来，“还有一个常见的例子，包括浏览器在内的许多应用软件在运行中都会显示一个进程条。该进程条的更新需要一个单独的线程，从效率上看它起的作用是负面的，但大大提高了用户体验，是软件人性化的表现。再举一例，媒体播放器一般都会提供暂停、快进、倒退、快放、慢放等按钮，如果不采用多线程，它们将形同虚设。此处并发式编程的作用在于提高了软件的响应能力，也改善了用户体验——有谁愿意驾驶一辆启动后不能刹车、不能倒车、不能变速、油尽方停的汽车呢？”
        </p><p>
            “岂止是不愿意，简直是恐怖！”叹号加重了语气。
        </p><p>
            “对于操作系统、各种实时系统和诸如数据库、网络服务器等基于服务的系统来说，在响应用户请求的时间上和资源的合理分配上有着极高的要求，并发式编程更是不可或缺。”句号接着补充。
        </p><p>
            问号紧接着又问：“并发式编程还有其他用处吗？”
        </p><p>
            冒号应道：“不同编程范式采用不同的视角和方法来设计和开发软件。并发式编程<span class="emphasis"><em>以进程为导向</em></span>（Process-Oriented）、以任务为中心将系统模块化。我们都知道，模块化在编程中是个好东西。”
        </p><p>
            引号心存疑惑：“过程式中引入了函数模块，对象式中引入了对象模块，但并发式似乎没有<span class="emphasis"><em>在语法上</em></span>引入新型模块，比如C和Java中的线程其实不过是函数或方法的包装而已。”
        </p><p>
            “我猜你的意思是：在定义一个线程之前，其主函数已经模块化了，不能把功劳记在并发式编程上，对吧？”冒号笑问，“你不能孤立静止地看待每个模块，还要考虑到模块之间的相互关联和作用。相比串行式，并发式在模块之间引入了新的通讯和控制方式。也就是说，原先的一些模块的定义和划分一定是建立在线程机制的基础上的。如果失去线程的支持，它们的合理性自然会打上问号，说不定整体设计都会受到牵连。这也体现了编程范式的<span class="emphasis"><em>渗透性和全局影响力</em></span>。”
        </p><p>
            教室上空弥漫的疑云一消而散。
        </p><p>
            冒号进一步指出：“除了用户主观上的需求和硬件客观上的可能外，并发式显得格外重要的另一深层原因是：许多程序是现实世界的模拟，而我们生活的世界不折不扣是并发式的。从某种意义上看，并发式的模拟比对象式的模拟更贴近世界。”
        </p><p>
            引号再次提问：“并发式编程在设计上有什么要求？”
        </p><p>
            “并发式编程<span class="strong"><strong>以资源共享与竞争为主线</strong></span>——又是对当今世界形势的一个逼真模拟。这意味着程序设计将围绕进程的划分与调度、进程之间的通讯与同步<a class="link" href="#note2"><sup>[2]</sup></a>等等来展开。合理的并发式设计需要诸多方面的权衡考量。”冒号说着，放出一段幻灯片——
        </p><div class="informalexample"><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>软件易于重用、维护、测试</p></li><li class="listitem"><p>公平有效地利用资源，优化程序性能如增大吞吐量、减少响应时间、提高效率等</p></li><li class="listitem"><p>保障进程安全，防止<span class="term">竞态条件</span>（race condition）</p></li><li class="listitem"><p>保持进程活性，避免死锁、饥饿、活锁、资源枯竭等</p></li><li class="listitem"><p>减少锁开销、上下文切换等带来的性能损失</p></li><li class="listitem"><p>妥善处理多进程在算法、调试等方面带来的复杂性</p></li></ul></div></div><p>
            叹号蹙眉：“并发式编程好是好，就是太复杂。”
        </p><p>
            “天下没有免费的午餐。有所得，必有所失。” 冒号淡淡地说，“并发式编程当然不容易，但也并非难以掌握。最重要的是，作为一个程序员，你不得不面对它。即使你不直接用并发式编程，你依赖的代码和依赖你的代码也可能用到；即使现在没有用并发式，将来也可能用到。如果采取避而不理的鸵鸟政策，早晚会被人点中你的死穴。虽然目前不会对并发式编程作更深入的探讨，但希望能引起你们足够的重视。”
        </p><p>
            句号谈及他的感受：“相比OOP在语言上得到的支持，并发式的支持力度好像很不够。”
        </p><p>
            冒号点头称是：“这是由并发式的复杂性和成熟度决定的。另外，在究竟是在语言级别上支持并发、还是交由操作系统处理的问题上，仁者见仁，智者见智。Ada、Java和C#等选择前者，在语法上对并发式编程有一定的支持；C和C++等选择后者，除了关键字volatile外，主要靠库函数支持。专门为并发式而设计的语言大多仅限于学术研究而非商业应用，Erlang语言<a class="link" href="#note3"><sup>[3]</sup></a>是少数的例外。顺带提一下，前面提到的声明式语言具有无副作用的特性，尤其适合于并发式编程。”
        </p><p>
            问号提了一个听似奇怪的问题：“并发式与前面提到的对象式有无共通之处？”
        </p><p>
            “并发式与对象式虽是互相正交的两种范式，倒真有些相通呢。”冒号回答，“它们均与三大核心范式正交，并且越来越广泛地向它们渗透着；均为传统编程的一种推广——并发式进程的个数为一时即为传统的串行式编程，对象的方法个数为为零即为传统的数据类型；均将整个程序系统分解为<span class="emphasis"><em>若干独立的子系统</em></span>，不同的是一个以<span class="emphasis"><em>任务</em></span>为单位，一个以<span class="emphasis"><em>对象</em></span>为单位；子系统之间均能交流与合作，不同的是一个以<span class="emphasis"><em>竞争</em></span>为主题，一个以<span class="emphasis"><em>服务</em></span>为主题。如果将程序系统视作公司，那么并发式系统是<span class="emphasis"><em>产品型</em></span>公司，每个进程是一名工人，其职责是执行<span class="emphasis"><em>单一</em></span>任务；对象式系统是<span class="emphasis"><em>服务型</em></span>公司，每个对象是一名服务员，其职责是提供<span class="emphasis"><em>系列</em></span>服务。由此可见，一名优秀的程序设计师也应该是一名优秀的管理者。”
        </p><p>
            句号提出：“迄今为止我们谈到了五种范式，能否对它们简单概括一下？”
        </p><p>
            “你提问的时机把握得不错嘛。”冒号开始如数家珍，“为便于记忆，我们不妨用‘一二三四五’来概括编程范式之最：最传统的<span class="emphasis"><em>一个</em></span>是命令式；最基本的<span class="emphasis"><em>两个</em></span>是命令式和声明式；最核心的<span class="emphasis"><em>三个是</em></span>命令式、函数式和逻辑式；最主要的<span class="emphasis"><em>四个</em></span>是命令式、函数式、逻辑式和对象式；最重要的<span class="emphasis"><em>五个</em></span>是命令式、函数式、逻辑式、对象式和并发式。最后，我们来对比一下五大范式。”
        </p><p>
            少顷，黑板上出现几行排比句——
        </p><div class="informalexample"><p>过程式：以过程为模块的君主体系，模块之间互相授命与听命</p><p>函数式：以函数为模块的数学体系，模块之间互相替换与合成</p><p>逻辑式：以断言为模块的逻辑体系，模块之间互相归纳与演绎</p><p>对象式：以对象为模块的民主体系，模块之间互相交流与服务</p><p>并发式：以进程为模块的生产体系，模块之间互相竞争与合作</p></div><p>
            “说回并发式编程，它要求我们摆脱以往习惯的按部就班的思维方式，对编程提出了更高的挑战。程序世界与现实世界一样，呈百舸争流、千帆竞发之势，不进则退啊！”言讫，冒号宣布，“第二堂课到此为止，欢迎下次光临。” 
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    并发式编程中以进程（process）或线程（thread）为基本单位。尽管它们有很大的差异，但为简明起见，这里不加区分地交替使用两个术语。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    <span class="term">同步</span>（synchronization）只在采用<span class="term">共享内存</span>（shared memory）的并发模型中需要，在采用<span class="term">消息传递</span>（message passing）的并发模型中并不需要。本文主要以前一种并发模型为讨论对象，它也是大多数语言或库（包括C、C++、Java、C#等）所支持的模型。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    Erlang是由爱立信开发的一种通用编程语言，支持函数式和并发式，采用消息传递的并发模型。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    并发式编程以进程为导向、以任务为中心、以资源共享与竞争为主线。
                </p></li><li class="listitem"><p>
                    并发式编程有助于提高运行效率、充分利用资源、提高软件的响应能力、改善用户体验、保证公平竞争，同时以进程为单位将系统模块化，更加真实地模拟现实世界。
                </p></li><li class="listitem"><p>
                    合理的并发式设计应该做到：软件易于重用、维护和测试；有效地利用资源，优化程序性能；保障进程安全和活性；减少性能损失和复杂度。
                </p></li><li class="listitem"><p>
                    对象式和并发式在传统编程的基础上，分别从不同的方向进行拓展：对象式在数据类型上进行推广——允许运算作为数据类型的成员；并发式在执行顺序上进行推广——允许不同运算的执行在时间上交替或者重合。它们与三大核心范式一道，组成了最重要的五大编程范式。
                </p></li><li class="listitem"><div class="table"><a name="id640460"></a><p class="title">五大重要范式对比：</p><div class="table-contents"><table summary="五大重要范式对比" border="1"><colgroup><col><col><col><col></colgroup><thead><tr><th>范式</th><th>体系</th><th>模块</th><th>模块关系</th></tr></thead><tbody><tr><td>过程式</td><td>君主体系</td><td>过程</td><td>授命与听命</td></tr><tr><td>函数式</td><td>数学体系</td><td>函数</td><td>替换与合成</td></tr><tr><td>逻辑式</td><td>逻辑体系</td><td>断言</td><td>归纳与演绎</td></tr><tr><td>对象式</td><td>民主体系</td><td>对象</td><td>交流与服务</td></tr><tr><td>并发式</td><td>生产体系</td><td>进程</td><td>竞争与合作</td></tr></tbody></table></div></div><br class="table-break"></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Abraham Silberschatz，Peter Galvin．Operating System Concepts，5ed.．Reading, MA：Addison-Wesley，1998．155-235
                </p></li></ol></div></div></div>

<!-- below is edited manually -->
<strong><span style="font-family: 宋体">课后思考</span></strong>
<ul style="margin-top: 0cm; list-style-type: none">
    <li>02-01 在你所掌握的语言当中，有哪些是命令式的？哪些是声明式的？其中，命令式语言里有哪些声明式的特征？声明式的语言里有哪些命令式的特征？它们各自有哪些优点和缺点？</li>
    <li>02-02 你编写的程序符合结构化编程的原则吗？</li>
    <li>02-03 你接触过函数式语言和逻辑式语言吗？如果没有，试着阅读相关的入门书籍，体会它们与过程式迥然不同的风味。（函数式语言推荐Haskell和 Scheme，逻辑式语言推荐Prolog）</li>
    <li>02-04 相比纯过程式的编程语言（如C语言），你认为OOP语言主要有哪些优势？又有哪些劣势？</li>
    <li>02-05 你认为在并发式编程设计中，最重要的是什么？最困难的是什么？</li>
</ul><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F07%2Fcolon-class-2_4%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A72.4%EF%BC%9A%E5%B9%B6%E5%8F%91%E8%8C%83%E5%BC%8F" id="wpa2a_4">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年09月4日 -- <a href="http://blog.zhenghui.org/2009/09/04/colon-class-2_2/" title="冒号课堂§2.2：声明范式">冒号课堂§2.2：声明范式</a> (1)</li><li>2009年09月15日 -- <a href="http://blog.zhenghui.org/2009/09/15/colon-class-4_2/" title="冒号课堂§4.2：逻辑范式">冒号课堂§4.2：逻辑范式</a> (0)</li><li>2009年09月13日 -- <a href="http://blog.zhenghui.org/2009/09/13/colon-class-4_1/" title="冒号课堂§4.1：函数范式">冒号课堂§4.1：函数范式</a> (0)</li><li>2009年09月6日 -- <a href="http://blog.zhenghui.org/2009/09/06/colon-class-2_3/" title="冒号课堂§2.3：对象范式">冒号课堂§2.3：对象范式</a> (3)</li><li>2009年09月21日 -- <a href="http://blog.zhenghui.org/2009/09/21/colon-class-5_1/" title="冒号课堂§5.1：教学计划">冒号课堂§5.1：教学计划</a> (1)</li><li>2009年09月19日 -- <a href="http://blog.zhenghui.org/2009/09/19/colon-class-4_4/" title="冒号课堂§4.4：情景范式">冒号课堂§4.4：情景范式</a> (0)</li><li>2009年09月17日 -- <a href="http://blog.zhenghui.org/2009/09/17/colon-class-4_3/" title="冒号课堂§4.3：汇总范式">冒号课堂§4.3：汇总范式</a> (5)</li><li>2009年09月11日 -- <a href="http://blog.zhenghui.org/2009/09/11/colon-class-3_4/" title="冒号课堂§3.4：事件驱动">冒号课堂§3.4：事件驱动</a> (2)</li><li>2009年09月10日 -- <a href="http://blog.zhenghui.org/2009/09/10/colon-class-3_3/" title="冒号课堂§3.3：切面范式">冒号课堂§3.3：切面范式</a> (4)</li><li>2009年09月9日 -- <a href="http://blog.zhenghui.org/2009/09/09/colon-class-3_2/" title="冒号课堂§3.2：超级范式">冒号课堂§3.2：超级范式</a> (2)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/07/colon-class-2_4/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>冒号课堂§2.2：声明范式</title>
		<link>http://blog.zhenghui.org/2009/09/04/colon-class-2_2/</link>
		<comments>http://blog.zhenghui.org/2009/09/04/colon-class-2_2/#comments</comments>
		<pubDate>Fri, 04 Sep 2009 15:48:57 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[函数式编程]]></category>
		<category><![CDATA[命令式编程]]></category>
		<category><![CDATA[声明式编程]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[逻辑式编程]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=341</guid>
		<description><![CDATA[<b>声明范式</b>——目标决定行动（<em>声明式编程简谈</em>）<br/> • 命令式编程是行动导向的，因而算法是显性而目标是隐性的；声明式编程是目标驱动的，因而目标是显性而算法是隐性的<br/> • 声明式编程重目标、轻过程，专注问题的分析和表达而不致陷入算法的迷宫，其代码也更加简洁清晰、易于修改和维护<br/> • 归根结底，编程是寻求一种机制，将指定的输入转化为指定的输出 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第二课 重要范式(2)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>
 
<div lang="zh-CN" class="article" title="声明范式"><div class="titlepage"><div><div><h1 class="title"><a name="id671200"></a>2.2 声明范式——目标决定行动</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>声明式编程简谈</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>给我一个支点，我能挪动地球</p></div><div class="attribution"><span>—<span class="attribution">Archimedes</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    命令式编程是行动导向的，因而算法是显性而目标是隐性的；声明式编程是目标驱动的，因而目标是显性而算法是隐性的
                </p></li><li class="listitem"><p>
                    声明式编程重目标、轻过程，专注问题的分析和表达而不致陷入算法的迷宫，其代码也更加简洁清晰、易于修改和维护
                </p></li><li class="listitem"><p>
                    归根结底，编程是寻求一种机制，将指定的输入转化为指定的输出
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>什么是声明式编程？它与命令式编程有何区别？</p></li><li class="listitem"><p>什么是函数式和逻辑式？</p></li><li class="listitem"><p>变量在命令式编程和声明式编程中有何不同的涵义？</p></li><li class="listitem"><p>声明式语言有何优点？为什么没有命令式语言流行？</p></li><li class="listitem"><p>命令式语言与声明式语言有无相通之处？</p></li><li class="listitem"><p>编程的本质是什么？命令式、函数式和逻辑式分别采用了怎样的编程机制？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            冒号迅速转移了话题：“下面我们来谈谈与命令式编程相对的<span class="term">声明式编程</span>（declarative programming）。顾名思义，声明式编程由若干<span class="term">规范</span>（specification）的声明组成的，即一系列陈述句：‘已知这，求解那’，强调‘做什么’而非‘怎么做’。声明式编程是人脑思维方式的抽象，即利用数理逻辑或既定规范对已知条件进行推理或运算。”
        </p><p>
            问号询问：“声明式产生的背景是什么呢？”
        </p><p>
            “声明式编程发轫于人工智能的研究，主要包括<span class="term">函数式编程</span>（functional programming，简称FP）和<span class="term">逻辑式编程</span>（logic programming，简称LP）。其中，函数式编程将计算描述为数学函数的求值，而逻辑式编程通过提供一系列事实和规则来推导或论证结论。其实支持它们的语言出现得并不比命令式的晚多少——最早的函数式语言Lisp（<span class="strong"><strong>LIS</strong></span>t <span class="strong"><strong>P</strong></span>rocessor）已有半个世纪的历史，最早之一的逻辑式语言Prolog（<span class="strong"><strong>PRO</strong></span>gramming in <span class="strong"><strong>LOG</strong></span>ic）也与C同龄。只是由于大多数更多地用于学术研究而非商业应用，颇有些‘养在深闺人未识’啊。”冒号有些惋惜，“起源的不同决定了这两大类范式代表着迥然不同的编程理念和风格：<span class="strong"><strong>命令式编程是行动导向（Action-Oriented）的</strong></span>，因而<span class="strong"><strong>算法是显性而目标是隐性的</strong></span>；<span class="strong"><strong>声明式编程是目标驱动（Goal-Driven）的</strong></span>，因而<span class="strong"><strong>目标是显性而算法是隐性的</strong></span>。为便于说明，我们分别用三种代表性的语言来实现阶乘（factorial）运算。”
        </p><p>
            冒号在黑板上打出投影——
        </p><div class="example"><a name="id671308"></a><p class="title"><b>阶乘的三种实现：</b></p><div class="example-contents"><p><span class="emphasis"><em>C（命令式）</em></span>——</p><pre class="programlisting">
    int factorial(int n) 
    {
        int f = 1;
        for (; n &gt; 0; --n) f *= n;
        return f;
    }</pre><p><span class="emphasis"><em>Lisp（函数式）</em></span>——</p><pre class="programlisting">
    (defun factorial(n)
      (if (= n 0)
        1                               //  若n等于0，则n!等于1
        (* n (factorial(- n 1)))))      //  否则n!等于n* (n-1)</pre><p><span class="emphasis"><em>Prolog（逻辑式）</em></span>——</p><pre class="programlisting">
    // 0! 等于1
    factorial(0,1).
    // 若M等于N-1且 M!等于Fm且F等于N*Fm，则N! 等于F
    factorial(N,F) :-   M is N-1, factorial(M,Fm), F is N * Fm. </pre></div></div><br class="example-break"><p>
            冒号提问：“撇开语法细节，大家说说以上三段代码区别在哪里？”
        </p><p>
            句号沉思片刻，答道：“C明确给出了阶乘的迭代算法，而Lisp仅描述了阶乘的递归定义，Prolog则陈述了两个关于阶乘的断言。”
        </p><p>
            冒号很满意：“一针见血！第二个问题：你们更习惯哪一种思维方式？”
        </p><p>
            逗号不加思索：“当然是第一种！”
        </p><p>
            冒号微笑着说：“这证明你至少是受过一定训练的程序员。大家回想一下，当你们初学编程时，是否习惯这种思维方式？”
        </p><p>
            叹号沉吟道：“好像不太习惯<code class="code">i = i + 1</code>之类的语句。”
        </p><p> “对！”冒号的一嗓子吓了众人一跳，“我们最早接触的变量是代数方程中的x、y、z等，本质上是<span class="emphasis"><em>抽象化的符号</em></span>，变量值是该符号在给定约束条件下的允许值。而命令式编程中的变量本质上是<span class="emphasis"><em>抽象化的内存</em></span>，变量值是该内存的储存内容。通俗地说，前者好比<span class="emphasis"><em>姓名</em></span>，所指之人是固定的；后者好比<span class="emphasis"><em>住址</em></span>，所住之人是变化的。此外，等号在代数中是一种约束，而在许多命令式语言中则表示赋值。因此<code class="code">i = i + 1</code>可以在命令式编程中出现，但绝不可能在数学推理中出现<a class="link" href="#note1"><sup>[1]</sup></a>——除非在反证法中。”
        </p><p>
            叹号又道：“现在回头再看代数，反倒有些不习惯了。”
        </p><p>
            “这就是思维的<span class="emphasis"><em>定势效应</em></span>。”冒号感慨道，“声明式编程让我们重回数学思维：函数式编程类似代数中的表达式变换和计算，逻辑式编程则类似数理逻辑推理。其中的变量也如数学中的一样，是抽象符号而非内存地址，因此没有赋值运算，不会产生变量被改写的<span class="emphasis"><em>副作用</em></span>（side-effect），也不存在内存分配和释放的问题。这既简化了代码，也减少了调试——不妨想一想，有多少bug是由于某个变量被意外改写或内存管理不慎而造成的？”
        </p><p>
            问号问道：“声明式语言与命令式语言看来是两个世界的产物，它们是否有相通之处？”
        </p><p>
            冒号答道：“首先，所有高级语言都建立于低级语言之上，最终转化为机器语言，声明式语言也不例外。其次，声明式语言与命令式语言并非泾渭分明，而是互相交叉渗透的。一些‘非纯粹’ 的声明式语言也提供变量赋值和流程控制，而一些命令式语言也在逐渐发展，通过利用其他程序或增加新的语言特征来实现声明式编程。总的说来，在命令式语言中融入声明式的元素应当是一种趋势。尤其是函数式，它的一些特征已经在许多命令式语言中得到了支持。比较而言，声明式编程重目标、轻过程，专注问题的分析和表达而不致陷入算法的迷宫，其代码也更加简洁清晰、易于修改和维护。从这种意义上说，声明式语言天然地就比命令式语言更高级。上节课提到的前三代计算机语言基本上都是命令式的，而后两代基本上都是声明式的，由此可见一斑。”
        </p><p>
            句号一拍脑袋：“命令式是模拟电脑的，声明式是模拟人脑的，人脑当然比电脑高级啦。”
        </p><p>
            冒号另有佐证：“早在命令式语言引入函数从而进化为过程式语言时，就已经开始向声明式过渡了。何以见得？比方说调用一个函数的语句：<code class="code">doWhat()</code>，这不正是在声明‘what to do’吗？至于‘how to do’，即函数的具体实现细节，则不劳调用者费心。这种声明式的风格，提高了语言的抽象能力和开发效率，促成了语言的升级。”
        </p><p>
            逗号仍然有些疑惑：“既然声明式编程有这么多好处，为什么命令式语言不仅占大多数，而且流行程度也不减呢？”
        </p><p>
             冒号回答：“编程语言的流行程度与其擅长的领域关系密切。声明式语言——尤其是函数式语言和逻辑式语言——擅长基于数理逻辑的应用，如人工智能、符号处理、数据库、编译器等，对基于业务逻辑的、尤其是交互式或事件驱动型的应用就不那么得心应手了。而大多数软件是面向用户的，交互性强、多为事件驱动、业务逻辑千差万别，显然命令式语言在此更有用武之地。”
        </p><p>
            大家频频颔首。
        </p><p>
            “值得指出的是，声明式编程并不仅仅局限于函数式和逻辑式。”冒号旋即补充道，“比方说，C#中的attribute、Java中的annotation和XDoclet库等采用的也是具有声明式特征的<span class="term">属性导向式编程</span>（Attribute-Oriented Programming，简称@OP）。再比如，Prograph<a class="link" href="#note2"><sup>[2]</sup></a> 、SISAL<a class="link" href="#note3"><sup>[3]</sup></a>等<span class="term">数据流语言</span>（dataflow language）采用的<span class="term">数据流式编程</span>（Dataflow Programming）与函数式编程有不少共同点，同样属于声明式的范畴。还有一些语言如Oz、CHIP等支持与逻辑式编程相交的<span class="term">约束式编程</span>（Constraint Programming）<a class="link" href="#note4"><sup>[4]</sup></a>。此外，大家熟悉的数据库语言SQL，<span class="term">样式语言</span>XSLT、CSS，<span class="term">标记语言</span>HTML、XML、SVG，<span class="term">规范语言</span>IDL（Interface Description Language）等等都是声明式的。算上它们，声明式语言所占的比例也是非常可观的。此前之所以没有提及，一方面，不少声明式语言采用的范式并没有专门的名称；另一方面，这些语言大多是<span class="term">领域特定语言</span>，并且不少并非<span class="term">图灵完备的</span>，有的连运算都没有。毕竟，目前我们的重点还是放在<span class="term">通用编程语言</span>上。”
        </p><p>
            问号突然想到了什么，指着投影问：“这里用Lisp实现阶乘的方法不也可以用在C上吗？”
        </p><p>
            冒号点点头，写下一段代码——
        </p><pre class="programlisting">
int factorial(int n) 
{
    return n == 0 ? 1 : n * factorial(n - 1);
}</pre><p>
            “这是C的递归实现。”冒号娓娓道来，“除了细微的语法差别外，二者的确很相似，这说明用命令式语言也可以讲出声明式的味道。实际上，命令式语言提倡迭代而不鼓励递归，早期的Fortran 甚至都不支持递归。一则迭代比递归更符合命令式的思维模式，因为前者贴近机器语言而后者贴近数学语言；二则除<span class="term">尾递归</span>（tail recursion）<a class="link" href="#note5"><sup>[5]</sup></a>外，一般递归比迭代的开销（overhead）大。相反，声明式语言提倡递归而不支持迭代<a class="link" href="#note6"><sup>[6]</sup></a>。就语法而言，它不允许迭代中的循环变量；就视角而言，迭代着眼微观过程而递归着眼宏观规律。”
        </p><p>
            叹号轻叹：“原来貌似普通的迭代和递归有那么多道道！”
        </p><p>
            “任何语言都难脱命令式或声明式的窠臼。事实上，凡是非命令式的编程都可归为声明式编程。因此，命令式、函数式和逻辑式是最核心的三种范式。为清楚起见，我们用一幅图来表示它们之间的关系。”说罢，冒号换了一个幻灯片——
        </p><div class="figure"><a name="id671551"></a><p class="title"><b>图2-4. 编程范式的简单分类</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure2-4.jpg" alt="编程范式的简单分类"></div></div></div><br class="figure-break"><p>
            末了，冒号归纳道：“归根结底，<span class="strong"><strong>编程是寻求一种机制，将指定的输入转化为指定的输出</strong></span>。三种范式对此提供了截然不同的解决方案：命令式把程序看作一个<span class="emphasis"><em>自动机</em></span>，输入是<span class="emphasis"><em>初始状态</em></span>，输出是<span class="emphasis"><em>最终状态</em></span>，编程就是设计一系列指令，通过自动机执行以完成状态转变；函数式把程序看作一个<span class="emphasis"><em>数学函数</em></span>，输入是<span class="emphasis"><em>自变量</em></span>，输出是<span class="emphasis"><em>因变量</em></span>，编程就是设计一系列函数，通过表达式变换以完成计算；逻辑式把程序看作一个<span class="emphasis"><em>逻辑证明</em></span>，输入是<span class="emphasis"><em>题设</em></span>，输出是<span class="emphasis"><em>结论</em></span>，编程就是设计一系列命题，通过逻辑推理以完成证明。绘成表格如下（如表2-1所示）——” 
        </p><div class="table"><a name="id671610"></a><p class="title"><b>表2-1. 三种核心编程范式的比较</b></p><div class="table-contents"><table summary="三种核心编程范式的比较" border="1"><colgroup><col><col><col><col><col><col></colgroup><thead><tr><th>范式</th><th>程序</th><th>输入</th><th>输出</th><th>程序设计</th><th>程序运行</th></tr></thead><tbody><tr><td>命令式</td><td>自动机</td><td>初始状态</td><td>最终状态</td><td>设计指令</td><td>命令执行</td></tr><tr><td>函数式</td><td>数学函数</td><td>自变量</td><td>因变量</td><td>设计函数</td><td>表达式变换</td></tr><tr><td>逻辑式</td><td>逻辑证明</td><td>题设</td><td>结论</td><td>设计命题</td><td>逻辑推理</td></tr></tbody></table></div></div><br class="table-break"><p>
            冒号见众人微显难色，宽慰道：“这部分理论性稍微强了些，对函数式和逻辑式也仅作了描述性说明，并未深入展开。不解之处，大可不必介怀，撒下的种子总有一天会萌动。先休息片刻，不要走开，广告之后更精彩。”
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    数学中应该用类似<span class="mathphrase">i<sub>n+1</sub> = i<sub>n</sub> + 1</span>的表示法。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    Prograph即<span class="strong"><strong>Pro</strong></span>gramming in <span class="strong"><strong>Graph</strong></span>ics，是一种可视化的、对象导向（OO）的数据流语言，它用图表（diagram）来取代文本编码。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    SISAL即<span class="strong"><strong>S</strong></span>treams and <span class="strong"><strong>I</strong></span>teration in a <span class="strong"><strong>S</strong></span>ingle <span class="strong"><strong>A</strong></span>ssignment <span class="strong"><strong>L</strong></span>anguage，是一种函数式数据流语言，擅长并行科学计算。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    约束式编程通过数据之间的约束关系来进行运算。它可以借助逻辑推理机制或其他数学和算法技巧来实现。有一种观点认为：用函数式、逻辑式或约束式三者之一的语言来编写程序，即是声明式编程。这种以编程语言反过来定义编程范式的说法值得商榷，但从中可看出约束式编程的代表性。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    尾递归是一种特殊的递归，其递归调用出现在函数的最后一步运算（尾部）。这类递归很容易通过手工或编译器转化为迭代形式，以优化性能。
                </p></li><li class="listitem"><p><a name="note6"></a>
                    有些声明式语言（例如Lisp的一个变种Scheme）虽然支持迭代，但一般是用尾递归来实现的。从本质上说，这只是一种语法上的甜头（syntactic sugar）。它与普通迭代的区别在于：前者的循环变量是重新绑定（rebind）的，而后者的循环变量是重复赋值（reassign）的。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    	命令式编程通过一系列改变程序状态的指令来完成计算，声明式编程只描述程序应该完成的任务。命令式编程模拟电脑运算，是行动导向的，关键在于定义解法，即“怎么做”，因而算法是显性而目标是隐性的；声明式编程模拟人脑思维，是目标驱动的，关键在于描述问题，即“做什么”，因而目标是显性而算法是隐性的。
                </p></li><li class="listitem"><p>
                    函数式编程通过数学函数的表达式变换和计算来求值。
                </p></li><li class="listitem"><p>
                    逻辑式编程通过一系列事实和规则，利用数理逻辑来推导或论证结论。
                </p></li><li class="listitem"><p>
                    命令式编程中的变量代表抽象化的内存，所存内容可能改变。声明式编程中的变量代表抽象化的符号，所指对象一般不会改变。
                </p></li><li class="listitem"><p>
                    声明式编程专注问题的分析和表达而不是算法实现，不用指明执行顺序，一般没有或极少副作用，也不存在内存管理问题。这些都大大降低了编程的复杂度，同时也非常适合于并发式计算。
                </p></li><li class="listitem"><p>
                    编程语言的流行程度与其擅长的领域密切相关。函数式语言和逻辑式语言擅长基于数理逻辑的应用，命令式语言擅长基于业务逻辑的、尤其是交互式或事件驱动型的应用。
                </p></li><li class="listitem"><p>
                    声明式语言与命令式语言之间并无绝对的界限，它们均建立于低级语言之上，并且互相渗透融合。
                </p></li><li class="listitem"><p>
                    在命令式语言中引入函数或过程，是一种向声明式风格的趋近。
                </p></li><li class="listitem"><p>
                    编程是寻求一种机制，将指定的输入转化为指定的输出。
                </p></li><li class="listitem"><p class="simpara"> 三种核心编程范式采用如下不同的机制——</p><p class="simpara">命令式：自动机机制，通过设计指令完成从初始态到最终态的转变。</p><p class="simpara">函数式：数学变换机制，通过设计函数完成从自变量到因变量的计算。</p><p class="simpara">逻辑式：逻辑证明机制，通过逻辑推理完成从题设到结论的证明。</p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Elena Bolshakova．PROGRAMMING PARADIGMS IN COMPUTER SCIENCE EDUCATION．International Journal &#8220;Information Theories &amp; Applications&#8221;，2005，Vol.12：285-290
                </p></li><li class="listitem"><p>
                    Ravi Sethi．Programming Languages: Concepts &amp; Constructs(英文版第2版)．北京：机械工业出版社，2002．301-340,423-470
                </p></li><li class="listitem"><p>
                    Wikipedia．Declarative programming．<a class="link" href="http://en.wikipedia.org/wiki/Declarative_programming" target="_top">http://en.wikipedia.org/wiki/Declarative_programming</a>
                </p></li></ol></div></div></div>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F04%2Fcolon-class-2_2%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A72.2%EF%BC%9A%E5%A3%B0%E6%98%8E%E8%8C%83%E5%BC%8F" id="wpa2a_6">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年09月7日 -- <a href="http://blog.zhenghui.org/2009/09/07/colon-class-2_4/" title="冒号课堂§2.4：并发范式">冒号课堂§2.4：并发范式</a> (2)</li><li>2009年09月15日 -- <a href="http://blog.zhenghui.org/2009/09/15/colon-class-4_2/" title="冒号课堂§4.2：逻辑范式">冒号课堂§4.2：逻辑范式</a> (0)</li><li>2009年09月13日 -- <a href="http://blog.zhenghui.org/2009/09/13/colon-class-4_1/" title="冒号课堂§4.1：函数范式">冒号课堂§4.1：函数范式</a> (0)</li><li>2009年09月3日 -- <a href="http://blog.zhenghui.org/2009/09/03/colon-class-2_1/" title="冒号课堂§2.1：命令范式">冒号课堂§2.1：命令范式</a> (2)</li><li>2009年09月21日 -- <a href="http://blog.zhenghui.org/2009/09/21/colon-class-5_1/" title="冒号课堂§5.1：教学计划">冒号课堂§5.1：教学计划</a> (1)</li><li>2009年09月19日 -- <a href="http://blog.zhenghui.org/2009/09/19/colon-class-4_4/" title="冒号课堂§4.4：情景范式">冒号课堂§4.4：情景范式</a> (0)</li><li>2009年09月17日 -- <a href="http://blog.zhenghui.org/2009/09/17/colon-class-4_3/" title="冒号课堂§4.3：汇总范式">冒号课堂§4.3：汇总范式</a> (5)</li><li>2009年09月11日 -- <a href="http://blog.zhenghui.org/2009/09/11/colon-class-3_4/" title="冒号课堂§3.4：事件驱动">冒号课堂§3.4：事件驱动</a> (2)</li><li>2009年09月10日 -- <a href="http://blog.zhenghui.org/2009/09/10/colon-class-3_3/" title="冒号课堂§3.3：切面范式">冒号课堂§3.3：切面范式</a> (4)</li><li>2009年09月9日 -- <a href="http://blog.zhenghui.org/2009/09/09/colon-class-3_2/" title="冒号课堂§3.2：超级范式">冒号课堂§3.2：超级范式</a> (2)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/04/colon-class-2_2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

