<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2chinesetwfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>冒号空间</title>
	
	<link>http://blog.zhenghui.org</link>
	<description>自然、人类、机器</description>
	<lastBuildDate>Sat, 03 Mar 2012 13:01:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/huizheng" /><feedburner:info uri="huizheng" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>huizheng</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/huizheng" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><feedburner:feedFlare href="http://xianguo.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://xgres.com/static/images/sub/sub_XianGuo_01.gif">订阅到鲜果</feedburner:feedFlare><feedburner:feedFlare href="http://www.zhuaxia.com/add_channel.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://img.feedsky.com/images/icon_subshot02_zhuaxia.gif">订阅到抓虾</feedburner:feedFlare><feedburner:feedFlare href="http://inezha.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://img.feedsky.com/images/icon_subshot02_nazha.gif">订阅到哪吒</feedburner:feedFlare><feedburner:feedFlare href="http://mail.qq.com/cgi-bin/feed?u=http%3A%2F%2Ffeeds.feedburner.com%2Fhuizheng" src="http://img.feedsky.com/images/icon_subshot02_qq.gif">订阅到 QQ邮箱</feedburner:feedFlare><item>
		<title>一条密码走天下——1pass4all简介</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/0tSmdlDAxtQ/</link>
		<comments>http://blog.zhenghui.org/2012/02/26/one-pass-for-all-intro_cn/#comments</comments>
		<pubDate>Sun, 26 Feb 2012 10:00:16 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[开发项目]]></category>
		<category><![CDATA[1pass4all]]></category>
		<category><![CDATA[bookmarklet]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[信息安全]]></category>
		<category><![CDATA[密码]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=975</guid>
		<description><![CDATA[<b>一条密码走天下</b>——1pass4all简介<br/> 1pass4all是一个小书签（bookmarklet），可方便而安全地生成密码 [...]]]></description>
			<content:encoded><![CDATA[   <p>&nbsp;</p>

<h1 class="title">一条密码走天下</h1>

<p>在这个互联网时代，网络账户大都是以用户名与密码组合的形式来鉴别的。由于
用户名一般是公开的，密码安全就显得至关重要了。设计一个好密码绝非易事，既要
足够强壮又要易记难猜。强密码的一个典型特征是熵（entropy）值高，通俗地说是
随机性强、规律性弱。那些用有规律或广为人知的数字序列或字串组成的密码固然不满足此条件，
一个长度不够、字符取值范围不广的密码在暴力破解面前同样弱不禁风。此外，有些密码
看似强壮，但如果与用户的个人信息紧密相联，也容易被知情人猜出或缩小检测范围。
事实上，绝大多数用户的密码要么不够强壮、要么容易猜测、要么（也是更多的）兼而有之。
去年底爆出的<a href="http://baike.baidu.com/view/7167245.htm">“密码门”事件</a>再次佐证了这一点。
其实情有可原，强壮而难猜的密码通常也难记。有些用户挖空心思地想出强密码，为不增加
设计负担和记忆负担，往往又忍不住在多处网站重复使用，就此埋下安全隐患：
一旦由于某种原因（如此次“密码门事件”）密码被暴露，将殃及多个账户。故而
对密码的另一要求是，不同账户的密码应尽可能不相同、甚至不相关。只不过这样一来，密码
维护的难度更是倍增。正因如此，一些密码管理软件应需而生，它们的核心思想基本上都
很相似， 即以加密的形式为用户保存所有的账户和密码信息，用户仅需记住一条密码
（以下称为“主密码”），便可随时查询内藏的密码库。</p>

<p>本人使用密码管理工具颇有时日，包括<a href="http://keepass.info/">KeePass</a>、
<a href="https://agilebits.com/onepassword">1Password</a>和<a href="https://lastpass.com/">LastPass</a>，
始终觉得它们各有缺憾。KeePass在功能和界面上不如1Password，但后者价格不菲，且主要
在Mac平台（后虽支持Windows，但界面上远逊，且不支持Linux）。LastPass相比前二者的
优势是支持云同步，但将密码库置于远程服务器也非全无顾虑，比如TechCrunch曾刊载
过<a href="http://techcrunch.com/2011/05/05/password-manager-last-pass-possibly-hacked">LastPass被破解的传闻</a>。另外，虽然这些管理器都能帮用户生成
随机密码，但既难记又难敲。您可能会问：为什么要记呢，这不正是它们的意义所在吗？
话虽如此，然而不可否认，每登陆账户都要额外启动一个程序委实不便。不错，用户可以选择
让浏览器记住密码，或者利用这些管理器在浏览器上的插件来自动填写，但这种便利是以
安全为代价的。我以前的做法是，自定义一些隐秘的变量，然后通过某些规则或函数
进行变换而产生密码。作为一个程序员，我甚至写了一些脚本让此过程自动化。可惜该法
有个弊端：一旦离开自己的电脑，便可能因缺乏脚本或脚本运行环境而失效。
某日细思此事，想到JavaScript当是比一般的脚本更佳的选择，盖因凡有电脑处便有浏览器，
凡有浏览器处便能运行JavaScript。只要把JavaScript代码上传到某个网络空间，即可
予取予求。由于运行的过程与结果既不会在网络中传输，也不会在本地保存，有效保障了
密码的安全性。于是，很自然地开始把焦点集中在
<a href="http://en.wikipedia.org/wiki/Bookmarklet">小书签（bookmarklet）</a>（或译为“书签工具”）上。所谓小书签，
无非是放在浏览器上的一种书签或网页上的一个超链接，与普通书签或超链接不同的是，
其网址（URL）不是某个网页地址，而是一段JavaScript代码。一旦被点击，
不会如普通书签那样进入到新网页，而会在当前网页中执行其内含的JavaScript程序。
此法方便、易安装、跨浏览器、且<a href="http://en.wikipedia.org/wiki/Unobtrusive_JavaScript">无干扰（unobtrusive）</a>
（注：不少人译为“无侵入”，但对应后者更贴切的英文当是unintrusive或non-intrusive）。</p>

<p>为避免重复劳动（即所谓的“重新发明轮子”），先在网上查寻是否已有现成产品可用，
结果发现<a href="http://supergenpass.com">SuperGenPass</a>较好地满足了自己的要求。简单地说，SuperGenPass能将
一个主密码按不同的网站域名转换成不同的密码。不出意料，它采用的是<a href="http://en.wikipedia.org/wiki/MD5">MD5</a>
算法。此算法可迅速将主密码与网络域名的组合<strong>单向（one-way）</strong>映射（计算机术语
是<a href="http://en.wikipedia.org/wiki/Cryptographic_hash_function">哈希（hash）</a>）为一个对输入敏感的、近似随机的字串。“单向”意味着
从输出难以反推输入，从而一条密码泄露不致暴露主密码；“敏感”意味着差异微小的输入
会导致差异极大的输出，从而一条密码泄露不会对其他密码造成危险；“近似随机”则意味
着输出字串可视为强密码。主意是不错，但于我并不新鲜。何况已知有人用<a href="http://en.wikipedia.org/wiki/SHA-1">SHA-1</a>
开发了<a href="http://angel.net/~nic/passwd.current.html">密码生成器</a>，年前还见有国人用
MD5实现了<a href="http://code.google.com/p/md5-password-creator/wiki/AboutApp">类似功能</a>。
SuperGenPass真正令我心动的地方是，它能就地生成密码——在用户于密码项中填入
主密码后，点击小标签便能用新产生的密码直接替换主密码。相比之下，原先在命令行中
运行脚本再将结果拷贝或重敲一遍的做法，既不安全也不方便。</p>

<p>事情变得简单多了。有了密码小标签，只需牢记一条密钥在心，便能自信地在不同的
网站以不同的强密码登陆，哪怕与这些密码素不谋面。不过传统的密码管理器
仍有用武之地。比如可以记录主密码的提示，以防万一哪天“临时性”失忆；可以
在可信赖的电脑上为非关键性的账户提供自动填单的功能；可以保存其他类型的
敏感信息或文件，等等。</p>

<p>在准备采用SuperGenPass之前，对其功能和源代码进行了一番考察，很遗憾地发现有些
缺陷令人难以释怀。首先，MD5<a href="http://www.kb.cert.org/vuls/id/836068">在理论上不够安全</a>。作为一个文件校验器
问题还不大，作为密码生成器则有些勉为其难了。更糟的是，SuperGenPass默认的
<a href="http://en.wikipedia.org/wiki/Salt_(cryptography)">密码盐（salt）</a>为空，且除非修改源代码便无法定制。这使其极易受到
<a href="http://en.wikipedia.org/wiki/Dictionary_attack">字典攻击</a>。另一问题是，SuperGenPass基于<a href="http://en.wikipedia.org/wiki/Base64">Base64</a>，
这意味着生成的密码主要有字母与数字组成（此外还有<code>+</code>与<code>/</code>），许多如<code>?</code>、 <code>!</code>、<code>$</code>
等特殊字符均被排除在外。这便是此前所说字符取值狭窄的问题。以一个8位密码为例，
64种字符的组合数不到94种字符（可打印字符共94个）的组合数的20分之一，相差一个数量级。
若是12位密码，比率更是低于百分之一，整整相差两个数量级。再者，SuperGenPass完全
不考虑用户名也是一大不足。本人在一些网站（如gmail、dropbox）上有多个账户，非常不
希望它们共享密码。诚然，给主密码加上用户名前缀或后缀便能衍生出不同实际密码。但
从名义上看，单个主密码的说法便已然不实；从理论上看，将密钥与数据简单地用字符串
拼接的做法不如<a href="http://en.wikipedia.org/wiki/HMAC">HMAC</a>算法更可靠。此外，在变更密码页中，SuperGenPass似乎
有点问题。不知为何，有时它自动将旧密码、新密码和确认新密码三项均作变换，有时却
只改变其中的一个或两个。权衡再三，最终决定自己从头开发一个密码小书签，这便是
<a href="http://en.zhenghui.org/1pass4all/">1pass4all</a> (意即one pass for all，一码永逸)
的由来。</p>

<p>1pass4all希望在SuperGenPass之上有如下改进：</p>

<ul>
<li><p>安全性：</p>

<p>鉴于<a href="http://en.wikipedia.org/wiki/SHA-2">SHA-2</a>比MD5和SHA-1更安全，HMAC比单纯的字串拼接更合理，
1pass4all基于HMAC-SHA224算法。产生的密码默认可包含任意可打印字符，当然根据
需求也可仅包含Base64字符、数字加字母或纯数字。密码的最大长度也从24位提高到26位。
为防止字典攻击，支持对密码盐的定制。同时为加大破解难度，支持定制hash的迭代次数
（为了<a href="http://en.wikipedia.org/wiki/Key_stretching">key stretching</a>）。进一步地，1pass4all可考虑
用户名，从而只要账户不同，即便主密码和网站均相同，相应的密码也会不同。</p></li>
<li><p>可用性：</p>

<p>只要启用“自动登陆”选项，1pass4all在登陆页面生成密码后会自动提交，无需弹出
确认窗口。如果一切顺利，该小书签表现得就像一个登陆按钮，只是暗中施了点“魔法”而已。
正常情况下，点击小书签仅对当前有焦点（如无，则为第一个）的密码项起作用，
这样不会让用户犯晕，也能避免不必要的转换（比如以前手工键入的旧密码）。</p></li>
<li><p>功能：</p>

<p>针对高级用户，1pass4all提供了专门的密码语法，可以在不弹出窗口的条件下指定一些
参数，如用户名、域名、密码长度、密码盐、迭代次数、密码字符集以及是否启用用户名
的自动检查、是否自动登陆等。</p></li>
</ul>


<p>和SuperGenPass一样，1pass4all也提供了移动版。有时用户不能或不想安装小书签，比如
在手机上、在他人电脑上、或在可能会盗取主密码的非信任网站上。另外，要想在非浏览器
应用上使用1pass4all，目前也非移动版不可。</p>

<p>对高级用户（尤其是程序员），则推荐使用自制（DIY）版，不仅可更随意地定制参数、
设置JavaScript代码的网址、改变界面的风格（以弥补原有界面的简陋），必要时更可修改算法。</p>

<p>若此文引发了您的些许兴趣，不妨到<a href="http://en.zhenghui.org/1pass4all/archive/install_zh.html">安装页面</a>
一试。更多说明请参见<a href="https://github.com/hzheng/1pass4all/blob/master/README.rst">README文件</a>。如有任何问题或建议，欢迎留言。
需要提醒的是，尽管1pass4all能极大地强化弱密码，但由于其算法是公开的，因此主密码
仍应足够强壮。如是，纵然“一条密码走天下”，亦可高枕无忧矣。</p>

<p><strong>附：</strong>本文的<a href="http://en.zhenghui.org/2012/02/21/one-pass-for-all-intro/">英文版</a>以及
<a href="http://en.zhenghui.org/2012/02/26/one-pass-for-all-intro_CN/">另一中文版</a>（对twitter或facebook用户推荐）</p>



<h2>参考文献</h2>

<p>[1] <a href="http://supergenpass.com">SuperGenPass</a></p>

<p>[2] <a href="http://en.wikipedia.org/wiki/Bookmarklet">Wikipedia: bookmarklet</a></p>

<p>[3] <a href="http://en.wikipedia.org/wiki/Unobtrusive_JavaScript">Wikipedia: Unobtrusive JavaScript</a></p>

<p>[4] <a href="http://en.wikipedia.org/wiki/MD5">Wikipedia: MD5</a></p>

<p>[5] <a href="http://en.wikipedia.org/wiki/Cryptographic_hash_function">Wikipedia: Cryptographic_hash_function</a></p>

<p>[6] <a href="http://en.wikipedia.org/wiki/Salt_(cryptography)">Wikipedia: Salt_(cryptography)</a></p>

<p>[7] <a href="http://en.wikipedia.org/wiki/SHA-2">Wikipedia: SHA-2</a></p>

<p>[8] <a href="http://en.wikipedia.org/wiki/HMAC">Wikipedia: HMAC</a></p><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2012%2F02%2F26%2Fone-pass-for-all-intro_cn%2F&amp;title=%E4%B8%80%E6%9D%A1%E5%AF%86%E7%A0%81%E8%B5%B0%E5%A4%A9%E4%B8%8B%E2%80%94%E2%80%941pass4all%E7%AE%80%E4%BB%8B" id="wpa2a_2">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年10月8日 -- <a href="http://blog.zhenghui.org/2009/10/08/colon-class-6_3/" title="冒号课堂§6.3：前台语言">冒号课堂§6.3：前台语言</a> (1)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=0tSmdlDAxtQ:qOR-Ts17M4U:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/0tSmdlDAxtQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2012/02/26/one-pass-for-all-intro_cn/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2012/02/26/one-pass-for-all-intro_cn/</feedburner:origLink></item>
		<item>
		<title>VIM之魅（下）</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/DJxPZvP_CfY/</link>
		<comments>http://blog.zhenghui.org/2011/12/13/charm-of-vim-2/#comments</comments>
		<pubDate>Tue, 13 Dec 2011 08:18:54 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[开发工具]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[Vi]]></category>
		<category><![CDATA[Vim]]></category>
		<category><![CDATA[键盘]]></category>
		<category><![CDATA[鼠标]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=944</guid>
		<description><![CDATA[<b>VIM之魅（下）<br/> • Vim的方法<br/> • 结语 [...]]]></description>
			<content:encoded><![CDATA[<p>&nbsp;</p>

<div id="vim" class="document">
<h1 class="title">VIM之魅（下）</h1>
<div id="id1" class="section">
<h1>Vim的方法</h1>
<p>如果说Vim的理念是“减”字诀，那么实现这些理念的方法便是“增”字诀： 增加模式种类、增加移动方式、增加操作对象、增加操作方式、增加定制方式。</p>
<div id="id2" class="section">
<h2>1. 增加模式种类</h2>
<p>前文提到引入普通模式的重要意义，但Vim并未就此止步，还提供了另外四种基本模式： 选择模式（select mode）、可视化模式、命令行模式和ex模式。</p>
<p>选择模式与可视化模式的目的均是选择文本以备后续操作。前者类似Windows下的选择模式， 在方便程度与威力上远不及后者，故略去不谈。</p>
<p>在普通模式下，按下 <tt class="docutils literal">v</tt> 、 <tt class="docutils literal">V</tt> 、 <tt class="docutils literal"><span class="pre">Ctrl-V</span></tt> 将分别开启以字符、行或块为单位 的可视化模式，此后用户可以通过多次使用Vim的移动命令来选定所需文本。 例如，在普通模式下输入 <tt class="docutils literal">v/the</tt> 并回车后，从光标初始处到此后最近的 <tt class="docutils literal">the</tt> 之前都将被高亮。如发现并非所求，继续按 <tt class="docutils literal">n</tt> 将使选择区域扩大到下一处 的 <tt class="docutils literal">the</tt> 之前，以后还可多次用 <tt class="docutils literal">j</tt> 或 <tt class="docutils literal">k</tt> 来延伸或压缩所选区域的行，或者 反复用 <tt class="docutils literal">h</tt> 或 <tt class="docutils literal">l</tt> 进行左右微调，用 <tt class="docutils literal">w</tt> 或 <tt class="docutils literal">b</tt> 来增减被选单词， 用 <tt class="docutils literal">f</tt> 或 <tt class="docutils literal">t</tt> 确定字符，等等。 相比直接通过正常模式操作，可视化模式允许用户在对文本片段进行编辑 之前加以检视、调整和确认，因而更加直观、准确和安全，并且让所选文本成为 批处理的整体对象，增加了编辑效率，同时减少了操作的碎片化。 与用鼠标相比，若是操作短文本或规律明显的文本，速度多不及普通模式下的操作； 若是操作文本较长且不太规律的文本，则往往不及可视化模式下的操作。 比如，要选中从当前行到倒数第二行的文本，若中间相距较远，用鼠标定然慢而不便。 性急如我者，一不留神就可能把鼠标拖出桌面之外。 使用可视化模式则异常轻松： <tt class="docutils literal">VGk</tt> ，完毕。</p>
<p>可视化模式不仅支持行操作，还支持列操作，这也是比普通模式更便利之处。 比如要交换文件中头两列的字符， 可键入 <tt class="docutils literal"><span class="pre">gg&lt;Ctrl-V&gt;Gdp</span></tt> 。</p>
<p>命令行模式与ex模式均可执行ex命令，只是后者在执行后不像前者那样返回正常模式。 普通命令虽然丰富，但远远谈不上完备。比如，无法同时编辑多处文本片段， 无法在不离开当前编辑窗口的条件下操作其他文件，无法获取其他文件中的内容， 无法读取环境变量，无法执行外部命令，等等。 有了ex命令，Vim的潜能开始无限释放，一方面充分地利用了外部资源， 另一方面大大减少了用户离开Vim的概率，从而提高编辑效率。</p>
<p>ex命令源自Unix下的行编辑器（line editor） <a class="reference external" href="http://en.wikipedia.org/wiki/Ex_(text_editor)">ex</a> ，Vim将其扩充为 Vimscript语言，成为第三方开发插件的主要工具（也可用Perl、Python、Ruby等语言）。 由于在ex模式下可通过 <tt class="docutils literal">:normal</tt> 执行普通模式下的命令，故而实际上完全涵盖了 后者。只是执行ex命令时前面需要冒号，后面需要回车，效率上有所不及。 根据个人需要，用户可自定义ex命令，也可把常用的ex命令通过 <tt class="docutils literal">nmap</tt> 、 <tt class="docutils literal">vmap</tt> 、 <tt class="docutils literal">imap</tt> 等映射为其他模式下的命令，以期最大限度地减少键击次数。</p>
<p>命令行模式除用 <tt class="docutils literal">:</tt> 开启以执行ex命令外，还可用 <tt class="docutils literal">/</tt> 或 <tt class="docutils literal">?</tt> 来启动正向 或反向的模式搜索，可用 <tt class="docutils literal">!</tt> 进行文本过滤（filter） （即：将所选文本输入给外部程序，并替之以输出结果），大大增强了Vim的 移动和编辑效率。</p>
<p>此外，在基本模式的基础上，还有六种衍生模式，暂略不提。 从以上可以看出，多种模式的并存令Vim鱼与熊掌兼得——功能上强大而灵活， 使用上便利而快捷。</p>
</div>
<div id="id3" class="section">
<h2>2. 增加移动方式</h2>
<p>真正让Vim有飞一般感觉的是其快速多变的移动能力。可谓上天入地，无所不至，无至不速。 上文提到HJKL代替方向键的意义，但那只能让光标偏移最小单位，并不适宜频繁地单独使用。 否则，那便不是飞行，而是爬行。由于Vim提供的移动命令过多，难免令人眼花缭乱， 为便于说明，特做如下分类，并列举一些典型但远非完备的实例。</p>
<div id="id4" class="section">
<h3>行间移动：</h3>
<p>行间移动是一种常见的需求，Vim提供了绝对行数和相对行数两种跳转方式。 这里有一个小窍门，为了便于确定移动行数，可通过 <tt class="docutils literal">:set number</tt> 显示绝对行号， 或 <tt class="docutils literal">:set relativenumber</tt> 显示相对行号。建议定制一个功能键（如F6）， 让行号随时可以在相对、绝对和隐藏之间循环切换。平时默认为相对行号或许更高效些， 一是相对行号比绝对行号小，通常键入更快；二是相对移动的 <tt class="docutils literal">j</tt> 、 <tt class="docutils literal">k</tt> 命令比绝对移动的 <tt class="docutils literal">G</tt> 命令少用一个shift键。</p>
<pre class="literal-block">
H            ——移至屏首
L            ——移至屏末
M            ——移至屏中
5H           ——移至屏首起第5行
5L           ——移至屏末起倒数第5行
gg           ——移至页首
G            ——移至页末
50%          ——移至页中
20G          ——移至页首起第20行
20j          ——下移20行
20k          ——上移20行
5gj          ——下移5个显示行（与文件行不同，显示行考虑折行）
5gk          ——上移5个显示行（同上注）</pre>
</div>
<div id="id5" class="section">
<h3>列间移动：</h3>
<p>作为一种近距离微调，列间移动非常实用。除了跳至行首或行末以外，最有用的便是 <tt class="docutils literal"><span class="pre">f/F/t/T/;/,</span></tt> 系列命令了。可惜中文是多字节的，无法享用此功能。</p>
<pre class="literal-block">
0            ——移至行首
$            ——移至行末
^            ——移至软行首（即忽略首空白）
g_           ——移至软行末（即忽略尾空白）
g0           ——移至屏幕行首（考虑折行）
gm           ——移至屏幕行中（同上注）
g$           ——移至屏幕行末（同上注）
20|          ——移至第20列

fx           ——移至本行下一个字符“x”处
Fx           ——移至本行上一个字符“x”处
tx           ——移至本行下一个字符“x”前
Tx           ——移至本行上一个字符“x”后
;            ——重复上次 f/F/t/T 命令
,            ——反向重复上次 f/F/t/T 命令</pre>
</div>
<div id="id6" class="section">
<h3>结构移动：</h3>
<p>在用户眼中，文本不是单纯的字符集合，不仅具有行列差别，更有结构差别。 常见的结构划分有：单词（word），句子（sentence），段落（paragraph） 和区块（section）等。 除了系统的默认设定外，Vim还允许用户自定义结构的界定方式。 有些遗憾的是，由于中文词与词之间没有分界，无法利用单词的跳转功能。</p>
<pre class="literal-block">
w            ——右移至最近词首
W            ——同上，但忽略非空白单词分隔符
b            ——左移至最近词首
B            ——同上，但忽略非空白单词分隔符
e            ——右移至最近词尾
E            ——同上，但忽略非空白单词分隔符
ge           ——左移至最近词尾
gE           ——同上，但忽略非空白单词分隔符

(            ——前移至最近句首
)            ——后移至最近句首
{            ——前移至最近段首
}            ——后移至最近段首</pre>
<p>除基本的文本结构外，程序员还对语法结构感兴趣。 <tt class="docutils literal">%</tt> 是一个方便的命令，它能让光标在匹配项之间来回游走。 除了 <tt class="docutils literal">(</tt> 与 <tt class="docutils literal">)</tt> 、 <tt class="docutils literal">[</tt> 与 <tt class="docutils literal">]</tt> 、 <tt class="docutils literal">{</tt> 与 <tt class="docutils literal">}</tt> 的括号匹配外， 还支持C风格的注释（ <tt class="docutils literal">/*</tt> 与 <tt class="docutils literal">*/</tt> ）与宏（ <tt class="docutils literal">#if</tt> 、 <tt class="docutils literal">#ifdef</tt> 、 <tt class="docutils literal">#else</tt> 、 <tt class="docutils literal">#elif</tt> 、 <tt class="docutils literal">#endif</tt> 等）匹配。 Vim更提供了matchit的插件（但并未默认安装），支持其他编程语言的匹配结构。 若仍嫌不满，用户也可自定义其他的匹配方式。 下面的命令是给程序员额外的福利——</p>
<pre class="literal-block">
[[           ——前移至最近区首（常用于跳至C类型的函数头）
]]           ——后移至最近区首（同上注）
[{           ——前移至最近代码区的首部（C类型语法）
]}           ——后移至最近代码区的尾部（同上注）
[m           ——前移至最近方法的首部（Java类型语法）
]m           ——后移至最近方法的首部（同上注）
[M           ——前移至最近方法的尾部（同上注）
]M           ——后移至最近方法的尾部（同上注）
gd           ——跳至当前变量的局部声明处
gD           ——跳至当前变量的全局声明处</pre>
<p>Vim具有折叠功能，故而支持折叠结构的跳转。</p>
<pre class="literal-block">
[z           ——移至当前展开的折叠区的首部
]z           ——移至当前展开的折叠区的尾部
zj           ——移至下一折叠区的首部
zk           ——移至上一折叠区的尾部</pre>
</div>
<div id="id7" class="section">
<h3>屏幕移动：</h3>
<p>在浏览文件时，用户往往更希望调整的是屏幕而不是光标。</p>
<pre class="literal-block">
Ctrl+F       ——向下滚动一屏
Ctrl+B       ——向上滚动一屏
Ctrl+D       ——向下滚动一个单位（默认半屏）
Ctrl+U       ——向上滚动一个单位（默认半屏）
Ctrl+E       ——向下滚动一行
Ctrl+Y       ——向上滚动一行
zl           ——向右滚动一列（仅在禁用折行时有效）
zh           ——向左滚动一列（同上注）
zt           ——置当前行于屏首
zb           ——置当前行于屏末
zz           ——置当前行于屏中
10z.         ——置第10行于屏中
50z-         ——置第50行于屏末</pre>
<p>鉴于滚屏操作十分常用而Ctrl键不便，建议用 <tt class="docutils literal">nmap</tt> 将^f/^b/^d/^u/^e/^y 分别替换为空格键、Shift+空格键、Enter键、Backspace键、下方向键、上方向键。</p>
</div>
<div id="id8" class="section">
<h3>搜索移动：</h3>
<p>几乎所有的编辑器都有搜索功能，但很少有Vim这般强大和方便。 强大体现在它不仅支持最基本的正则表达式，还支持懒惰模式 （ <a class="reference external" href="http://en.wikipedia.org/wiki/Regular_expression#Lazy_quantification">lazy mode</a> ）， 甚至可以 <em>跨行</em> 匹配。方便则体现在搜索过程对用户友好，没有讨厌的弹出窗口， 支持高亮匹配，支持增量搜索（光标在关键词输入过程中即开始运动，不必等待回车键）， 支持智能大小写判断，一键重复正向或反向搜索，等等。另外，特别推荐四个诱人的命令：</p>
<pre class="literal-block">
*            ——正向搜索光标所在的单词（精确匹配）
#            ——反向搜索光标所在的单词（精确匹配）
g*           ——正向搜索光标所在的单词（模糊匹配）
g#           ——反向搜索光标所在的单词（模糊匹配）</pre>
<p>如果要在多个文件中搜索，可以用内部命令 <tt class="docutils literal">vimgrep</tt> 或外部命令 <tt class="docutils literal">grep</tt> 。 前者在正则表达式上更强大，也更通用（Windows下没有自带的grep），但速度不及后者。 我个人的选择是在配置中加上一行： <tt class="docutils literal">set grepprg=ack\ <span class="pre">-a</span></tt> ，以便让更好用的 <a class="reference external" href="http://betterthangrep.com/">ack</a> 来取代grep。</p>
</div>
<div id="id9" class="section">
<h3>定点移动</h3>
<p>定点移动是Vim又一特色，迅速而精确的远程移动让浏览和编辑变得前所未有的轻松。</p>
<p>Vim的标记（mark）相当于一种书签，用来记录用户关注的热点位置。 假设用户对当前光标所处位置感兴趣，可输入命令 <cite>mx</cite> （x可以是任何字母）。 以后只要输入 <cite>`x</cite> 即可返回原标记处，或用 <cite>&#8216;x</cite> 返回原标记处所在的行首。 用作标记的字母有大小写之别。小写为局部标记，用于缓冲区内部跳转， 仅对当前编辑的文件有效，且在缓冲区关闭后失效。 大写为全局标记，可在不同的文件之间跳转，不因缓冲区关闭而失效（需设定 <tt class="docutils literal">viminfo</tt> ）。</p>
<p>比用户标记更有用的是系统标记。Vim贴心地在一些热点上留下暗记，以便用户回访。 例如，用户每次退出Vim时的光标位置都会被保留，最近十个分别用数字0到9来命名。 下面是其他一些实用的标记跳转：</p>
<pre class="literal-block">
``           ——跳转至当前缓冲区最近跳转点（可实现两点之间的来回跳转）
''           ——同上，但仅精确到行
`.           ——跳转至当前缓冲区最近修改处
`[           ——跳转至上次改动或拷贝处的首部
`]           ——跳转至上次改动或拷贝处的尾部
`&lt;           ——跳转至最近可视化选择区的首部
`&gt;           ——跳转至最近可视化选择区的尾部
`"           ——跳转至上次退出当前缓冲区时光标的最后位置（在普通模式下）
`^           ——跳转至上次退出当前缓冲区时光标的最后位置（在插入模式下）</pre>
<p>所有标记均可通过命令 <tt class="docutils literal">:marks</tt> 来显示，以供查询。要获得更好的视觉效果， 不妨试试 <a class="reference external" href="http://www.vim.org/scripts/script.php?script_id=152">ShowMarks</a> 插件，它利用Vim的sign功能将隐性标记显性化了。</p>
<p>除了标记列表外，Vim还维护了一张变化列表。 该表记录了用户每次修改文本的位置，运行命令 <tt class="docutils literal">:changes</tt> 即可察看。 相应的跳转命令是：</p>
<pre class="literal-block">
g;           ——跳转至变化列表中的较旧处（支持数字前缀）
g，           ——跳转至变化列表中的较新处（支持数字前缀）</pre>
<p>程序员在浏览代码时经常需要在不同的源文件中跳转（比如察看某个函数的定义）， Vim为此提供了标签（tag）支持。 与标记不同，标签需要依赖外部工具如 <a class="reference external" href="http://ctags.sourceforge.net/">ctags</a> 、 <a class="reference external" href="http://cscope.sourceforge.net/">cscope</a> 等来产生。 使用命令 <tt class="docutils literal">:tag &lt;tagname&gt;</tt> 可实现标签跳转，不过更简便的还是将光标置于 关键词之上，然后通过 <tt class="docutils literal"><span class="pre">Ctrl-]</span></tt> 转至其定义处，必要时可用 <tt class="docutils literal"><span class="pre">Ctrl-T</span></tt> 返回。 用鼠标亦可完成以上任务，但并不推荐。</p>
<p>最后，上述各种移动命令所产生的跳跃点均保存于跳转列表中（最多不超过100个）， 可通过命令 <tt class="docutils literal">:jumps</tt> 检视。这意味着用户可以在原来的轨迹上来回跃迁——</p>
<pre class="literal-block">
Ctrl-O       ——跳转至跳转列表中的较旧处（支持数字前缀）
Tab或Ctrl-I  ——跳转至跳转列表中的较新处（支持数字前缀）</pre>
</div>
<div id="id10" class="section">
<h3>其他移动</h3>
<p>还有一些其他类型的移动，试列举一二：</p>
<pre class="literal-block">
gf           ——跳转至光标之上或之后的路径所对应的文件
gF           ——同上，但跳至文件后所指定的行数
]s           ——跳转至下个错误拼写处
[s           ——跳转至上个错误拼写处</pre>
</div>
</div>
<div id="id11" class="section">
<h2>3. 增加操作对象</h2>
<p>Vim丰富的移动方式让用户以最小的代价——包括手指、目光、时间和精力——把所感兴趣的 文本带入视线范围并将光标精确定位。 这只是编辑的第一步，下一步是在当前位置进行文本操作。 一般编辑器修改文本多通过Backspace键、Delete键、方向键并结合鼠标完成，效率低下。 究其原因，主要在于操作对象太过单一：要么是以字符为单位，粒度太小； 要么以高亮区域为单位，选定太慢。 Vim则不同，提供了各种粒度的操作对象，供用户在不同需求下选择，大大提高了编辑效率。</p>
<p>Vim的编辑命令具有统一的形式：数字 + 操作符（operator） + 文本对象， 表示对某一文本对象进行指定次数的操作。其中，数字部分为可选项，默认为1； 有些操作符后不接对象，正如不及物动词后不接宾语。 一切似乎都平淡无奇，直到文本对象与移动命令自然而奇妙地结合在一起，瞬时光芒四射。 具体地说，文本对象可由移动命令所扫过的字符片段来定义。 于是，有多少移动方式，便对应多少文本对象。 这已经是我们第二次看到移动命令的重用（reuse）了（前一次用于可视化模式下）。</p>
<p>举例来说，操作符 <tt class="docutils literal">d</tt> 表示删除，移动命令 <tt class="docutils literal">w</tt> 表示前进到下一单词，则 <tt class="docutils literal">dw</tt> 将删除从当前光标至下一单词之前的所有字符。 假设当前光标处于某单词的首部， <tt class="docutils literal">2dw</tt> 将删除该单词及其后一单词。 如果知道移动命令本身也可加数字前缀，则 <tt class="docutils literal">2d3w</tt> 将删除6个单词，与 <tt class="docutils literal">3d2w</tt> 、 <tt class="docutils literal">6dw</tt> 、 <tt class="docutils literal">d6w</tt> 的效果相同。 类似地，操作还可以句子、段落、语法结构为单位， 以行或列为单位，以屏幕为单位，以匹配模式为单位，或以定点为界限，等等。</p>
<p>进一步地，Vim在Vi的基础上新增了其他的文本对象，用于可视化模式下的选择 和普通模式下的编辑。兹列几项如下——</p>
<pre class="literal-block">
aw           ——一个单词（支持数字前缀）
is           ——一个句子内部（支持数字前缀）
ap           ——一个段落（支持数字前缀）
a"           ——一个双引号区域
i}或i{或iB   ——一个“{}”块内部（支持数字前缀）
a&gt;或a&lt;       ——一个“&lt;&gt;”块（支持数字前缀）
at           ——一个标签块（支持数字前缀）</pre>
<p>不妨看一个典型用例。假设有一段文字：</p>
<pre class="literal-block">String name = "Shen Xin";</pre>
<p>用户希望将 <tt class="docutils literal">Shen Xin</tt> 换成 <tt class="docutils literal">Zhang Ming</tt> ，此时光标位于该行的头部。 Vi（不是Vim）的常见做法是： <tt class="docutils literal">f"</tt> 到达第一个引号， <tt class="docutils literal">l</tt> 右移一个字符， 然后 <tt class="docutils literal">ct"</tt> 清空人名并进入插入模式。 而在Vim下，只要键入 <tt class="docutils literal">ci"</tt> 即可达到同样效果，节省了一半键击。</p>
<p>再看一个HTML片段：</p>
<pre class="literal-block">
&lt;tr&gt;
    &lt;td&gt;
        ...
    &lt;/td&gt;

    &lt;td&gt;
        ...
    &lt;/td&gt;

    .
    .
    .

    &lt;td&gt;
        ...
    &lt;/td&gt;
&lt;/tr&gt;</pre>
<p>假定光标位于某个 <tt class="docutils literal">td</tt> 标签内部，要拷贝整个HTML行（即 <tt class="docutils literal">tr</tt> 标签块），Vi的一种做法是： <tt class="docutils literal"><span class="pre">?&lt;tr</span></tt> 并回车到达行首， <tt class="docutils literal">y</tt> 开启拷贝操作， <tt class="docutils literal"><span class="pre">/\/tr&gt;/e</span></tt> 指定行尾，再回车完成任务，共需十多次按键。 利用行数或段落移动可能会省一些键，但都不如Vim来得惬意： <tt class="docutils literal">y2at</tt> 。</p>
<p>还不止于此，Vim甚至允许用户自定义文本对象。比如设置一个操作符待定模式 （opeator-pending mode，Vim的六种衍生模式之一）下的映射： <tt class="docutils literal">omap af :normal <span class="pre">[[v%&lt;CR&gt;</span></tt> （af意指“a function”） ，便建立了一个C语言风格 的函数对象。若觉得晦涩，不妨回顾一下， <tt class="docutils literal">:normal</tt> 表示执行普通模式命令， <tt class="docutils literal">[[</tt> 跳至函数头部的 <tt class="docutils literal">{</tt> 处， <tt class="docutils literal">v</tt> 开启可视化模式， <tt class="docutils literal">%</tt> 表示选择区的 末端为与 <tt class="docutils literal">{</tt> 匹配的 <tt class="docutils literal">}</tt> ，由此定义了一块函数区域，用 <tt class="docutils literal">af</tt> 来命名。 同样地，用户可按缩进、折叠、语法或其他区域划分方式来定义文本对象。</p>
<p>除文本对象外，用鼠标或键盘产生的选择区域、用折叠命令隐藏的文本片段等皆可 作为整体的操作对象。</p>
<p>在命令行模式下，还可按行数范围或模式匹配来指定操作对象。 比如， <tt class="docutils literal">%s/x/y</tt> 将所有行（ <tt class="docutils literal">%</tt> ）中的第一个 <tt class="docutils literal">x</tt> 换成 <tt class="docutils literal">y</tt> ， <tt class="docutils literal"><span class="pre">/第一章/+1,/第二章/-1d</span></tt> 将删除光标后“第一章”与“第二章”之间的所有文字， <tt class="docutils literal"><span class="pre">.-5,.+5w</span> a.txt</tt> 把以光标为中心的11行文字保存到名为 <tt class="docutils literal">a.txt</tt> 的文件中。</p>
<p>通过增加文本对象，Vim使文本操作的粒度更加多样化，从而提高了批量处理的概率。 同时，由于文本对象更趋结构化与语义化，让思维与文字之间的转换更加流畅自然， 从而减少了编辑失误的概率。</p>
</div>
<div id="id12" class="section">
<h2>4. 增加操作方式</h2>
<p>当用户把光标移至合适的位置，并选定合适的操作对象以后，剩下的就是执行具体操作了。</p>
<div id="id13" class="section">
<h3>新增文本</h3>
<p>最常用的操作是增加新文本。Vi在普通模式下用 <tt class="docutils literal">a</tt> 、 <tt class="docutils literal">A</tt> 、 <tt class="docutils literal">i</tt> 、 <tt class="docutils literal">I</tt> 、 <tt class="docutils literal">o</tt> 或 <tt class="docutils literal">O</tt> 切换到插入模式。Vim新增了 <tt class="docutils literal">gI</tt> ，与 <tt class="docutils literal">I</tt> 的区别是，它保证将 光标置于首列。此外Vim还增加了 <tt class="docutils literal">gi</tt> ，便于从上回退出缓冲区的插入点继续工作。 一个貌似简单的插入操作便有这么多的花样，目的很明确，那就是尽量减少键击次数。 另外，恐怕很少人意识到以上命令均支持数字前缀。如在普通模式下输入 <tt class="docutils literal">100a</tt> ， 然后插入一些文字，当用户再次回到普通模式下时，方才输入的文字将自动重复一百次。 从这里再次印证两件事：一是Vim为减少按键可谓处心积虑——本来复制操作就支持数字前缀， 但仍为插入操作加此功能；二是插入模式的确被视为暂态，随时等待退出。</p>
<p>一般不建议在插入模式下使用命令，但 <tt class="docutils literal"><span class="pre">Ctrl-Y</span></tt> 与 <tt class="docutils literal"><span class="pre">Ctrl-E</span></tt> 有时还是有用的。 它们分别在插入模式下重复光标上行或下行对应列的字符。 这么做并不省手，但省眼省心，符合Vim的理念。</p>
<p>插入模式下另一对命令 <tt class="docutils literal"><span class="pre">Ctrl-P</span></tt> 与 <tt class="docutils literal"><span class="pre">Ctrl-N</span></tt> 可实现文本的自动补全， 以 <tt class="docutils literal"><span class="pre">Ctrl-X</span></tt> 开启的子模式可产生更具体的完成提示，如后接 <tt class="docutils literal"><span class="pre">Ctrl-F</span></tt> 指 文件名匹配， 后接 <tt class="docutils literal"><span class="pre">Ctrl-K</span></tt> 指字典匹配，后接 <tt class="docutils literal"><span class="pre">Ctrl-S</span></tt> 指拼写建议， 后接 <tt class="docutils literal"><span class="pre">Ctrl-O</span></tt> 指万能补全（omnicompletion）等。 不过经设置或安装插件，可尽量通过Tab键或Shift-Tab来完成， 如此更符合减少手指移动的原则。</p>
<p>插入模式下的 <tt class="docutils literal"><span class="pre">Ctrl-R</span></tt> 能输入与寄存器（稍后将会介绍）相关的内容，
比如 <tt class="docutils literal"><span class="pre">Ctrl-R</span> "</tt> 将输入最近一次的内部拷贝（即无名寄存器中的内容），
<tt class="docutils literal"><span class="pre">Ctrl-R</span> +</tt> 将输入最近一次的外部拷贝（即系统剪贴板），
<tt class="docutils literal"><span class="pre">Ctrl-R</span> .</tt> 将输入最近一次的插入文本，等等。
更特别地， <tt class="docutils literal"><span class="pre">Ctrl-R</span> =</tt> 后将输入表达式。例如  <tt class="docutils literal"><span class="pre">Ctrl-R</span> =12*50</tt>将打出 <tt class="docutils literal">600</tt> ，相当于一个小计算器。</p>
<p>如果希望输入一些特殊字符，如希腊字母、数学符号、象形符号等，可在<tt class="docutils literal">Ctrl-K</tt>后键入两个字母以产生digraph（二合字母）。具体对照表可通过命令<tt class="docutils literal">:digraphs</tt>来显示。</p>
</div>
<div id="id14" class="section">
<h3>删除文本</h3>
<p>与插入操作相对的是删除操作。最普通也是最低效的方式是在插入模式下使用删除键。 使用 <tt class="docutils literal">x</tt> 或 <tt class="docutils literal">X</tt> 同样可删除字符，但不需退出普通模式，还支持数字前缀。 <tt class="docutils literal">d</tt> 命令既可前接可视化区域，又可后接文本对象，是万能的删除方式。 作为频繁使用的行删除命令， <tt class="docutils literal">dd</tt> 被设计为叠字无疑是明智的，而用 <tt class="docutils literal">D</tt> 命令 代替另一常用的 <tt class="docutils literal">d$</tt> 也是省键之举。 除此之外，如前例所示，命令行下也能完成删除操作。</p>
</div>
<div id="id15" class="section">
<h3>替换文本</h3>
<p>替换操作的方式更加多样。最简单的是以字符为单位的 <tt class="docutils literal">r</tt> （在可视化模式下也可批量替换）， 如需连续替换可用 <tt class="docutils literal">R</tt> 进入替换模式，如需删除若干字符后进入插入模式，可用 <tt class="docutils literal">s</tt> 。 与负责删除操作的 <tt class="docutils literal">d</tt> 命令相对应的是负责改动操作的 <tt class="docutils literal">c</tt> 命令： <tt class="docutils literal">{可视化模式}c</tt> 、 <tt class="docutils literal">c + 文本对象</tt> 、 <tt class="docutils literal">cc</tt> 与 <tt class="docutils literal">C</tt> 。</p>
<p>文本编辑经常涉及大小写变换，Vim对此当然不会视而不见。 <tt class="docutils literal">~</tt> 是大小写转换符，可将若干字符的大小写对换。如 <tt class="docutils literal">6~</tt> 作用于 <tt class="docutils literal">aBcDeF</tt> 的结果是： <tt class="docutils literal">AbCdEf</tt> 。 <tt class="docutils literal">g~</tt> 更强大，后可接文本对象。于是前面的命令可用 <tt class="docutils literal">g~e</tt> 代替，虽然多敲一字符，却不必数字符，以手动换心动，效率只高不低。 利用可视化模式 <tt class="docutils literal">ve~</tt> 也可完成任务，但产生了可视化区域的副作用。 不出意料地， <tt class="docutils literal">g~~</tt> 将当前行所有字母进行大小写转换。 如果希望将目标字符固定为大写或小写，则用 <tt class="docutils literal">gU</tt> 或 <tt class="docutils literal">gu</tt> 命令。 类似地， <tt class="docutils literal">gUU</tt> 将当前行所有字母变成大写， <tt class="docutils literal">guu</tt> 将当前行所有字母变成小写。</p>
<p>对文本进行格式上的调整也是一项常规需求。 Vim提供了 <tt class="docutils literal">J</tt> 与 <tt class="docutils literal">gJ</tt> ，能将多行文字并为一行，极为实用。 <tt class="docutils literal">&lt;</tt> 、 <tt class="docutils literal">&gt;</tt> 分别将文本左移或右移一个缩进单位 （在插入模式下用 <tt class="docutils literal"><span class="pre">Ctrl-T</span></tt> 或 <tt class="docutils literal"><span class="pre">Ctrl-D</span></tt> ）。 <tt class="docutils literal">=</tt> 命令能自动调节缩进， <tt class="docutils literal">gq</tt> 命令能格式化选定的文本。 按惯例，以上命令在应用于行时，均采用叠字： <tt class="docutils literal">&lt;&lt;</tt> 、 <tt class="docutils literal">&gt;&gt;</tt> 、 <tt class="docutils literal">==</tt> 、 <tt class="docutils literal">gqq</tt> 。 若需左、中、右对齐，则分别使用 <tt class="docutils literal">:left</tt> 、 <tt class="docutils literal">:center</tt> 、 <tt class="docutils literal">:right</tt> 。</p>
<p><tt class="docutils literal"><span class="pre">Ctrl-A</span></tt> 、 <tt class="docutils literal"><span class="pre">Ctrl-X</span></tt> 是一对鲜为人知但却非常有用的命令， 它们能分别 对光标之上或之后的数字进行增减运算，并且不仅支持十进制，还默认支持八进制和十六进制。 假设光标后有一个字串 <tt class="docutils literal">0x2a</tt> ，输入 <tt class="docutils literal"><span class="pre">2&lt;Ctrl-X&gt;</span></tt> 后它将变成 <tt class="docutils literal">0x28</tt> ， 且光标也移至 <tt class="docutils literal">8</tt> 处。适当设置后，此二命令甚至能对单字母进行增减。</p>
<p>在命令行模式下可执行更复杂的替换操作。 命令 <tt class="docutils literal">:s</tt> 可充分利用正则表达式的威力进行文本替换， 若要重复上次替换，只需键入 <tt class="docutils literal">&amp;</tt> , 若要对所有行重复上次替换，只需键入 <tt class="docutils literal">g&amp;</tt> 。 前面还提到，通过 <tt class="docutils literal">!</tt> 能调用外部程序进行过滤操作。 比如 <tt class="docutils literal">!apsort</tt> 将当前段落（ 即 <tt class="docutils literal">ap</tt> 对象）中的所有行按字母进行排序 （即 <tt class="docutils literal">sort</tt> ）。 由于过滤程序毫不受限，这便意味着用户能随心所欲地对文本进行任何替换。</p>
</div>
<div id="id16" class="section">
<h3>复制、移动与粘贴</h3>
<p>对于编辑器都支持的复制、移动、粘贴等功能，Vim也是花样百出。 一般编辑器的剪贴板只保存用户显式剪切或拷贝的文字，而Vim把任何被改动、删除或 拷贝（yank）的文字都存于相当于剪贴板的寄存器中。 利用此特点，组合命令 <tt class="docutils literal">xp</tt> 轻易实现了两个相邻字符的交换， 不仅比输入模式下少一次按键，而且手指不会因方向键而离开主键区。</p>
<p>更美好的是，用户可指定具体的寄存器。比如 <tt class="docutils literal">"a5dd</tt> 将删除的5行存于名为 <tt class="docutils literal">a</tt> 的寄存器中，以后可用 <tt class="docutils literal">"ap</tt> 粘贴到其他地方。 指定寄存器虽增加了按键次数，但也保证了重要的文本不被轻易覆盖。 另一有趣的用法是，以大写字母命名的寄存器会将新内容追加（而不是覆盖）到 对应小写字母的寄存器中。比如继刚才的操作之后，用户在某处敲入 <tt class="docutils literal">"A2yy</tt> ， 则此时寄存器中将有7行文字。</p>
<p>Vim的寄存器不仅不止一个，而且不止一种。 例如，最近的修改、删除或拷贝的文本存在 <em>无名寄存器</em> （说是无名，实则有名： <tt class="docutils literal">"</tt> ）中， 最近拷贝的存在 <em>数字寄存器</em> <tt class="docutils literal">0</tt> 中， 最近修改或删除的存在寄存器 <tt class="docutils literal">1</tt> 至 <tt class="docutils literal">9</tt> 中。 对于少于一行的删除（如 <tt class="docutils literal">dw</tt> ）还专门有个名为 <tt class="docutils literal">-</tt> 的 <em>微删除寄存器</em> 。 如果不希望改变寄存器内容，可使用名为 <tt class="docutils literal">_</tt> 的 <em>黑洞寄存器</em> 。 为便于与Vim的外部环境交流， <em>系统寄存器</em> <tt class="docutils literal">*</tt> 与 <tt class="docutils literal">+</tt> 对应系统剪贴板，寄存器 <tt class="docutils literal">~</tt> 保存最近一次从外部程序（如Word）拖放（Drag-and-Drop）过来的文本。 此外，还有 <em>只读寄存器</em> <tt class="docutils literal">%</tt> 、 <tt class="docutils literal">#</tt> 、 <tt class="docutils literal">.</tt> 和 <tt class="docutils literal">:</tt> ， 以及记录最近搜索模式名为 <tt class="docutils literal">/</tt> 的寄存器。 依然是惯例， <tt class="docutils literal">:registers</tt> 列出重要寄存器的名字及内容。</p>
<p>最后，如果用户不愿意移动光标或屏幕，也可在命令行模式下完成复制、移动与粘贴的任务。</p>
</div>
<div id="id17" class="section">
<h3>重复</h3>
<p>Vim让人觉得重复不再是一件令人乏味的事，有时甚至是一种享受。</p>
<p>命令 <tt class="docutils literal">.</tt> 重复上次的变化，但不包括命令行命令。 若要重复上次的命令行命令，可用 <tt class="docutils literal">@:</tt> 。 如同在unix shell中一样，在命令行模式下用 <tt class="docutils literal">!!</tt> 重复上次的shell命令。 若要在一定范围内重复多次命令，可用 <tt class="docutils literal">范围:g/模式/Ex命令</tt> 或 <tt class="docutils literal">范围:g/模式/normal 普通命令</tt> ， 即在指定范围内对匹配某一模式的行重复执行给定的命令。 将其中的 <tt class="docutils literal">g</tt> 换成 <tt class="docutils literal">g!</tt> 或 <tt class="docutils literal">v</tt> 则将命令作用于非匹配行。</p>
<p>复杂的重复可使用Vim的宏（macro）。比如用 <tt class="docutils literal">qq</tt> 将记录接下来的按键，用 <tt class="docutils literal">q</tt> 结束。 以后可用 <tt class="docutils literal">@q</tt> 重放，再后来可用 <tt class="docutils literal">@@</tt> 重放。 与简单的拷贝不同，宏不仅能复制插入模式下的按键，还能记录普通模式下的按键。 举个稍微复杂的例子，假设光标所在行的文字是 <tt class="docutils literal">(1)</tt> ， 在普通模式下依次键入 <tt class="docutils literal"><span class="pre">qqYp&lt;Ctrl-A&gt;q8@q</span></tt> ，便能产生从一到十的编号列表 （提示： <tt class="docutils literal">Y</tt> 表示行复制， <tt class="docutils literal">p</tt> 表示粘贴， <tt class="docutils literal"><span class="pre">Ctrl-A</span></tt> 是前述的数字增加命令）。</p>
<p>即使更复杂的重复在Vim下也是可能的，命令 <tt class="docutils literal">:source</tt> 或 <tt class="docutils literal">:runtime</tt> 可调用 Vimscript脚本，不过那主要是程序员们大展身手的地方了。</p>
</div>
<div id="id18" class="section">
<h3>撤销与恢复</h3>
<p>在撤销与恢复的操作方面，Vim再度显示其独到之处。</p>
<p>首先，在基本的撤销命令 <tt class="docutils literal">u</tt> 与 恢复命令 <tt class="docutils literal"><span class="pre">Ctrl-R</span></tt> 之外，还有 行撤销命令 <tt class="docutils literal">U</tt> ，即撤销对某行最近进行的所有修改。 其次，以上命令与其他普通命令一样，也支持数字前缀，即可一次性执行多次 撤销或恢复。 更有特色的是，Vim的撤销与恢复状态不是堆栈（stack）结构，而是树（tree）结构。 通俗地举例来说，在一般编辑器中，如果在作出某种修改后发布撤销命令， 然后再作另一种修改，再撤销。此时用户若执行恢复命令，将恢复第二次修改的内容， 而第一次修改的痕迹完全被抹去。一旦用户意识到操作错误，很可能会后悔不迭。 为避免此类事件发生，Vim提供了撤销列表，保留了各个时刻的文本状态，用户随时 可通过命令 <tt class="docutils literal">g-</tt> 和 <tt class="docutils literal">g+</tt> 来前后遍历，也可用命令 <tt class="docutils literal">:earlier</tt> 或 <tt class="docutils literal">:later</tt> 根据修改时间的范围来恢复状态，还可用命令 <tt class="docutils literal">:undo</tt> 跳到状态树的某个分支 （此后再用 <tt class="docutils literal">u</tt> 与 <tt class="docutils literal"><span class="pre">Ctrl-R</span></tt> 前后遍历）。在此推荐插件<a href="http://sjl.bitbucket.org/gundo.vim/">Gundo</a>，它不仅清晰地展示了撤销与恢复的树形结构，实现快速恢复，还能预览各个修改版本之间的差别。</p>
<p>一般编辑器在文件关闭后无法撤销或恢复上次的修改，Vim则不然。 用户只要略加设置，便能实现文件修改状态的持久化。</p>
</div>
<div id="id19" class="section">
<h3>文件操作</h3>
<p>Vim提高文件操作效率的关键在一个字—— <em>多</em> ，即多分区、多缓冲区（buffer）、 多窗口、多标签页、多文件浏览、多文件读写与多类型文件处理。</p>
<p>多分区指通过文本折叠将一个文件分为多个折叠区，让文件的结构层次更加清晰， 并且减少了因文件过长而带来的频繁的手指移动和目光移动。 Vim中以 <tt class="docutils literal">z</tt> 开头的折叠命令超过20个，足见其丰。</p>
<p>多缓冲区（buffer）指在一个窗口内可同时编辑多个文件。 缓冲区之间的切换方式很多，如用 <tt class="docutils literal">:bn</tt> 或 <tt class="docutils literal">:bp</tt> 进行缓冲区翻页， 用 <tt class="docutils literal"><span class="pre">Ctrl-^</span></tt> 在两个缓冲区之间来回切换，用 <tt class="docutils literal">:e #N</tt> 按缓冲区编号编辑， 用 <tt class="docutils literal">:b 文件名</tt> 按缓冲区的文件名选择编辑对象 （文件名支持自动完成，且不必输全，如 <tt class="docutils literal">:b R</tt> 可转至文件README.txt）。 如果同时载入缓冲区的文件过多，还有大量优秀的管理缓冲区的插件可资利用，
比如 <a href="https://wincent.com/products/command-t">command-t</a>、<a href="http://www.vim.org/scripts/script.php?script_id=1984">FuzzyFinder</a>、<a href="http://www.vim.org/scripts/script.php?script_id=42">bufexplorer</a>等。</p>
<p>要想充分利用屏幕资源，减少缓冲区切换的代价，或希望在多个文件之间进行对比编辑， 可利用Vim的多窗口功能。Vim可对窗口进行横向和纵向多次切分， 还能调整窗口的大小、位置、焦点等。</p>
<p>当多缓冲区、多窗口仍不满足需求时，可开启多标签窗口（tabbed window）， 以提高对多文件的处理能力。</p>
<p>大多用户习惯用文件浏览器来选择或管理文件，幸好在Vim中可不假他求，其内置的 netrw插件便具备此功能。不过，非内置的 <a class="reference external" href="http://www.vim.org/scripts/script.php?script_id=1658">NERD tree</a> 插件 似乎更受欢迎。</p>
<p>读取和写入文件是编辑器最基本的功能。Vim的特点是能在不离开当前缓冲区的条件下， 读取其他文件的内容，或把缓冲区中的部分文本写入其他文件中。 如 <tt class="docutils literal">:35r infile</tt> 将名为 <tt class="docutils literal">infile</tt> 的文件内容插入到当前文件的第35行下， <tt class="docutils literal">1,7w outfile</tt> 把当前文件的头7行保存到名为 <tt class="docutils literal">outfile</tt> 的文件中。</p>
<p>最后，为减少用户离开的机会，Vim能透明读写多种类型的远程文件、压缩文件和加密文件。 如果利用插件或其他第三方工具，还可对更多类型的文件进行读写操作。</p>
</div>
<div id="id20" class="section">
<h3>自动操作</h3>
<p>贴心的编辑器应当尽可能地减少用户不必要的操作。 Vim可根据用户设置，完成自动换行，自动缩进，自动将Tab换成空格、自动折叠、 自动读取、自动保存等任务。 不过真正“懒惰”的Vim用户是不会满足于此的，他们会“辛勤”地端起终极武器 <tt class="docutils literal">autocmd</tt> 。</p>
<p>简单地说， <tt class="docutils literal">autocmd</tt> 是一种事件驱动式（event-driven）的命令， 能在某些用户感兴趣的事件发生之时自动执行预先指定的命令。例如， <tt class="docutils literal">autocmd BufLeave,FocusLost * wa</tt> 将在用户离开当前缓冲区时自动保存当前文件， <tt class="docutils literal">autocmd BufNewFile *.html 0r template.html</tt> 将在用户编辑一个全新的HTML文件 时自动加载模版文件 <tt class="docutils literal">template.html</tt> ， <tt class="docutils literal">autocmd BufReadPost *.doc %!antiword %</tt> 可在打开Word文档后利用 <a class="reference external" href="http://en.wikipedia.org/wiki/Antiword">Antiword</a> 将其转化为文本格式，等等。 考虑到适用于autocmd的事件多达近80种，涵盖了与编辑相关的各个阶段和行为，用户 足以借此打造一个高度自动化的编辑平台。</p>
</div>
<div id="id21" class="section">
<h3>其他操作</h3>
<p>Vim的操作方式还有许多，比如通过一系列的 <tt class="docutils literal">z</tt> 命令操作拼写列表； 通过 <tt class="docutils literal">:help</tt> 或 <tt class="docutils literal">K</tt> 显示在线帮助；通过 <tt class="docutils literal">:sh</tt> 开启一个shell； 通过 <tt class="docutils literal">:redir</tt> 将命令行或shell的执行结果导入文件、寄存器或变量； 用命令 <tt class="docutils literal">:hardcopy</tt> 和 <tt class="docutils literal">:TOhtml</tt> 将所选文字分别以PostScript和HTML的形式输出； 用命令 <tt class="docutils literal">ga</tt> <tt class="docutils literal">g8</tt> 或 <tt class="docutils literal">:list</tt> 显示非打印字符； 用命令 <tt class="docutils literal">g <span class="pre">Ctrl-G</span></tt> 显示行、列、字符、词等统计数据，等等。 此外，用户还能计算表达式、显示各种变量、设置和状态，并能实时改变各种设置或状态， 包括文件类型、界面格式、键盘映射、高亮设置、配色方案（colorscheme），等等。</p>
<p>最后，Vim虽不推荐用鼠标，但仍在Vi之上增加了鼠标操作，并且提供了菜单和工具条。 一方面，这降低了学习难度，对初学者显得更加友好。 另一方面，毋庸讳言，鼠标也有优于键盘的时候。 例如，在双手离开主键区时，用鼠标定位视线所及的区域往往更快捷； 在浏览长文件时，用鼠标滑轮前后滚屏通常更轻松。</p>
</div>
</div>
<div id="id22" class="section">
<h2>5. 增加定制方式</h2>
<p>窥一斑而知全豹。上述简介虽远未穷尽Vim的功能，但其丰富与强大已是彰显无遗。 难能可贵的是，Vim的灵活性与可扩展性并不因此而稍减，这让用户拥有足够的控制权。</p>
<div id="id23" class="section">
<h3>启动定制</h3>
<p>启动Vim需要一系列步骤，每步皆可由用户定制。以从命令行启动为例， 可供选择程序的就有：vi、vim、gvim、view、gview、rvim、rgvim、evim、eview、 vimdiff、gvimdiff等。其中，以g打头的是图形（graphic）模式；以e打头的是简易 （easy）模式，即与其他编辑器一样，只有输入模式（除非按 <tt class="docutils literal"><span class="pre">Ctrl-L</span></tt> 进入普通模式）； 以r打头的是限制（restricted）模式，该模式下无法启动shell命令； 以view结尾的是只读（read-only）模式；以diff结尾的是比较模式。 每个程序又可接不同的参数，进一步控制Vim的行为。</p>
<p>Vim最重要的配置是vimrc文件，实为一些ex命令的集合，在Vim初始化时自动执行。 图形模式下的Vim还会追加执行gvimrc文件中的命令。除了缺省的 .vimrc（Windows下是_vimrc）和.gvimrc（Windows下是_gvimrc）外， 用户也可通过命令行参数来指定其他文件，或用特殊文件名 <tt class="docutils literal">NONE</tt> 跳过初始化。</p>
<p>此外，除非在命令行参数中指明禁用插件，Vim启动时还会在运行时路径（runtimepath） 下寻找并执行插件，包括plugin目录下的 <em>全局插件</em> 与ftplugin目录下的 <em>文件类型插件</em> 。 所谓插件，无非是一些可重用的能完成某种特定功能的Vim脚本。 用户可以自己编写，也可利用他人的成果。 Vim的 <a class="reference external" href="http://www.vim.org/scripts/index.php">官方网站</a> 有近4千种插件供人下载， 极大地扩展了Vim的现有功能。</p>
<p>正常情况下，Vim启动时还会执行viminfo文件中的命令。该文件保留了以前命令行历史、 搜索字符串历史、 搜索与替换模式、寄存器内容、标记、缓冲区列表、全局变量等等， 使得一些重要编辑历史不因退出而消失。</p>
<p>viminfo储存的是一些全局信息，如果希望保留某一窗口的设置，可以利用view文件； 如果希望保留所有窗口的设置，可以利用session文件；如果希望保留撤销与恢复信息， 可以利用undo文件。这些文件均为自动生成，但用户也可手工修改。 它们在Vim下次启动之时将被加载，以恢复原先的窗口、 标签页、折叠区域、光标位置、 标记位置、跳转位置以及其他设置。 毫无疑问，这种持久化（persistence）有效地减少了因关闭Vim而带来的重复操作。</p>
<p>对Vim最重要的定制均应在启动之前完成，以下略加展开。</p>
</div>
<div id="id25" class="section">
<h3>命令定制</h3>
<p>Vim尽管提供了足够多的命令，依然给予用户充分的定制空间。</p>
<p>一种定制方式是：用户为原有的命令赋予新的行为。 比如，对于缩进命令 <tt class="docutils literal">=</tt> 和格式化命令 <tt class="docutils literal">gq</tt> ，用户可分别通过定义 <tt class="docutils literal">equalprg</tt> 或 <tt class="docutils literal">formatprg</tt> 调用指定的外部程序， 也可分别通过定义 <tt class="docutils literal">indentexpr</tt> 或 <tt class="docutils literal">formatexpr</tt> 调用自定义的表达式。</p>
<p>另一种定制方式是：通过 <tt class="docutils literal">nmap</tt> 、 <tt class="docutils literal">vmap</tt> 、 <tt class="docutils literal">omap</tt> 等命令建立各种模式下的键盘映射。 用户借此可自定义各种实用的组合命令，定义新的文本对象（见前例），也可改变原命令的定义。 只要用户愿意，他甚至可以将原有的命令集合改得体无完肤，重建一套截然不同的命令体系。</p>
<p>最强大的定制方式，当然是利用VimScript或其他脚本语言及外部程序来定义全新的命令， 这也是许多插件所做的工作。</p>
</div>
<div id="id26" class="section">
<h3>编辑定制</h3>
<p>Vim能根据文件的后缀或内容自动识别上百种文件类型，并据此选用相应的预处理机制、 语法高亮机制、文本宽度、缩进风格、折叠方式等。 在此基础上，用户可新增或修改文件类型、语法结构、处理机制以及各种格式选项。 通过这类定制，为用户创造良好的编辑环境，有利于提高编辑效率。</p>
<p>针对不同类型的文件结构，用户可进一步量身定制，以期最大限度地减少按键。 比如对于冗余度较高的XML、HTML类型的文件，可通过安装插件 <a class="reference external" href="http://www.vim.org/scripts/script.php?script_id=301">xmledit</a> 、 <a class="reference external" href="http://www.vim.org/scripts/script.php?script_id=2981">ZenCoding</a> 或 <a class="reference external" href="https://github.com/rstacruz/sparkup/">sparkup</a> 来减轻编辑负担。</p>
<p>一个常用节省键击的方式是通过 <tt class="docutils literal">iabbrev</tt> 来定制输入模式下的缩写。 要想实现更高级的类似TextMate的片段（snippet）功能，可安装插件 <a class="reference external" href="http://www.vim.org/scripts/script.php?script_id=2540">snipMate</a> 。</p>
<p>自动补全（autocomplete）能大大提高输入的效率和准确度。 Vim根据用户对 <tt class="docutils literal">complete</tt> 选项的设置， 从当前缓冲区、其他窗口缓冲区、加载缓冲区、 字典（dictionary）、词典（thesaurus）、包含文件（included files）、 标签文件等中选择，也可通过设置 <tt class="docutils literal">omnifunc</tt> 调用万能补全函数。</p>
<p>另外，要避免文本的拼写错误，用户可设置语种及其相应的拼写文件。 同理，要避免代码的语法错误，用户也可通过定制或安装插件来完成。</p>
</div>
<div id="id27" class="section">
<h3>其他定制</h3>
<p>Vim提供给用户的选择还有很多。比如，用户可打造完全个性化的界面，包括鼠标、 菜单、 工具栏、光标、状态栏、标签页、滚动条、提示框（tooltip）、字体、配色方案，等等； 可定制折叠方式（foldmethod）、折叠级别（foldlevel）、折叠文本（foldtext）等； 可定制搜索是否高亮匹配、增量移动、大小写敏感、智能判断、循环扫描等； 可定制交换（swap）文件以及备份（backup）文件的保存路径、后缀名以及是否禁用； 可定制错误格式（errorformat）以利用QuickFix来调试程序； 可定制文件的编码、格式（dos、unix或mac）、加密方法（zip或blowfish）； 可定制个人的帮助文件、笔记乃至知识管理系统；如此等等，恕难一一列举。</p>
</div>
</div>
</div>
<div id="id28" class="section">
<h1>结语</h1>
<p>本文不是对Vim功能简单的堆砌和罗列——那只会让初学者更加望而却步—— 而是试图总结它的设计理念和实现方法，以此来说明其“编辑器之神”之誉既非过词， 亦非幸致。与之相应地，Emacs有“神之编辑器”的称号。假如果真如此，那说明 人类用户还是选择Vim为好——他们不是神，无法变出第三只手去hold住修饰键 （此为戏言，还请Emacs的拥趸勿恼）。</p>
<p>或许有人认为编辑器效率对工作效率的影响十分有限，毕竟人们大部分时间是花在思考上， 而不是花在编辑上。但不要忘记，用户的大部分思考需要参考不同的文件以及同一文件的不同 片段，有时还需要对文本进行必要的调整或修改，如果编辑器不能给予用户 “移动如飞、改动如电”的能力，必然频繁消耗用户的目光、手指和注意力，最终降低工作效率。 换言之，“快”的意义绝不仅仅在于节省的那些浏览或修改的时间，更重要的是 节省思维与文字之间的转换开销，让思考活动与编辑行为尽可能地交融无碍。</p>
<p>有种说法是“程序高手都用Vim”，还有种说法是“用Vim的都是程序高手”。 说实话，程序高手与是否用Vim并无必然联系。但有一点可以肯定，优秀的程序员总会不断 追求更加高效的工作方式，就像不断追求更加高效的代码一样。 无论是开源的Vim、Emacs、Eclipse或NetBeans，还是闭源的Visual Studio、Xcode、 SlickEdit或TextMate，各有其长，关键在于使用者能否做到“运用之妙，存乎一心”。 话说回来，见到那些声称只用Notepad来写代码的“高手”，心头还是不免为之一紧： 这是怎样的自虐啊。</p>
<p>惯用Vim者会发现它有一个副作用：一旦换到其他编辑器下，就像一个习惯奔跑的人 不得不停下来踱步一样，那种感受直可用“兔心龟步”来形容。 不过仅仅用“快”来形容Vim还是不够的，还得加上一个“柔”字，即它具有高度的灵活性和 可扩展性。毫不夸张地说，正是Vim的高可定制化让其原本已极其巨大的威力变得几无极限。 假如用户艳羡其他编辑器中的某项功能，大可利用Vim的柔性复制过来。</p>
<p>Vim丰富的命令与灵活的定制为熟手津津乐道，也让生手视为畏途。 实际上，正如此前所指出的那样，Vim的多模式特征让其命令更有意义也更易记忆。 下面以普通模式下的命令为例（注意箭头后的首字母）：</p>
<pre class="literal-block">
a(A) =&gt; Append                       b(B) =&gt; Backward word
c(C) =&gt; Change                       d(D) =&gt; Delete
e(E) =&gt; End of word                  f(F) =&gt; Find
g(G) =&gt; Go                           H    =&gt; Home of window
i(I) =&gt; Insert                       J    =&gt; Join
K    =&gt; Keyword under the cursor     L    =&gt; Last line of window
m    =&gt; Mark                         M    =&gt; Middle line of window
n(N) =&gt; Next find                    o(O) =&gt; Open a new line
p    =&gt; Paste                        r(R) =&gt; Replace
s(S) =&gt; Substitute                    t(T) =&gt; Till
u(U) =&gt; Undo                         v(V) =&gt; Visual mode
w(W) =&gt; Word move                    y(Y) =&gt; Yank</pre>
<p>由上可见，除了代替方向键的 <tt class="docutils literal">hjkl</tt> 四小写字母外，剩下只有三对字母 <tt class="docutils literal">Q(q)</tt> 、 <tt class="docutils literal">z(Z)</tt> 和 <tt class="docutils literal">x(X)</tt> 了。这三个均属最难组词的字母， 其中 <tt class="docutils literal">x(X)</tt> 代表删除或剪切，与以前打字机删除字符相同，形状上也像剪刀； <tt class="docutils literal">z</tt> 主要用于折叠命令，可以理解为 <strong>Z</strong> ip（拉链），形状上也具折叠态。 其他如 <tt class="docutils literal">|</tt> 代表列、 <tt class="docutils literal">&gt;</tt> 代表缩进、 <tt class="docutils literal">(</tt> 代表句首， <tt class="docutils literal">{</tt> 代表段首等 皆极具象征意义。至于命令行模式下的就更不用说，大多都是单词或词组的简写。</p>
<p>除了富有涵义的单词和隐喻之外，Vim的命令和选项的设计还处处透着一致性， 进一步减少了记忆的负担。事实上，这也是Vim的 <strong>宗旨</strong> 之一。 比如，移动命令与文本对象相一致；相同字母的大小写 命令之间通常是对应的，且小写的更常用；相同首字母的命令中叠字者更常用， 且多表示行操作；代表方向的 <tt class="docutils literal">hjkl</tt> 应用于折叠移动（ <tt class="docutils literal">zj</tt> 、 <tt class="docutils literal">zk</tt> ）、 窗口移动（ <tt class="docutils literal"><span class="pre">Ctrl-W-J</span></tt> 、 <tt class="docutils literal"><span class="pre">Ctrl-W-K</span></tt> 、 <tt class="docutils literal"><span class="pre">Ctrl-W-H</span></tt> 、 <tt class="docutils literal"><span class="pre">Ctrl-W-L</span></tt> ）、 插入模式下的移动（ <tt class="docutils literal"><span class="pre">Ctrl-G-J</span></tt> 、 <tt class="docutils literal"><span class="pre">Ctrl-G-K</span></tt> ）等 以及ex命令中 <tt class="docutils literal">!</tt> 的用法，等等。</p>
<p>至于定制方面，网上有大量的Vim配置可供参考。初学者可先行“拿来主义”， 挑一个合适自己的来用，以后慢慢学习领会，渐进改造直至称心如意。</p>
<p>Vim虽以难著称，其实有一个宗旨：让新手尽快上路，然后在使用中逐步累积知识。 对于老手而言，Vim又是常学常新的。纵是在Vim世界里浸淫十余年者，也随时可 享俯拾遗珠之乐。</p>
<p>人们常常强调Vim陡峭的学习曲线，却忽略事情的另一面，即它的效率曲线也是陡峭的。 当然Vim的独特性决定了它永远成不了最流行的编辑器，但这实在不重要， 重要的是凡窥其门径者，无一不留恋难舍，大有“除却巫山不是云”之感。 好在即使离开了Vim，手指依旧可以在别处弹奏着同样独特的韵律。 在许多编辑器或IDE下，都可使用Vi或Vim的键盘绑定，其中包括：Emacs、 Eclipse、 NetBeans、IntelliJ、Visual Studio、SlickEdit、XCode、TextMate、Komodo等。 在非编辑器的环境中，同样有Vi（Vim）的身影。 在Firefox、Chrome、Safari等浏览器中都有类Vim的插件， 在*nix的命令行终端下经配置也可使用Vi， 在Mac下有一些软件可以为大多数应用程序（Cocoa Application）的文本框绑定Vi输入法。 所有这些的背后，都站着一大批受到Vim魅力感召的人们。</p>
<p>回到本文开篇的问题：Vim的魅力何在？我的答案是：表面上在于快和柔， 本质上则在于对用户的最大尊重——尊重用户的体验与感受，尊重用户的自由与智慧。 Vim以其独有的理念充分地发挥了用户的能动性与想象力，从而营造出一个空寂的世界， 在那里无按钮菜单之分神，无弹出窗口之聒噪，无鼠标之不便， 只有意念在键盘上静静而自然地流淌，让人沉浸其中而不愿自拔。 最后，请允许我用一段绝非刻意而为之的对比来重述自己的观点：</p>
<pre class="literal-block">
Vim最大的障碍不在于其本身，而在于用户的自我束缚。
Vim最大的魅力不在于其本身，而在于用户的自我解放。</pre>
<p>（全文完）</p>
<p>附：本人的 <a class="reference external" href="https://github.com/hzheng/vimconf">Vim配置</a> 。</p>
</div>
<div id="id30" class="section">
<h1>参考文献</h1>
<ol class="arabic simple">
	<li>Bram Moolenaar. VIM USER MANUAL</li>
	<li>Arnold Robbins, et al. Learning the vi and Vim Editors(7ed)</li>
	<li>Kim Schulz. Hacking Vim 7.2</li>
	<li><a class="reference external" href="http://en.wikipedia.org/wiki/Vim_(text_editor)">Wikipedia: Vim</a></li>
	<li><a class="reference external" href="http://en.wikipedia.org/wiki/Vi">Wikipedia: Vi</a></li>
	<li><a class="reference external" href="http://en.wikipedia.org/wiki/Markup_language">Wikipedia: Markup Language</a></li>
	<li><a class="reference external" href="http://en.wikipedia.org/wiki/Lightweight_markup_language">Wikipedia: Lightweight Markup Language</a></li>
	<li><a class="reference external" href="http://www.moolenaar.net/habits.html">Seven habits of effective text editing</a></li>
	<li><a class="reference external" href="http://arstechnica.com/open-source/news/2011/11/two-decades-of-productivity-vims-20th-anniversary.ars">Two decades of productivity: Vim&#8217;s 20th anniversary</a></li>
	<li><a class="reference external" href="http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html">Graphical vi-vim Cheat Sheet and Tutorial</a></li>
	<li><a class="reference external" href="http://www.viemu.com/a-why-vi-vim.html">Why, oh WHY, do those #?@! nutheads use vi?</a></li>
</ol>
</div>
</div>
<p>&nbsp;</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2011%2F12%2F13%2Fcharm-of-vim-2%2F&amp;title=VIM%E4%B9%8B%E9%AD%85%EF%BC%88%E4%B8%8B%EF%BC%89" id="wpa2a_4">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年11月30日 -- <a href="http://blog.zhenghui.org/2011/11/30/charm-of-vim-1/" title="VIM之魅（上）">VIM之魅（上）</a> (26)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=DJxPZvP_CfY:zJoUzEAY6qU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/DJxPZvP_CfY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2011/12/13/charm-of-vim-2/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2011/12/13/charm-of-vim-2/</feedburner:origLink></item>
		<item>
		<title>VIM之魅（上）</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/sGCEqDqstCA/</link>
		<comments>http://blog.zhenghui.org/2011/11/30/charm-of-vim-1/#comments</comments>
		<pubDate>Wed, 30 Nov 2011 15:08:13 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[开发工具]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[Vi]]></category>
		<category><![CDATA[Vim]]></category>
		<category><![CDATA[键盘]]></category>
		<category><![CDATA[鼠标]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=918</guid>
		<description><![CDATA[<b>VIM之魅（上）</b><br/> • 前言<br/> • 关于Vim的误解<br/> • Vim的理念 [...]]]></description>
			<content:encoded><![CDATA[<p>&nbsp;</p>
<div id="vim" class="document">
<h1 class="title">VIM之魅（上）</h1>
<div id="id1" class="section">
<h1>前言</h1>
<p>本月，著名的文本编辑器 <a class="reference external" href="http://en.wikipedia.org/wiki/Vim_(text_editor)">Vim</a> 迎来了它的 <a class="reference external" href="http://arstechnica.com/open-source/news/2011/11/two-decades-of-productivity-vims-20th-anniversary.ars">二十大寿</a> 。 凑巧本人最近正在折腾Vim的配置和插件，心头正痒之时逢此佳际，便再也按捺不住写博的冲动。 其实，为Vim撰文的念头早已有之，只是于今为烈。</p>
<p>Vim的全称Vi IMproved（早期名为Vi IMitation），顾名思义是Unix上流行编辑器 <a class="reference external" href="http://en.wikipedia.org/wiki/Vi">Vi</a> 的模仿和改进版。 毫不夸张地说，自从接触计算机以来，见识过无数的应用软件，深得吾心者唯Vim一款而已。 有时用到兴处，不免心生感慨：这才是软件中的极品呢，识之而不荐之简直就是一种罪过啊。 想起Vim还是慈善软件（careware或charityware），而自己从未给可怜的乌干达孩子们捐过一分钱， 更觉有些羞惭。为了心中的负疚感，我也早该为它写点什么了。</p>
<p>谈及Vim的优点，开源、免费、小巧、成熟、跨平台、文档丰富、社区活跃等等固是不假， 但相较其真正的妙处，以上皆不足道。 事实上，这些因素直接影响的主要是软件的使用成本，而非使用价值。 真正卓越的软件，会让深谙其道的用户不惜代价地拥有，因为它最终将带来远超成本的回报。 <a class="reference external" href="http://macromates.com/">TextMate</a> （恰好也是一种文本编辑器）便是一例， 虽收费不菲，且仅能运行于Mac， 但仍然吸引了大量的用户。 不少人甚至为了能用上它而专门购买Mac机，正如其开发者所号召的那样： <a class="reference external" href="http://blog.macromates.com/2007/buy-a-mac-get-textmate">Buy a Mac, get TextMate!</a></p>
<p>用户对Vim向来毁誉不一：初见者往往嫌其平凡甚或简陋，初试者往往觉其古怪难用， 而熟用者则食髓知味（词虽暧昧，却极准确）并奉为极致。 为何同一产品的评价如此两极分化？Vim究竟有何魅力值得大书特书？ 下面就此谈谈个人的一些见解，并分享一些心得。不当之处敬请指正，不足之处欢迎补充。</p>
</div>
<div id="id2" class="section">
<h1>关于Vim的误解</h1>
<p>首先澄清一些对Vim的常见误解。</p>
<ol class="arabic simple">
	<li>Vim太古老，与现代编辑器相比早已过时</li>
</ol>
<p>的确，计算机领域日新月异，一个年届二十的软件算得上是高龄者了。 何况Vim的大部分精华传承自Vi，而后者已三十有五，更是垂垂老矣。 然从另一个角度看，一个软件在漫长时间的洗礼和无数后辈的冲击之下能够屹立不倒， 不正彰显出它的伟大吗？更令人称奇的是：时至今日，Vi/Vim的许多理念不仅没有落后， 反依旧保持领先。关于这一点，容我稍后再述。 此外，Vim本身也在不断地发展，就在一年前还发布了最新稳定版（7.3）。</p>
<ol class="arabic simple" start="2">
	<li>Vim是程序员（或geek）的玩意，一般人用不上</li>
</ol>
<p>虽然Vim在程序员手中更能发挥威力，但编程绝非其唯一的用武之地。 除了IT人士，学生、教师、科研工作者、记者、文字工作者等等，凡常需文字编辑者均可考虑使用。</p>
<ol class="arabic simple" start="3">
	<li>Vim难学又难用，犯不着为区区一个编辑器而为难自己</li>
</ol>
<p>相比一般的编辑器，Vim的学习曲线无疑陡峭得多，其用法更是异乎寻常。 但从长远来看，对于一个频繁处理文字的人来说，这点投资绝对是值得的。 “磨刀不误砍柴工”的道理谁都明白，可生活中愿意磨刀的人还真不多。 一个常见的例子是，许多经常打字的人宁肯用“二指禅”，也不愿练习盲打。 放着一本万利的事不做，偏去干无本微利的活，与其说是惰性太大，不如说是理性太少。 再说，Vim虽然命令丰富、功能强大，却并不因此而显得复杂（这当然得益于精巧和谐的设计）。 根据本人经验，十分钟便能入门，一周内即可基本熟练。 此时虽距精通尚远，但效率已远超一般编辑器了。 总的说来， <strong>学用Vim最大的障碍并不在于其本身，而在于固有观念和习惯对人的束缚</strong> 。</p>
<ol class="arabic simple" start="4">
	<li>Vim只能用于编辑纯文本格式的文件，适用范围太窄</li>
</ol>
<p>首先为纯文本格式正名。不得不说，人们实在太过倚重二进制格式了。 就拿最常用的Word来说，用户不妨自问一下： 有多少次是习惯性地打开Word编辑文档而实际上只用到了记事本（Notepad）的功能？ 又有多少次只用到了写字板（WordPad）的功能？ 即便用到了Word的某些高级功能，是否又为各种排版问题而大费周章？ 第一种情况说明纯文本格式足矣，后两种情况可考虑用 <em>标记语言</em> （ <a class="reference external" href="http://en.wikipedia.org/wiki/Markup_language">Markup Language</a> ）代替。 所谓标记语言，即是在文本中规定一些特殊的语法标记，从而将普通文本结构化。 最常见的标记语言是广泛用于互联网的HTML和XML（Word 2003也开始支持XML）。 此外，广泛运用于学术报告和科技论文的 <a class="reference external" href="http://www.latex-project.org/">LaTeX</a> 也是一种标记语言，在处理排版、图表、文献等方面远胜Word，当然学习门槛也远比后者为高。 近年来， <a class="reference external" href="http://textile.sitemonks.com/">Textile</a> 、 <a class="reference external" href="http://daringfireball.net/projects/markdown/">MarkDown</a> 、 <a class="reference external" href="http://docutils.sourceorge.net/rst.html">reStructuredText</a> 等 <em>轻量级标记语言</em> （ <a class="reference external" href="http://en.wikipedia.org/wiki/Lightweight_markup_language">Lightweight Markup Language</a> ） 开始流行。比起XML，它们更简洁、更直观，因而也更方便用纯文本编辑器生成。 本网站的以前博文是由基于XML的 <a class="reference external" href="http://docbook.org">DocBook</a> 生成的， 从本文开始则采用reStructuredText。 之所以不用Word， 是因为它价格不菲（盗版也要花良心、安全、时间等成本）、 平台单一（主要运行于Windows，Mac版较难用）、格式封闭、体积较大、速度较慢、 不够安全（可能中毒）、欠缺稳定（有时崩溃）、难以用Vim编写（二进制格式）等等。 此外，Word的一大优点是“ <em>所见即所得</em> ”（WYSIWYG），但相应的缺点是“ <em>所得仅所见</em> ”。 由于内容与形式混成一体，既不利于维护，也不利于扩展。 作为对比，DocBook、LaTeX、reStructuredText等标记文本将内容与形式分离， 同一内容不仅可生成多种文件格式（如HTML、XML、PDF、ODF、RTF等），还能生成不同的样式风格； 反过来，同一样式可应用于不同的内容（Word也有模版，但弗如远甚）。</p>
<p>不仅类似Word的字处理器可以采用文本格式替代，类似Excel的电子表格同样如此。 （注：按说应用软件与文件格式本不应混为一谈，但由于二者关系紧密，为行文简洁本文暂不区分） 用XML结合XSLT技术，或者用CSV结合Vim插件，不仅可制作表格，还能统计数据。 如果需要轻量级数据库，不一定要用Access、SQLite或者MySQL，XML或 <a class="reference external" href="http://en.wikipedia.org/wiki/YAML">YAML</a> 也许已经足够。 图形文件也不是非二进制格式不可的，不妨考虑 <a class="reference external" href="http://en.wikipedia.org/wiki/Scalable_Vector_Graphics">SVG</a> 矢量图 或者 <a class="reference external" href="http://en.wikipedia.org/wiki/DOT_language">DOT语言</a> ， 还可试试有趣的 <a class="reference external" href="http://en.wikipedia.org/wiki/ASCII_art">ASCII Art</a> 。 甚至声音文件也有文本形式的 <a class="reference external" href="http://abcnotation.com/">ABC记谱法</a> 。</p>
<p>相比二进制格式，文本格式的好处有很多：格式公开透明，适用各种平台和工具； 方便人工编辑、查看和搜索；利于脚本处理和版本控制； 不会因部分数据毁损而出现打不开文件的情形，等等。 这当然不是在劝说大家放弃使用以二进制文件为基础的应用软件， 毕竟后者更加易学、易用，界面也更美观，功能通常也更完备， 而是旨在说明：文本格式绝不是低级、原始或狭窄的代名词。 无论是私人的记事本、邮件、日记，还是网络的博客、论坛、维基， 或者报刊文章、论文书籍，文本格式皆可充任主力军。</p>
<p>醉翁之意不在酒，以上列举了纯文本的诸般优点，自然是为了给Vim更多一展身手的机会。 不过Vim对非文本文件也不是完全束手无策的。 例如，它能直接编辑zip、gzip、tar、bzip2等格式的压缩文件。 以前我一直用 <a class="reference external" href="http://keepass.info/">KeePass</a> 统一管理密码， 但总觉有些不便，在得知最新Vim已支持在线编辑加密文件（Blowfish算法）后， 开始有了改用Vim的打算。其实何止是压缩和加密文件，理论上对于一切已知算法的文件， Vim皆可在打开、编辑、保存之后保持原有的二进制格式。另外，假如用户熟悉某种二进制格式，在设定 &#8220;:set binary&#8220; 后，也可直接编辑原始的二进制文件（如JPG文件）。</p>
</div>
<div id="id3" class="section">
<h1>Vim的理念</h1>
<p>冗长的铺垫之后，终于迎来了本文的正题：Vim到底好在哪里？ 这个问题的答案既是一言难尽，也可一字道尽，那就是—— <em>快</em> 。 手指随心而蹈之时，如奔雷闪电，快得无与伦比，快到不可理喻。 Vim何以如此之快？以在下之见，诀窍在于它秉承的理念： 减少使用鼠标、减少敲击键盘、减少手指移动、减少目光移动。</p>
<div id="id4" class="section">
<h2>1. 减少使用鼠标</h2>
<p>不可否认，鼠标是一项伟大的发明，直观、灵活、简便、易用。 但如同对二进制格式一样，人们对鼠标（或其他定点设备）也是过度依赖了。 大多时候，鼠标的方便是以降低效率为代价的。 在Windows下，一个常见的使用场景是：鼠标点击“开始”菜单，滑动 到某个应用后点开应用程序，在程序中继续使用鼠标操作各种菜单或工具栏。 很少人会这么做：通过键盘（Win+R）打开运行对话框，然后键入命令从而启动程序。 更少人愿意花功夫为常用的程序绑定自设的快捷键，并学习和使用程序自带的快捷键功能。 究竟有多少人不会使用除Ctrl-C（复制）和Ctrl-V（粘贴）以外的热键不得而知， 不过前不久Google的一位专家有一个惊人的发现， <a class="reference external" href="http://www.theatlantic.com/technology/archive/2011/08/crazy-90-percent-of-people-dont-know-how-to-use-ctrl-f/243840/">九成美国互联网用户不知Ctrl-F有何功用</a> 。</p>
<p>常识告诉我们，在使用电脑时，只有在眼、手、心三者合一的情况下才能达到最高效率， 而在频繁使用鼠标的过程中，用户的眼光在菜单、工具栏、按钮、弹出窗口与程序主版之间来回地切换， 手指在键盘和鼠标之间不断地移动，注意力不时被分散，效率因此大打折扣。比较几个最常见的操作场景：是点击“确定”或“取消”按钮快，还是用回车键或Esc键快？是把鼠标移到小叉处关闭窗口快，还是按Alt+F4、Ctrl-W或Cmd-W快？是在键盘输入时切换到鼠标以点击邻近输入框快，还是用Tab或Shift-Tab快？在切换程序时，是用鼠标在任务栏点击快，还是用Alt-Tab或Cmd-Tab快？
在文本编辑中，鼠标的弊端愈加明显。以一个简单情形为例： 光标在某行的中间，用户希望把该行与下行对调。若用鼠标，则须先将其移至行头， 选定整行，然后拖放到下一行（碰上Notepad这类不支持拖放的编辑器就更费事了）。 鼠标稍有不灵，或眼神不济，或手指不稳，都可能导致操作失误。费眼、费手、费时、费神。 换成Vim用户，在正常模式下闭目输入 <tt class="docutils literal">ddp</tt> 三字符即可，此时前者恐怕才刚刚摸到鼠标呢。 如果把任务换成：将当前行移至最末行，请非Vim用户再用鼠标拖放试试？ 他只能祈祷最末行不要距离太远，尤其不要在滚动窗口之外。 Vim用户遇此则毫无难色，一套连环四击（ <tt class="docutils literal">ddGp</tt> ）便完事大吉。</p>
<p>Vim注重键盘的基因来自Vi，作为Unix下的一个典型应用，后者完全不支持鼠标。 这可以看成一种历史局限，但也正是这种局限迫使设计者把键盘利用到极致。 请看下图——</p>
<p><img class="align-center" src="http://blog.zhenghui.org/img/vi-vim-cheat-sheet.gif" alt="http://blog.zhenghui.org/img/vi-vim-cheat-sheet.gif" /></p>
<p>图中的键盘（包括上下）总共95种按键，仅1种（“\”键）未被征用，利用率高达99%。 即便如此，也远未穷尽Vim的命令。 <a class="reference external" href="http://michael.peopleofhonoronly.com/vim/">另一参考图</a> 更详尽些，依然只是冰山一角。</p>
</div>
<div id="id6" class="section">
<h2>2. 减少敲击键盘</h2>
<p>在Word下，前述交换两行文字的任务也可不用鼠标完成：先按Home键，再依次按Shift键、 End键、Ctrl-X、下方向键、Ctrl-V。只是共计八键，比Vim多了一倍有余。 论起精减Vim（Vi）键击次数的功臣，当首推其独树一帜的 <em>多模式（multi-mode）</em> 特征。 据我所知，编辑器中除号称Windows下TextMate的 <a class="reference external" href="http://www.e-texteditor.com/">E Text Editor</a> 支持multi-edit模式外， 其他皆无明确的模式概念。 关于模式，常有两种误解。 一是以为Vim只有两种或三种模式，而实际上它有六种基本模式和六种衍生模式。 二是以为其他编辑器是单模式。其实，任何编辑器至少有两种模式：一种是 插入模式（insert mode），一种是命令模式（nd mode）。 每当用户按住如Ctrl、Alt、Fn、Win、d等修饰键（modifier key）时， 实际是进入了命令模式，一旦松开修饰键，便回到插入模式。由于此过程转瞬即逝， 用户往往并无模式的观念，故有人称之为 <a class="reference external" href="http://en.wikipedia.org/wiki/Mode_(computer_interface)#Quasimodes">准模式（quasimode）</a> 。 Vim也有准模式，只是并非主流。 因此， <strong>Vim与非Vi类编辑器之间的区别不在于有无模式，而在于模式能否持续</strong> 。 模式不能持续带来的一个问题是，一旦需要对文本进行连续调整时，会频繁求助修饰键 （用鼠标只会更慢）。Vim则不然，一旦通过Esc键切换到命令模式后便一直保持， 直到用户主动退出。其间所有命令不再需要多余的修饰键，自然节省了键击次数。 这颇为类似Caps Lock键的功用，即一旦开启便转为大写字母模式，不再需要Shift键的修饰。另外，一些键盘的Insert键也能从默认的插入模式转至改写模式（overtype mode）——
相当于Vim中的替换模式（replace mode）——输入的字符将覆盖原有的字符。</p>
<p>修饰键的另一局限是，每键只能修饰一个普通键，这为命令的设计和使用带来了困难。 与Vim齐名的编辑器Emacs同样以功能强大、命令繁多而闻名，但由于没有显式模式， 键盘上的普通字符根本不够用，只好经常采用多个修饰键组合的命令，以致于有人戏称Emacs是 &#8221; <strong>E</strong> sc- <strong>M</strong> eta- <strong>A</strong> lt- <strong>C</strong> trl- <strong>S</strong> hift&#8221;的缩写。 模式让Vim摆脱了修饰键的羁绊，大大扩展了命令的设计空间， 这也是Vim的命令虽多却简洁易记的根本原因。</p>
<p>模式是Vim初学者碰到的最早也是最大的障碍，一次次的哔哔声更令人或茫然、或沮丧。 很多人因此知难而退，殊不知翻过此山后，前面将是一马平川，任君驰骋 （参见趣味的 <a class="reference external" href="http://unix.rulez.org/~calver/pictures/curves.jpg">学习曲线图</a> ）。 一切特别的别扭之后通常都隐藏着某种特别的自然，要克服模式的障碍，除了勤加练习外， 更重要的是理解其设计理念。模式的奥妙不用外寻，尽在其名之中。此话何解？ 前面提到命令模式，那是为了方便Vim与其他编辑器对比，其实Vim相应的术语是 普通模式（normal mode）。“普通”二字可不普通，这意味着对编辑器的彻底颠覆——</p>
<pre class="literal-block">在一般编辑器中，插入模式是“普通”的，命令模式是“特殊”的；
在Vi类编辑器中，命令模式是“普通”的，插入模式是“特殊”的。</pre>
<p>这启示我们，使用Vi类编辑器的一个诀窍是： <strong>让普通模式成为常态，让插入模式成为暂态</strong> 。 具体地说，打开Vim便默认进入普通模式，需要添加文字时才切换到插入模式， 一旦完成立刻返回普通模式（通常不正是这么用替换模式的吗？）。这就避免了用户时常错判当前模式的问题—— 当他在把脑中的思想转化为文字时，应当是插入模式； 当他正在酝酿思考、修改文字或跳转页面时，应当是普通模式。 养成习惯后自然成为下意识，不必从光标、状态栏或哔哔声来判断当前模式， 这才是更高效的用法。</p>
<p>进一步考察Vi的模式哲学，会发现它具有天然的合理性。除了打字员，一般人 在编辑文档时大部分时间都是用于思考、修改、浏览的，只有少部分时间在 输入全新的内容。这点对程序员而言尤其正确，设计代码、修改代码、 浏览代码的时间总是大大多于编写代码的时间。 对文字创作者而言同样成立，世上有多少人能做到文思泉涌、下笔千言、 倚马可待且一字不易呢？再天才的作家不也讲究反复推敲吗？ 对那些文字“借鉴”者来说就更重要了， <tt class="docutils literal"><span class="pre">Ctrl-C</span> <span class="pre">Ctrl-V</span></tt> 之后， 剩下的不都是些拼拼凑凑、改头换面的活儿吗？</p>
<p>光有模式哲学是不够的，还得靠强大的命令体系支撑。如果在普通模式下 不能高效地完成各种任务，那么让其成为常态也是没多大意义的。 好在Vim的命令丰富而不失简洁、强大而不失灵活，常人凡能想到的编辑浏览动作， Vim都有相应的命令，并且通常不止一种。</p>
<p>除模式之外，避免重复动作是Vim节省按键次数的另一大法宝。 最基本的拷贝粘贴自不必说，Vim还能自动将一些改动的文字保存在寄存器 （register，相当于剪贴板）中，以供日后粘贴之用， 更可让用户将复制内容放入指定的寄存器中。</p>
<p>在进行文字搜索时， <tt class="docutils literal">;</tt> 可重复上次行内（字符）搜索， <tt class="docutils literal">,</tt> 可反向重复上次行内搜索， <tt class="docutils literal">n</tt> 可重复上次全局（字串）搜索， <tt class="docutils literal">N</tt> 可反向重复上次全局搜索。在命令行模式（command-line mode）中进行文本替换时， <tt class="docutils literal">&#038;</tt> 可重复上次替换。
 使用可视化模式（visual mode）时， <tt class="docutils literal">gv</tt> 可恢复上次进入该模式时选定的区域。</p>
<p>还有更多：键入 <tt class="docutils literal">.</tt> （小数点），便能重复上次在普通模式下的编辑命令，何其方便； 键入 <tt class="docutils literal">@:</tt> ， 便能重复上次在命令行模式下的编辑命令，何其自然 （ 注：Vim中 <tt class="docutils literal">@</tt> 代表回放， <tt class="docutils literal">:</tt> 是命令行模式的切换键）； 用 <tt class="docutils literal">q</tt> 命令启动宏（macro），更能录制一系列按键动作，可随时无限次重放，何其强大。</p>
<p>Vim不仅能帮用户重复一次操作，还可重复多次。 在普通模式下，数字前缀可指定命令的重复次数。 如 <tt class="docutils literal">dd</tt> 代表删除1行文字， <tt class="docutils literal">10dd</tt> 则将删除10行。 在命令行模式下，可对指定范围和匹配模式的行进行统一处理。 如 <tt class="docutils literal">20,100g/the/d</tt> 表示删除第20行到第100行之间包含 <tt class="docutils literal">the</tt> 字串的所有行。 在可视化模式下，也能对所选区域进行统一操作。 如 <tt class="docutils literal">Vr*</tt> 将当前行的所有字符全部变成星号。</p>
<p>若对Vim进行适当配置，节约键击的方式更是五花八门。 比如，按Tab键便能在插入模式、命令行模式下自动完成单词， 参考的范围有当前打开的所有文件、指定的字库、程序的关键词、函数库、文件的路径等等。 利用 <tt class="docutils literal">map</tt> 功能可以建立各种模式下的键盘映射，定义自认为更合理、更简洁的命令和缩写。 如果经常把 <tt class="docutils literal">the</tt> 写成 <tt class="docutils literal">teh</tt> ， 可设置 <tt class="docutils literal">abbreviate</tt> 进行自动纠正。 如此等等，不胜枚举。</p>
<p>狂热的Vimer都以少敲按键为荣，正如 <a class="reference external" href="http://vimgolf.com/">vimgolf.com</a> 上所说：Real Vim ninjas count <strong>every</strong> keystroke（真正的Vim忍者会数每个按键）。 普通用户倒也不必如此，不过一定要明白一件事：当你经常重复某些编辑动作的时候， 很可能一个的美妙命令正擦肩而过。假如当真无此命令，Vim也授予了你自创的权力。 每当我在Vim下的指法开始凌乱，甚至忍不住去碰鼠标的时候， 或者频繁在插入模式下修改内容的时候，总会感到Vim之神在一旁坏笑： shame on you！（译成时兴的中文网语是：你弱爆了！）</p>
</div>
<div id="id8" class="section">
<h2>3. 减少手指移动</h2>
<p>Vim不仅提倡少用鼠标、少敲键盘，还提倡少动手指。 显而易见，在按键次数相同的情况下，手指的移动距离越小，效率自然越高。</p>
<p>模式的存在使得Vim的命令不太依赖修饰键，多由字母键组成。 用户的手指无疑更青睐后者，不仅更熟悉，移动距离和扭动角度也更小。 反观Emacs，用户频繁用小指按修饰键，会产生所谓的 <a class="reference external" href="http://en.wikipedia.org/wiki/Emacs#Emacs_pinky">Emacs小指问题</a> ， 以致于有人恨不能用脚踏板来代替修饰键（别说， <a class="reference external" href="http://www.techready.co.uk/Assistive-Technology/Foot-Switches/Savant-Elite-Triple-Foot-Switch">还真有</a> ），以解救受苦受难的小指。</p>
<p>除模式之外，最令Vim初学者感到不习惯的恐怕要数方向键的用法了。 在Vim的正常模式下，一般不用方向键来移动光标，取而代之的是 H（左）、J（下）、K（上）、L（右）键。 事出有因，Bill Joy当年开发Vi时所用的ADM3A键盘 （ <a class="reference external" href="http://upload.wikimedia.org/wikipedia/commons/a/a0/KB_Terminal_ADM3A.svg">见图</a> ） 没有专门的方向键，而与H、J、K、L诸键合一。 这似乎又是历史局限带来的福利，事实证明这是Vi类编辑器高效的又一大要素。 由于四键分布在右手主键（即J键）附近，远比按方向键方便。考虑到方向键的使用频率， 如此设计大大减少了手指的移动距离。Vi这一标志性的风格，也为一些应用软件 （如mutt、Nethack）和一些网站（如Gmail、Greader）所借鉴。</p>
<p>Vim初学者另一个抱怨是，用来切换到普通模式的Esc键地处偏远，与Emacs用户同病相怜。 这似乎有违前面所说的原则。不过如果仔细看看ADM3A键盘就能理解了：上面 的Esc键在现代键盘的Tab键处。解决的办法有很多，我的做法是修改系统设置， 将Esc键与Caps Lock键对换（后者于我全无用处，还占到风水宝地）。</p>
<p>Vim有不少叠字母命令，如 <tt class="docutils literal">cc</tt> 、 <tt class="docutils literal">dd</tt> 、 <tt class="docutils literal">gg</tt> 、 <tt class="docutils literal">yy</tt> 、 <tt class="docutils literal">zz</tt> 、 <tt class="docutils literal">ZZ</tt> 、 <tt class="docutils literal">&lt;&lt;</tt> 、 <tt class="docutils literal">&gt;&gt;</tt> 、 <tt class="docutils literal">``</tt> 、 <tt class="docutils literal">''</tt> 、 <tt class="docutils literal">[[</tt> 、 <tt class="docutils literal">]]</tt> 、 <tt class="docutils literal">@@</tt> 等等。 很明显，同样键入两个字母，当二者相同时手指的移动距离最小，也最省心。</p>
</div>
<div id="id11" class="section">
<h2>4. 减少目光移动</h2>
<p>要提高编辑效率，不仅要经济用手，还要经济用眼。 目光频繁地搜寻或切换不仅本身消耗时间，更对思路的连贯性造成干扰，导致工作效率降低。 Vim针对用户频繁的编辑或浏览的需求，提供了周到的服务，能最大限度地 减少目光搜寻时间和目光转移次数。</p>
<p>同样由于历史原因，Vi当初是用于终端执行的程序，没有多窗口，虽可用 <tt class="docutils literal"><span class="pre">Ctrl-Z</span></tt> 或 命令 <tt class="docutils literal">:stop</tt> 切换到其他程序，但终究不便。因此，设计者千方百计地减少用户 离开当前窗口的机会，从而减少用户的目光和注意力的转移。</p>
<p>避免离开编辑窗口最自然的一个方法是，在编辑环境下完成尽可能多的任务。 例如，为了减少到终端的切换，Vim除了能编辑本地文件外，还能通过FTP、RCP、SCP、HTTP 等协议直接编辑远程文件。 又如，Vim本身的命令虽然不少，但主要是编辑方面的功能。 为弥补这一不足，Vim提供了外部命令的接口，如可通过设置 <tt class="docutils literal">makeprg</tt> 变量来指定make程序，设置 <tt class="docutils literal">grepprg</tt> 变量来指定grep程序，设置 <tt class="docutils literal">helpprg</tt> 或 <tt class="docutils literal">keywordprg</tt> 变量来指定帮助程序，等等。Vim更能直接执行shell命令，必要时还可读取外部命令的输出结果。 如果安装 <a class="reference external" href="http://www.vim.org/scripts/script.php?script_id=2771">Conque Shell</a> 插件，甚至可以在Vim中直接运行（模拟）终端本身。</p>
<p>顺带提一下，虽然有人认为Vim算不得真正意义上的IDE（集成开发环境），在这方面不如 Emacs那般强大，但开发人员所需要的多文件浏览、全局搜索、代码大纲（outline）、 语法高亮、自动补全（autocomplete）、代码片段（snippet）、自动格式化、语法检验、 文本比较、编译、运行、调试、单元测试、与SCM等工具集成等功能，在Vim内部均可完成。 考虑到它无敌的编辑效率、灵活的配置、强大的脚本支持 （包括原生的Vimscript以及外来的Perl、Python、Ruby等）以及相比 一般IDE在启动速度、内存占用等方面的优势，完全称得上是开发神器。</p>
<p>不少人对Vim朴素得近乎寒碜的界面耿耿于心，殊不知那些花哨的界面、时髦的效果都是吸引目光、分散注意力的元凶。以前对Mac电脑印象最深的就是桌面底部那些华丽而生动的应用程序图标，可待我真正拥有以后，很遗憾地发现它们的风采完全没有展示的机会。因为最常见的应用皆有快捷键绑定，次常见的应用也可通过Quicksilver或Alfred来唤出。</p>
<p>在我的个人配置中，Vim是全屏显示的，且既无菜单，也无工具条，更无按钮，连滚动条都禁用，几乎称得上是零界面了。 窃以为这才是最好的界面，所有的服务都默默藏于幕后，不占用一寸屏幕资源， 不消耗一丝目光，却能在所需之时应键而出。在能自动识别人类意念的软件被发明之前， 实在很难找到比这更好的人机交互方式了。</p>
<p>为减少目光的移动，Vim在Vi之上新增了折叠（fold）功能，可把次要或暂不关心的文本 隐藏起来。根据实际需求，用户可选择具体的折叠方式，可手动折叠，也可按缩进、 语法、标记或比较（diff）来折叠，还可按自定义的表达式来折叠。</p>
<p>不少人都说Vim难学，但他们很可能忽略了一件事：Vim的在线帮助极为详尽且极为便利。 vimtutor为初学者提供了半小时的入门教程，此后用户对于任何有疑问的命令或主题，键入 <tt class="docutils literal">:h &lt;关键词&gt;</tt> 即可，其中关键词部分支持 通配符。如果连命令或主题都不清楚，可试试 <tt class="docutils literal">:helpgrep</tt> 命令。假如连帮助本身 也不会用，那就敲 <tt class="docutils literal">:help help</tt> 吧。 总之，用户完全不必在使用Vim时查找其他的参考书籍或电子文档， 他们的目光又少了一个离开的理由。</p>
<p>（未完待续）</p>
</div>
</div>
</div>
<p>&nbsp;</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2011%2F11%2F30%2Fcharm-of-vim-1%2F&amp;title=VIM%E4%B9%8B%E9%AD%85%EF%BC%88%E4%B8%8A%EF%BC%89" id="wpa2a_6">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年12月13日 -- <a href="http://blog.zhenghui.org/2011/12/13/charm-of-vim-2/" title="VIM之魅（下）">VIM之魅（下）</a> (24)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=sGCEqDqstCA:83HNDhYkHOw:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/sGCEqDqstCA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2011/11/30/charm-of-vim-1/feed/</wfw:commentRss>
		<slash:comments>26</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2011/11/30/charm-of-vim-1/</feedburner:origLink></item>
		<item>
		<title>论思维的刚性与柔性（科学的迷信-4）</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/vt5qabwGvvo/</link>
		<comments>http://blog.zhenghui.org/2011/07/12/hard-thinking-and-soft-thinking-4/#comments</comments>
		<pubDate>Tue, 12 Jul 2011 11:22:08 +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=882</guid>
		<description><![CDATA[<b>论思维的刚性与柔性</b>——科学的迷信（4）<br/> • 科学是有边界的<br/> • 综述 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center;"><span style="font-family: 宋体;">论思维的刚性与柔性（科学的迷信-4）</span></h1>

<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/article.css" type="text/css"></head>
<!-- below comes from generated html -->
<div class="article" title="论思维的刚性与柔性" lang="zh-CN">
<div class="titlepage">
<div>
<div>
<div class="author">
<h3 class="author">郑晖</h3>
</div>
</div>
</div>
<p>&nbsp;</p>

<hr /></div>
<div class="section" title="正文">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both;"><a name="content"></a>科学的迷信（4）</h2>
</div>
</div>
</div>
<p><span class="strong"><strong>七、科学是有边界的</strong></span></p>
<p>自近代以降，科学以锐不可当之势，不断地在未知领域上开疆拓土。尽管至今仍有大量的荒地尚未开垦，但乐观的人们都相信：科学迟早会占领所有的知识高地。果真如此吗？</p>
<p><span class="strong"><strong>1. 科学与哲学</strong></span></p>
<p>答案应当是否定的。科学不可能解决人类所有的困惑，也不是人类获取知识的唯一途径。 实际上，与科学的版图接壤的还有另一片天地——哲学。从历史来看，现代自然科学的前身是自然哲学，属于哲学的范畴。关于这一点，我们不难从两件事实窥出端倪：史上最伟大的科学著作首推牛顿的《<span class="strong"><strong>自然哲学</strong></span>的数学原理》；直到今天，欧美的理学博士仍被称为<span class="strong"><strong>哲学</strong></span>博士（Ph.D）。尽管亚里士多德对哲学进行了细分，把自然哲学归为第二哲学，而把形而上学<a class="link" href="#note1"><sup>[1]</sup></a> 归为第一哲学，但在很长一段时间里科学与哲学是浑然一体的。直到文艺复兴以后，各种以观察、实验和数学为主要方法来研究自然的学科才渐渐分化出来，分别成为独立的物理、化学、生物等具体科学。</p>
<p>关于哲学与科学之间的关系，有两种代表性观点。一种认为哲学是第一科学，是科学之母，具体科学时刻需要接受哲学的指导；一种认为哲学不过是哲学家们玩弄的空洞的概念游戏，毫无实践意义。只要不带意识形态的色彩，便很容易发现前一种观点过于夸大其词。现代科学自有一套完整而成熟的研究方法，通常并不需要某种玄妙或形而上的思想指导。假如一定要声称科学家们的成功是自觉不自觉地符合了某些哲学思想（比如唯物辩证法）的结果，那不仅是牵强附会，而且委实有些滑稽——一种不学而知的思想有何值得称道的？至于后一种观点，随着科学的日趋成熟和哲学的日趋边缘化而渐有市场。不过，只要对哲学稍加了解，就不难得出一个结论：无论科学的版图如何扩张，也无法完全吞并哲学的领地。</p>
<p>哲学常为人诟病的一点是它缺乏实际功用，殊不知这正是其特色之一。哲学总是在虚无与实在之间的真空中游走，上不求助于宗教的神祗（但会考察一切关于超自然的学说，比如宗教哲学），下不拘泥于世俗的实际（但会考察一切关于实际的学问，比如科学哲学）。其实，哲学何止是不能解决实际问题，它甚至连非实际问题也不打算解决。假如哲学“不小心”真的解决了某个问题，那么该问题也该退出哲学领域了（可谓真正“彻底”的解决）。如果就此认为哲学毫无实际价值，那就大错特错了。事实上，哲学的无能之处正是其强大之处。一方面，哲学的研究对象不像具体科学那样仅仅局限于某个特定的领域，它试图对诸如存在、意识、知识、价值等最普遍的、最基本的问题进行抽象而系统的思考，包括关于<span class="term">实在</span>（reality）本质的形而上学，其核心是关于存在实体的<span class="term">本体论</span>（ontology，或译为“存在论”）<a class="link" href="#note2"><sup>[2]</sup></a>；关于知识本质的<span class="term">认识论</span>（epistemology）和关于价值本质的<span class="term">价值论</span>（axiology）<a class="link" href="#note3"><sup>[3]</sup></a>。另一方面，哲学也会对其他学科领域的基础、假设、理论、价值等进行反思。这种反思是对认识的认识，对思想的思想。尤需强调的是，哲学最重要的价值不在于正确地回答问题，而在于正确地提出问题。如海德格尔（Martin Heidegger）所言，哲学活动是对超乎寻常之事发超乎寻常之问<a class="link" href="#note4"><sup>[4]</sup></a>。人类一切知识的不清晰、不确定或不自洽的地带，都是滋生着哲学的温床。黑格尔对此有个十分形象的比喻：哲学就像一只猫头鹰<a class="link" href="#note5"><sup>[5]</sup></a>，总在黄昏起飞。哲学与其他学科的关系，<span class="strong"><strong>与其说是指导者与被指导者的关系，不如说是评论者与被评论者的关系</strong></span>，因为这更能体现哲学的功用——它既是一面镜子，通过排除成见的反观和寻根究底的思辨来考察和评价后者；又是一根鞭子，通过永无休止的怀疑和鞭辟入里的批判来敦促和刺激后者。</p>
<p>前文对科学的一些反思，包括科学的可靠性、科学对假设的依赖性、科学的真理性、科学理性的纯粹性、科学的局限性、理性的局限性等，基本上都属于认识论或科学哲学的范畴。这些问题随着科学的发展也许会在一定程度上得到局部的缓解，但绝不可能根本解决，从而保证了哲学在科学的步步紧逼之下依然拥有一片永久的保留地。</p>
<p>科学试图在主观认识与客观世界之间建立恰当的映射，如前所述，无人能担保这种映射的准确性。更有甚者，无人能担保客观世界的真实性。古代庄子有庄周梦蝶抑或蝶梦庄周的困惑， 近代笛卡尔的名言“我思故我在”也是基于对外部世界的怀疑而产生的。在电影《黑客帝国》（Matrix）和《盗梦空间》（Inception）中，最大看点便是对真实与梦境、乃至多层梦境之间差别的解读。在这里，“人生如梦”、“梦里不知身是客”已从文学修辞嬗变为哲学思辨，从而为哲学找到了另一处栖身之所。</p>
<p>科学的根本目的是求真，尚且不能完全覆盖真的区域，更不用说善的区域了。有人会说，科学能给人们减少痛苦、延长寿命、增加自由、带来幸福，这难道不是最大的善吗？很遗憾，这是把目的与结果混为一谈。更何况科学是把双刃剑，谁也不知道它所带来的环境污染、能源危机、资源短缺、核武威胁等问题是否会最终加速人类的灭亡。还有人会引用布莱希特（Bertolt Brecht）借伽利略之口而说出的一句名言：科学唯一的目的是为了减轻人类生存的艰辛。然而与其说这是对科学目的的阐述，不如说是对科学的美好愿望以及对科学家的衷心的告诫<a class="link" href="#note6"><sup>[6]</sup></a>。同样，我们也不能因为科学造成的一些负面效应而认为科学是恶的或者求恶的。纯粹的科学应当是价值中立的，只关乎真假而无关善恶。虽然由于科学活动是由人来进行的，从而无法保证绝对的价值中立，但至少价值的定义和判断是科学所鞭长莫及的。诸如克隆人、安乐死是否合情合理之类的问题，无法在科学内部解决，而得交由伦理学、社会学、法学等来讨论。其中伦理学也称道德哲学，是哲学的一个分支。</p>
<p>类似地，关于美的研究也是科学的一个盲区。诚然，美可以启示甚至在一定程度上评判科学，反过来科学也展示了理论之美、自然之美。关于这一点，大科学家狄拉克、庞加莱、爱因斯坦等都发表过许多极为精辟的见解（审美思维也是本系列的主题之一——柔性思维的一部分，留待后叙）。可毕竟美本身不是科学的研究对象，而是美学的研究对象。美学也称感觉学，还是哲学的一个分支。</p>
<p>让我们用一个比喻来结束科学与哲学之间关系的讨论：站在科学的大地上瞭望，哲学是天边的地平线——背之而行时，它是最初的起点；向之而行时，它是无尽的终点。</p>
<p><span class="strong"><strong>2. 科学与宗教</strong></span></p>
<p>相比哲学，人们（尤其是在大陆接受教育的人们）对宗教有着更多更深的误解。前面我们曾批评过“科学真理”的提法，因为科学虽然旨在追求真理，但与真理却是两个范畴。当然如果稍微宽容些，这种措辞倒也无伤大碍，而“宗教迷信”的说法则极其恶劣，完全不可容忍。因为在汉语语境中，迷信（superstition）意味着愚昧、无知、荒谬和反科学<a class="link" href="#note7"><sup>[7]</sup></a>，把二者相提并论，隐含着对宗教无比的轻蔑和鄙视。其实，<span class="strong"><strong>迷信固然是一种愚昧，把宗教等同于迷信何尝不是另一种愚昧</strong></span>。</p>
<p>毋庸讳言，宗教与迷信有着千丝万缕的联系（有时后者会直接披着前者的外衣），并且多表现为信仰或崇拜某种神秘的、超自然的对象，但它们在本质上着天壤之别。（主流）宗教通常有着迷信所不具备的特征：悠久的传统、严密的组织、系统的教义、规范的仪式、固定的场所、庞大的信众。不过这些还只是表象的差异，难以反击“宗教只是有组织、有系统的迷信”的观点。宗教与迷信本质的差异可从以下几个方面来分辨——</p>
<div class="itemizedlist">
<ul class="itemizedlist" type="disc">
	<li class="listitem">理论：宗教提供一整套系统的世界观、人生观、价值观、道德观，而迷信提供的往往是一些支离破碎的观点或说法</li>
	<li class="listitem">原则：宗教的原则是一以贯之的，而迷信则多具随机性且难以自圆其说</li>
	<li class="listitem">追求：宗教追求终极真理和人文关怀，而迷信无此追求</li>
	<li class="listitem">哲学：宗教与哲学息息相关，互相影响，迷信一般与哲学无关</li>
	<li class="listitem">科学：宗教在本质上并不否定或排斥科学，而迷信常常排斥或曲解科学</li>
	<li class="listitem">理性：宗教虽然建立于信仰之上，并认为信仰超越理性（超理性），但并不排斥理性，反而鼓励人们通过学习教义并结合玄想和体悟等方式来开启智慧，而迷信则用非理性和盲信盲从来封闭智慧</li>
	<li class="listitem">道德：宗教宣扬平等互爱、诸恶莫作、众善奉行等道德观，迷信通常不关心道德</li>
	<li class="listitem">形式：宗教的形式是为思想服务的，并不过分拘泥执着，而迷信多注重形式上的精确</li>
	<li class="listitem">实践：宗教提倡通过实践与修行来体悟，而迷信通常只需要表象的仪式或行为</li>
	<li class="listitem">自觉：选择宗教多是自觉自愿的，选择迷信多是被动无奈的</li>
	<li class="listitem">自由：宗教最终给人以自由，而迷信最终给人以束缚</li>
	<li class="listitem">动机：宗教的动机是获得长远的内心安顿、精神提升和人格完善，而迷信的动机通常是极为短视和世俗功利的，并无长远或深度的诉求。</li>
	<li class="listitem">持续：宗教不是一时的狂热，是持续而深沉的，而迷信通常是阵发的、短暂的</li>
	<li class="listitem">希望：对于希望，宗教予人承诺，迷信予人幻象。一旦不能达成，宗教徒依然可以接受，而迷信者则会产生幻灭之感（常识告诉我们：动机越功利、越具体，就越容易失望）。</li>
	<li class="listitem">感情：对于信仰的对象，宗教徒的感情是敬中有爱，希望与之联结，而迷信的感情多是敬中无爱，倾向于躲避（敬鬼神而远之）</li>
	<li class="listitem">情绪：宗教是激情和热忱的，迷信是冷淡和消沉的</li>
	<li class="listitem">效果：宗教给人带来超越感和内心的和谐、平静、安详、喜乐，迷信常常带来压抑感和内心的不安、焦虑、无助、恐惧</li>
	<li class="listitem">现实：宗教徒认为现实世界是友善的，愿意与之交融，而迷信者认为现实世界是有敌意的，希望逃离</li>
</ul>
</div>
<p>最后，是否迷信不仅与信仰的对象有关，也与信仰的主体有关。如果一个人信仰某种宗教，但并不真正了解其教义，只是毫无主见地接受，或者只为满足某种私欲（比如为了升官发财而求神拜佛），那么这种信仰就不是正信，仍是一种迷信。综上所见，宗教与迷信如同两条源头相近的河水，虽然时有交汇，但一个最终流向大海，一个最终坠入深渊。</p>
<p>常被夸大的不仅有宗教与迷信的联系，还有宗教与科学的对立。最能反映宗教与科学冲突有三个关联的名词：黑暗野蛮的中世纪、教条陈腐的<span class="term">经院哲学</span>（scholasticism）和扼杀科学的罗马天主教会，而这一切又集中地反映在布鲁诺（Giordano Bruno）的遭遇上。（国内）对此常见的说法是：布鲁诺是一位科学的殉道士，他因为坚持哥白尼的日心说、反对中世纪传统、反对经院哲学、反对宗教神学而被教会施以火刑。于是得出结论：科学与宗教的水火不容。然而越来越多的论据表明，事实并非如此。首先，布鲁诺绝非反对宗教，只是他信仰的<span class="term">泛神论</span>（pantheism）与天主教的<span class="term">一神论</span>（ monotheism）产生了激烈的冲突。其次，布鲁诺主要是从宗教（如赫尔墨斯主义Hermeticism）而非科学的角度来理解和支持日心说的<a class="link" href="#note8"><sup>[8]</sup></a>。最后，教会指控布鲁诺的<a class="link" href="http://en.wikipedia.org/wiki/Giordano_Bruno#Imprisonment.2C_trial_and_execution.2C_1592.E2.80.931600" target="_top">八条罪状</a>中除第五条（主张存在多个世界及其永恒）与日心说沾边以外，其他都是有关他反对基督教、宣扬异教的观点或行为。另一个常用作宗教戕害科学的例子是：伽利略也因支持哥白尼的学说而遭到教会迫害。同样有资料表明，该事件并不那么简单。天主教对他的科学理论并无异议，而且伽利略本人也以基督徒自称<a class="link" href="#note9"><sup>[9]</sup></a>。甚至连哥白尼本人，一直都是虔诚的天主教徒。更有趣的是，在相当长一段时间里，作为一名神职人员，他的主业是宗教活动，副业才是科学研究。考虑到在中世纪及文艺复兴中前期科学、哲学、神学几乎是三位一体的，这一切实在也算不得稀奇。</p>
<p>唯物论者常常对史上最伟大的科学家（绝无“之一”）牛顿晚年专注神学而感到遗憾和惋惜，这实在是看错对象表错情。且不说牛顿一生中著述最多的不是关于科学而是关于宗教和神秘学，单从其壮年所著的划时代巨著《自然哲学的数学原理》即知，牛顿绝非“晚节不保”地从科学阵营倒向了宗教阵营。该著在运用数学和公理化的方法并结合实验成功地发现天体运动规律之后，突然把科学导入到神学的轨道：“太阳、行星和彗星的这个极精致的结构不可能发生，除非通过一个理智的和有权能的存在的设计和主宰…承认至高上帝的存在是必然的，且同样要承认他是永恒的和无处不在的”。这令人联想到关于上帝存在的一个经典“证明”：在野外捡到一块粗糙的石头自是不觉有异，而捡到一块精致的手表，则难免会推想一位钟表匠的存在。既然大自然如此地和谐而有规律，那么一位造物主就呼之欲出了。当牛顿发现太阳系具有某种不稳定性，甚至请出上帝来纠偏。莱布尼茨（ Gottfried Leibniz）因此嘲笑牛顿的上帝没有他所吹捧的那么高明，只是位蹩脚的钟表匠——给钟表上好发条之后还得不时去维护它。好在后来拉普拉斯对理论进行了修正，不再劳驾上帝。这正是前文曾提起的拉普拉斯“不需要上帝的假设”的背景。需要指出的是，无论是莱布尼茨还是拉普拉斯，都是信仰上帝的，只是他们认为上帝在创造世界之后便功成身退，不再干预世界。</p>
<p>牛顿认为，科学的主要任务是从<span class="strong"><strong>机械</strong></span>的因果链中追溯到<span class="strong"><strong>非机械</strong></span>的第一因。无论是天体运行规律的存在、还是绝对时空的假设，无论是万有引力的本质、还是是第一推动力的来源，皆处因果链之尽头，在牛顿看来它们只能源于全能的上帝。无怪乎《自然哲学的数学原理》的编者序言中有这样一段文字：它（牛顿理论）是反对无神论攻击的最坚强的堡垒，没有别处有比从这个箭筒抽出来的武器更适合对付不信神的乌合之众。不过十分讽刺的是，牛顿的门徒继承了他的科学理论，却抛弃了他对终极原因的谨慎态度，反而使牛顿力学变成了机械论、决定论和大无畏的无神论的武器。这不禁令人联想到达尔文，他的进化论一向被看作是无神论的重要依据（有神学家也把它当作有神论的依据），可他本人却只是不可知论者，并不否定上帝的存在。</p>
<p>关于上帝存在的证明有很多，包括宇宙论、目的论（如上述的“钟表匠类比”）、本体论等，但均难以服众。对于上帝存在的否证也有不少，同样难经推敲。最常见的责难是：上帝如果是万能的，那么他能举起他自己不能举起的石头吗？该质疑的荒谬之处在于：如果所谓的“万能”不包含悖论，那它就是个伪问题；如果容许包括悖论，又凭什么认定上帝不能打破这种非此即彼的逻辑呢？倒是休谟的质疑有些“技术含量”：上帝能阻止罪恶吗？若不能，则它不是全能的；若能而不愿，则它不是全善的。对此可有多种反驳方式：个体眼中的恶不同于上帝眼中的恶；善恶是相对的，没有恶则无以彰显善，没有体验痛苦便无以体验欢乐；上帝不愿干涉人类的自由，包括作恶的自由；无恶的世界助长享乐，无助于责任心的培养和人格的完善；没有恶的天堂与没有死亡的人生同样地无聊；等等。即使退一步，也至多承认上帝的全能与全善是有限度的，并不能否定上帝的存在。事实上，休谟也是赞同上帝的存在的<a class="link" href="#note10"><sup>[10]</sup></a>。归根结底，如康德在《纯粹理性批判》中花大量篇幅所指出的那样，逻辑必然性不能取代实在必然性，企图用逻辑证明（或否证）上帝的存在注定是劳而无功的。更一般地，理性无论是走<span class="strong"><strong>先验</strong></span><a class="link" href="#note11"><sup>[11]</sup></a>之桥，还是乘<span class="strong"><strong>经验</strong></span>之舟，都无法到达<span class="strong"><strong>超验</strong></span><a class="link" href="#note12"><sup>[12]</sup></a>的彼岸。</p>
<p>由于在认识世界、获得真理的方式上，宗教与科学各执一端——宗教求助<span class="term">启示</span>（revelation）和<span class="term">信仰</span>（faith），而科学求助逻辑与实证，双方有时不可避免地发生冲突。在中世纪，占据统治地位的宗教在一定程度上压制了科学；在启蒙时代（或理性时代），羽翼渐丰的科学对宗教又发起了猛烈的反击。但毕竟它们与哲学一样，都在关注世界的本源和本质，都是为了获得可靠的知识和坚实的信念（belief），只是取道相异：宗教自上而下，科学自下而上，哲学游走其中。只要协调得当，它们不仅不会冲突，反而可以通过对话或合作的形式相互促进和补充。</p>
<p>中世纪经院哲学的代表人物阿奎那（Thomas Aquinas）曾提出知识有两个来源：一个是（基督教）信仰的神秘，另一个是人类理性所推出的真理。他认为（自然）科学是可通过学习、教育而获得的低级感知，而作为高级感知的神学只能由上帝偶尔闪现的神迹和启示来获得，从而为神学和科学提供了一个和谐的图景。另一位基督教神学大师奥古斯丁（Aurelius Augustinus） 也认为：世俗知识与宗教信仰之间并非截然对立的，认识是为了信仰，信仰是为了可以认识；但信仰比认识更为重要，信仰是认识的目的，认识是信仰的手段。</p>
<p>以上只是神学家的一面之词，那么科学家又是如何看待宗教与科学之间的关系呢？牛顿的观点虽然很明确，但有人会归因于彼时的科学尚未成熟。这里引述爱因斯坦的观点：“科学只能断言‘是什么’（真——事实判断），而不断言‘应该是什么’（善——价值判断），宗教则正相反……尽管宗教的和科学的领域泾渭分明，可二者还是有牢固的相互关系和依存性……科学只能由那些全心全意追求真理的人来创造，这种感情的源泉来自宗教的领域。同样属于这个源泉的是这样一种信仰：相信那些对于现存世界有效的规律是合乎理性的……<span class="strong"><strong>没有科学的宗教是盲目的，没有宗教的科学是跛足的</strong></span>。” <a class="link" href="#note13"><sup>[13]</sup></a>虽然爱因斯坦所谓的宗教是宇宙宗教，信奉的上帝是斯宾诺莎（Baruch Spinoza）式的，并非人格化的上帝，但本文本也不把人格神作为宗教的必要条件。实际上，三大宗教之一的佛教就是没有人格神的，作为（广义）宗教的儒教同样如此。</p>
<p>历史上，宗教与科学的结合至少可上溯到两千五百多年前古希腊的毕达哥拉斯（名义上勾股定理的发明者）。他创立的毕达哥拉斯学派集政治、学术和宗教于一体，其“万物皆数”的观点及宗教神秘主义被柏拉图继承和发扬，对西方的神学、哲学和科学产生了深远影响。后世功勋卓著的大科学家们，如哥白尼、笛卡尔、开普勒、伽利略、牛顿、莱布尼茨、麦克斯韦、海森堡等都有一个共同的信念：大自然中隐藏着固有的和谐，服从最完美、最理性的数学法则；科学的目的即是找出这些法则，并用其解释所有的现象，从而彰显上帝的伟大和荣耀。换言之，对宗教的追求成为科学研究的原始动力和终极目标。<span class="strong"><strong>如果说哲学是科学的母亲，那么不妨说宗教是科学的助产妇</strong></span>。作为一个反面例子，李约瑟在解释由他名字命名的问题“中国为什么没有发展现代科学”时提出的一个理由是：中国人没有“大自然有规律且能被人类所理解”的信念。这种信念固然不必一定是宗教的，也可以是哲学的（如前文曾提到的实在性、规律性、可知性等哲学假设），但宗教的信念无疑更为持久、更为热烈、更为终极。可以说，无论从认识的方式还是从认识的层次上看，<span class="strong"><strong>科学的尽头是哲学，哲学的尽头是宗教</strong></span>。</p>
<p>无宗教信仰者对宗教的神秘性始终耿耿于怀，这使得他们无法不条件反射地把宗教与迷信、神话、荒诞等词联系在一起。殊不知失去神秘性的宗教便不再是宗教，正如失去不确定性的哲学便不再是哲学（相反，科学总是在寻求揭秘和确定性）。再次引用爱因斯坦的话：“神秘感是我们所能得到的最美妙的体验，它是产生真正艺术与真正科学的最根本的激情。谁不了解这一点，谁熄灭了惊奇之火，谁的眼神便会黯淡无光，虽生犹死。正是这种神秘的、甚至掺和了恐惧的体验形成了宗教。意识到有些事情是我们无法参透的，那些至深的理和至炫的美我们仅能触及皮毛，这种认识和情感构成了真正的宗教情怀。” <a class="link" href="#note14"><sup>[14]</sup></a>身处科学前沿者，往往在知识和理性的边界处寻求突破，能深切地体会到科学的局限性，对哲学和宗教的需求和感受也比常人更为强烈。反是那些享受他人科学成果的人，对科学的前景更易产生虚幻盲目的乐观。因此，大自然的神秘常能拨动前者的宗教琴弦，而后者却难为所动。</p>
<p>按信仰的基础来分，有神宗教可粗略划为两种：<span class="term">自然宗教</span>（natural religion）<a class="link" href="#note15"><sup>[15]</sup></a>与<span class="term">启示宗教</span>（revealed religion，或译为天启宗教）。自然宗教以<span class="term">自然神论</span>（deism）<a class="link" href="#note16"><sup>[16]</sup></a>为代表，主张信仰应且仅应基于理性和日常经验；启示宗教以犹太教、基督教、伊斯兰教为代表，主张信仰应基于经文和神秘体验。显然，对于理性的非信仰者来说，后者的神秘感更强，也更难接受。的确，相较于科学，宗教没有可预见性、可检验性和可重复性。即便有人声称获得了某种神秘的宗教体验，但由于只是多变而不确定的个体经验，且具有被动性（passivity）、短暂性（transiency）、难言性（ineffability）和知悟性（noetic quality）<a class="link" href="#note17"><sup>[17]</sup></a> ，难以让人服膺。 但若就此把所有的宗教体验均归于错觉幻象或虚构臆造，则未免失之武断。将科学无法证实或难以解释之事等同于不可发生之事，那实在是想象力的悲哀<a class="link" href="#note18"><sup>[18]</sup></a>。不妨设想，一个人的宗教感如同耳朵之于音乐、眼睛之于色彩。耳聪目明者乐之陶之，耳聋目盲者却无知无觉。可以想见，前者美妙热烈的体验不会引起后者丝毫的共鸣，而后者无论摆弄何种理论或证据，也无法令前者怀疑对音乐或色彩的真实感知。我们当然不会天真地指望一个类比就能敲开宗教之门，只希望非信仰者能微启一扇天窗，为信仰之光预留一丝可以透射的缝隙。</p>
<p>人们常为神或上帝之类的超自然物是否存在而纠结，为无福见证神迹而遗憾，实则大可不必，这些并非宗教的核心价值所在。广义上看，宗教认为有一个看不见的秩序，而我们最高的善就在于跟它的正当关系<a class="link" href="#note19"><sup>[19]</sup></a>。宗教学奠基人缪勒（ Max Muller）曾说过：“无论一种宗教何等地不完善或幼稚，它总是把人的灵魂置于上帝面前，而无论上帝的概念多么不完善或幼稚,它总是代表了人类灵魂在当时所能达到和把握的无上完善的理想。”路巴（ James H. Leuba）也说：“上帝并非被认识，也不是被理解，而是被使用……宗教的目标并不是上帝，而是生活，一种更广阔、更丰富、更满意的生活。”由此可见，宗教实乃此岸与彼岸之间的桥梁或渡船，是为了能让信仰者上契超越者——无论那是一位神、一种力量还是一种境界——以求超越个体生命的局限性。换言之，宗教表面上是在鼓励人们对超越者的崇拜，实质上是在帮助人们实现自我超越。这种对人的终极关怀（ultimate concern）是宗教最大的功能，也是科学爱莫能助的地方。对于那些暂时仍对信仰持怀疑态度的人，建议从实用主义的角度来看（虽然信仰者并不赞同这样）：无人能证明科学理论与真实世界是完全相符的，但无人能否定科学有效地改善了人类的物质世界；同样地，无人能证明上帝的存在，但无人能否定宗教有效地改善了人类的精神世界。既然人们对科学欣然接受，又何以对宗教拒之门外呢？</p>
<p>总而言之，宗教对科学既是一种研究领域的补充——从经验世界到超验世界，也是一种思维方式的补充——通过主动地把部分逻辑理性<span class="strong"><strong>交托</strong></span>与阈下意识（subliminal）而获取启示 ，既是一种情绪的补充——用它的谦卑和热烈去中和科学带来的傲慢和冷淡，也是一种精神的补充——为被科学竭力机械化和物质化的世界提供一方心灵的栖息地 。这些决定了无论科学如何扩展，也无法侵入宗教的内核。不仅如此，哪怕科学把一门宗教的教义批得体无完肤，也难以撼动宗教。因为教义只是宗教的一种投射，而敲打影子是不会伤及原身的。真正的宗教建立于一种难以言表的直接经验之上，它是更具真切性的超感官体认、更具确信度的超理性顿悟<a class="link" href="#note20"><sup>[20]</sup></a>，由其铸就的信仰大坝纵在科学浪潮的冲击之下也自岿然不动。</p>
<p>关于科学与宗教的关系，《人的宗教》一书有段话极具启示性：生命有如一张极大的挂毡，我们却从其反面来面对它。科学总是试图从毛毡后由线与结组成的迷魂阵中去推测其正面的图案，而宗教则直接从正面把整体的美与和谐显现出来。</p>
<p>科学、哲学、宗教是人类洞察宇宙的三大途径，无论从过去的历史还是从未来的趋势来看，三者之间在交叠处互相融合、在对立中互相对话、在冲突中互相促进，但永远无法互相取代。成熟后的科学由于立足实际、贴近生活且有立竿见影之效，必然更容易为人所接受。作为强势的一方，科学不断地挤压哲学中虚无空洞的泡沫，扫除宗教中陈腐荒谬的教条。表面上看，后二者似乎毫无还手之力，只能偏安一隅，但在科学的深处依然离不开哲学的假设和宗教的激励。尤其不要忘记，科学所获得的知识是有限的，而未知是无限的——那里正是哲学和宗教的辖区。</p>
<p><span class="strong"><strong>八、综述</strong></span></p>
<p>科学最显著的一个特点是它的祛魅功能，即努力驱除自然的神秘、消解自然的神圣，把人类从惶恐的惊疑和盲目的信仰中解脱出来，进而获取对自然尽可能多的掌控权以谋求自身的福利。然而过犹不及，科学的进步滋长了人类的自私、贪欲和狂妄，科学主义者们因此逐渐丧失了对自然起码的敬畏，过高估计理性的力量，甚至认为自然科学是人类唯一的正确的认知方式。他们试图把<span class="term">社会科学</span>自然科学化，把<span class="term">人文学科</span>（humanities）科学化，把精神物质化，把主观客观化。他们似乎没有意识到，把人类对真善美的诉求粗暴地简化为对真的偏执，不仅无助于接近客观真理，也无助于增进人类福祉。有鉴于此，本系列的第一部分（即“科学的迷信”）试图为科学本身祛魅，指出科学的许多特征被严重高估了，其中包括：确定性、精确性、可靠性、客观性、中立性、有效性、合理性、真理性、逻辑性、普适性、独立性、无界性、万能性，等等。总的说来，科学是人类用理性编织的巨网，虽然成功地从知识海洋中打捞出无数的珍宝，但这张网既不足够宽阔也不足够细密，甚至不足够结实，更多的宝藏或在网外逍遥，或从网内流失。这不是在贬损科学的价值，而是把科学最崇尚的怀疑精神和批判精神运用于科学本身。倒是那种把科学过度神圣化、绝对化、扩大化的做法才违背了科学精神，才是对科学真正的伤害。</p>
<p>在这孤独的星球上，无知与恐惧如无边的黑暗始终笼罩着人类。纵使科学光芒万丈，也无法照亮整个夜空。值得欣慰的是，在光芒的边际燃烧着哲学的火焰，在光芒的背后还矗立着宗教的灯塔。是的，人类不仅需要科学的力量，还需要哲学的启迪和宗教的关怀、以及伦理的规范和艺术的愉悦。唯有如此，被科学解构得机械而乏味的宇宙才会保持些许生机和趣意，被科学遗落的、躁动而脆弱的心灵才能获得些许平复和慰藉。</p>
<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> 形而上学这个好端端的词汇被黑格尔及其追随者污染了，他们把形而上学作为与“辩证法”相对立的一种机械教条的研究方法来批判。本文所提到的形而上学一概采用学术界公认的本来涵义，它研究的是关于一切存在的基本性质，即“作为存在的存在”（being qua being，亚里士多德语），是最基本的哲学。</p></li>
	<li class="listitem">
<p><a name="note2"></a> 除本体论外，形而上学还包括<span class="term">宇宙学</span>（cosmology）、<span class="term">哲学人类学</span>（philosophical anthropology）、<span class="term">宗教哲学</span>（ philosophy of religion） 等。</p></li>
	<li class="listitem">
<p><a name="note3"></a> 价值论主要包括伦理学、美学（艺术哲学）、社会与政治哲学。</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> 事实上，原文“I hold it that the only proper goal of science is to relieve the miseries of human existence”中“唯一正确的目标”与“唯一的目标”是有区别的。</p></li>
	<li class="listitem">
<p><a name="note7"></a> 必须指出，“迷信”有两种涵义：一种特指通常意义上的迷信（superstition），即非理性地相信某种行为或仪规（如招魂、驱鬼、符咒、辟邪等巫术）具有神奇的效力，此处即取此义；一种泛指盲目地相信（blindly worship），本节的题目《科学的迷信》即取此义。</p></li>
	<li class="listitem">
<p><a name="note8"></a> 赫尔墨斯主义者往往以太阳为神，故认为太阳应在行星系统中占中心地位。这种信念是布鲁诺支持日心说的主要原因。</p></li>
	<li class="listitem">
<p><a name="note9"></a> 参见《科学的灵魂》35-38页，《物理学和哲学》166-170页。</p></li>
	<li class="listitem">
<p><a name="note10"></a> 如休谟在《自然宗教对话录》所说：“问题不在于神的存在，而在于神的性质”。</p></li>
	<li class="listitem">
<p><a name="note11"></a> 译自德语 transzendental，指先天的、先于经验的。</p></li>
	<li class="listitem">
<p><a name="note12"></a> 译自德语 transzendent，指超出经验世界的。</p></li>
	<li class="listitem">
<p><a name="note13"></a> 摘录自《爱因斯坦文集》第三卷“科学和宗教”一节。</p></li>
	<li class="listitem">
<p><a name="note14"></a> 意译自《Ideas and Opinions》第11页。</p></li>
	<li class="listitem">
<p><a name="note15"></a> “自然宗教”可能有多种涵义。此处指通过理性和日常经验来构建教义的宗教,与启示宗教相对。</p></li>
	<li class="listitem">
<p><a name="note16"></a> 严格说来，自然神论不是一种宗教，而是一种宗教哲学。它认为上帝按照自然规律创造世界后便不再干预世界。从历史上看，它是十七至十八世纪时科学理性逐渐壮大但尚无力摧毁宗教的产物，是科学、哲学与宗教相互作用的结果。</p></li>
	<li class="listitem">
<p><a name="note17"></a> 摘自《宗教经验种种》。</p></li>
	<li class="listitem">
<p><a name="note18"></a> 另一个典型的例子是，外星文明和史前文明均未获得科学证实，但如果坚称它们绝不可能，甚至将其归为伪科学，那就有想象力贫乏之嫌了。</p></li>
	<li class="listitem">
<p><a name="note19"></a> 摘自《人的宗教》。</p></li>
	<li class="listitem">
<p><a name="note20"></a> 用罗素的说法，这属于本能信念（instinctive belief）。所有知识都必须建立在本能信念的基础上。</p></li>
</ol>
</div>
</div>
<div class="section" title="参考文献">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both;"><a name="ref"></a>参考文献</h2>
</div>
</div>
</div>
<p>注：带*号者为重点推荐书籍</p>
<div class="orderedlist">
<ol class="orderedlist" type="1">
	<li class="listitem">
<p>*Karl Popper. The Logic of Scientific Discovery(科学发现的逻辑)</p></li>
	<li class="listitem">
<p>*Karl Popper. Conjectures and Refutations: The Growth of Scientific Knowledge(猜想与反驳： 科学知识的增长).</p></li>
	<li class="listitem">
<p>*Karl Popper. 开放社会及其敌人</p></li>
	<li class="listitem">
<p>Karl Popper. 历史决定论的贫困</p></li>
	<li class="listitem">
<p>Karl Popper. 开放的宇宙</p></li>
	<li class="listitem">
<p>Karl Popper. 通过知识获得解放</p></li>
	<li class="listitem">
<p>Karl Popper. 客观知识：一个进化论的研究</p></li>
	<li class="listitem">
<p>Karl Popper. 波普尔自传：无尽的探索</p></li>
	<li class="listitem">
<p>诺图洛(著), 宫睿(译). 波普On Popper</p></li>
	<li class="listitem">
<p>*Thomas S. Kuhn. The structure of scientific revolutions(3rd ed) (科学革命的结构)</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>*A.F.查尔默斯(著), 鲁旭东(译). 科学究竟是什么（第三版）</p></li>
	<li class="listitem">
<p>*R.卡尔纳普(著), 张华夏(译). 科学哲学导论</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>*Bertrand Russell. The History of Western Philosophy(西方哲学史)</p></li>
	<li class="listitem">
<p>Bertrand Russell . The Problems of Philosophy(哲学问题)</p></li>
	<li class="listitem">
<p>Bertrand Russell . 宗教与科学</p></li>
	<li class="listitem">
<p>约翰·H·布鲁克(著)，苏贤贵(译). 科学与宗教</p></li>
	<li class="listitem">
<p>*亚里士多德(著), 吴寿彭(译). 形而上学</p></li>
	<li class="listitem">
<p>海德格尔(著), 熊伟(译). 形而上学导论</p></li>
	<li class="listitem">
<p>笛卡尔(著), 庞景仁(译). 第一哲学沉思集</p></li>
	<li class="listitem">
<p>A.N.怀特海(著), 周邦宪(译). 过程与实在</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>*W.C.Dampier(著), 李珩(译). 科学史（及其与哲学和宗教的关系）</p></li>
	<li class="listitem">
<p>*Nancy Pearcey, Charles Thaxton(著), 潘柏滔(译). 科学的灵魂：500年科学与信仰、哲学的互动史</p></li>
	<li class="listitem">
<p>威廉·布罗德 / 尼古拉斯·韦德. 背叛真理的人们：科学殿堂中的弄虚作假</p></li>
	<li class="listitem">
<p>*牛顿(著), 赵振江(译).  自然哲学的数学原理</p></li>
	<li class="listitem">
<p>*Albert Einstein. Ideas and Opinions(思想和观点)</p></li>
	<li class="listitem">
<p>*Albert Einstein. 狭义与广义相对论浅说</p></li>
	<li class="listitem">
<p>*Albert Einstein. 爱因斯坦文集3</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>*M. Mitchell Waldrop. Complexity: The Emerging Science at the Edge of Order and Chaos (复杂：诞生于秩序与混沌边缘的科学)</p></li>
	<li class="listitem">
<p>*罗杰·彭罗斯(著), 许明贤(译). 皇帝新脑</p></li>
	<li class="listitem">
<p>*W. 海森堡 (著), 范岱年(译). 物理学和哲学：现代科学中的革命</p></li>
	<li class="listitem">
<p>John Gribbin(著), 张宪润(译). 深奥的简洁：从混沌、复杂到地球生命的起源</p></li>
	<li class="listitem">
<p>Henry Pollack(著), 李萍萍(译). 不确定的科学与不确定的世界</p></li>
	<li class="listitem">
<p>保罗·西利亚斯(著), 曾国屏(译). 复杂性与后现代主义</p></li>
	<li class="listitem">
<p>P·切克兰德(著), 左晓斯(译). 系统论的思想与实践</p></li>
	<li class="listitem">
<p>冯·贝塔朗菲(著), 林康义(译). 一般系统论：基础发展和应用</p></li>
	<li class="listitem">
<p>金观涛. 系统的哲学</p></li>
	<li class="listitem">
<p>F.J.戴森(著), 李笃中(译). 全方位的无限</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>B.K.里德雷(著), 李泳(译). 时间、空间和万物</p></li>
	<li class="listitem">
<p>*曹天元. 上帝掷骰子吗：量子物理史话</p></li>
	<li class="listitem">
<p>马丁・戴维斯(著), 张卜天(译). 逻辑的引擎</p></li>
	<li class="listitem">
<p>*Carl Sagan(著), 金吾伦(译). 布鲁卡的脑： 对科学传奇的反思</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>*M·克莱因(著), 张祖贵(译). 西方文化中的数学</p></li>
	<li class="listitem">
<p>*M·克莱因(著), 李宏魁(译). 数学：确定性的丧失</p></li>
	<li class="listitem">
<p>*M·克莱因(著), 刘志勇(译). 数学与知识的探求</p></li>
	<li class="listitem">
<p>*侯世达.哥德尔 艾舍尔 巴赫：集异璧之大成</p></li>
	<li class="listitem">
<p>*休斯顿·史密斯(著), 刘安云(译). 人的宗教</p></li>
	<li class="listitem">
<p>*威廉·詹姆斯(著), 蔡怡佳(译). 宗教经验种种：对人性的研究(The Varieties of Religious)</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>Melville Y. Stewart. Philosophy of Religion(宗教哲学经典选读)</p></li>
	<li class="listitem">
<p>Thomas Aquinas. Summa Theologica(神学大全)</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>Alvin Plantinga. Faith and Rationality: Reason and Belief in God</p></li>
	<li class="listitem">
<p>H.李凯尔特(著), 凃纪亮(译). 文化科学和自然科学</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%2F2011%2F07%2F12%2Fhard-thinking-and-soft-thinking-4%2F&amp;title=%E8%AE%BA%E6%80%9D%E7%BB%B4%E7%9A%84%E5%88%9A%E6%80%A7%E4%B8%8E%E6%9F%94%E6%80%A7%EF%BC%88%E7%A7%91%E5%AD%A6%E7%9A%84%E8%BF%B7%E4%BF%A1-4%EF%BC%89" id="wpa2a_8">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2010年09月20日 -- <a href="http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/" title="论思维的刚性与柔性（科学的迷信-3）">论思维的刚性与柔性（科学的迷信-3）</a> (0)</li><li>2010年04月22日 -- <a href="http://blog.zhenghui.org/2010/04/22/hard-thinking-and-soft-thinking-2/" title="论思维的刚性与柔性（科学的迷信-2）">论思维的刚性与柔性（科学的迷信-2）</a> (0)</li><li>2010年04月19日 -- <a href="http://blog.zhenghui.org/2010/04/19/hard-thinking-and-soft-thinking-1/" title="论思维的刚性与柔性（科学的迷信-1）">论思维的刚性与柔性（科学的迷信-1）</a> (0)</li><li>2010年04月2日 -- <a href="http://blog.zhenghui.org/2010/04/02/hard-thinking-and-soft-thinking-0/" title="论思维的刚性与柔性（前言）">论思维的刚性与柔性（前言）</a> (44)</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></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=vt5qabwGvvo:sO9G96Um5Uo:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/vt5qabwGvvo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2011/07/12/hard-thinking-and-soft-thinking-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2011/07/12/hard-thinking-and-soft-thinking-4/</feedburner:origLink></item>
		<item>
		<title>我那难缠的小女</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/7XIGfiSIjoQ/</link>
		<comments>http://blog.zhenghui.org/2011/05/10/daughter/#comments</comments>
		<pubDate>Tue, 10 May 2011 05:40:53 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[生活]]></category>
		<category><![CDATA[娱乐]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=868</guid>
		<description><![CDATA[女儿趣事实录 [...]]]></description>
			<content:encoded><![CDATA[
<h2 style="text-align: center;"><strong>我那难缠的小女</strong></h2>
<p>俗话说，家家有本难念的经，本人也未能幸免。家有一女，02年9月生人，在外乖巧柔顺，在内乖张不驯。以下是她的一些言行实录，供一时之娱。</p>
<p><strong>臭美</strong></p>
<p>女孩都爱臭美，但小女爱得未免有些离谱。略举几例，即可见一斑。4岁那年，她公然向我索要胭脂，作为一名负责任的父亲，我断然拒绝了这一无理要求。她竟不吵也不闹，默默地走开了。一段时间后重新出现，小脸蛋上赫然挂着两朵红晕。心下生奇：家中并无胭脂啊？原来，她是对着镜子生生把自己的脸给拧红的。小小年纪便会使苦肉计了，这得多大的决心和毅力啊。</p>
<p>有一次家中吃柚子，发现她静静地、笔直地躺在地上，仔细一看，差点没笑趴：她把三块大小形状皆合适的柚子皮放在身上，正好搭成了一个黄色比基尼。</p>
<p>另有一回，她在洗手间索要纸巾，尽管急着想出来，但在我递交时却拒绝接货。正疑惑间，她语带不屑地说：你身上这套衣服太丑了，换套再来。简直岂有此理嘛，这是友情赞助，不是友情演出，怎么还有着装要求？！我羞愤交加地想。有时我真怀疑，假如自己长得再寒碜一点，她会要求她妈换个老公吗？</p>
<p>眼见得她对美的追求愈演愈烈，万般无奈之下，我准备了一番老套的说教。有一天趁她顾影自怜之际，语重心长地对她说：有的人啊，长得虽然很漂亮但品德很差，这样是不好的。话音刚落，她指着自己的鼻子：嗯，我就是。看着她毫无羞愧的眼睛，我彻底无语了。</p>
<p>&nbsp;</p>

<p><strong>辣嘴</strong></p>
<p>女孩的嘴多半很甜，她属于少数派，大部分时间不甜反辣，且好诡辩，喜迂恶直。下面是几段情景对话：</p>
<p>三岁</p>
<ul>
	<li>有人问她：你知道爸爸的属相吗？她一声不吭，既不点头也不摇头。大家都以为她不知道答案，过了一会，她突然扭头大声问我：爸爸，今天为什么不吃鸡呢？（我的属相正是鸡)</li>
	<li>为了劝她进里屋，我对她说：这个房间很亮。她却道：等一下就不亮了。我正纳闷呢，只听“啪！”的一声，她关掉了灯。</li>
</ul>
<p>四岁</p>
<ul>
	<li>我问：为什么哭了？她答：我没哭，是嘴巴在哭。（假哭？）</li>
	<li>一日在路上，她声称要下雨了。我问：你怎么知道？她说：我听见雷声了。我猜她是想找个理由回家，便问：我怎么听不见？她骄傲地说：你只有两个耳朵，而我有四个。（特异功能？）</li>
	<li>见我的背心上有个洞，她问：为什么你要穿破衣服呢？我说：爸爸把好衣服给你穿了。她不解：为什么？我深情地回答：因为喜欢你啊。她反问：那你喜不喜欢你自己呢？</li>
	<li>给她讲完灰姑娘的故事后，老婆满怀期待地问：如果妈妈有一天要死了，你会怎样做呢？她一瓢冷水浇过来：等你死了不就知道了吗？</li>
	<li>老婆和她姐姐各自试穿新买的衣服，一套红装、一套绿装。问她：大姨和妈妈谁漂亮？她两面讨好：都漂亮，一个大红，一个大绿。（难得嘴甜一回）</li>
</ul>
<p>五岁</p>
<ul>
	<li>她猛地一屁股坐下，把沙发上的遥控器压得凌空转体360，外公急道：你把遥控器压坏了！她辩解道：遥控器都跳起来了，怎么会压住呢？</li>
	<li>一次她对外公耍脾气：不许说话！也不许笑！要我看你的牙齿啊？</li>
	<li>她写字有时比较潦草，我告诫她不要乱写。她毫不买账：乱写的字也是一种字。</li>
	<li>外面打响雷，她有些害怕，我欲上前抚慰，她却不让碰。正伤心中，她解释道：你的假牙里有金属。</li>
	<li>看电视剧《西游记》时，外公故作惊恐状，她嘴一撇：你以为真的有孙悟空吗？那是骗你们老人家的。你以为他会跑出来打你吗？不会的——（他）被电视机挡住了！</li>
</ul>
<p>六岁</p>
<ul>
	<li>路上随口问她：25+16=？她立刻回答41。窃喜，她何时学会了两位数的进位加法？问她是如何得到答案的，答曰：心里的小人告诉我的。待我再追问那个小人，她却始终不肯透露。</li>
	<li>奶奶问：这几天做了算术没有？她答：做了就做了，没做就没做。</li>
	<li>见我的头发有些凌乱，她讥讽道：你是植物人吧？头发长得像树叶。（什么比方？至多像乱草嘛。）</li>
</ul>
<p>七岁</p>
<ul>
	<li> 她一直不肯分床睡觉，我晓之以理：你已经长大了，该一个人单独睡，不应再要人陪了。她反问：那你为什么和妈妈睡在一起？大人都做不到，为什么要小孩做到？</li>
	<li>奶奶要她背乘法口诀，几番威逼不成，转而利诱道：我可以满足你一个条件。她问：什么条件都可以吗？奶奶坚定地颔首。她狡黠地一笑：我的条件是——不背乘法口诀。</li>
	<li>为了劝她喝药，我说：你连打针都不怕，还怕吃药？药只是苦又不疼。 她回道：那疼又不苦。一时间，我陷入逻辑混乱中…</li>
	<li>有次我逗她：爸爸现在很饿，想把你吃掉。 她丝毫不惧：我是你吐出来的，怎能又把我吃回去呢？（这是谁告诉她的？）</li>
	<li>见她在翻看两岁时的照片，我故意为难她：是你小时候漂亮还是现在漂亮？她答：小时候可爱，现在漂亮。</li>
	<li>发现她有个常用的英语单词不会，我忍不住说：这么简单的单词都不会？ 她颇不服气：会了就简单，不会才难。</li>
	<li>她突然说不想读书。情急之下，我警告她：不读书长大了就要讨饭哦。我承认，这不是一个好的教育方式。很快地，我就受到了惩罚。她眼珠一转：那你老了也要讨饭！我说：爸爸读了很多书，不会讨饭的。她不信：你老了没工资。我想启发她世界上还有一种叫做退休金的东西：你看，爷爷奶奶都老了，为什么没有讨饭呢？她说：那是因为他们的宝宝有钱。微怔之后，我明白了她的意思：你是说等我老了而你——我的宝宝没钱，于是只好去讨饭？她得意地点头。心下一寒，起先是我威胁她，到头来她反威胁我了？</li>
	<li>有一晚，看着窗外的月亮，她忽然感慨道：今夜的月亮好椭圆啊！（玩起了抒情，难得啊）</li>
</ul>
<p>八岁</p>
<ul>
	<li>数学考试中，她错把小于号（&lt;） 写成大于号（&gt;），问她原因，回答是：不小心把试卷放倒了。</li>
	<li>在放学途中，我问她一道题目，她拒绝回答，诡称：你问的问题到我的耳朵里就是我的了，因此不用回答了。</li>
	<li>见她不肯吃牛肉，我叹道：小时候爸爸想吃牛肉都吃不上。她也叹道：小时候我不想吃牛肉却被逼着吃。（同苦）</li>
	<li>她不肯吃饭，老婆“威胁”道：再不吃饭，我把你扔到天桥去，她回嘴：我把你扔到天堂去。（惊悚）</li>
</ul>
<p>九岁</p>
<ul>
	<li>她突然称我为书呆子，心下一惊，很多年没人这么“夸”我了。问她为什么，她说：你整天呆在书旁边。跟我玩文字游戏吗？正想着，她又补充了一条：经常说些叫人听不懂的话。</li>
	<li>老婆问：“等妈妈老了，你会养我吗？”她回答：“会的，而且我还要教育你，要你做什么就做什么。”（这哪里是反哺，分明是借机报复啊）</li>
	<li>有一次她在学校犯了错误，我问其原因，她自豪地回答：“老师教导我们要乐观。”我一愣，这算哪门子理由啊？她接着说：“不惹点事，怎么体现乐观呢？”</li>
	<li>见老婆在镜前涂护肤品，她也凑了上来，在比对了两张脸后提议道：我们可以合伙做个广告，擦护肤品前的脸是你这样，擦完后就成我这样了。</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%2F2011%2F05%2F10%2Fdaughter%2F&amp;title=%E6%88%91%E9%82%A3%E9%9A%BE%E7%BC%A0%E7%9A%84%E5%B0%8F%E5%A5%B3" id="wpa2a_10">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>无相关文章</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=7XIGfiSIjoQ:Z4H3ivDQVwI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/7XIGfiSIjoQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2011/05/10/daughter/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2011/05/10/daughter/</feedburner:origLink></item>
		<item>
		<title>答读者问(3)——再谈抽象</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/mpHv7h3YksA/</link>
		<comments>http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/#comments</comments>
		<pubDate>Sun, 17 Apr 2011 19:21:55 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[抽象]]></category>
		<category><![CDATA[透明]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=829</guid>
		<description><![CDATA[回答上位读者的进一步提问 [...]]]></description>
			<content:encoded><![CDATA[
<h2><strong>答读者问(3)</strong></h2>
<p>以下内容试图回答上位读者在<a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/">答读者问（2）</a>后的进一步提问。</p>
<p><strong>读者</strong>七心葵问：</p>
<blockquote>
<p>得蒙先生专门撰文答复，深感荣幸，先生针对我的问题做了详细的解答并给出大量的例证，点出了很多我没有想到过的东西。 需要说明的是我其实是更倾向于OO的，喜欢OO带来的代码的整洁感和易重用，也知道不同的开发方法或语言范型有不同的适用场合，不可一概而论。对于那些驳斥OO的牛人，我不是因为他们是牛人所以怀疑OO，只是觉得他们提出的问题，自己的确得不到解答。上面提出来来的两点，我不认为是足以推翻OO，但是的确认为其有独特的价值（尤其是对比OO来看）所在，因而专门询问这两方面的情况。</p>
<p>关于我提的两个问题：<br /> 1 OO的抽象的对立面（“反抽象”的叫法似乎太过了，或者应该说是 “反OO抽象”，或者更进一步“反’静态类型的OO’抽象”？）；<br /> 2 数据驱动编程；<br /> 我仔细想了想先生的话,并陆续读完了先生的整本书, 还是觉得有些地方想再听听先生的看法，这里写出来，请多多指教。</p>
<p>毋庸置疑，抽象是人类处理复杂性的利器。但是，在我看来，“抽象”这个词本身就够抽象，抽象有很多种，有不同角度的抽象，有不同层次的抽象。<br /> 在OO里面，我认为“抽象”通常的定义是“抽象就是有选择的忽略”。那么如果脱离开OO的范畴甚至脱离开软件程序设计的范畴来讲，什么是抽象呢? 很难定义，我们不妨从其反面看，什么不是抽象呢？具体的东西不抽象——具象，或叫做“具体事物”。相比于“抽象”，具象对人类来说显然是一种更直观、更可操控，因而显得更自然、更可理解的思维。</p>
<p>从这个角度讲，“everything is file”，这里的“file” 其实是一个概念模型，是一个“隐喻”；对比或与非门、0/1编码,它当然是一种抽象，但更是一种具象，因为Unix/Linux 真的把一切都实现成了文件！<br /> 1 你可以所有操作文件的API来操作它，也可以使用用户接口界面CLI通过操作它；你可以直接查看各种真正的文件的内容或属性，也可以通过查看/proc下相应文件来查看进程运行中的内存，还可以通过通过它查看各种I/O数据流。<br /> 2 如果以OO的眼光看，“文件”中的诸多子类型，其实存在“接口退化”的现象，例如：你无法删除/proc目录下表示进程运行时内存的文件（即便你有权限）。</p>
<p>这里的要点，我认为，是逻辑模型直接映射到了物理实现，因而显得透明，直观，可显，也就是“浅、平、透”。也就是透明性原则的副标题“设计要可见，以便审查和调试”所强调的要点。《Unix编程艺术》（以下称 TAOUP）中强调的其他原则，如 每个程序做一件事并做好，c语言的薄胶合层，文本化协议，多进程分离功能，数据驱动编程，微语言设计，无不体现着这一点：使逻辑模型直接映射到物理实现，可视可显、可以操作、可以度量、可以把玩可以打磨。</p>
<p>当然，OO设计当然也可以把逻辑设计映射到具体的物理模块，比如分目录的做法，比如分包的做法，比如最终编译为一个动态链接库的做法，都可以将抽象的模块（或层或区） 映射到这样 透明直观可显 的物理实现上，但是我认为这并不等价(尽管分包分库的做法确实已经使得程序的模块性和可理解性变得相对直观很多了)。 譬如，考虑Apache www服务器最早运行CGI动态生成网页的方式： 每一个CGI本身作为一个可执行程序，只是从stdin输入往stdout输出，每一个CGI可以通过C语言编写，也可以通过shell，perl等脚本语言编写，其本身既能和Apache配合，也可以在CLI命令行中直接使用、测试、把玩、打磨。并且，CGI还有其他的好处，例如可以按名字按目录控制权限，可以作为一项资源被引用（RESTful架构）等。 这让人联想到硬件部件具有的长宽高的物理属性，虽然这不是硬件设计的重点，但是一个集成芯片确实可以在关键时候垫桌脚；同样，一个CGI确实可以在必要的时候用作命令行管道中的一个过滤器，而这些都不是那些”由逻辑模块映射成物理模块的动态链接库”等可以做到的（也许能用到一些？比如延迟加载等特性）。</p>
<p>其实，在 TAOUP 一书中，作者有一次把这些 “已经实现的具象” 称为“基本抽象”： （见 第20章：Unix程序员在30年风雨中学到最有经验的回应，就是回到最初的准则–优先从 字节流，命名空间，进程 等 Unix基本抽象中得到更多的效用，而不是增加新的东西。）</p>
<p>先生一书中曾提到过硬件行业往往因为诸多原因比软件行业更能贯彻OOP的理念，这里的Unix下软件设计这种“保持基本抽象、十分警惕过度纯虚化抽象”的做法似乎更类似于硬件行业的做法。事实上，我认为硬件行业不是能更好的贯彻OOP理念，而是硬件行业的接口标准比较规范（正如书中所述的原因）而不易虚化不易重新制定抽象规范（双刃剑！让人想起来电子垃圾邮件，电子邮件由于其本身的低成本和世界范围内的连通而得以流行，也正是因为同样的原因被发送垃圾邮件的人得以利用并难以预防！）。 试推想，如果所有的操作系统的API都是符合Posix标准，所有的DBMS都遵从SQL标准，软件行业看起来也会像硬件行业一样规范有序而无需经常抽象！——如果具象层面就能解决的问题，为何要抽象呢；换言之，如果抽象的规范为所有应用软件所遵循，又何必强调接口和实现的形式上分离呢。呵呵，当然没有这么理想化的世界（但是好像硬件行业的世界的确是这样的？或者还是有些变动名堂的吧，要不也不会IDE转USB，USB又转SCSI这样的接口了，当然，这些也是规范化标准化了的），这让我想起来《c++沉思录》中提到的一个笑话，说Lisp程序员在处理那些“不能很好的符合s-expression模式的数据时，他们居然认为解决这些问题的惟一办法就是让全世界都只使用Lisp这一种语言！”（从这个角度来说，OO也许好一点，至少OO在想办法把周边非OO的东西先转化为OO再处理：平的数据文件转变成有嵌套层次的对象，关系型数据库中的数据映射为对象，协议内容包装为DTO对象，简单的值包装成 valueObject，分配内存并初始化 转换为 使用工厂生产对象，原本的功能函数 转变为 serviceObject；UI的对象似乎是天然的，真正映射到现实世界的就只剩下实体对象了！这不正是当年 smalltalk 的困境么？——感谢JVM吧，同时也别奇怪为什么C++程序员总是那么OO得不够专业）</p>
<p>在这里，与其说Unix文化所强调的是一种抽象，不如说是一种隐喻。抽象只给你接口，隐喻给你必要的梗概信息，以避免“抽象泄露”（见《Joel说软件》一书）。譬如，windows操作系统下，本地磁盘是一个磁盘，一个可访问的异地远程目录也可以挂载为一个磁盘；当你只是打开小文本文件的时候，这两个磁盘（共同的抽象）表现出来没什么区别，但是当你想分别在两个磁盘上运行大型游戏的时候，可能就会发现加载远程磁盘的游戏内容很慢，因为抽象遗漏了一些你在这种情况下必须注意的信息。</p>
<p>这里我再说我对两个问题的看法： 1 “透明” 说到“透明”这个词，我认为“透明”这个词犹如灰色之余黑色白色，说灰色更近于黑色或者更近于白色，其实还是取决于灰的程度如何，因此说“因为透明而看得见”，或者“因为透明而看不见” 应该都是可以的。而在TAOUP一书中，我认为“透明”一词的含义确实更偏重于“因为透明而看得见”，比如该书第20章有这么一段话： 透明性是重用性的关键，被重用组件不仅要透露给用户“做什么”，还有透露“怎么做”。 OO的做法显然与这里强调的不同，OO强调封装强调黑盒，只告诉用户“做什么”；这里是白盒，会告诉用户“做什么以及怎样做”。</p>
<p>2 “文本化” text文件比binary文件抽象程度更高更低当然并不一定，但是无结构的文本文件的确是平坦的，和关系型数据库一样，是反OO抽象的（映射成OO时是需要 序列化/反序列化或者ORM 的）；但是Unix并不关心于此，Unix关心文本文件内的内容(不论是内容的格式还是字符集)是可显的，透明的，可用来调试，有利于维护，是具象的，直观的，并且可以接合其他过滤器程序处理的，其本身的形式就是元（也许更具体的应该说包括 CSV、TAB键分隔等）。</p>
<p>以上这些，其实与您书中提到的两种抽象“规范抽象，参数抽象”并不矛盾； 我从内心深处喜欢OO，OO带给我更多的整洁感和美的享受。而我也总有一些时候认为，也许Unix文化下的这些准则 只是在更小的局部中解决问题会用到的原则等。 但是数据驱动编程，（我认为其起源于朴素的 参数化思想 和 代码生成技术）则真正的是一种导向与众不同的设计范型。</p>
<p>Booch 书中提到的三类软件设计方法：Top-down structured design、Data-driven design与Object-oriented design，(也是引述别人的)，我对其中的 Data-driven design （简称 DDD似容易和 domain-driven design 弄混，既然也有叫做 Data-driven programming，我这里简称 DDP）尤其感兴趣，因为我想知道可以和 结构化设计，OO设计 这样并驾齐驱，号称为众多软件设计方法之祖的这样一门编程语言有何奥妙，为何少听人说道？</p>
<p>这里先说一下我为何愿意相信这三类软件设计方法是众方法之祖，因为：</p>
<p>1 权威作者的认可 如 booch 书中所说“这些方法中的大多数基本上都是类似主题的一些变奏”，“绝大多数方法都可以归为以下三类之一”</p>
<p>2 我个人相信如果从某一个角度看，抓住本质的话，众多的分类可以归并成几类的</p>
<p>3 逻辑的分析： 所谓的“软件设计方法”决定了设计软件的方式（-orinted），我认为这3类方法是完全从不同的角度来看待 软件设计的， 其中 结构化世界 和 OO 不相容在同一个设计中不能并存；而 DDP 则与其他两种方法都各自相容； 众多编程范式可以归入这3类软件设计方法,例如: DDP 其实就是 机制与策略分离，DDP的起源应该是朴素的 参数化抽象 思想, 包含了以下这些编程范式： language-orinted programming( declarative programming、function programming、logic-driven、knowledge-driven ) meta-programming( generic programming、aspect-orinted programming、代码生成技术 ) 而 event-driven 这种范式似乎划归到 OO 中去更为合适，</p>
<p>TAOUP一书中专门提到了 DDP 和 OO 的对比： “数据驱动编程有时会跟面向对象混淆起来…他们之间至少有两点不同。第一，在数据驱动编程中，数据不仅仅是某个对象的状态，实际上还定义了程序的控制流；第二，OO首先考虑的是封装，而数据驱动编程看重的是编写尽可能少的固定代码。Unix中 数据驱动编程的传统比OO更深厚。” Booch书中也提到对DDP的描述 “在这种方法中，系统输入和输出之间的映射关系驱动着软件体系的结构。与 结构化设计 一起，数据驱动设计 已经成功地被应用于一些复杂的领域。” 这里的“映射关系”我理解为 “机制与策略相分离”的 机制的部分，也对应于 LOP 中的 解释器 或 虚拟机，即“程序中固定不变的部分”。 因为booch书引用的相关文献我并没有找到进一步参考学习，所以我只是从个人的理解上进行分析。先生内功深厚，涉猎广博，是否可以予以指点？</p>
<p>按照Tanenbaum在《结构化计算机组成》一书中所言： 机器的指令系统层提供了底层或与非门，0/1数据，电子部件 的一层虚拟机； 操作系统在此之上提供了另一层虚拟机（C语言层紧附于这层虚拟机，所以说是一个“薄胶合层”）， 高层语言在此基础上构建更高层的虚拟机（无论是 编译时面对的编译器平台和库 还是 运行时面对的解释器）。</p>
<p>沿用 Tanenbaum 的观点，我认为 数据驱动编程（包括LOP中的解释器）就是在操作系统层之上再构建一层虚拟机 的过程。 为什么这么说? 虚拟机之上 和 虚拟机之下 的部分的对比，其实不是数据和指令的对比，也不是虚拟机的软件指令和机器硬件指令的对比， 而是 相对固定的部分 和 相对易于变动的部分 的对比，是变化率的对比，这正是 数据驱动编程（策略与机制分离） 的本质，是一种朴素的 参数化 的思想。</p>
<p>数据与代码分离，本质上其实是 相对固定不变的部分（机制） 和 相对容易变动的部分（策略） 的分离。 数据驱动编程，本质上是符合分离原则和保变原则的，如 bob大叔所说，变化率 是程序始终应该注意的一个问题。 由此联想到的还有：（数据和代码的分离，微语言和解释器的分离，被生成代码和代码生成器的分离）； 更近一步：（微内核插件式体系结构，）</p>
<p>元编程 应该说是更加泛化的 数据驱动编程，元编程 不是新加入一个间接层，而是退居一步，使得当前的层变成一个间接层。 元编程 分为 静态元编程（编译时） 和 动态元编程（运行时）， 静态元编程本质上是一种 代码生成技术 或者 编译器技术； 动态元编程一般通过 解释器（或虚拟机）加以实现。</p>
<p>数据驱动编程当然也不应该说是“反抽象的”，但的确与 “OO抽象”的思维方式是迥然不同，泾渭分明的， 如TAOUP一书中所述：“在Unix的模块化传统 和 围绕OO语言发展起来的使用模式之间，存在着紧张的对立关系” 应该说 数据驱动编程的思路与 结构化编程 和 OO 是正交的，更类似一种”跳出三界外，不在五行中”的做法。 或许这是您书中言明的，抽象分为”规范抽象”和”参数化抽象”的精义所在？（我不知道您的这两种抽象思想划分的名字来自于何处，但是我很喜欢这一对词）</p>
<p>考虑 数据驱动编程 与 OO 的不同机制，可以考虑一下 虚拟机 和 类框架 的不同： 虚拟机将逻辑细节（策略）抛给微语言代码；（只提供域原语，更稳定更灵活）——给用户出的是主观题，要求用户具有一定能力 类框架提供所有可能的领域组件，供用户组合；（提供了所有需要的东西，比较僵化）——给用户出的是选择题，对用户要求没有上面那么高</p>
<p>总而言之，在我看来，TAOUP 强调的这两种Unix世界下的传统 对应于 OO所强调的，可以概括如下： 1 不强调 数据抽象，但强调模块化封装，强调浅平透的设计，强调直观简单的具象； 2 不强调 类层次结构，但强调 策略和机制的分离（从而最多2层，策略层再分为 相对固定的部分 和 相对变动的部分 从而形成新的虚拟机层不容易，但不是不可能） 3 多态本来就不是OO所特有的，Linux内核里面 实现 VFS和各种格式的文件系统时就采用了这种很自然的做法；考虑到 duck type，mixin 等动态语言中的多态机制就更不能如此说了。 多态对于人类来说其实是很自然的做法，它给程序设计带来了模糊语义，从而使得代码具有更强的表现力，更简单的形式。</p>
<p>是否可以这么说：有两个世界，OO的世界，和OO以外的世界。 OO的世界对于程序员来说是理想化的，层次分明，抽象整洁的，非常便于处理复杂性的，各司其职共同协作处理问题的，即您书中所言的公民社会； OO以外的世界，具象横陈的世界，其复杂性的来源正如brooks所说 分为本质复杂性（业务本身的） 和 非本质复杂性（技术上的）。 ——但是在我看来，这些两种复杂性的差别并不在于哪些是业务的哪些是技术的哪些是业务的，而在于哪些是已经被固化下来的。</p>
<p>某个领域之所以处理起来还比较复杂，是因为：</p>
<p>1 领域本身的确很复杂：<br />a 领域内的逻辑本身的确很复杂，很难有确定的算法来处理<br />b 领域内的逻辑还是在变化中的，<br />c 领域内逻辑的数量是极大的。</p>
<p>2 领域基础条件的不成熟：<br />a 还没有探索出好的模型，或者该模型还没有被普遍认识到，<br />b 还没有制定出好的各个层面的规范并集体遵守，<br />c 还没有固化足够多的恰当的机制的部分。</p>
<p>3 人类心智的限制，一切的背后都有 人的因素 作为依据：<br />a 人同时关注的信息数量：7+-2 （所以要分模块）<br />b 人接收一组新信息的平均时间5s （所以要简单，系统总的模块数不要太多）<br />c 人思维的直观性（人的视觉能力 和 模糊思维能力），这意味这两点：<br />A “直”——更善于思考自己能直接接触把玩的东西；（所以要“浅平透”、使用具象的设计），<br />B “观”——更善于观图而不是推算逻辑；（所以要 表驱动法，数据驱动编程，要UML，要可视化编程——当然MDA是太理想化了）<br />d 人不能持续集中注意力（人在一定的代码行数中产生的bug数量的比例是一定的，所以语言有具有表现力，要体现表达的经济性）<br />所以要 机制与策略分离，要 数据和代码分离（数据驱动编程），要 微语言，要 DSL，要LOP，<br />e 人是有创造欲，有现实利益心的（只要偶可能总是不够遵从规范，或想创造规范谋利——只要成本能承受，在硬件领域就不行）</p>
<p>您点出的一句话 “如果抽象解决的复杂不及其带来的复杂，则说明该抽象是不当的。” 可能正点出了要点所在。<br />我绝无意批判“抽象”，我只是想说明，的确有另外的思维方式的存在—— 1 直观具象的、浅平透的设计，2 数据驱动编程</p>
<p>小子浅薄，借贵宝地洋洋洒洒说了这么多，愿得先生指点，解后进心中之惑，不胜感谢。</p>
<p>&nbsp;</p>
</blockquote>
<p><strong>作者</strong>hui答：</p>
<blockquote>
<p>面对如此长篇的评论，作为博主我表示压力很大：）事实上，无论是从评论的篇幅还是内容上看，皆可独立成篇。这里如此大段地引用，窃感颇有掠人之美的嫌疑。不过作为作者，也为能有这么一位认真的读者而欣慰不已。</p>
<p>言归正题。关于抽象，《冒号课堂》中虽有不少阐述，但限于篇幅，未能完全展开。正因意犹未尽，总想找机会续谈，以解心中之痒，你的提问客观上加速了这一进程。</p>
<p>应当说，你对抽象的理解基本上是到位的，只是稍有偏差。比如你提到抽象是一种<em>选择性的忽略</em>（selective ignorance），这无疑是正确的，只可惜你前面加上了“OO”的限制，这就有点画蛇添足了。OO尽管有其侧重的抽象形式，如数据抽象（data abstraction）、类型层级（type hierarchy）、多态抽象（polymorphic abstraction），但它们并未脱离一般抽象的范畴。</p>
<p>你在文中提到：具象对人类来说显然是一种更直观、更可操控，因而是显得更自然、更可理解的思维。听上去不无道理，但似乎有点执着于“抽象”的表面字义了。大多数人对“抽象”一词的第一反应是<em>形容词</em>的抽象（abstract），于是总不自觉地将之与“深奥”、“模糊”、“不直观”、“不具体”等相关联，这固非大谬，然而在编程设计中，人们关注或强调的抽象更多地当是<em>名词</em>的抽象（abstraction）或<em>动词</em>的抽象（abstract，指“抽象化”）。比如著名的针对接口编程（programming to interface）原则、依赖反转（Dependency Inversion）原则，本质上都是提倡针对抽象编程，这样做的好处暂且不提，代码一定会因此变得更不直观、更难操纵、更不自然、更难理解吗？恐怕未必吧。如果说增加的抽象层（abstraction layer）（比如abstract class、interface等）有时会制造一些复杂的话（但最终目的是为了减少复杂），那么普通API的调用则是再常见不过了。相比一个API清晰的接口和明确的规范，它的冗长、复杂的实现代码虽然更加具体，但不是更难以把握和理解吗？</p>
<p>为避免空泛的语言分析，这里顺手拣个例子：不妨注意看上段话中的“形容词”、“名词”、“动词”的字体，我在html源码中特意为它们分别加上了&lt;em&gt;标签，以示强调。为什么不用更简单的&lt;i&gt;标签呢？表面原因是：&lt;i&gt;标签已经过时了，并且只要读者的浏览器支持，此处&lt;em&gt;中的内容会显示比较美观的楷体而不是斜体（根据css中的设置），而深层原因是：标签&lt;em&gt;比&lt;i&gt;的抽象层次更高。看到此处，恐怕许多人会暗生疑惑：两个再简单不过的html标签，竟也扯上了抽象，是不是有些小题大作？再说，&lt;em&gt;与&lt;i&gt;不是一样地直观、具象吗？哪里有一点抽象的影子？其实不然。往大处说，抽象存在于每一段代码之中，哪怕是不入一般程序员法眼的html代码；更广泛地，抽象存在于人们每时每刻的思维之中——如果不有意无意地忽略一些细节，人类的思维寸步难行。往小处说，i是italic（斜体）的缩写，而em是emphasis（强调）的缩写，前者是手段，后者是目的；前者是表象，后者是实质。从这个意义上看，后者当然更抽象。认清这一点，便更能认识到css的价值所在：它把传统的混杂了内容与形式的html进行了抽象分解，让html专注于表达数据（data），css专注于表达样式（style）。这样html标签不再身兼内容与形式的双重职责（如&lt;i&gt;），而仅仅让标签的名称来表达规范的语义，让标签的内容来表达实际的数据（如&lt;em&gt;）。其好处是显而易见的：一方面，在数据不变的情况下，程序员可根据客户喜好定制不同的css；另一方面，相同的css可以应用于不同的数据。于是，形式与内容两方面的可维护性和可重用性都得以增强。与此同时，它们的抽象性也得到了增强，因为每一次分解——或者说，关注点的分离（SoC）——都是被分解者之间彼此无视对方的产物，即所谓的“选择性的忽略”。标签&lt;em&gt;之所以比&lt;i&gt;更抽象，正是由于它忽略了样式的细节（注：尽管标签&lt;i&gt;同样也能用css来定制表现形式，但它没有明确的规范语义）。最后，不要忘记标签&lt;i&gt;本身也蕴涵着抽象：在html规范的保证下，编码者根本不用费心如何让一段文字变成斜体，也不用关心用户使用何种浏览器。饶舌至此，是欲说明一点：设计（不限于编程设计）中的抽象与口语中的抽象并不完全等同，由于其相对性和普适性，看似具体的事物也有抽象的一面，看似抽象的事物也有具体的一面。在提及抽象时，建议少通过形容词（abstract）来联想，多用名词（abstraction）或动词（抽象化）。或者更好地，根据上下文把抽象直接替换为“规范”（specification）、“约定”（convention）、“协议”（protocol）、“概念”（concept）、“语义”（semantic）、“接口”（interface）、“服务”（service）、“职责”（responsibility）之类的名词，或“忽略”、“分离”、“提炼”、“抽取”之类的动词。而抽象的对立面，用“具体”、“具象”、“明晰”、“直观”等就不如“细节”（detail）、“实现”（implementation）等来得更贴切。</p>
<p>你在文中还提到：“everything is file”中的“file” 其实是一个概念模型，是一个“隐喻”…与其说Unix文化所强调的是一种抽象，不如说是一种隐喻。前一句非常正确，后一句值得商榷。正如书中（P181）指出的那样：模型是抽象的结晶，而抽象成功的标志之一是形成引起共识的概念。这些概念在现实世界中通常有自然而朴素的对应，否则难以达成共识。OOP之所以盛行，与它在许多应用中比较符合人类的认知模式是密不可分的。比如，在图形界面的设计中，让Window类对应于窗口，Button类对应于按钮、TextField类对应于文本框；在银行业务中，让Bank类对应于银行、Account类对应于帐户、Transaction类对应于交易，等等。无论是设计者还是使用者，都能迅速地理解程序的设计和编码意图。相反，如果有人把一些关联度不大的数据与函数粘合为一个类，整体上没有形成一个合理有效的概念，那么这个类能称之为真正意义上的抽象吗？当然，有些概念在现实世界中难以找到直接的对应物，这时人们就会借助隐喻（metaphor）了。file把生活中的物理文件与磁盘中的信息块对应起来，二者具有共通性，且形象易记，是一个成功的抽象。总之，建立模型、形成概念、采用隐喻的过程，与抽象不仅毫不矛盾，而且正好合辙。</p>
<p>除了抽象的层次、抽象的角度、概念模型、隐喻等以外，你还涉及了抽象的一些其他关键点，可见你对抽象的思考的确是非常深入的。其中包括：</p>
<ul>
	<li><em>抽象的扩展</em>（expansion）</li>
</ul>
<p>实际上，如果追根究底的话，file还不算得真正的隐喻，因为最初计算机中的file指的真是物理的<a href="http://en.wikipedia.org/wiki/Punched_card">打孔卡片</a>。因此，说是抽象的扩展更加确切。同为抽象的概念，Unix用户脑中的file远比Windows用户的更为抽象。在一般的windows用户的眼中，文件与文件夹（folder）是泾渭分明的。而在Unix中，file不仅包含普通文件，而且包括目录（directory）。不仅如此，在Unix下的链接（link），用户终端（terminal）、硬件设备（device）、网络连接（network connection）以及包括管道（pipe）、共享内存（shared memory）、UNIX域套接字（unix domain socket）等在内的进程间通信（IPC），通通可看成file。顺便提一下，你为了强调file的具象，提到“Unix/Linux <strong>真的</strong>把一切都实现成了文件”。可你下面所举的proc文件系统就不是传统意义上<strong>真正</strong>的文件系统，而是伪文件系统（pseudo-filesystem）。其实这仍是抽象的体现，具体来说，是抽象的扩展——从打孔卡片扩展到磁盘字节块、到I/O字节流（byte stream）、到更广泛的信息资源（resources）。同样，你引用TAOUP中的那句话，“Unix优先从 字节流，命名空间，进程 等 Unix基本抽象中得到更多的效用，而不是增加新的东西”，也反映Unix中的一些抽象并非一蹴而就的，而是逐步扩展的。扩展抽象（而非新建抽象）的一个好处是，减少因新概念的引进而带来的复杂性，并且强化原有的抽象；另一好处是，能让针对该抽象而设计的代码具有更广泛的适用性。</p>
<ul>
	<li><em>抽象的退化</em>（reduction）</li>
</ul>
<p>理想的抽象是对某一概念本质特征进行精准的捕捉和描述，不要多也不要少。多则限制该抽象的适用范围，少则不能充分利用该抽象的特性。然而在实际编程中，这几乎是不可能的。当抽象过多时，便会产生退化现象。比如，除非被卸载（unmount），/proc下的文件无法被删除；比如，一个声明为List类型的UnmodifiableList类不支持add、remove等操作；等等。</p>
<ul>
	<li><em>抽象的泄露</em>（leakage）</li>
</ul>
<p>“抽象只给你接口，隐喻给你必要的梗概信息，以避免‘抽象泄露’”。如果你该处的抽象泄露来自Joel的<a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html">The Law of Leaky Abstractions</a> ，那么这种泄露绝非隐喻所能避免的。事实上，这是因为抽象过少之故，即在某些场合下，抽象没有提供足够的信息。为了挖掘必要的信息，人们不得不钻开抽象严密封装的外壳（crust），深入到它声称能够忽略的、此时却不能忽略的内部细节，由此形成了泄露。Joel提到的本地磁盘与远程磁盘的差别是一例，另一个典型的例子是：在Windows下用鼠标拖放文件图标时，将移动相应的文件。这种抽象操作明显来自隐喻，然而当目标文件与源文件处于不同的驱动器时，操作的结果却是文件拷贝，从而增加了用户的学习成本。尽管程序开发者有充分的技术理由区分不同驱动器，但他们没有理由让用户关心其中的细节。<strong>程序设计者一定要牢记：你可以嘲笑用户的无知，但必须重视用户的抽象</strong>。与前面的例子不同，这种抽象的泄露原本是可以避免的，故而Alan Cooper在《about face》中对此专门作出了批评。此外，在静态类型的语言中，向下转型(downcasting)之所以不受推荐，不仅因为它不够安全，也因为试图深入到更底层类型的代码可能是抽象泄露的征兆。</p>
<p>“如果所有的操作系统的API都是符合Posix标准，所有的DBMS都遵从SQL标准，软件行业看起来也会像硬件行业一样规范有序而无需经常抽象”。有道理，不过如果注意到“规范”本身就是一种抽象，这么说可能更准确：软件行业如果像硬件行业一样规范有序，许多不必要的<em>中间</em>抽象就能省去了。</p>
<p>抽象是对付复杂的有效工具。一方面，通过抽象剔除或屏蔽非本质的部分、提炼并合并本质的部分（即《冒号课堂》中提到的减法与除法），从而降低复杂性；另一方面，通过抽象对复杂分而治之，比如，宏观上水平分层（layer），垂直分区（partition），微观上分模块、分步骤，等等。说到这里，有读者可能会不屑一顾：不就是简化、合并、分层、分块、分步吗？听起来一点也不新鲜，为何一定要拽出“抽象”这个词呢？且慢，除了复杂外，软件设计的另一大敌人是变化。（注：当然，如果把变化作为复杂的一个因素也未尝不可。为方便说明问题，这里特意把变化单列）现实中有这样的程序员，他能独立地设计并开发具有一定难度和规模的软件系统，但他的代码对用户需求的变动具有异常的敏感性，并且很难被其他程序员理解，甚至也很难被以后的自己理解。这是何故？仔细审查，会发现他的代码虽然逻辑正确、格式规范、形式上井井有条，各种设计模式的身影不时出没，甚至还遵循着业界推崇的各种最佳实践，唯独缺乏“抽象”的神韵。例如，虽然遵循了模块化的原则，但每个模块的职责并不清晰，分工也不明确，模块之间的交互耦合不像是前世的约定，更像是今生的邂逅。重要的API没有详细的文档说明，即使有，实现代码也常常不买账。当具体的代码缺失了抽象的外壳，便丧失了必要的缓冲区，一旦变化来临，难免引发代码的强烈震荡。总而言之，把抽象作为设计的出发点，不仅能更合理地组织代码，而且为代码之间划分了明确的<strong>概念边界</strong>。这种边界的划分越合理、边界的规范越明晰、边界内的代码越遵循规范，系统越稳定、越易维护。</p>
<p>凡事有度，过犹不及。对于软件开发而言，抽象再重要，它也只是手段而非目的。如果待解决的问题并不足够复杂、或者并不足够多变，那么过多的抽象反而成为掣肘。Eric Raymond在TAOUP中对OOP抱怨最大的地方是，OOP语言容易让抽象泛滥。这种指责有一定的道理，相比Unix编程中最常见的C语言，OOP语言增加了多种抽象机制，加之OO界倡导的设计模式、各类流行的框架、企业级架构模式等等，使得OO程序员习惯了为代码裹上层层厚重的抽象（尽管他们可能并未意识到抽象）。一旦抽象层过多、抽象点过密，会造成代码难以理解、优化、调试和维护，运行效率也会大打折扣，这与Unix提倡的“简单、清晰、透明”（即你提到的“浅平透”）格格不入。话说回来，真正有错的不是OOP或抽象本身，而是那些唯对象论者和滥用抽象者的错。</p>
<p>关于Unix中透明（此处指“透明得看得见”）与文本化的问题，我上次的回答过于匆忙，既然你此次再度提起，这里专门补充一下。有一个重要背景不能不提，Unix环境下的编程设计有一个显著的特征：程序经常以进程（process)为单位来模块化，TAOUP中称之为“multiprogramming”。Unix推崇这种设计方式，是有其天然的优势作基础的：相对廉价的进程创建、多种强大的进程间通信（IPC）机制、丰富多样的命令和工具、方便的脚本环境，等等。且不论由此卷入了并发范式，这种设计方式本身就很独特，与人们习惯的以函数或类为基本模块单位的做法大相异趣。你指出了Unix下软件设计透明的一面，这固然在一定意义上与抽象相对，但可能没有意识到这种透明很大程度上仍源自抽象。何出此言呢？方才言及，Unix下有丰富的应用，它们一般都遵从“只做一件事、做好一件事”的哲学，并且每个应用通常都有详尽的文档（man page），这不是绝佳的抽象和规范吗？大多数应用都是二进制文件，这不是不透明（opaque）吗？（即使有公开的源码，恐怕也很少人费事去读）可见，正是底层应用建立了高密度的抽象，才使得上层应用的抽象较为稀疏，从而显得简单、浅显和透明。此外，Unix下大量采用脚本编程，而脚本语言的抽象一般比通用程序语言、尤其是编译型语言少，因为后者为防御变化带来的源码修改而引入的许多抽象，对于前者往往并不那么重要。</p>
<p>说到文本化，与Unix的multiprogramming设计也是密切相关的。由于鼓励通过合作的进程来分解复杂，进程之间的交互便显得尤为重要，而文本流（text stream）是最通用的接口。一旦文本作为接口，哪怕再透明，它也具有了一定的抽象职能。至于为什么是文本而非二进制（binary），我以为并非问题的本质。诚然，文本更简单、直观、易于人工编辑，但如果文本格式不规范，同样会增加代码的复杂度。相反，如果二进制文件有严格而公开的规范，并且有方便的API进行读写操作，那么同样适合作为进程之间的通信接口。只是事实上，Unix下有大量对文本处理的工具，如cut、grep、head、sort等之类过滤器（filter）以及更强大的小语言sed、awk等，乃至大语言perl、python等，而二进制格式的工具相应就少得多。文本的直观透明性倒是带来一个好处，可用于挖掘抽象规范之外信息。举个小例子，要求在Linux下获得硬盘（假设mount在/dev/hda上）的序列号。直接用C语言实现并不困难，但如何用一行命令来实现呢？通过hdparm命令可以获得硬盘的一些参数，但没有专门针对序列号的，故而无法直接获得所需的数据。好在该命令的输出是文本的，经过观察和试验，我们完成了任务：<code>hdparm -i /dev/hda | awk -F= '/SerialNo=/{print $NF}'</code>。不过这并非没有后顾之忧，由于hdparm对输出格式并无明确的规范，上述代码并不能保证对Linux的所有发行版本都适用。如果不追求理论上的完美，该解法倒也差强人意：换台机器如果不灵，大不了再作调整。这很公平，从抽象之外获取的”非法“信息，自然要付出一定的代价——或者是不确定性的代价，或者是额外编码的代价。</p>
<p>上次你提到数据驱动编程与语言导向编程（LOP），当时我只强调了二者的区别，没有仔细思考二者的联系，一时失察，还望见谅。从数据驱动的特点来看，参数抽象可看作其最初级的形式，领域特定语言（DSL）可看作其最高级的形式。具体分析非三言两语所能言明，或许另开专题更好。你的其他思考也非常有价值，我对抽象的论述也远未尽意，只是夜深人乏，恕我暂不能一一展开，下次有机会再讨论吧。</p>
<p>最后，请别再称呼我为“先生”，我会恍惚觉得自己老朽了。</p></blockquote>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2011%2F04%2F18%2Fmore-words-on-abstraction%2F&amp;title=%E7%AD%94%E8%AF%BB%E8%80%85%E9%97%AE%283%29%E2%80%94%E2%80%94%E5%86%8D%E8%B0%88%E6%8A%BD%E8%B1%A1" id="wpa2a_12">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (58)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</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月6日 -- <a href="http://blog.zhenghui.org/2009/09/06/colon-class-2_3/" title="冒号课堂§2.3：对象范式">冒号课堂§2.3：对象范式</a> (3)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=mpHv7h3YksA:ykOi430Tr8Y:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/mpHv7h3YksA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/</feedburner:origLink></item>
		<item>
		<title>答读者问(2)——关于抽象</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/LGWguLN6WM8/</link>
		<comments>http://blog.zhenghui.org/2011/03/20/words-on-abstraction/#comments</comments>
		<pubDate>Sun, 20 Mar 2011 15:27:42 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[对象导向]]></category>
		<category><![CDATA[抽象]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=799</guid>
		<description><![CDATA[答一位读者关于抽象的提问 [...]]]></description>
			<content:encoded><![CDATA[<h2><strong>答读者问(2)</strong></h2>
<p>本贴是临时回答一位读者的提问，匆匆草就，以后有空再整理。长时间未发博，此篇权作凑数。</p>
<p><strong>读者</strong>七心葵问：</p>
<blockquote>
<p>个人做linux下c/c++游戏开发，上学的时候一度 “抽象” “隐喻” 这类的东西似懂非懂；后来工作后慢慢觉得其实 “抽象”这个词还是很宽泛的，我一般将“抽象”狭义的理解成 OO里面 接口与实现 的提法，即“有选择地忽略”。 但是我认为在计算机科学或工程中，“抽象”一直有其对立面，“反抽象”，也就是《Unix编程文化》里强调的“浅平透”（个人理解），不知郑老师是否看过此书？对于该书尤其是对于“OO”的评价一节有何看法？ booch的《面向对象分析与设计》中曾经提到，有人做过统计，几乎所有的设计的方法学大体上都可以分为三类：1 结构化设计 2 数据驱动设计 3 面向对象设计。 对于“数据驱动设计（编程）”，我在工作中经常彷徨于是用c的过程式语言+数据驱动设计呢，还是完全OO化设计，郑老师《冒号课堂》一书中似没有提到多少（个人认为LOP可以看作是数据驱动编程的一种高级形式，也就是DSL或者微语言），是否可以做一个简单介绍？</p></blockquote>
<p>&nbsp;</p>

<p><strong>作者</strong>hui答：</p>
<blockquote>
<p>关于抽象，实是大有文章可做。早就计划就此主题展开一系列的讨论，苦于近来项目缠身，一直无暇顾及。The art of UNIX programming毫无疑问是本好书，但要说unix哲学是反抽象，则不太赞同。事实上，unix成功之处恰恰在于充分运用了抽象。“everything is file”就是最典型的抽象，文件这一概念在unix中的广泛而抽象的应用为unix的设计增色不少。unix提倡&#8221;write programs that do one thing and do it well&#8221;，便是强调每个小程序都是具有单一职责的抽象功能体。unix推崇filter模式，并极力推荐遵循规范的plain text作为程序之间最通用的接口。unix强调portability比efficiency更重要，而可移植的通常是普适的、抽象的，为了效率则常常“因地制宜”，其结果是以牺牲抽象为代价。（当然这并不意味着抽象是神圣不可侵犯的，也不意味着抽象越多越好）至于”Write simple parts connected by clean interfaces“，“Separate policy from mechanism; separate interfaces from engines“等更是抽象原则的贯彻。 抽象与封装紧密相关，而在字面上封装似乎与透明相对，或许你因此感到”透“（transparency）是抽象的反面？这恐怕是一种误解。其实”透“意味着简单、干净、优雅、可读性（readability）强、规范（specification）清晰、协议（protocol）明确。比如，text文件比binary文件更简单、更”透“，但并不意味着比后者的抽象程度低。相反，私有的（proprietary）文件格式由于规范程度低或不够公开而导致抽象程度较低，对接口的要求更苛刻，与其他程序难以合作，应用起来范围更狭窄或更困难。总之，unix提倡transparency，鼓励接口简单、明晰，以提高代码的可读性、可维护性，让bug难以藏身，这个与抽象性毫无矛盾。另一个类似的常见误解是，把设计上的封装与源码的封闭关联起来，或者以为封装的目的是为了代码的安全性，这些都源于对抽象或封装的认识上的模糊。此外，你提到的“浅、平”，或许指unix提倡的KISS原则（Keep it Simple, Stupid）。不过抽象本不一定意味着复杂、深奥，它只是处理复杂的一种行之有效的手段。<strong>如果抽象解决的复杂不及其带来的复杂，则说明该抽象是不当的</strong>。文字有时候是有欺骗性的。《冒号课堂》的附注中曾提到一个例子，正是transparent。很多时候它在英文中的意思不是“因透明而看得见”，而是”因透明而看不见“。如果一个接口后面的机制对用户而言是transparent的，正说明它是抽象的，而不是相反。另外值得指出的是，抽象是一个相对的概念，脱离上下文来谈论抽象或具体是毫无意义的。</p>
<p>提到对OO的评价，拥趸固然众多，大唱反调为数亦不少，其中不乏业界名流。如Linux之父Linus Torvalds，STL之父Alexander Stepanov，包括The art of UNIX programming的作者Eric Raymond都曾对OOP作出了十分尖锐的批判。其实名人同样会偏激，甚至比普通人更偏激，因为他们有牛气、有傲气、有底气。他们的批判或许一针见血，但一般人既无深厚的功力，又不了解评论的背景，看看热闹可以，动辄怀疑正在使用的技术就大可不必了。（这就好比一位手持木剑的高手胜了一位手持铁剑的低手，然后不屑地对后者说：其实，木剑更锋利）一切技术都只是工具，决定因素还是使用者对工具的理解程度和熟练程度。相比过程式，OO（准确地说，是融合过程式的OO）从理论上说肯定是更为强大，因为它在前者的基础上增加了新的抽象机制。但如果不理解抽象的本质，为了OO而OO，代码可能比纯粹过程式的更糟。高级的工具并不一定能带来高级的产品。这里多说一句，技术人员在没有达到足够火候的时候，不要随意臧否某项技术。当你尽情吹捧它时，小心背后的陷阱；当你大肆批驳它时，你可能根本未解其妙。淡定，淡定。</p>
<p>Booch提到的三类软件设计方法：Top-down structured design、Data-driven design与Object-oriented design自然都是重要的设计方式，但还存在其他的方法，如event-driven，stream-processing、logic-driven、knowledge-driven等。你提到究竟该用过程式语言+数据驱动设计，还是完全OO化设计，很抱歉，我只能回答：无法一概而论，因项目需求而异、因开发者而异。（不过“<strong>完全</strong>OO化”从来是不必要的）一方面，不同的项目domain不同，有的侧重数据、有的侧重算法、有的侧重流程、有的侧重交互、有的侧重事件，相应采用的设计与语言也会有所分别。另一方面，与开发者对设计方法和语言的有关，如果你特别擅长C，对C++不熟悉或者不喜欢，那么完全不必一定要用OO。（当然，OO思想同样可以用C来实现）</p>
<p>LOP与数据驱动还是有分别的，它更侧重的主要不是数据，而是某个特殊领域。它通过提高语言的抽象性（更贴近所关注的高层领域逻辑，远离底层语言的细节）来提高代码的可读性、可维护性和开发者的效率。</p>
<p>希望以上简略的回答能对你有所帮助，时间有限，恕未能完全展开。待得空时，会在这方面进行更具体、更深入的阐述。</p>
<p>&nbsp;</p>
</blockquote>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2011%2F03%2F20%2Fwords-on-abstraction%2F&amp;title=%E7%AD%94%E8%AF%BB%E8%80%85%E9%97%AE%282%29%E2%80%94%E2%80%94%E5%85%B3%E4%BA%8E%E6%8A%BD%E8%B1%A1" id="wpa2a_14">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (58)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</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年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=LGWguLN6WM8:S1FnFoaYFyc:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/LGWguLN6WM8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2011/03/20/words-on-abstraction/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2011/03/20/words-on-abstraction/</feedburner:origLink></item>
		<item>
		<title>论思维的刚性与柔性（科学的迷信-3）</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/qZnQ9Ix2Zjw/</link>
		<comments>http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/#comments</comments>
		<pubDate>Mon, 20 Sep 2010 13:45:18 +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>
		<category><![CDATA[系统论]]></category>
		<category><![CDATA[逻辑]]></category>
		<category><![CDATA[量子力学]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=775</guid>
		<description><![CDATA[<b>论思维的刚性与柔性</b>——科学的迷信（3）<br/> • 科学是有局限的<br/> • 理性是有局限的 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">论思维的刚性与柔性（科学的迷信-3）</span></h1>

<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/article.css" type="text/css"></head>
<!-- below comes from generated html -->   
<div lang="zh-CN" class="article" title="论思维的刚性与柔性"><div class="titlepage"><div><div><div class="author"><h3 class="author">郑晖</h3></div></div></div><hr /></div><div class="section" title="正文"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="content"></a>科学的迷信（3）</h2></div></div></div><p>
        </p><p>
            <span class="strong"><strong>五、科学是有局限的</strong></span>
        </p><p>
            18世纪末19世纪初，在科学的疾风劲吹之下，笼罩在大自然周围的迷雾渐渐散去，神学的绝对统治地位摇摇欲坠。值此科学盛景，拉普拉斯（Pierre-Simon Laplace）一语点睛：“宇宙现状是过去之果、未来之因。假如一位智者（后人称之为‘拉普拉斯妖’）知晓某一时刻所有导致自然运动的力和构成自然的所有物体的位置，那么他用一个公式就能描述所有物体（无论巨细）的运动。对他而言，万事皆定，将来如同过去一般历历在目”当被拿破仑问起为何在其科学著作中只字不提上帝时，这位在政治上一贯柔顺的人物表现出哲学上的刚强，再出豪言：“陛下，我不需要那个假设”
        </p><p>
            拉普拉斯道出了那个时代绝大部分科学家的心声，表达了用科学法则代替上帝法则作为宇宙主宰的信念和豪情。美中不足的是：虽然他拒绝上帝的假设，却也无法解释被牛顿归功于上帝的第一推动力。即便抛开这一点不说，他对科学力量的估计也是过于乐观了。您可能会说：这只能说明时代的局限，不能说明科学的局限。随着时代的发展，现代物理学不是弥补了经典物理学的不足吗？即使现代科学仍不够完善，以后还会继续发展的，科学仍具有无限的可能。此言颇为在理，不过我们在此要质疑的不是拉普拉斯时代的理论本身——现代人去苛责两个世纪前的理论，未免有失公允——而是理论背后的科学观，包括<span class="term">因果律</span>（causality）、<span class="term">决定论</span>（determinism）、<span class="term">还原论</span>（reductionism）、<span class="term">机械论</span>（mechanism）、<span class="term">实在论</span>（realism）、<span class="term">唯物论</span>（materialism），等等。以下我们将会看到，这些思想一方面在不同程度上受到现代科学的挑战，另一方面又在相当程度上为现代科学所倚重，正是这种刚性的需求和内蕴的矛盾折射出科学固有的局限性。
        </p><p>
            <span class="strong"><strong>1. 因果律与决定论</strong></span>            
        </p><p>
            因果律描述的是两个相继发生的事件之间的关系，前者为因（cause），后者为果（effect），并笃信凡事皆有因。决定论通常指<span class="term">因果决定论</span>（causal determinism），不仅主张“有果必有因”的反推，也主张“有因必有果”的正推，因而当前的一切可以决定今后的一切。（若用函数关系来刻画决定论，则可以抛弃因果概念——已知A得到B，不代表A是B的原因）<span class="term">强决定论者</span>（hard determinist）甚至把这一观点延伸到意识领域，从而否定<span class="term">自由意志</span>（free will）的存在。
        </p><p>
            为客观世界建立严格的、完整的因果链不仅是科学的基本原则，也是科学的永恒信念与终极目标。借助正反双向的因果链条，科学得以展现其巨大的魅力和威力，不仅能完美地解释过去，更能准确地预测未来，极大地提高了人类认识世界和改造世界的能力。实际上这正是拉普拉斯所推崇的思想，当然他的表述更加极端——所有的因果律甚至可归结为单个关于时间的微分方程。无奈越来越多的证据表明，这个宏大而诱人的理想很可能只是人类的一厢情愿。根据广义相对论，存在着一些<span class="term">时空奇点</span>（spacetime singularity），如宇宙大爆炸之初或黑洞中心，因果律将被无情破坏，现有的科学理论均告失效。除了这类极端情形外，<span class="term">放射性衰变</span>（radioactive decay）现象也令因果决定论者感到十分尴尬。人们发现放射性同位素的衰变速率丝毫不受外界因素的影响，其衰变过程也无法精确预测，具有完全的随机性（这种特性甚至被用来实现计算机上的随机数生成器）。待到量子力学的浪潮来袭之时，因果决定论更是受到前所未有的猛烈冲击。其中，最重要的两股力量分别来自<span class="term">哥本哈根解释</span>（Copenhagen interpretation）<a class="link" href="#note1"><sup>[1]</sup></a>中的<span class="emphasis"><em>玻恩定则</em></span>（Born rule）和不确定性原理<a class="link" href="#note2"><sup>[2]</sup></a>。前者通过对描述微观粒子状态的<span class="term">波函数</span>（wave function）的概率诠释，正面向决定论宣战：即使人们能准确地测量电子的一切初始状态，依然不能准确地预测其将来的位置。后者则干脆从理论上断绝了绝对准确测量的可能性，从而让拉普拉斯的理想彻底化为泡影。量子力学的巨大成功迫使许多决定论者退而求其次，不再坚持严格意义上的因果决定论，转而支持概率意义上的因果决定论。还有人试图在宏观领域坚守决定论的阵地，理由是量子力学仅仅描述了微观领域的规律。然而薛定谔（Erwin Schrödinger）为对付哥本哈根派而放出的一只猫（Schrödinger’s cat）<a class="link" href="#note3"><sup>[3]</sup></a>——尽管只是思想实验——在半死不活的呻吟中，把不确定性的幽灵从微观世界放逐到了宏观世界。
        </p><p>
            量子力学从理论上否定了因果决定论，而混沌学进一步从实践上否定了因果决定论。前者虽然在哲学意义上更深刻，但毕竟不是终极理论,不少人依然心存侥幸，指望有朝一日出现更完备的理论，如<span class="term">隐变量理论</span>（hidden variable theory），足以把一切不确定性从科学的领域中驱除。曾为量子力学奠基人之一的爱因斯坦便是其中的一员，他用“上帝不是在掷骰子”来表达对决定论坚定不移的信念。从实用的角度看，混沌学的破坏力更大，因为它获得了无可置疑的数学支持。混沌系统有一个显著的特点，即系统对初始条件具有敏感依赖性。一个通俗的比喻是：一只蝴蝶在巴西扇动翅膀，有可能在北美引发一场飓风。这未免有些艺术夸张了，与此有关的更现实的一个说法是：长期的天气预报是不可能的。混沌现象最令人惊奇之处在于，不确定性竟可产生于一个确定性系统，这与量子世界内禀的不确定性大相异趣。以著名的<span class="term">三体问题</span>（three-body problem）为例：假设宇宙仅由太阳、地球和月亮三个物体组成，它们遵循牛顿运动定律和万有引力定律。在已知三体各自的质量、初始位置和速度的情况下，求解它们的运动规律。很明显，这是拉普拉斯的决定论系统的一个极简版本。可就是这样一个貌似简单的系统，不仅没有显式解析解，而且运动轨道异常复杂，既不是周期的，也不是稳定的<a class="link" href="#note4"><sup>[4]</sup></a>。任何偶然的外界干扰、微小的测量误差或计算精度的损失，都可能对结果产生重大影响，真可谓“差之毫厘，谬以千里”。更何况，真实的宇宙中天体多如恒河沙数，怎一个“混沌”了得？由此可见，混沌系统即使在理论上遵循因果律，也能造成因果链条事实上的扭曲或断裂，最终削弱科学引以为傲的预测力。
        </p><p>
            <span class="strong"><strong>2. 还原论与机械论</strong></span>            
        </p><p>
            如果说因果律是科学研究的基本信念，那么还原论（或简化论）便是科学研究的基本方法<a class="link" href="#note5"><sup>[5]</sup></a>。为了研究一个复杂的现象或系统，还原论的方法是：将其分解为多个组成部分，通过对部分的研究获得对原先整体的认识。如果把该方法从广度上应用于整个宇宙，从深度上应用于基本粒子，那么世界上的一切现象均可归结为基本粒子的运动。哪怕是高级的生命运动，最终也不过是一些低级的机械运动的组合。这正是拉普拉斯的预设，它直接导致了机械论，即世界无非是一架机器，或者是一座精密而准确的时钟。上帝如果还有存在的价值，那就是为这座钟事先上好发条。反过来，机械论也让还原论有了逻辑的基础——神秘存在于细节之中，只要孜孜不倦地解析每个现象，一切神秘将无所遁形。
        </p><p>
            还原论和机械论一直以来都是科学发展中的主导思想，前者为科学提供方法，后者为科学提供目标。可以说，科学的每一次进步都离不开还原论——观察、实验、分析、抽象无不渗透着还原思想的精髓；科学的每一次进步又坚定了机械论——那些曾神秘莫测的现象在科学的解剖之下显现出机器的骨骼。
        </p><p>
            就在还原论和机械论合力把科学推向巅峰之时，渐现力有未逮的迹象。考察一个简单的事实：一滴水中的分子数超过10<sup>21</sup>个<a class="link" href="#note6"><sup>[6]</sup></a> 。即使世间所有现象真的可以还原为基本粒子的机械运动，谁又会指望通过计算每个粒子的行为来获取宇宙的规律呢？正是考虑到这一点，玻尔兹曼（Ludwig Boltzmann）在研究气体分子运动时，首次把代表不确定性的概率统计引入号称严密的物理学。尽管宏观的热力学被成功地还原为微观的统计力学，但却付出了确定性的代价，让机械论变得可望而不可即。在此，还原论还遇到一个实质性的困难，即<span class="term">可逆性佯谬</span>（reversibility paradox）：宏观的热力学过程是<span class="term">不可逆的</span>（irreversible）（比如高温物体向低温物体传热的过程），而微观的粒子运动却是<span class="term">可逆的</span>（reversible）。
        </p><p>
            量子力学依旧扮演着颠覆者的角色。统计力学和混沌学中的随机性尚可归于主观因素（例如研究者的计算能力不足），而量子力学中的随机性则完全是客观的，这是对机械论更为致命的打击。另外，存在于量子过程以及测量过程中的整体性（wholeness）或不可分性（individuality）也与还原论的局部分割思想格格不入。
        </p><p>
            还原论的方法包含分解与整合两个基本步骤。先看分解过程，它依赖于一个隐性假设：事物分解到一定层次后，问题将变得足够简单。如若不然，分解岂非无休无止？可当人们把纷繁芜杂的宏观世界还原到微观世界，才惊讶地发现那里的随机性和复杂性似乎不减反增。分形（fractal）中具有<span class="term">自相似性</span>（self-similarity）的几何图形更绝，无论把它分割成多少份，局部的复杂度与整体的复杂度完全相同！可见，分解层次的深入并不能保证复杂性的消减。此外，分解过程还有一个要求，即分解的各部分之间尽可能互不干扰，以便各个击破，此谓<span class="emphasis"><em>分而治之</em></span>（divide and conquer）。但由于事物的普遍联系性，该要求不可能完全满足（例如三体问题就不可能简单地还原为二体问题）。为了贯彻还原论的方法，必然会有意忽略组成部分之间的一些关联，而这种忽略产生的偏差在某些情况下是无法忽略的（比如在一个混沌系统中）。
        </p><p>
            整合过程同样并非易事。一种情形是分解过细而造成整合困难，比如刚才提到的热力学问题。另一种情形是分解过程中丢失了部分组成要素或关系信息，导致完整性的缺失而无法忠实地反映原有的整体特性。退一步说，即使分解后毫发无损，还原也未必有效。还原论的核心思想是“整体等于部分之和”，这在整体是部分的简单线性叠加的情形下无疑是正确的。但在非线性系统中，整体有可能大于部分之和。比如，一个极为简单的非线性迭代函数也可能产生整体上的混沌。再如<span class="term">系统论</span>（systems theory）中的<span class="term">复杂系统</span>（complex system）<a class="link" href="#note7"><sup>[7]</sup></a>，其特点是：在组成个体之间、系统与外界环境之间动态的相互作用之下，系统通过<span class="term">自组织</span>（self-organization）能产生个体所不具备的、全新的整体性特征，即术语所称的<span class="term">涌现</span>（emergence）。一个有趣的例子来自蚁群。研究发现，尽管蚁后并不统一发号施令，每个蚂蚁的行为均是自发的，相互的交流也是局部的，但整体蚁群却表现出惊人的宏观智能，比如发现运送食物的最短路径、处置尸体的最佳位置等。其他常见的复杂系统还有：神经系统、生态系统、社会系统、股票市场，等等。值得一提的是，宇宙飞船、超级计算机等系统尽管复杂，却不属于复杂系统，只能称为<span class="emphasis"><em>复合</em></span>（complicated）系统。因为从它们的个体中便能获得系统的完整描述，换言之，它们是可还原的。
        </p><p>
            在学科层面上，还原论主张：政治学还原为社会学，社会学还原为心理学，心理学还原为生物学，生物学还原为化学，化学还原为物理学。但事实上，每门学科在自身所在的层次上皆有其独有的特性、概念、法则和方法，将其还原为更底层学科经常是徒劳无益——除了滋长还原论者、机械论者以及物理学家们的优越感以外。
        </p><p>
            人们终于认识到还原论失效的症结所在：它在灵巧地挥舞着解剖刀时，常常会不经意地斩断整体的联络经脉，从而阉割了整体原有的有机特质。庖丁的手再巧、刀再利，也不可能把肢解的牛体拼回原样。于是作为与还原论相对的<span class="term">整体论</span>（holism）开始获得重视，一批以整体论为主体思想的系统理论随之兴起，如“老三论”（一般系统论、控制论和信息论）、“新三论”（耗散结构论、协同论、突变论）等。尽管如此，科学追本溯源的本性和对定量、精确的内在需求决定了还原论始终会占主导地位。无论人们多么不情愿将血肉之躯等同于冰冷无情的机器，也无论机械论在诠释生命现象中如何屡屡碰壁，科学也不会放弃把灵感、冲动、自由意志乃至社会现象还原为基本粒子碰撞的努力。
        </p><p>
            <span class="strong"><strong>3. 实在论与唯物论</strong></span>
        </p><p>
            现代科学与实证科学几乎是同义词，实证对于科学的重要性不言而喻。实证基于一个前提：科学研究的对象是不依赖观察或测量的。从哲学上说，这是一种实在论的体现，即相信客观世界是完全不依赖于主观而独立存在的。与其说实在论是科学的一种信念，不如说是一种方便的假设。试想，假若实验对象的本来性质因观测而改变，甚至压根不存在所谓的本来性质，那么以实验为基石的科学还能真实地反映客观规律吗？这可绝非庸人自扰。不确定性原理已经表明，观察行为的确会对观察对象产生影响。不仅如此，玻尔（Niels Bohr）用<span class="term">互补原理</span>（complementary principle）来解释<span class="term">波粒二象性</span>（wave-particle duality）时指出：电子是波还是粒子完全取决于观察方式。一时间古典的幻梦被打碎，科学一直执着追寻的世界“本来”面目陡然间失去了意义。不仅传统<span class="term">认识论</span>中的主客体分离模式被打破，而且贝克莱（George Berkeley）主教那句曾被无数唯物主义者嘲笑的“存在即被感知”的名言竟然隐隐散发出真理的光辉，<span class="term">本体论</span>（ontology）中的唯物论因此受到灼伤，这让机械还原论者情何以堪？他们可是主张把包括精神在内的一切现象都还原为物质形式的啊。如果再考虑那只薛定谔的猫和从中引申出的<span class="term">魏格纳的朋友</span>（Wigner&#8217;s friend）<a class="link" href="#note8"><sup>[8]</sup></a>，主观与客观、物质与意识、实在与虚无之间的界限就更加模糊了。
        </p><p>
            量子力学尽管理论优美且久经考验，但它对科学所信赖和依靠的因果律、还原论和实在论的挑战超出了常人的直觉和底线。出于对量子理论完备性的怀疑，爱因斯坦等人提出了<span class="term">EPR佯谬</span>（EPR paradox）<a class="link" href="#note9"><sup>[9]</sup></a>。遗憾的是，阿斯派克特（Alain Aspect）等人设计出违反<span class="term">贝尔不等式</span>（Bell&#8217;s inequality）的实验，最终化解了EPR的责难。结果，<span class="term">定域实在论</span>（local realism）被否定，定域性和实在性鱼与熊掌不可兼得。假如一定要坚持实在论，便不能阻止类似心灵感应的超距作用幽灵般地出现，而这对现有科学框架的威胁也不遑多让。
        </p><p>
            <span class="strong"><strong>六、理性是有局限的</strong></span>
        </p><p>
            科学的局限一方面归因于客观世界的复杂多变，另一方面则归因于人类理性的先天贫弱。须知整个太阳系在宇宙中也不过是沧海一粟，一个人需要何等的无知和狂妄才喊得出“人定胜天” <a class="link" href="#note10"><sup>[10]</sup></a>口号啊。诚如帕斯卡（Blaise Pascal）所说：“理性所走的最后一步就是承认有无穷多的事物超出了其认识范围”康德在《纯粹理性批判》里曾系统地揭示了理性的局限，尤其书中四个<span class="term">二律背反</span>（antinomy）反映了人类在时空观、还原论、因果律等方面的认识困境，绝不是单纯的辩证法所能消解的。
        </p><p>
            最能体现理性局限的是数学和逻辑学。虽然二者名义上被划归<span class="term">形式科学</span>（formal science）的范畴，但如果用可证伪性来衡量，它们并不属于科学。尽管如此，由于数学和逻辑学是所有<span class="term">经验科学</span>（empirical science）的基石，它们的局限必然也是科学的终极局限。更关键的是，作为纯粹心智的产物，所有的数学结论和逻辑推理均是先验的，与经验事实无关（但可能受经验事实的启示），没有被未来实验推翻的可能<a class="link" href="#note11"><sup>[11]</sup></a>，因而它们的局限将是理性无法逾越的终极局限。
        </p><p>
            历史上，数学家们几乎在同样的时刻犯了与物理学家们同样的错误。1900年开尔文勋爵（本名William Thomson）在英国皇家学会的新年致辞中宣称物理的主体建设已毕，只剩下修修补补的工作了<a class="link" href="#note12"><sup>[12]</sup></a>。虽然他观察到晴空万里的物理世界有两朵不和谐的乌云，却不曾想它们竟让这门学科陷于风雨飘摇之中。无独有偶，同年庞加莱在国际数学大会上自豪地宣称数学上绝对的严密已经实现，再次把大厦将竣的宣言变成了大厦将倾的预言。不久，<span class="term">罗素悖论</span>（Russell&#8217;s paradox）<a class="link" href="#note13"><sup>[13]</sup></a>撼动了数学的根基——集合论，引发了数学史上的第三次危机。人们沮丧地发现，理性的精确性和确定性不仅在物理中丧失了，甚至在数学上也不能幸免。更令科学家们感到困窘的是，他们的争论最后都不可避免地超出了科学或数学的范畴，进入到哲学领域。物理上的哲学分歧此前已有涉及，下面简单介绍一下二十世纪初的三大数学哲学流派：<span class="term">逻辑主义</span>（logicism）、<span class="term">直觉主义</span>（intuitionism）和<span class="term">形式主义</span>（formalism）。
        </p><p>
            逻辑主义认为数学可还原为逻辑，故而只是逻辑的扩展。然而，逻辑派所依赖的一些公理如<span class="term">无穷公理</span>（axiom of infinity)、<span class="term">选择公理</span>（axiom of choice）、<span class="term">可约性公理</span>（axiom of reducibility）等却备受争议。它们的逻辑性、合理性乃至真理性并非都是自明的，它们的选定更非逻辑的产物。尤其是，逻辑主义将数学完全归结于逻辑，不仅抹煞了二者之间的界限，而且无法解释一个根本性的问题：一个纯粹靠逻辑演绎的学科为何能广泛而有效地应用到自然科学？或者说，在思维完全从自然脱离之后，思维规律何以与自然规律保持一致？此外，逻辑主义把整数建立在逻辑之上，但在此之前实质上已涉及了整数概念，涉嫌循环论证。
        </p><p>
            与逻辑主义针锋相对的是直觉主义。前者竭力依赖逻辑，后者竭力摆脱逻辑而诉诸直觉。直觉派虽不否认逻辑的必要性，但反对把逻辑作为真理的来源。他们认为逻辑推导并不比直接感悟更可靠，与其遵守外在的形式化的逻辑规则，不如遵从来自内心的理性约束。由于坚持数学对象是智力构造的产物，直觉主义只承认<span class="emphasis"><em>构造性</em></span>（constructive）的陈述和证明，因而拒绝<span class="term">实无穷</span>（actual infinity），排斥选择公理，反对在无穷集合中使用排中律（即任何命题非真即假）。但如此一来，雄伟的数学宫殿虽不至土崩瓦解，也只剩断壁残垣了。难怪希尔伯特（David Hilbert）抗议道，限制数学家使用排中律正如限制天文学家使用望远镜。除了实用性上的局限，直觉主义在理论上也存在不足：人类的直觉很难清晰地界定，更不能保证绝对的正确。
        </p><p>
            如果要考问何处方能寻得严密可靠的数学，逻辑主义会回答：在逻辑里，直觉主义会回答：在头脑中，而形式主义会回答：在纸面上。以希尔伯特为首的形式派主张抽去数学的一切实际意义，将之彻底形式化为符号操作的游戏。形式主义面临与逻辑主义相似的诘难：毫无意义、脱离实际的符号为何能与经验世界相契合？再者，既然一切都是同样空洞的符号，那么选择某些特定公理系统的理由是什么呢？对此形式派虽不十分坦然，但仍着力打造一套完备而相容的<span class="term">形式系统</span>（formal system），以将所有数学尽囊其中。可惜，哥德尔（Kurt_Gödel）的两个<span class="term">不完备定理</span>（Gödel&#8217;s incompleteness theorems）如同两盆冷水把他们从美梦中浇醒。原来，任何一个包含初等数论的形式系统，如果是相容的——不包含任何矛盾，则必是不完备的——存在既不能证明又不能否证的命题；事实上，该系统自身的相容性就是无法（在系统内部）证明的。素以绝对精确、永远正确著称的数学居然无法摆脱不确定的阴影，人类的理性受到空前的质疑，这无疑是值得惋惜的。可是，充满灵性和创造力的数学思维没能沦为符号化的机械程序，人类的理性受到应有的尊重，无疑又是值得庆幸的。
        </p><p>
            哥德尔定理不仅暗示人类不可能一劳永逸地获取所有的数学真理，同样也为基于形式系统的计算机科学<a class="link" href="#note14"><sup>[14]</sup></a>设置了天花板（数学家们松了口气，尊严和饭碗同时保住了）。例如，<span class="term">图灵停机问题</span>（Turing’s halting problem）堪称计算机科学版的哥德尔定理。让人难以置信的是，这类<span class="emphasis"><em>不可计算</em></span>（incomputable）或<span class="emphasis"><em>不可判定</em></span>（unsolvable）的问题非但不是孤例，而且远远多于可计算或可判定的。用数学语言来说，前者是<span class="term">不可数的</span>（uncountable），后者是<span class="term">可数的</span>（countable）。从这个意义上说，电脑虽是人脑的延伸，但延伸的范围非常有限，可以当拐杖却不能当翅膀。
        </p><p>
            独立于经验的理性（即康德所谓的“纯粹理性”）可贵之处在于，它超越了人类肉身的物理局限，能析天地之理，察万物之变。但理性有一个最大的障碍：<span class="term">无穷</span>（infinity）。 从芝诺（Zeno of Elea）关于运动的一系列悖论（阿基里斯与龟、二分法、飞矢不动等），到康德关于时间与空间、复合与单纯、原因与结果等一系列的二律背反，从贝克莱的无穷小悖论，到康托（Georg Cantor）的最大<span class="term">基数</span>（cardinal number）悖论，无穷总如巨大的黑洞，一再吞噬着理性的光辉。数学家们对无穷的态度完全可以用爱恨交加来形容。一方面，无论从研究对象还是研究手段的角度看，数学都离不开无穷；另一方面，无穷又是滋生悖论和争议的温床，这从数学史上的三次危机以及三大数学流派之间的分歧即可看出。以选择公理为例，它可通俗地描述为：任给若干装球的盒子，总能从每个盒中各选出一只球来。如果盒子数量有限，选择自然不成问题，但如果盒子数量是无限的呢？大多数数学家承认此公理，事实上他们总在有意无意地使用它。不过包括直觉主义在内的<span class="term">构造主义</span>（constructivism）却认为这种所谓的选择过于抽象，不具备可构造性，故拒不接受。特别当他们发现选择公理会导出一些违背直觉的结论时，反对的声音就更大了。比如基于选择公理的<span class="term">分球怪论</span>（Banach-Tarski paradox）<a class="link" href="#note15"><sup>[15]</sup></a>听起来就十分荒诞：可以把一个三维实心球拆成有限块，经过旋转和平移后拼出与原球大小相同的两个球！<a class="link" href="#note16"><sup>[16]</sup></a>很显然，在无穷的问题上，存储容量和计算速度均为有限的电脑照旧不能给人脑提供实质性的帮助。
        </p><p>
            理性的另一个常见障碍是<span class="term">自指</span>（self-reference）。从逻辑上的说谎者悖论（“我在说谎”），到数学上的罗素悖论、哥德尔定理，到计算机科学上的停机问题，乃至哲学上康德的二律背反（用理性来研究理性本身），自指屡屡让理性陷入“狗咬尾巴”的怪圈。人们开始意识到，理性必须适当舍弃过于宏大的“叙事风格”，谨慎地对待原先惯用的“所有”、“一切”等字眼。但这就意味着，理性要想打开自指的死结，须先打开自身的缺口（颇有点“欲练神功，必先自宫”的味道）。
        </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>
                    可参见<a class="link" href="http://en.wikipedia.org/wiki/Copenhagen_interpretation" target="_top">http://en.wikipedia.org/wiki/Copenhagen_interpretation</a>。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    原来的译法（“测不准原理”）有些不妥，会让人误以为物理量本身是确定的，只是测不准而已。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    可参见<a class="link" href="http://en.wikipedia.org/wiki/Schr%C3%B6dinger%27s_cat" target="_top">http://en.wikipedia.org/wiki/Schr%C3%B6dinger%27s_cat</a>。
                </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>
                    假设一滴水是0.05毫升，则含水分子个数为0.05/18×阿伏加德罗常数≈1.67×10<sup>21</sup>。
                </p></li><li class="listitem"><p><a name="note7"></a>
                    请勿将复杂系统与混沌系统混为一谈。二者尽管都具有非线性、不可预测性和复杂性，但后者对初始条件极度敏感，而前者虽然具有活跃性，但也具有一定的稳定性，以维持系统自身的生存。
                </p></li><li class="listitem"><p><a name="note8"></a>
                    可参见<a class="link" href="http://en.wikipedia.org/wiki/Wigner%27s_friend" target="_top">http://en.wikipedia.org/wiki/Wigner%27s_friend</a>。
                </p></li><li class="listitem"><p><a name="note9"></a>
                    可参见<a class="link" href="http://en.wikipedia.org/wiki/EPR_paradox" target="_top">http://en.wikipedia.org/wiki/EPR_paradox</a>。
                </p></li><li class="listitem"><p><a name="note10"></a>
                    “人定胜天”一词可能有不同的解读，这里采用最常见的词义。
                </p></li><li class="listitem"><p><a name="note11"></a>
                    由于数学证明越来越复杂，或许某个数学定理会被错误地证明并接受，但这不也说明了理性的局限吗？
                </p></li><li class="listitem"><p><a name="note12"></a>
                    原话是：There is nothing new to be discovered in physics now. All that remains is more and more precise measurement。
                </p></li><li class="listitem"><p><a name="note13"></a>
                    罗素悖论的一个通俗版本是理发师悖论：一位理发师声称他给且只给任何不给自己刮脸的人刮脸，请问他该不该给自己刮脸？
                </p></li><li class="listitem"><p><a name="note14"></a>
                    计算机科学也属于形式科学。
                </p></li><li class="listitem"><p><a name="note15"></a>
                    严格说来，应称为“分球定理”。只是因为它有悖常识，才被称为“怪论”或“悖论”。
                </p></li><li class="listitem"><p><a name="note16"></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%2F2010%2F09%2F20%2Fhard-thinking-and-soft-thinking-3%2F&amp;title=%E8%AE%BA%E6%80%9D%E7%BB%B4%E7%9A%84%E5%88%9A%E6%80%A7%E4%B8%8E%E6%9F%94%E6%80%A7%EF%BC%88%E7%A7%91%E5%AD%A6%E7%9A%84%E8%BF%B7%E4%BF%A1-3%EF%BC%89" id="wpa2a_16">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年07月12日 -- <a href="http://blog.zhenghui.org/2011/07/12/hard-thinking-and-soft-thinking-4/" title="论思维的刚性与柔性（科学的迷信-4）">论思维的刚性与柔性（科学的迷信-4）</a> (0)</li><li>2010年04月22日 -- <a href="http://blog.zhenghui.org/2010/04/22/hard-thinking-and-soft-thinking-2/" title="论思维的刚性与柔性（科学的迷信-2）">论思维的刚性与柔性（科学的迷信-2）</a> (0)</li><li>2010年04月19日 -- <a href="http://blog.zhenghui.org/2010/04/19/hard-thinking-and-soft-thinking-1/" title="论思维的刚性与柔性（科学的迷信-1）">论思维的刚性与柔性（科学的迷信-1）</a> (0)</li><li>2010年04月2日 -- <a href="http://blog.zhenghui.org/2010/04/02/hard-thinking-and-soft-thinking-0/" title="论思维的刚性与柔性（前言）">论思维的刚性与柔性（前言）</a> (44)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</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></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=qZnQ9Ix2Zjw:JpK6prwnn9E:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/qZnQ9Ix2Zjw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/</feedburner:origLink></item>
		<item>
		<title>答读者问(1)——对程序员的一些个人建议</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/mybAzCQGhIM/</link>
		<comments>http://blog.zhenghui.org/2010/06/03/advice-on-programmer/#comments</comments>
		<pubDate>Thu, 03 Jun 2010 04:35:30 +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=716</guid>
		<description><![CDATA[答一位《冒号课堂》读者的提问 [...]]]></description>
			<content:encoded><![CDATA[
<h2><strong>答读者问</strong></h2>
<p>以下内容摘自<a href="http://bbs.zhenghui.org">冒号论坛</a>的一个<a href="http://bbs.zhenghui.org/topic/%E8%8F%9C%E9%B8%9F%E8%AF%BB%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E5%90%8E%E7%9A%84%E6%84%9F%E8%A7%A6%EF%BC%8C%E6%9C%9B%E9%83%91%E6%99%96%E8%80%81%E5%B8%88%E8%A7%A3%E6%83%91">话题</a>，考虑其具有一定的典型性，故转至<a href="http://blog.zhenghui.org">冒号空间</a>，希望能对更多的读者有所帮助，也算是一种<span style="text-decoration: underline;">代码重用</span>吧。</p>
<p>&nbsp;</p>

<p><strong>读者Jee问</strong>：</p>
<blockquote>
<p>之前在Top  language里的一位网友像我推荐您的《冒号课堂》，书中eric向您建议开设社区，我猜测可能会有，就找到您的博客发现此地，很幸运您是一个如此有责任心的作者。</p>
<p>我是一名没有什么理论基础的不合格的计算机专业毕业生，毕业后却对软件方面技术非常感兴趣，可能与个性有关。于是在这近2年的找工作和工作过程中看了一些书，也和一些过来人聊过，总体来说让我对软件编程有了一点认识。在阅读您的冒号课堂之前，我曾一度认为我所差的是经验和一些诸如高级算法之类的进阶技术， 可现在，一个用了一个多月时间的夜晚阅读《冒号课堂》之后的我发现我所差的不仅仅是那些，而是最基础最根本的对计算机本身的认识，对数学的认识，对软件工程的认识。</p>
<p>我不想能有速成一说，只想能够现在正视自己，脚踏实地的一点一点的学习和进步，哪怕让我自学4年大学课程也未尝不可，只是有些时候有点找不到一个开始。我数学不好，作为一名程序员我想这是个令人沮丧的事实，我英语也不好，当看到蹩脚的一些翻译著作后痛苦不已。我想尝试着去改变这些，但是却不知该如何去做， 您知道，作为一名已经进入社会的成年人，我需要承受一些生存的压力和一些生活的负担，我希望能更好的利用每天那抽出来的时间，所以望郑晖老师能给我指出一条明道。</p>
<p>我一直没有说我从事的语言和方向，因为我知道这并不是核心，也不是想从您这得到如何学习XX语言等。万分打扰，还望见谅。</p></blockquote>
<p>&nbsp;</p>

<p><strong>作者</strong>hui答：</p>
<blockquote>
<p>你提到的问题十分典型，我非常理解你的心情，同时也非常乐意分享一些个人的看法。</p>
<p>虽然你在言语之中流露出不少负面的情绪，但我看到的却是正面的希望。首先，你对软件技术很感兴趣，而兴趣是学习和工作的最大动力。一般说来，我也没兴趣回答那些对编程不感兴趣者的有关编程的问题。一方面，我会劝他们改行，否则彼此都痛苦；另一方面，我建议的方法通常也不适合他们。其次，你很清楚地意识到自己在哪些方面不足，这是一切进步的基础。许多程序员意识不到自己的无知，甚至自以为足够有知，那又如何能进步呢？最后，你不指望任何捷径，愿意通过踏踏实实的学习来弥补不足。在浮躁之风盛行的当下，这点也是难能可贵的。</p>
<p>关于数学基础，窃以为并非什么太大的问题。几乎每个得知我数学背景的人都会对我说：哦，学数学的人来学计算机自然容易啦。事实上，这种观点虽然极为普遍，但也极为肤浅。本人从事数学14年（从本科算起）、从事计算机12年（与前者有部分重合），在这一点上还是比较有发言权的。事先说明，以下提到的数学不包括高中数学。其实大多数从事软件开发的人员用不到太多的数学知识，他们只需要正常的逻辑思维能力和抽象思维能力。整天拿数学说事，要么是无知，要么是找借口，要么是装高深。当然，我不否认一些高级算法、计算机理论以及人工智能等领域可能涉及到高深的数学知识（其实也只是图论、组合数学、数论、概率论、计算几何、抽象代数、数学逻辑等中的一小部分），但那毕竟只是少数。我也不否认自己的数学背景有助于对编程的理解，但投入产出比太低，不值得作为经验来推广。不过若想成为一位计算机科学家，那就另作别论了——这时数学懂得再多也会嫌少的。</p>
<p>倒是英语我希望你更重视些。我在<a href="http://blog.zhenghui.org/2009/09/10/colon- class-3_3/">《冒号课堂》中</a>专门提过阅读原著的必要性，而且你也意识到译著的质量问题。建议不必特地去学习英语（你本来就会了，不是吗？），只要坚持读经典原著即可。其实，计算机方面的英文算是很容易的了，关键是克服自己的惯性和惰性。开始可能不习惯，看多了就习惯了。在此提醒一点，在阅读时请有意识地培养自己对英语的语感，就像编程时要有意识地培养自己对编程语言的语感一样。</p>
<p>总之，<strong>对于程序员来说，数学没有人们认为的那么重要，英语没有人们认为的那么不重要</strong>。</p>
<p>再说说专业方面的问题。你提到愿意重新自学大学课程，虽精神可嘉，但未必可取。从软件（或建筑）设计的观点来看，这是<span style="text-decoration: underline;">bottom-up</span>法。作为学生，最好采用这种方法，但你已经参加工作了，所以我建议你更多地采用<span style="text-decoration: underline;">top-down</span>法。这当然不是轻视基础知识，而是认为获取知识最高效的方法莫过于<span style="text-decoration: underline;">按需</span>（on  demand）学习。在实际工作中意识到某个知识点的重要性，从而有针对性地弥补短板，这样学习起来不仅更有效率，也更有兴味。需要强调的是，绝不能只是 “头痛医头”，而要“拔萝卜带出泥”。只有寻根究底、以点带面，才能快速有效地建立起自己的知识结构体系。对于软件开发这类实践性很强的专业来说，该法尤其奏效。</p>
<p>话又说回来，这种项目驱动式的学习方法也是有一定局限的。毕竟大多项目涉及的深度和广度通常都很有限，单纯凭此建立起来的知识体系不可能非常完善。 这就需要平时有计划地阅读一些经典著作以加强深度，并定期浏览一些高质量的技术网站以加强广度。</p>
<p>以上谈的都是一些较为宏观的建议，我想你需要的是更加具体的建议。《冒号课堂》上已经阐述了不少关于编程语言、编程范式、设计原则方面的观点，此处不复赘言。我想特别强调一点——<strong>把握抽象</strong>（abstraction）。事实上，无论是在书中还是本论坛中，我都不厌其烦地再三提到抽象的重要性，今后有时间还会深入地挖掘这一主题。对编程的语言、范式、设计、实现体会得越深，对抽象体会得也越深。借用Hakell的设计者之一Paul  Hudak的一句略带夸张的话（overstatement）：<em>“abstraction, abstraction, abstraction” are  the three most important things in  programming</em>。一定会有人会问：难道编程语言就不重要了吗？设计模式就不重要了吗？算法设计就不重要了吗？那是他们尚未真正理解何为抽象——抽象不仅渗透在编程范式之中，也渗透在编程语言之中；不仅反映在设计原则之中，也反映在设计模式之中；不仅体现在架构设计之中，也体现在算法设计之中。</p>
<p>说来也怪，明明是想提“具体”建议的，偏偏又扯出了“抽象”，大概不是你想要的答案吧？既然你是计算机专业毕业的，又有一定的工作经验，其实也不需要太过具体的建议。你的苦恼是找不到努力的方向，而这个方向恐怕还是得靠自己去寻找。建议试试两种方法：研读一本有趣的名著或开发一个有趣的应用。只要深入其中，相信绝不会再为找不到方向而发愁，说不定倒会为方向太多而发愁呢。</p>
<p>最后，说句更实际点的话：如果平时能有意识地积累一些计算机以外的<span style="text-decoration: underline;">领域知识</span>（domain  knowledge），比如金融、电信、教育、企业管理等等，对提高个人在IT业的核心竞争力也是大有裨益的。当然，前提是你有兴趣或有条件获得这些知识。</p>
<p>一家之言，希望能对你有所帮助。</p></blockquote>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2010%2F06%2F03%2Fadvice-on-programmer%2F&amp;title=%E7%AD%94%E8%AF%BB%E8%80%85%E9%97%AE%281%29%E2%80%94%E2%80%94%E5%AF%B9%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%84%E4%B8%80%E4%BA%9B%E4%B8%AA%E4%BA%BA%E5%BB%BA%E8%AE%AE" id="wpa2a_18">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li><li>2010年09月20日 -- <a href="http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/" title="论思维的刚性与柔性（科学的迷信-3）">论思维的刚性与柔性（科学的迷信-3）</a> (0)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (58)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</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></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=mybAzCQGhIM:6tCSdxaDP3s:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/mybAzCQGhIM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/06/03/advice-on-programmer/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2010/06/03/advice-on-programmer/</feedburner:origLink></item>
		<item>
		<title>分布&amp;分享——《分布式Java应用：基础与实践》推荐序</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/ZjfTCncIHLQ/</link>
		<comments>http://blog.zhenghui.org/2010/05/27/preface-of-distributedjavaapp/#comments</comments>
		<pubDate>Thu, 27 May 2010 11:51:27 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[计算机]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[书评]]></category>
		<category><![CDATA[分布式计算]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=701</guid>
		<description><![CDATA[《分布式Java应用：基础与实践》推荐序 [...]]]></description>
			<content:encoded><![CDATA[<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<h1 style="text-align: center;"><span style="font-family: 宋体;">分布&amp;分享</span></h1>
<!-- below comes from generated html -->
<div class="article" title="分布&amp;分享——《分布式Java应用：基础与实践》推荐序" lang="zh-CN">
<div class="titlepage">
<div>
<div>
<div class="author">
<h3 class="author">郑晖</h3>
</div>
</div>
</div>
<p>&nbsp;</p>
<hr /></div>
<div class="toc"><dl> <dt><span class="section"><a href="#content"></a></span></dt> </dl></div>
<div class="section">
<p>分布式计算不是一门年轻的技术,早在上个世纪70年代末便已是计算机科学的一个独立分支了; 它也不是一门冷僻的技术,从 C/S 模式到 P2P 模式,从集群计算到网格计算,乃至风靡当下的云计算, 都是其表演的舞台。另一方面,Java 作为一门应网络而生的语言,对分布式计算有着天然的友好性, 同时也是当今最流行的编程语言。然而令人稍感意外的是,以“分布式 Java 应用”为专题的书籍并不 多见,佳作则更少。至于国人所著者,请恕在下孤陋,尚未得见。不过细想之下,其实不足为奇。要 开发一个高质量的分布式 Java 应用,以达到高性能、可伸缩、高可用、低延迟的要求,同时保证一致性、容错性、可恢复性和安全性,是何等的不易啊。它对程序开发的挑战在于:不仅需要对 Java 语言、 类库、各种框架及相关工具极为熟悉,还需要对底层的 JVM 机制、各种网络协议有足够的了解;它对 分析设计的挑战在于:不仅需要熟悉传统的应用架构模式,还需要掌握适用于分布式应用的架构模式 和算法设计。此外,分布式应用对数据库管理员、测试分析人员等也提出了更高的要求。一本书若想涵盖这么多的内容,其难度可想而知。作者不仅需要具备起码的理论素养,更需要有丰富的实践经验。 如果仅仅是纸上谈兵,对读者是无甚裨益的。</p>
<p>正因如此,半年多前收到林昊先生的部分书稿时,便窃感欣喜,认定这是一个很好的选题。作为淘宝网的系统架构师,他有着令人艳羡的得天独厚的实践机会。尤其难得的是,林昊亲身经历了淘宝网的快速成长和转型期,饱尝其间的成败甘苦,从中也获得了许多宝贵的第一手经验。如今,他选择将这些经验以书籍的形式与众共享,对有志于分布式应用开发的读者而言无疑是一大福音。</p>
<p>本书的基础部分介绍了分布式 Java 应用的基本实现方式(重点是SOA)、相关的 JDK 类库和第三方框架,并对 JVM 的基本机制进行了深入解析;实践部分则关注于高性能、高可用和可伸缩系统的构建等。全书文风朴实,并附有大量的代码、数据和图表,比较符合大多数程序员的口味，也非常具有实践指导意义。如果用挑剔的眼光看,本书在深度、广度和高度上还有继续改进的余地,比如:对关键性的并发设计和算法设计可以介绍得更深入些;尚未涉及分布式应用中的安全问题;在性能调优一章中对 Java 源码级别的优化介绍似嫌不足; 未能高屋建瓴地总结和提炼出分布式应用独有的编程和设计原则、架构和思维模式,等等。当然,对于这样一本上乏经典可参、下需躬身实践的书籍来说,以上多属苛求。事实上,本人在审稿过程中也是获益良多。</p>
<p>另值一提的是,林昊先生利用业余时间写作,已是经年有余,其间数易其稿,且从善如流,充分体现了技术人员的求道精神。本书的主题在明是<strong>分布</strong>, 在暗则是<strong>分享</strong>——分享一段成长经历、分享一份宝贵经验,无论对作者还是读者,都是善莫大焉。</p>
<p dir="rtl">郑晖</p>
<p dir="rtl">2010年5月24日于广州</p>
</div>
</div>

P.S.

该书详情请见豆瓣：<a href="http://book.douban.com/subject/4848587/">分布式Java应用：基础与实践</a>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2010%2F05%2F27%2Fpreface-of-distributedjavaapp%2F&amp;title=%E5%88%86%E5%B8%83%26%23038%3B%E5%88%86%E4%BA%AB%E2%80%94%E2%80%94%E3%80%8A%E5%88%86%E5%B8%83%E5%BC%8FJava%E5%BA%94%E7%94%A8%EF%BC%9A%E5%9F%BA%E7%A1%80%E4%B8%8E%E5%AE%9E%E8%B7%B5%E3%80%8B%E6%8E%A8%E8%8D%90%E5%BA%8F" id="wpa2a_20">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年10月3日 -- <a href="http://blog.zhenghui.org/2009/10/03/colon-class-6_2/" title="冒号课堂§6.2：平台语言">冒号课堂§6.2：平台语言</a> (2)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=ZjfTCncIHLQ:3hJd_7fVt_o:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/ZjfTCncIHLQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/05/27/preface-of-distributedjavaapp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2010/05/27/preface-of-distributedjavaapp/</feedburner:origLink></item>
		<item>
		<title>论思维的刚性与柔性（科学的迷信-2）</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/5CQLhB5ERsM/</link>
		<comments>http://blog.zhenghui.org/2010/04/22/hard-thinking-and-soft-thinking-2/#comments</comments>
		<pubDate>Thu, 22 Apr 2010 07:42:31 +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=694</guid>
		<description><![CDATA[<b>论思维的刚性与柔性</b>——科学的迷信（2）<br/> • 一切科学都是假说<br/> • 没有绝对理性的科学 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">论思维的刚性与柔性（科学的迷信-2）</span></h1>

<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/article.css" type="text/css"></head>
<!-- below comes from generated html -->   
<div lang="zh-CN" class="article" title="论思维的刚性与柔性"><div class="titlepage"><div><div><div class="author"><h3 class="author">郑晖</h3></div></div></div><hr /></div><div class="section" title="正文"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="content"></a>科学的迷信（2）</h2></div></div></div><p>
        </p><p>
            <span class="strong"><strong>三、一切科学都是假说</strong></span>
        </p><p>
            有两个司空见惯的词组：科学真理和科学假说。殊不知一个是自相矛盾，一个是同义反复。科学是人造的，真理是天造的，将二者并论，如同说“人造纯天然”一样滑稽。而“科学假说”的提法则与“人造非天然”具有相同的冗余度，因为所有的科学本质上都是假说。有人会说这是在抹煞科学假说与科学理论之间的区别，理由是前者是尚未证实的主观推测，而后者是被证实的客观真理。其实前面的讨论早已化解了这一责难，现在请出波普尔（Karl Popper）先生来一锤定音。
        </p><p>
            我们一直在谈论着科学，却从未明确地给出它的定义。与其重复乏味无趣、充满误导且毫无启示性的名词解释，不如探讨一个更有意义的问题：科学与非科学的划分标准是什么？对于这个<span class="term">划界问题</span>（demarcation problem），以逻辑实证主义为代表的证实主义坚持证实原则，即能被经验证实的理论便是科学，否则便不是。我们不再重述该观点的致命硬伤，只多言一句：证实论者看起来非常强调科学的客观性，自己偏偏违背了客观原则——对理论经过有限次检验后便断定其完全被证实，是不是太过主观了？波普尔的<span class="term">证伪主义</span>（falsificationism）反其道而行之，坚持证伪原则：一个理论是否是科学的，当且仅当它是<span class="term">可证伪的</span>（falsifiable）或<span class="term">可反驳的</span>（refutable）或<span class="term">可验证的</span>（testable）。乍听上去真有些荒诞，把科学与“真”（理）、（证）“实”这样的正面词汇相剥离已是有违常理了，现在竟然要与负面的（证）“伪”为伴了？居然还“当且仅当”？简直是“当当”两记闷棍嘛。
        </p><p>
            有必要先解释一下，所谓一个命题是可证伪的，并不是指它一定是错误的，而是指它在理论上容许有反例，或者等价地，它有可能被经验所否定。为更好地理解这个定义，不妨反过来思考：一个不可证伪的命题意味着什么呢？一种情形是命题在逻辑上永真，诸如2+3=5、“哥哥比弟弟年长”、“一周有七天”等等。另一种情形是命题无法被经验验证。比如“存在一个全知全能的上帝”、“我们生活的世界都是虚幻的”等等。逻辑学和数学中的命题属于第一种情形，因而这两门学科在波普尔看来都不属于科学。这并不奇怪，因为二者是纯粹理性的产物，并无经验上的意义。宗教和哲学中的命题属于第二种情形，所以宗教和哲学也属于非科学。请注意，这丝毫不意味着宗教命题或哲学命题是错误的或不重要的，仅仅是说它们无法被检验。
        </p><p>
            一个至关重要的问题是：可证伪理论的合理性在哪里？在回答这个问题之前，我们返回开篇的问题：科学为什么值得信赖？此前业已论证，科学并没有常人想象的那么可靠。科学研究的每个环节——观察、归纳、证实都不能保证它的绝对可靠性，更何况所有的科学理论还建立在假设之上。其实这些都还不是最关键的，它们只是科学工作者所关心的问题，普通民众并不关心也无从关心。可为什么后者对科学的信任度丝毫不亚于前者呢？当然不是因为他们直接参与或见证了科学研究的全过程，而是因为在日常生活中一再见识到科学的预见力（这种预见力一般通过技术来展现），从而感受到科学的价值和力量，以至于对科学坚信不疑。这也不难理解为什么古人更相信巫师、算命先生而不是科学家，因为当时的科学尚不具备令人信服的预见力。精准的预见力不仅给人们一种心理上的强烈震撼，还能带来更多新的事实。宇宙何其之大，事实何其之多，指望漫无目标的观察带来有价值的结果，无异于相信手执铁锹便可从自家后院里挖出金子。有了理论预测，人们才能针对性地安排一些实验，既可以有效地获得新发现，还能有效地检验新理论。可以想见，没有牛顿力学的预测，加勒（Johann Galle）不会从浩渺的天空中幸运地发现海王星；没有广义相对论的预测，爱丁顿也不会带领探险队远赴非洲观测日全食。
        </p><p>
            事情渐渐明朗了，判断一个命题是否有经验性的价值，关键看它是否包含目前尚未知晓的信息。“明天要么下雨要么不下雨”，对则对矣，可全无用处。“明天将要下雨”，虽未必对，但至少可作参考。有人会说：一个百分之百正确的命题岂不更有价值？此言差矣，如果一个命题不经经验事实的检验就能保证正确，那要么是一个<span class="term">重言式命题</span>（tautology），要么是一个数学命题<a class="link" href="#note1"><sup>[1]</sup></a>，不能带来超出逻辑或数学以外的知识，故而算不得是一种经验预见。“好人死后上天堂，坏人死后下地狱”倒是包含了预见性信息，惜乎无法被证实，故而它的价值仅停留于宗教或道德层面而非经验层面。作为鲜明的对照，牛顿力学因准确地预见了海王星而达到辉煌的顶点，广义相对论因准确预言了恒星光线在太阳附近的偏转角而轰动世界。
        </p><p>
            波普尔的证伪（或称否证）学说深受“犹太三杰”——弗洛伊德、马克思和爱因斯坦的影响。他本人不仅也是犹太裔，而且可以算是弗洛伊德的徒孙（为弗洛伊德的弟子阿德勒工作过），早年（自认为）是一个共产主义者，与爱因斯坦也有过直接的交往。这三位以及其追随者对各自理论的态度大相径庭，令波普尔感触极深。精神分析学者从来不预测任何事情，但却宣称能解释一切事情。一个人因私利而杀人，他们可以解释；另一个人为正义而牺牲，他们也可以解释。马克思主义者也不遑多让，他们能把任何可以设想的事件解释为对他们理论的证实。资本家降低工资？那是对工人的剥削，是本性使然。资本家提高工资？那是为了调和劳资矛盾，乃情非得已。何时该坚持本国特色、何时该与国际接轨？只要“活学活用”辩证法，何愁不左右逢源？他们也不是全无预测，不过一旦预测失败，总能很轻易地通过引入辅助性的特设来挽救原有的理论。爱因斯坦的态度则迥然不同。他总是在寻求判决性实验，如果结果不出所料，并不能确证其理论；如果结果与预言相悖，则宣告理论失效。比如，他明确地声称：如果不能发现引力红移现象，那么广义相对论将是不可信的。强烈的对比让波普尔得出一个论断：<span class="strong"><strong>科学态度是批判的态度</strong></span>，必须彻底抛弃教条主义，让理论事先作出超出常识的、尽可能精确的预言，并坦然地接受实验的否证，而不是事后百般地辩解。由于因果关系的不对称性，预见结果比解释原因困难得多，因而先见之明比后见之明有说服力得多。这时候请股评家们来现身说法是再合适不过的了。提起前一天的股市行情，无论如何诡谲多变，他们都能分析得头头是道，“必然”二字贴满了一脸。可一到第二天的行情预测，他们便开始含糊其辞，扭头从“必然”王国走进了“自由”王国。
        </p><p>
            可证伪理论是时代的产物。牛顿的经典物理学曾被看作终极的宇宙真理，拉格朗日（Joseph Lagrange）的话道出了当时科学家的心声：“牛顿是伟大的，他发现了宇宙的规律；牛顿也是幸运的，因为宇宙只有一个。”随着相对论和量子力学的兴起，牛顿理论作为绝对真理的神话彻底破灭。作为这一科学史上重大事件的见证者，波普尔意识到：科学的本质不在于无误性，而在于可错性。此处的错，不一定指彻头彻尾的错误。例如，牛顿力学并未完全被否定，在宏观低速的领域仍然是适用的。爱因斯坦说得好：一个理论的最好命运莫过于它能指出一条通往一个更广泛理论的道路，而它在新的理论中作为一种极限情形继续存在。
        </p><p>
            证伪论的合理性还体现在它聪明地绕开了那些使证实论难以自拔的沼泽地。它不纠缠于认识论中有关知识的来源以及可靠性等问题，也不再枉费心机地为归纳法正名，更干脆地放弃了对科学的真理性的执着坚守，把传统的“观察－归纳－证实”的实证机制用“问题－猜想－反驳”的试错机制来代替（或：问题1－尝试解决－消除错误－问题2）。由于证伪过程是一种否定后件式的假言推理，属于<span class="term">演绎推理</span>（deductive reasoning），具有归纳推理所不具备的逻辑上的严格性。只是一个反例便足以推翻一个定律，这种类似“一票否决”的机制听起来未免过于残酷。对此波普尔的说法是：不可反驳性不是一个理论的长处，而是它的短处。不敢冒被反驳的危险的理论是没有实质性内容的，如果我们的目的是追求知识的增长，那么就应该放弃追求理论的（逻辑）高概率（没有任何实质内容的重言式概率最高）。理论的内容越丰富，则包含的信息越多，禁止发生的现象越多，也就越容易被未来的经验所反驳。从这里我们看到了经济学原理的影子：<span class="strong"><strong>理论的价值与风险成正比</strong></span>。此外，波普尔还把生物学中的进化论引入认识论，认为知识的增长是一个自然选择的结果，人类拥有的知识时时刻刻由迄今在适者生存的竞争中幸存下来的假说组成。
        </p><p>
            证伪论不仅重新定义了科学理论——可以被证伪但尚未被证伪的猜想或假说，也重新定义了科学态度——不是小心地呵护理论、谨慎地避免错误，而是不断地挑战理论、努力地发现错误并从错误中学习。必须了解，当一个理论完备到足以解释世间发生的任何事件的时候，当一个理论灵活到足以躲避任何攻击的时候，当一个理论被声称是放之四海而皆准、永远颠扑不破的时候，当一个理论被高喊着要坚决捍卫的时候，当一个理论被用作评判其他理论的标准的时候，恰恰是该理论远离真理接近教条、远离科学接近迷信的时候。
        </p><p>
            <span class="strong"><strong>四、没有绝对理性的科学</strong></span>
        </p><p>
            波普尔开创了<span class="term">批判理性主义</span>（critical rationalism）的先河，他的证伪论大胆新颖、简洁有力。但证伪论也有它的局限，同样要接受他人的批判，而这也正是它所倡导的。实际上，一个理论不可能因为一个反例就轻易地被否证。有时是因为反例本身来源于观察，而观察并不完全可靠；有时是因为忽略了某些不该忽略的因素；有时被否证的理论通过少量的修改可以继续存在。总之，否证论过于刚断激进，忽略了理论的柔韧性和稳定性（波普尔本人也意识到否证的界限并不总是那么清晰的）。例如，牛顿理论在解释水星<span class="term">近日点进动</span>（perihelion precession）的问题上遭到了困难，但人们并没有立即抛弃该理论，而是孜孜不倦地寻找支持的证据，甚至希望能有类似海王星那样的发现。直到广义相对论成功地解释这一现象并被广泛地接受后，这种努力才被放弃。此外，否证论是从微观的角度来探求科学发展的模式，全然忽略了科学理论的历史背景和整体框架。
        </p><p>
            库恩（Thomas Kuhn）是一个证伪论的反对者，当然也是证实论的反对者。他创造性地提出了一套<span class="term">范式</span>（paradigm）理论，认为科学的发展不是线性递增的渐进过程，而是周期性的革命或突变，即<span class="term">范式转移</span>（paradigm shift）（这一术语并非库恩创造的，不过后来被广泛地应用于其他领域）。所谓范式，不是指某个具体的理论，而是指<span class="term">科学共同体</span>（scientific community）共有的世界观和方法论，包括价值标准、形而上学原则、符号体系、理论框架、应用方法等等。通俗地说，范式就是看待问题和解决问题的模式与套路，既有客观成分——理论和方法体系，也有主观成分——心理信念。据此，库恩把科学的发展过程描述为如下几个周期性的阶段——
        </p><div class="blockquote"><blockquote class="blockquote"><p>前科学－常态科学－危机－革命－新的常态－新的危机</p></blockquote></div><p>
            在从事某一学科的研究者对共同研究的问题尚未达成基本共识之前，该学科处于<span class="term">前科学</span>（prescience）阶段。各种候选范式激烈碰撞、互相融合，最后脱颖而出的范式成为公认的准则。于是，研究活动开始从无组织到有组织、从多样化到单一化，此时便进入<span class="term">常态科学</span>（normal science）阶段。拥有统一范式的学科标志着它的成熟，库恩以此作为区别科学与非科学的一个重要特征（虽然他并不十分热心划界问题）。在常态科学阶段，科学家们倾向于维护和发展现有的框架，不会轻易地否定它。即使出现一些难以解释的<span class="term">反常</span>（anomaly）现象，也会尽量地通过修正理论来克服。在经过一段稳定期之后，困难愈来愈多、愈来愈严重以至失去控制，科学家们的固有信念渐渐动摇，<span class="term">危机</span>（crisis）随之来临。直到一种能有效化解危机的新型范式出现，并被越来越多的科学家认同的时候，<span class="term">科学革命</span>（scientific revolution）便产生了。
        </p><p>
            举一个范式转移的实例。19世纪末，经典物理学取得了无与伦比的全面成功，作为三大支柱的经典力学、经典电磁学和经典热力学，当仁不让地成为各领域的范式。物理学家们无不踌躇满志，俨然已掌握了大自然的终极密钥，早把哲学家们的谆谆告诫抛诸脑后。孰料天空飘来的“两朵乌云”（迈克尔逊-莫雷实验与黑体辐射实验）引起了一场暴风骤雨，看似坚不可摧的物理学大厦竟变得摇摇欲坠。幸得相对论与量子力学应运而生，分别驱散了这两朵乌云，最终让物理学安然度过危机。它们也众望所归地成为两个崭新的范式，完成了现代物理学的一次重大革命。
        </p><p>
            范式理论不仅否定了科学的渐进性，更对科学的客观性提出了挑战。即使当前范式百孔千疮，科学家们仍倾向于坚守，这已不能用事实或理论来解释，只能说与信念有关。量子力学奠基人普朗克（Max Planck）迫于情势提出了量子假设，但出于对经典范式的深深信念，后期一直试图把自己的假设纳入其中。他曾深有体会地说：“一个新的科学真理并不是靠说服它的对手并使其看见真理之光取胜，而是由于它的对手死了，新的一代熟悉它的人成长起来了”即使比普朗克显得更为开放的洛伦兹（Hendrik Lorentz），也遗憾没有在旧的基础崩溃之前死去。范式信念的烙印之深，由此可见一斑。难怪库恩把范式转移比作心理学上的<span class="term">格式塔</span>（Gestalt）转换、宗教上的改宗、政治上的改革。无知的现代人常常会忍不住嘲笑亚里士多德的理论，诸如五大元素说、地心说、重力说等，是否想过自己又会怎样地被后代嘲笑呢？不同的历史时期有不同的范式，那是近乎宗教般的信仰，岂是轻易可以超越的？
        </p><p>
            同样地，一个新范式的接受与否也不全靠严密的逻辑论证或严格的实践检验，有时取决于科学直觉、宗教或哲学观点、对和谐的信念、个人心理、社会心理等非理性因素。比如哥白尼反对地心说的一个根据是：它的体系过于复杂，不能彰显造物主的伟大。他提出的日心说在当时并没有足够实验数据的支持，相反还有不少不利的事实，但由于比地心说更简洁、更自然，仍吸引了不少包括开普勒和伽利略在内的科学家参与到新范式的建构中来。“让一个方程符合美比符合实验更重要”狄拉克（Paul Dirac）这句“唯心”的名言在麦克斯韦方程组中得到了最好的应验。麦氏方程组从内容到形式无不体现出科学的和谐与美妙，人们在惊叹的同时也加深了对其正确性的信念。只可惜，美并没有一个量化的标准。开普勒的行星轨道方程在很长时间内不被接受，就是因为包括伽利略在内的科学家都不喜欢椭圆，认为正圆才是最高贵最完美的。无论宗教、哲学、美学等带给人的启示是正面的还是负面的，它们都实实在在地影响到了范式的接受程度。此外，社会因素也不可忽视。爱因斯坦的相对论在很长时间里只有极少数人能真正理解，可为什么很快能广泛地被世人所接受？除了有限的实验支持和理论本身的优美之外，媒体和科学界推动的<span class="term">社会建构</span>（social construction）也是功不可没的<a class="link" href="#note2"><sup>[2]</sup></a>。
        </p><p>
            库恩认为范式具有<span class="term">不可通约性</span>（incommensurability），即不同的范式之间难以比较，没有绝对的好坏标准。但他本人拒绝被冠以<span class="term">相对主义</span>（relativism）的帽子，理由是他并不否认范式革命的进步性和不可逆性。不过下面的例子多少说明相对主义也不无道理。亚里士多德认为行星的圆周运动是自然的、无需解释的。牛顿认为匀速直线运动才是自然的，行星作（近似）圆周运动是由于引力作用。他同样也没有解释其中的原因，只是把它们分别归结为自己的第一运动定律和万有引力定律。在后人看起来，牛顿的说法无疑是更科学的。然而到了爱因斯坦那里，万有引力再次消失，行星轨道的弯曲是因为时空弯曲的缘故。如果认同广义相对论，那么亚里士多德的说法岂不是在某种程度上比牛顿的更接近真理？<a class="link" href="#note3"><sup>[3]</sup></a>
        </p><p>
            比较几种不同的科学观，大致可以说：逻辑实证主义重在真，波普尔重在善，库恩重在美。逻辑实证主义强调科学的真理性，那是毋庸置疑的。何以说波普尔重在善呢？波普尔批驳了逻辑实证主义一厢情愿的求真信念，提倡理性的批判精神，由此而带来的美德将是：诚实公正、尊重他人、勇于认错、反对权威、鼓励开放、崇尚自由、提倡民主，等等。波普尔的这种求善思想在《开放社会及其敌人》中得到最明显的体现，尽管那不是一本科学哲学方面的著作。库恩则强调科学的发展不是单靠自身内部的逻辑力量，还深受社会文化的影响。他认为不同的范式之间在逻辑上并无优劣之分，选择的标准不完全是客观的，还有包括审美（简单、对称、和谐、一致、优雅等）、直觉、信念、价值判断等在内的心理要素。
        </p><p>
            拉卡托斯（Imre Lakatos）认为波普尔的证伪论过于朴素（如忽略了理论的坚韧性和整体性），库恩的范式论过于非理性（如过分强调社会和心理因素），集二者之大成提出了科学研究纲领方法论。<span class="term">研究纲领</span>（research programme）是一组具有严密内在结构的科学理论系统，由中心的<span class="term">硬核</span>（hard core）和周围的保护带（protective belt）组成。其中硬核是纲领的不容反驳的基础理论，保护带是容许反驳的辅助假设。比如，地心说是托勒密天文学研究纲领的硬核，牛顿三大运动定律和万有引力定律是牛顿力学研究纲领的硬核。当研究纲领遭遇反常现象，将通过修改保护带来维护硬核，使研究纲领免遭证伪。一方面，拉卡托斯继承了库恩的<span class="term">历史论</span>（historicism）和<span class="term">整体论</span>（holism），把范式改造为研究纲领，承认科学家们协同地在一个框架内工作，但否定框架之间的不可通约性以及框架选择对信念的过分依赖性。另一方面，他把波普尔的<span class="term">朴素证伪论</span>（naïve falsificationism）在划界标准和证伪规则上进行了改造：一个理论被称为是科学的，仅当它比其先行或与其竞争的理论有更强的发现新颖事实的能力之时<a class="link" href="#note4"><sup>[4]</sup></a>；一个理论被证伪，当且仅当另一个理论有更强的解释和预测能力。拉卡托斯的<span class="term">精致证伪论</span>（sophisticated falsificationism）否定了判决性实验的存在，保证了科学理论发展的连续性。
        </p><p>
            正统的科学观受到越来越强烈的质疑，波普尔无情地把科学置于证伪的烈火之上炙烤，库恩悄悄地在科学的土壤里播下革命的种子，拉卡托斯小心地给科学裹上保护带浸泡在反常的海洋之中。但在费耶阿本德（Paul Feyerabend）的眼里，这三位还是显得过于委婉和温情了。他抡起<span class="term">认识论无政府主义</span>（epistemological anarchism）的大锤，二话不说就砸碎了科学的圣像。听听他的发言——
        </p><div class="blockquote"><blockquote class="blockquote"><p>科学只是人类众多思想形态的一种，但并不一定是最好的一种。</p><p>科学的优越性不是被论证的，而是被假定的。</p><p>科学是最新、最富有侵略性、最教条的宗教机构。</p><p>认识论无政府主义者会毫不反悔地为最陈腐或最荒诞的陈述辩护。</p><p>不存在着什么“科学的方法”。</p><p>如果存在着一种永远不变的科学方法原则，那就是“怎么都行”（anything goes）。</p><p>科学是一种本质上属于无政府主义的事业。</p><p>专家的意见经常带有偏见，是不可靠的，需要有外部控制。</p><p>外行可以而且必须监督科学。</p><p>
                如果科学由于它的成就而受到称赞，那么神话应该受到百倍的称赞。因为神话的发明者开创了文化，而科学家只是改变了文化，而且并非总是改得更好。
            </p></blockquote></div><p>
            持传统科学观点者听到这些难免会耳热心惊，甚而会因心中的女神被亵渎而恼羞成怒。然平心而论，费氏观点虽有偏颇之处，却也不无道理。他正确地意识到：理论的增长是对科学有益的，而齐一性则损害科学的批判力和创造力；不顾具体条件而硬性规定一些所谓科学的规则，不仅不会给科学带来什么帮助，反而会阻碍科学的发展。他还通过自己对中医的切身经历，得出一个结论：仅仅因为中医不符合西医的理论或规则而将其排斥是十分荒谬的<a class="link" href="#note5"><sup>[5]</sup></a>。费耶阿本德对缺乏人性关怀的<span class="term">科学主义</span>（scientism）也深恶痛绝，认为科学最重要的问题不是求真，而是求善，即它在何种程度上增加了个人的幸福和自由。他一方面批判科学方法论、理性主义和科学沙文主义，一方面提倡科学的民主化、研究方法的多元化和非理性的合理化，从而填平了科学与非科学之间的鸿沟，消解了理性与人性之间的矛盾。正如休谟那样，费耶阿本德的目的是：<span class="strong"><strong>用理性反思理性，将人性注入理性</strong></span>。人们往往对“无政府主义”这样的词汇有天然的反感，但认识论的无政府主义完全不同于政治上的无政府主义。尤其是，费耶阿本德声称它只是在为时代把脉后开的一剂猛药（偷师中医？）。既然是药，就难免有毒，也不可能永远吃下去，否则必会矫枉过正。或许下面这句话更能反映他的本意——
        </p><div class="blockquote"><blockquote class="blockquote"><p>今日之科学或是明日之童话，今日之神话或是明日之科学。</p></blockquote></div></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>
                    维特根斯坦（Ludwig Wittgenstein）认为数学也是重言式。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    相对论在最初的接受中部分地得益于倡导者的威信与资源，但最终还是靠实践检验。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    程序员还可以考虑<span class="term">编程范式</span>（programming paradigm）的例子。过程式、对象式（OOP）、函数式、逻辑式等不同的范式各有优劣，没有绝对的高下之分。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    这意味着：新理论在逻辑上包含更多的经验内容，并且其中有些已被证实。
                </p></li><li class="listitem"><p><a name="note5"></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%2F2010%2F04%2F22%2Fhard-thinking-and-soft-thinking-2%2F&amp;title=%E8%AE%BA%E6%80%9D%E7%BB%B4%E7%9A%84%E5%88%9A%E6%80%A7%E4%B8%8E%E6%9F%94%E6%80%A7%EF%BC%88%E7%A7%91%E5%AD%A6%E7%9A%84%E8%BF%B7%E4%BF%A1-2%EF%BC%89" id="wpa2a_22">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年07月12日 -- <a href="http://blog.zhenghui.org/2011/07/12/hard-thinking-and-soft-thinking-4/" title="论思维的刚性与柔性（科学的迷信-4）">论思维的刚性与柔性（科学的迷信-4）</a> (0)</li><li>2010年09月20日 -- <a href="http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/" title="论思维的刚性与柔性（科学的迷信-3）">论思维的刚性与柔性（科学的迷信-3）</a> (0)</li><li>2010年04月19日 -- <a href="http://blog.zhenghui.org/2010/04/19/hard-thinking-and-soft-thinking-1/" title="论思维的刚性与柔性（科学的迷信-1）">论思维的刚性与柔性（科学的迷信-1）</a> (0)</li><li>2010年04月2日 -- <a href="http://blog.zhenghui.org/2010/04/02/hard-thinking-and-soft-thinking-0/" title="论思维的刚性与柔性（前言）">论思维的刚性与柔性（前言）</a> (44)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=5CQLhB5ERsM:1e_vT7MbSU0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/5CQLhB5ERsM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/04/22/hard-thinking-and-soft-thinking-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2010/04/22/hard-thinking-and-soft-thinking-2/</feedburner:origLink></item>
		<item>
		<title>论思维的刚性与柔性（科学的迷信-1）</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/Non-gOxiLR8/</link>
		<comments>http://blog.zhenghui.org/2010/04/19/hard-thinking-and-soft-thinking-1/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 08:08:59 +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=682</guid>
		<description><![CDATA[<b>论思维的刚性与柔性</b>——科学的迷信（1）<br/> • 没有绝对可靠的科学<br/> • 一切科学都建立在假设之上 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">论思维的刚性与柔性（科学的迷信-1）</span></h1>

<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/article.css" type="text/css"></head>
<!-- below comes from generated html -->   
<div lang="zh-CN" class="article" title="论思维的刚性与柔性"><div class="titlepage"><div><div><div class="author"><h3 class="author">郑晖</h3></div></div></div><hr /></div><div class="section" title="正文"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="content"></a>科学的迷信（1）</h2></div></div></div><p>
        </p><p>
            科学，一个神圣的词汇，作为名词是真理的同义语，作为形容词是正确的换喻词。渺小的人类正是依仗科学这把神兵利器，俨然已可与大自然分庭抗礼。从来没有一种信仰能象科学那样深入人心，胆敢怀疑科学者似乎不是愚昧便是狂妄。本节标题把科学与迷信并列，绝非哗众取宠，而旨在说明一个现象：科学在破除大量迷信的同时，也渐渐变成了一种新的迷信。
        </p><p>
            有关科学的神话不胜枚举，下面是一些最常见的说法——
        </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">科学是客观纯粹的、绝对理性的、价值中立的、普遍正确的、确定无疑的</li><li class="listitem">科学观察是客观的、价值无涉的，并且先于科学理论</li><li class="listitem">科学的发现均始于观察，完全依靠逻辑推理，因此结论是可靠的</li><li class="listitem">科学建立于事实之上，科学实验是客观的、可重复的，因而科学是可靠的</li><li class="listitem">科学真理是可以被证明的，科学理论就是被证实的科学假说</li><li class="listitem">科学不需要假设</li><li class="listitem">科学是精确的，至少可以无限地趋于精确</li><li class="listitem">科学的发展完全是由其自身的内部逻辑力量推动的</li><li class="listitem">	科学终究能了解宇宙的终极真理，是万能的（在广度和深度上没有极限）</li><li class="listitem">科学没有边界，也没有极限</li><li class="listitem">不符合科学、不能用科学来解释的东西都是无用的或不正确的，应当坚决摒弃</li><li class="listitem">科学不需要形而上学</li><li class="listitem">科学与宗教不相容</li><li class="listitem">科学给人类带来幸福</li></ul></div><p>
            接下来我们将一一刺破这些迷信的肥皂泡。为了让话题更加集中，如无特别说明，以下科学主要指狭义的自然科学。
        </p><p>
            <span class="strong"><strong>一、没有绝对可靠的科学</strong></span>
        </p><p>
            首当其冲的一个问题是：科学如此值得信赖的根本原因是什么？也许可以换种问法：科学与其他途径获得的知识有何本质的不同呢？最常见的一种解释是：科学是从客观事实中严格推导出来并通过实践检验的。言下之意，从可靠的事实出发作可靠的推理，得出的结论自然也是可靠的，更何况还要经过反复的验证。真是环环相扣、无懈可击，简直不容置疑。事实上，我们的思维早已被训练得如此地刚性，以至于对此哪怕只闪现一丝怀疑的火花，都会本能地自行掐灭，以防灼伤大脑。本着寻根究底的科学精神，我们发现以上解释实际上隐含着三个观点：一、人的感官经验（sense experience）是确实的和精确的——<span class="term">实证主义</span>（positivism）的观点；二、从一系列特殊的事实能推出一般性的结论——<span class="term">归纳主义</span>（inductivism）的观点；三、被经验证实（empirically verified）的理论是有效的——<span class="term">证实主义</span>（verificationism）的观点。随着哲学标签的引入，我们的讨论开始弥漫起学术的气息，希望您感到的是陶醉，而不是晕眩。下面，让我们试着往思维中掺入一点柔性的元素，以审视的目光重新打量这三个观点。
        </p><p>
            <span class="strong"><strong>1. 关于经验观察的可靠性：</strong></span>
            人们永远不可能通过观察获得绝对客观的事实。
        </p><p>
            首先，由于测量仪器精度有限、测量方法或理论公式不够完善、实验条件不尽理想、实验者存在生理局限，实验中的各种偶然误差和系统误差无法避免。随着实验手段的进步，测量误差可以降低，但永远不可能完全消除。海森堡（Werner Heisenberg）的<span class="term">不确定性原理</span>（uncertainty principle）甚至从理论上保证了有些成对的共轭（conjugate）物理量（如位置与动量）不能同时达到任意精度。有人认为只要测量误差足够小，那么对结果的影响总是可以忽略不计的。可惜<span class="term">混沌</span>（chaos）现象的出现破坏了这种乐观的情绪，人们发现一个非线性动力系统可能会对初始值具有极其敏感的依赖性，即俗称的“蝴蝶效应”。换言之，一个混沌系统对测量误差是零容忍的。
        </p><p>
            其次，观察者的测量行为可能影响到被观察的事实。正如量子力学中所揭示的那样，观察者既是观众，又是演员。当我们测量水温时，温度计已经改变了水温；当我们测量轮胎气压时，已经放跑了部分气体；当我们偷窥动物的世界时，往往在不意间会惊飞几只灵敏的鸟儿。
        </p><p>
            最后，观察者的客观性存疑。种种实验表明，人的感官并不绝对可靠。耳听固然是虚，眼见也未必为实，难道忘了我们是怎么一次次地被魔术师们欺骗的吗？不仅如此，观察者的实验行为依赖于其理论知识和实验目的，同时也掺杂着个人情感和价值判断，从而不可能做到绝对的中立、客观和理性。一个家喻户晓（但未必真实）的例子是牛顿因观察到苹果的落地而得出了万有引力定律，为什么其他人都熟视无睹？另一个著名案例来自密立根（Robert Andrews Millikan），他在作油滴实验时有意识地去掉了“丑陋的”数据以迎合完美的理论，并因此获得了诺贝尔物理学奖。
        </p><p>
            <span class="strong"><strong>2. 关于归纳推理的可靠性：</strong></span>
            归纳推理（数学归纳法除外）是不严格的。
        </p><p>
            实验事实通常具有特殊性，在逻辑上表现为一个<span class="term">特称命题</span>（particular proposition）。科学理论通常具有一般性，在逻辑上表现为一个<span class="term">全称命题</span>（universal proposition）。从一组特称命题推导出一个全称命题，被称为<span class="term">归纳推理</span>（inductive reasoning）。容易看出，这种推理并无逻辑上的保障。即使发现一万只乌鸦全是黑的，也不能断定“天下乌鸦一般黑”。对此，归纳主义者提出：应观察大量的事实，如果这些事实均符合同样的规律，则可断定理论成立。可究竟多大才算“大量”？没有一个公认的标准。不仅理论的必然性无法保证，甚至连大概率的正确性也是一种奢望。因为事实总是有限的，而理论涵盖的对象却通常是趋于无限的，有限的样本相对无限的取值空间而言，在概率上都趋于零。更何况有些实验是无法多次进行的，举个极端的例子：原子弹的杀伤力实验在整个世界历史上只进行过两次。
        </p><p>
            归纳法除了有数量问题外，还有个时间问题：过去成立的事实不代表将来也会成立。极端如休谟（David Hume）者，甚至拒绝相信明天的太阳定会升起，哪怕它此前从未旷过一天工。如果您闻听此言有将其暴打的冲动，不要忘记他可是近代最重要哲学家之一（许多人认为“之一”二字都是多余的），既不痴傻疯癫，也非胡搅蛮缠。有人据理力争：归纳法不是在以前的理论形成中屡试不爽吗？哼哼，休谟又在一旁冷笑了：这分明是在循环论证！归纳法在场合A是成功的，归纳法在场合B是成功的，。。。，由此推出归纳法在任何场合都是成功的。这个推理本身不正是归纳推理吗？用归纳推理来论证归纳推理的正确性，不是循环论证是什么？每当NBA评论员用历史数据来预测胜负的时候，每当某些人动辄以“历史证明”来寻求其断言的合理性的时候，不知道他们的颈后是否感受到来自休谟鼻孔里的森森凉气？
        </p><p>
            对于归纳法的另一个质疑容易受到忽视，但同样令归纳主义者感到如芒在背。众所周知，实验数据通常是有误差的，而理论公式通常却是精确的。回忆一下，胡克定律、欧姆定律的线性正比关系，库仑定律、万有引力定律的平方反比关系，哪一个不是简洁精确的？试问：从不够精确的特殊事实如何能推导出精确的一般结论？从严格意义上说，恐怕连归纳推理都算不上吧？
        </p><p>
            <span class="strong"><strong>3. 关于理论检验的可靠性：</strong></span>
            一个理论无论被成功地检验过多少次，也不能被证实。
        </p><p>
            该问题从某种意义上说是上两个问题的重复，只不过一个发生在理论确立之前，一个发生在理论确立之后。实验结果与理论预测常会有一定的出入，而误差的允许范围并无明确的界定。更糟的是，人们在检验理论之时往往比建立理论之前带有更多的主观性。为了维护现有的理论，支持者总会精心挑选有利的事实或者增加辅助性的特设来自圆其说，颇有“事后诸葛亮”的嫌疑——心理学上称之为<span class="term">后视偏差</span>（hindsight bias）。倘若一个理论在验证过程中陷于为辩护而辩护、为修正而修正的境地，那么很可能已经偏离了真理的航道。地心说便是一个典型实例。该学说在解释行星运动时一再遇到困难，但支持者们总能找出一些牵强的理由和生硬的假设来挽救。例如，为了解释行星逆行现象，托勒密（Claudius Ptolemaeus）专门提出本轮（epicycle，周转圆）理论。地心说就这么一路缝缝补补，竟然也流行了两千年，直到为哥白尼的日心说所取代。
        </p><p>
            科学的可重复性原则也是为人津津乐道的。相当一部分人认为，当一个理论在被不同的人、不同的时间、不同的地点被反复验证后，其正确性便可以得到保障。特异功能被斥为伪科学的一个原因便是其不具备可重复性。事实上，验证次数不是一个绝对的衡量标准。1919年，爱丁顿（Arthur Eddington）的一次日全食观测便成了<span class="term">判决性实验</span>（crucial experiment），此前无数次被检验的经典力学在一夜之间变得脆弱不堪，而头一回被检验的广义相对论则开始逐渐为人们所接受。再者，可重复性本身就是一个相对的概念，任何两次实验都不可能完全地对等。有些理论囿于客观条件、伦理道德等限制，直接检验的机会极少，甚至完全没有，主要通过理想实验、模拟实验、替代实验等间接方式。如进化论、宇宙大爆炸理论、一些生物医学理论，等等。
        </p><p>
            <span class="strong"><strong>二、一切科学都建立在假设之上</strong></span>
        </p><p>
            传统的<span class="term">认识论</span>（epistemology）认为，（科学）知识是观念符合对象的结果。至于二者是如何相符的，在近代西方有两种截然不同的观点：一个是笛卡尔（Rence Descartes）创立的<span class="term">唯理论</span>（rationalism），主张知识源于先天观念，推崇演绎法；另一个是培根（Francis Bacon）创立的<span class="term">经验论</span>（empiricism），主张知识源于感官经验，推崇归纳法。两大派别多年来一直争战不息，休谟的出现更加剧了二者的矛盾。他从经验论的阵营中杀出，把经验论引向<span class="term">怀疑论</span>（skepticism）的死胡同，并通过否定归纳原理断绝了它的归路。不等唯理论幸灾乐祸，休谟回手对它的软肋发出致命的一击——否定绝对的因果律。每当人们说“太阳把石头晒热”的时候，休谟就会不识时务地跳将出来：凭什么说“太阳晒”导致了“石头热”？我能够感受到的只是太阳晒和石头热，却感受不到二者的因果关系。人们之所以相信两种现象之间因果联结，只是因为它们恒常地前后相伴。但那不过是一种下意识的联想或习惯性的信念，找不到任何逻辑的依据。很显然，一旦因果链条被割断，唯理论的战车将彻底陷入瘫痪。休谟的破坏力还不止于此，他坚称：观念就是观念，永远不可能是对象，谁也不能保证它们一定相符，从而把整个认识论推向彻底的<span class="term">不可知论</span>（agnosticism）。如果您觉得休谟在无理取闹，那就大错特错了。他的观点尽管令人不快，却是无可辩驳的。当人们确信自己的主观认识符合了客观事实的时候，却未意识到这本身就是一个主观判断，与先前的归纳推理一样坠进循环论证的泥沼。从实质上看，唯理论以非理性的方式来坚持理性，而休谟以怀疑理性的方式来坚持更纯粹的理性。作为一个“温和的怀疑论者”（休谟自称），休谟一方面拒绝轻信和盲从，一方面为冷冰冰的科学注入人性，明确划分了知识与信仰的界限，并为理性和宗教的盲目崇拜者分别开了一副清醒剂。
        </p><p>
            休谟撬动了人们一直迷信的科学理性的根基，也将另一位哲学伟人——康德（Immanuel Kant）从<span class="term">独断论</span>（dogmatism，或译为教条主义，此处指唯理论）的沉梦中惊醒。后者从此开始了长达十一年的闭关修炼，终于推出划时代巨著《纯粹理性批判》（Critique of Pure Reason），力图在独断与怀疑、理性与经验、演绎与归纳、唯物与唯心之间取得调和。鉴于唯理论无法解释科学知识的经验依赖性，而经验论又无法解释科学知识的普遍必然性，康德创立了<span class="term">先验唯心论</span>（transcendental idealism）。他的一个论点是：科学知识源于<span class="term">先天综合判断</span>（synthetic a priori judgment）。打个粗糙的比方，如果把人脑比作电脑——当然是能自动开发和升级软件的超级电脑，当这台电脑刚出厂时，唯理论者认为它已是各种软件齐备，经验论者它认为只是一台裸机，而康德折衷地认为它仅预装了操作系统的<span class="term">内核</span>（kernel）。他有一句惊世骇俗的名言：“我们的理智不是从自然引出规律，而是把规律强加于自然”，并且毫不谦虚地把自己的这套理论比作哥白尼式的革命（反对者则讽刺为托勒密式的反革命）。其理由是：哥白尼把“太阳围绕地球转”改成了“地球围绕太阳转”，而他把“（认识的）主体围绕着客体转”改成了“（认识的）客体围绕着主体转”。即知识不是观念符合对象，而是对象符合观念。在他看来，经过这样的关系反转，认识论的难题便会迎刃而解。
        </p><p>
            康德哲学并非无可挑剔，但有一点是不易之论，即所有的科学理论都是人给自然设立的法则，哪怕后者未必遵从。对此一个有力的支持证据是，所有的科学理论均建立于人为的假设之上。不少人会觉得不可思议，因为在他们的潜意识里，科学与假设是互相对立的概念——科学理论都是真理啊，“真”理岂能“假”设？不妨这么说，如果一套科学理论是一座大厦，那么事实是砖石，而假设是地基。没有事实，科学理论固然无法构建；但没有假设，科学研究甚至无法开始。
        </p><p>
            有一个假设是所有的科学都无法逃避的，即科学研究的对象是有规律的。（如果您觉得这本是天经地义的话，请复习以上休谟先生的发言）人们采纳该假设绝不是因为证明了客观规律的存在性，而是出于一种信念，或者一种无奈——倘若不信，我们还能做什么呢？柏格森（Henri Bergson）就认为，所谓的规律只是科学家脑中歪曲的图像。
        </p><p>
            关于规律的假设还得进一步加强：规律不仅存在，而且要足够地简单，简单到可以为人类所认识。（如果您觉得这是在侮辱人类的智慧，那一定是对人类的智力水平作了未经证实的乐观假设）为什么那么多的物理定律可以用简单而精确的数学公式来表达？正如前文指出的那样，这些定律从未被真正证明过，不只是因为归纳法的局限，也因为粗略的实验数据无法严格地验证精确的数学关系。这些定律能被广泛接纳，仅仅是因为科学家们坚信规律应当是简单的。假如一定要拒绝规律的简单性假设，那么就得承认科学理论不是真理，至多只是对规律的简化描述。
        </p><p>
            仅仅假设客观世界是有规律的、可认知的还远远不够，科学家们在研究过程中总会有意无意地增加许多实用的假设。比如，声称“我不作假设”的牛顿在他的力学理论无意中用到了绝对时间的假设，而爱因斯坦在他的狭义相对论中则有意地引入了光速不变的假设。为了更好地说明问题，我们介绍一下大科学家庞加莱（Henri Poincaré，也译为彭家勒）提出的<span class="term">约定论</span>（conventionalism）。该理论认为，在科学创造过程中总会选择一些<span class="term">约定</span>（convention），这些约定无所谓真假，仅仅是出于方便才提出来的。约定论行走于经验论和唯理论之间，一方面认为约定不是凭空产生的，而是受经验的引导或提示；另一方面认为约定是人类自由创造的产物。在一定程度上，庞加莱也赞同康德的先验论，认为一些算术公理是先天综合判断，但同时也认为欧氏几何公理仅仅是约定（否则无法解释非欧几何的合理性），并非康德所坚信的绝对真理。相比康德主张的人为自然立法，庞加莱主张的是人为科学立法。他提出，一个被反复验证过的规律可以通过约定从定律提升到原理，从此不再接受实验的检验，成为永恒的严格的真命题<a class="link" href="#note1"><sup>[1]</sup></a>——这就部分地解释了被我们一再质疑的精确公式的合理性。一个适当选择的原理虽然牺牲了客观性，但却能给科学研究带来极大的便利。比如，牛顿第二运动定律（F=ma）可以提升为原理（比如作为力或质量的定义），成为力、质量和加速度之间的永恒关系。正是基于这种思想，他建议将光速不变作为公设，因而被爱因斯坦尊为相对论的先驱。约定并不是假设的全部，庞加莱把科学假设细分为三类——
        </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p class="simpara">自然假设（natural hypothesis）</p><p>
                    这是一类极其自然且难以避免的假设，实质上是伪装的定义或约定。比如超距无关性假设：相距较远的两个物体之间互相独立。显而易见，没有这一假设任何实验都无法开展。比如因果连续性假设：微小的扰动导致的后果也是微小的。这保证了实验者对物体的稳定性和测量误差有一定的容忍度。再比如线性假设：路径在微小的范围可用直线段代替（回忆一下用多边形周长逼近圆周长）。不到万不得已人们是不会舍弃自然假设的，当然也不是没有例外——超距无关性假设在黑洞附近完全失效，量子力学和混沌学动摇了因果连续性假设，而<span class="term">分形</span>（fractal）的出现则破坏了线性假设（参考一个事实：英国海岸线是无限长的）。
                </p></li><li class="listitem"><p class="simpara">中性假设（indifferent hypothesis）</p><p>
                    这类假设是辅助性的、没有客观倾向性的，即使换成其他假设也不会影响结论。之所以选择一个而不是另一个，完全是为了方便计算或有助于理解具体问题。比如我们可以假设物体是连续的，也可以假设是由原子构成的（不连续的）。只要不被误解，它们都是有益无害的。
                </p></li><li class="listitem"><p class="simpara">概括假设（generalization）</p><p>
                    这类假设是概括的经验性假设，随时接受客观实验的证实或证伪。
                </p></li></ul></div><p>
            还有一点值得注意，科学都建立于数学之上，而数学又建立在公理之上。所谓公理，无非是一些公认的却又无法证明的命题，本质上也是人为的设定。从这个角度看，科学也是离不开假设的。有人会反驳，数学公理虽然无法被证明，却是不证自明的。可惜随着非欧几何的横空出世，此言不攻自破。
        </p><p>
            实证主义，尤其是<span class="term">逻辑实证主义</span>（logical positivism或neo-positivism）主张把<span class="term">形而上学</span>（metaphysics）从科学中彻底排除，以捍卫科学的纯粹性。然而，由于科学与假设之间千丝万缕的联系，注定了这种努力是徒劳的。况且，对客观世界可知性的假设不正是一个地地道道的形而上学问题吗？
        </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>
                    为便于理解这一观点，不妨假设某定律阐述的是事实A与事实B之间的关系。可以引入一个抽象的中介事实C，使之与A之间有一个严格的、精确的、永恒的关系，即所谓的约定性原理。假如以后A与B之间的关系需要修正，只需调整B与C之间的关系即可，不影响A与C之间的恒定关系。
                </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%2F2010%2F04%2F19%2Fhard-thinking-and-soft-thinking-1%2F&amp;title=%E8%AE%BA%E6%80%9D%E7%BB%B4%E7%9A%84%E5%88%9A%E6%80%A7%E4%B8%8E%E6%9F%94%E6%80%A7%EF%BC%88%E7%A7%91%E5%AD%A6%E7%9A%84%E8%BF%B7%E4%BF%A1-1%EF%BC%89" id="wpa2a_24">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年07月12日 -- <a href="http://blog.zhenghui.org/2011/07/12/hard-thinking-and-soft-thinking-4/" title="论思维的刚性与柔性（科学的迷信-4）">论思维的刚性与柔性（科学的迷信-4）</a> (0)</li><li>2010年09月20日 -- <a href="http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/" title="论思维的刚性与柔性（科学的迷信-3）">论思维的刚性与柔性（科学的迷信-3）</a> (0)</li><li>2010年04月22日 -- <a href="http://blog.zhenghui.org/2010/04/22/hard-thinking-and-soft-thinking-2/" title="论思维的刚性与柔性（科学的迷信-2）">论思维的刚性与柔性（科学的迷信-2）</a> (0)</li><li>2010年04月2日 -- <a href="http://blog.zhenghui.org/2010/04/02/hard-thinking-and-soft-thinking-0/" title="论思维的刚性与柔性（前言）">论思维的刚性与柔性（前言）</a> (44)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=Non-gOxiLR8:WW9QC9od7B8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/Non-gOxiLR8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/04/19/hard-thinking-and-soft-thinking-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2010/04/19/hard-thinking-and-soft-thinking-1/</feedburner:origLink></item>
		<item>
		<title>论思维的刚性与柔性（前言）</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/3yqq9L6MY9k/</link>
		<comments>http://blog.zhenghui.org/2010/04/02/hard-thinking-and-soft-thinking-0/#comments</comments>
		<pubDate>Fri, 02 Apr 2010 10:04:33 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[思维]]></category>
		<category><![CDATA[刚性思维]]></category>
		<category><![CDATA[柔性思维]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=671</guid>
		<description><![CDATA[<b>论思维的刚性与柔性</b>——前言<br/> 为什么要写这篇文章？ [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">论思维的刚性与柔性（前言）</span></h1>

<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/article.css" type="text/css"></head>
<!-- below comes from generated html -->   
 <div lang="zh-CN" class="article" title="论思维的刚性与柔性"><div class="titlepage"><div><div><div class="author"><h3 class="author">郑晖</h3></div></div></div><hr /></div><div class="section" title="前言"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="content"></a>前言</h2></div></div></div><p>
            尊敬的读者，您会认同以下哪些说法呢？
        </p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">科学的发现靠的都是逻辑思维。</li><li class="listitem">不存在脱离语言的思维。</li><li class="listitem">科学完全建立在事实之上，是客观而精确的。</li><li class="listitem">一切科学真理终将被发现，也一定能被证明。</li><li class="listitem">有因必有果、有果必有因。</li><li class="listitem">科学=真理、宗教=迷信、非科学=伪科学。</li><li class="listitem">             唯心论是幼稚的、形而上学是僵化的、不可知论是愚昧的，只有唯物辩证法是正确的。</li><li class="listitem">哲学总能指导科学。</li><li class="listitem">宗教总是阻碍科学的发展。</li><li class="listitem">上帝没有藏身之所，人类是宇宙中最智慧的生物。</li><li class="listitem">Marxism是一门科学。</li><li class="listitem">中医、气功、特异功能等都是伪科学。</li><li class="listitem">中国的科技水平在古代是世界领先的，只是在近代才开始落后了。</li><li class="listitem">时间是均匀流逝的、与空间和物质无关的。</li><li class="listitem">空间是连续的、三维的、平直的、无界的、与时间和物质无关的。</li></ol></div><p>
            假如您赞同以上某些观点，您将是我意料之中的读者；假如您反对以上所有观点，您将是我意料之外的知音。下面的问题或许更现实、更有趣些——
        </p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">电脑会全面超过人脑吗？</li><li class="listitem">牛顿被落地的苹果砸过吗？</li><li class="listitem">中国人的数学比美国人强吗？</li><li class="listitem">中国人为什么难以避免中式英语？</li><li class="listitem">应试教育和文理分科的弊端在哪里？</li><li class="listitem">勤定能补拙吗？</li><li class="listitem">智商测试可靠吗？</li><li class="listitem">脑筋急转弯能训练思维吗？</li><li class="listitem">如何培养幽默感？</li><li class="listitem">网络上为什么充满了“牛逼”、“傻逼”、“装逼”这类字眼？</li><li class="listitem">为什么网络争论通常是无效的？</li></ol></div><p>
            您一定会问：一篇文章有可能解决这么多的问题吗？它们之间又有什么必然的联系呢？正所谓醉翁之意不在酒，本文的重点也不在于解决这一堆难题，而在于提出一种“<span class="strong"><strong>刚柔思维</strong></span>”的观点。这当然不是在翻版国产的<span class="term">阴阳思维</span>或舶来的<span class="term">辩证思维</span>，主人的手艺再差，也不会靠炒冷饭来招待客人的。如果说后二者是看待事物的二分法——阴与阳、矛与盾，那么前者便是思维方式的二分法——刚性思维与柔性思维。换言之，后二者关注的是<span class="strong"><strong>事物的正反两面</strong></span>，而前者关注的是<span class="strong"><strong>思维的上下两层</strong></span>。
        </p><p>
            为阐明观点起见，本文将涉及多种学科和领域。如果您对某些方面知之甚少，不必望而却步。请记住，它们只是辅助论据，并非核心论题。容忍自己的无知，正是柔性思维的一种表现。如果您是某些方面的专家，慧眼识出文中谬误，也不必拂袖而去。别忘了，错误的论证并不必然导出错误的结论。容忍他人的无知，同样是柔性思维的一种表现。
        </p><p>
            本人在数学和计算机领域尚知一二，在其他领域则未知万一。以微薄之才而论天地之道、心灵之术，何啻以升量石。每念至此，不胜惶惑，情知贻笑大方事小、贻误后学事大。特于文后详列文献出处，以供读者参阅，或可稍安惴惴之心。然则如此，文中话题无不敏感，观点无不鲜明，结论无不坚定。或有人问：这般前后不一，岂非貌恭实倨、虚伪之至？其实不然。敬畏未知，怀疑已知，非此则无以广博，是为思维之柔；不惧未知，坚信已知，非此则无以精深，是为思维之刚（疑似坠入辩证法的泥淖）。思维之法，自当刚柔并济，文中要旨即在于此。欲知刚柔之义、刚柔之用，且听下回分解。
        </p><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><p>
            该文的成因可一言蔽之：久积于心，不吐不快。用计算机的语言来说：它是为防止大脑内存过载（memory overload）而将部分想法序列化为语言（serialization）、持久化为文字（persistence）最终形成的一个快照（snapshot）。之所以选择开源（open source），是希望能对他人有所裨益。除此之外，别无他意，亦无他求。为避自命高深或故作矫情之嫌，并免无谓口舌之争，后续文章将关闭评论（参见问题：为什么网络争论通常是无效的？）。用恶俗得令人倒胃的流行网语来说：本人既不牛逼，也不傻逼，更不装逼。拒不相信者，请打道回府；有心赐教者，请在此<a class="link" href="http://blog.zhenghui.org/contact/" target="_top">留言</a>；由衷赞赏者，请五星相赠（本博客支持评分机制）；鄙视不屑者，请一分示嘲（很抱歉没有提供零分或负分）。
        </p><p>
            文中“刚性思维”与“柔性思维”的提法不幸与某些文章撞车，好在只是名称上的冲突，内涵并不相同。特此说明。
        </p></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%2F2010%2F04%2F02%2Fhard-thinking-and-soft-thinking-0%2F&amp;title=%E8%AE%BA%E6%80%9D%E7%BB%B4%E7%9A%84%E5%88%9A%E6%80%A7%E4%B8%8E%E6%9F%94%E6%80%A7%EF%BC%88%E5%89%8D%E8%A8%80%EF%BC%89" id="wpa2a_26">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年07月12日 -- <a href="http://blog.zhenghui.org/2011/07/12/hard-thinking-and-soft-thinking-4/" title="论思维的刚性与柔性（科学的迷信-4）">论思维的刚性与柔性（科学的迷信-4）</a> (0)</li><li>2010年09月20日 -- <a href="http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/" title="论思维的刚性与柔性（科学的迷信-3）">论思维的刚性与柔性（科学的迷信-3）</a> (0)</li><li>2010年04月22日 -- <a href="http://blog.zhenghui.org/2010/04/22/hard-thinking-and-soft-thinking-2/" title="论思维的刚性与柔性（科学的迷信-2）">论思维的刚性与柔性（科学的迷信-2）</a> (0)</li><li>2010年04月19日 -- <a href="http://blog.zhenghui.org/2010/04/19/hard-thinking-and-soft-thinking-1/" title="论思维的刚性与柔性（科学的迷信-1）">论思维的刚性与柔性（科学的迷信-1）</a> (0)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=3yqq9L6MY9k:rQlY0PO5xmQ:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/3yqq9L6MY9k" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/04/02/hard-thinking-and-soft-thinking-0/feed/</wfw:commentRss>
		<slash:comments>44</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2010/04/02/hard-thinking-and-soft-thinking-0/</feedburner:origLink></item>
		<item>
		<title>《冒号课堂》在台湾上市</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/aTPUgMzk_ug/</link>
		<comments>http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/#comments</comments>
		<pubDate>Wed, 10 Mar 2010 03:14:53 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[上市]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=644</guid>
		<description><![CDATA[公告：《冒号课堂》今起在台湾上市 [...]]]></description>
			<content:encoded><![CDATA[
<h2><strong>公告</strong>：《冒号课堂》今起在台湾上市。</h2>
<p>&nbsp;</p>

<p><strong>基本信息</strong>：</p>
<div><img src="http://blog.zhenghui.org/img/colonclass/bookcover-tw.jpg" alt="" /></div>
<p>书名：程式設計範式與OOP的思考術：冒號老師的十三堂課</p>
<p>作者：鄭暉</p>
<p>书号：9789862013090</p>
<p>出版社：<a href="http://www.drmaster.com.tw/Bookinfo.asp?BookID=PG20288">博碩文化股份有限公司</a></p>
<p>出版日期：2010年03月10日</p>
<p>定价：NT$490</p>
<p>页码：496</p>
<p>规格：17*23</p>
<p>语言：中文/繁體</p>
<p>&nbsp;</p>

<p><strong>网站主页：</strong></p>
<p>博客：<a href="http://blog.zhenghui.org/">http://blog.zhenghui.org/</a></p>
<p>豆瓣：<a href="http://www.douban.com/subject/4031906/">http://www.douban.com/subject/4031906/</a></p>
<p>&nbsp;</p>

<p><strong>网上书店</strong>：</p>
<p>博客來網路書店：  <a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010462958">http://www.books.com.tw/exep/prod/booksfile.php?item=0010462958</a></p>
<p>誠品網路書店：<a href="http://www.eslite.com/product.aspx?pgid=1001120321928546&amp;cate=156&amp;sub=212">http://www.eslite.com/product.aspx?pgid=1001120321928546&amp;cate=156&amp;sub=212</a></p>
<p>天瓏網路書店：<a href="https://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=9789862013090&amp;sid=55495">https://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=9789862013090&amp;sid=55495</a></p>
<p>金石堂網路書店：<a href="http://books.yam.com/book/Book_Page.asp?ActId=future&amp;LID=1109&amp;KMCode=2014713210920">http://books.yam.com/book/Book_Page.asp?ActId=future&amp;LID=1109&amp;KMCode=2014713210920</a></p>
<p>金石堂網路書店：<a href="http://www.kingstone.com.tw/book/book_page.asp?LID=se008&amp;kmcode=2014713210920&amp;Actid=wise">http://www.kingstone.com.tw/book/book_page.asp?LID=se008&amp;kmcode=2014713210920&amp;Actid=wise</a></p>
<p>三民網路書店：<a href="http://www.sanmin.com.tw/page-product.asp?pf_id=99D155e9w102s87g103V70u111l129uOFfJOi1060LoB">http://www.sanmin.com.tw/page-product.asp?pf_id=99D155e9w102s87g103V70u111l129uOFfJOi1060LoB</a></p>
<p>華通書坊：<a href="http://www.huatung.com/index.php?PHP_action=view_book&amp;PHP_bookid=6qe9ml5wj9pm7qj3ck616291268305819">http://www.huatung.com/index.php?PHP_action=view_book&amp;PHP_bookid=6qe9ml5wj9pm7qj3ck616291268305819</a></p>
<p>法錸城網路書店：<a href="http://www.flytownbooks.com/itemdesc.asp?ProductID=PG20288">http://www.flytownbooks.com/itemdesc.asp?ProductID=PG20288</a></p>
<p>益大資訊：<a href="http://etaitbook08.pixnet.net/blog/post/30557874">http://etaitbook08.pixnet.net/blog/post/30557874</a></p>
<p>3dWoo電腦書店：<a href="http://www.3dwoo.com/showBookDetail.asp?nb=25377">http://www.3dwoo.com/showBookDetail.asp?nb=25377</a></p>
<p>華文網網路書店：<a href="http://www.book4u.com.tw/book_Detail.asp?goods_ser=kk0263881">http://www.book4u.com.tw/book_Detail.asp?goods_ser=kk0263881</a></p>
<p>&nbsp;</p>

<p>CP1897.com商務網上書店（香港）：<a href="http://www.cp1897.com.hk/product_info.php?BookId=9789862013090">http://www.cp1897.com.hk/product_info.php?BookId=9789862013090</a></p>
<p>&nbsp;</p>

<p><strong>各地书店</strong>：</p>
<p><a href="http://www.drmaster.com.tw/service-03.asp">http://www.drmaster.com.tw/service-03.asp</a></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2010%2F03%2F10%2Fcolonclass-sales-tw%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E5%9C%A8%E5%8F%B0%E6%B9%BE%E4%B8%8A%E5%B8%82" id="wpa2a_28">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (58)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=aTPUgMzk_ug:qQu96DpT4pU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/aTPUgMzk_ug" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/</feedburner:origLink></item>
		<item>
		<title>《冒号论坛》开放</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/CAYtrzVwMdc/</link>
		<comments>http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/#comments</comments>
		<pubDate>Fri, 01 Jan 2010 03:10:51 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[冒号论坛]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=625</guid>
		<description><![CDATA[公告：本博新开《冒号论坛》，欢迎赏光！ [...]]]></description>
			<content:encoded><![CDATA[
<h2 style="text-align: center;">致读者</h2>
<div>
<p>值此新年到来之际，本博特开辟<a href="http://bbs.zhenghui.org">冒号论坛</a>，欢迎各位赏光！</p>
<p>目前论坛上已开设一个主讨论区（forum）：<a href="http://bbs.zhenghui.org/forum/colonclass-discussion">《冒号课堂》讨论区</a>，用于<a href="http://www.douban.com/subject/4031906/">《冒号课堂——编程范式与OOP思想》</a>的作者和读者之间的交流，包括回复读者对书中的疑问、方便读者给本书提意见或建议等等。根据需要，今后也可能开设其他的讨论区。</p>
<p>稍有不便的是，您需要登录才能在该论坛回帖或开帖。好在手续极其简单，您只需在论坛上方点击<a href="http://bbs.zhenghui.org/register.php">Register</a>，填写一个用户名和email地址即可登记成功。不久您会收到一封email，里面有您的密码，凭此便可登录论坛。第一次登录后，您可以点击自己的用户名，修改密码和个人信息。如果您对某个讨论（forum）或话题（topic）感兴趣，可以订阅相应的RSS。另一个附带的好处是，该论坛与博客是集成的，登录论坛即可登录博客（反之亦然），这样在博客上回帖时就不必再输入个人信息了。如果您在登录时遇到困难，请在此帖留言。多谢您的合作！</p>
</div>
<div>
<p style="text-align: right;">郑晖</p>
<p style="text-align: right;">2010年1月1日</p>
</div>
<p>&nbsp;</p>

<div>
<p>PS:</p>
<p>由于近来广告贴过多，论坛用户登记请先到<a href="http://blog.zhenghui.org/contact/">此处</a>留言，博主会尽早答复。不便之处，还请多多见谅！</p>
</div>
<div>
<p style="text-align: right;">郑晖</p>
<p style="text-align: right;">2010年11月8日</p>
</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%2F2010%2F01%2F01%2Fcolonclass-bbs-openning%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AE%BA%E5%9D%9B%E3%80%8B%E5%BC%80%E6%94%BE" id="wpa2a_30">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (58)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=CAYtrzVwMdc:biJgwxhbAfM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/CAYtrzVwMdc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/</feedburner:origLink></item>
		<item>
		<title>《冒号课堂》意见收集</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/NlcBFfY1Uig/</link>
		<comments>http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/#comments</comments>
		<pubDate>Sat, 07 Nov 2009 07:48:18 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=549</guid>
		<description><![CDATA[征求对《冒号课堂》一书的问题、意见和建议 [...]]]></description>
			<content:encoded><![CDATA[<h2 style="text-align: center;">致读者</h2>
<div>
<p>《冒号课堂》业已<a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/">上市</a>，如果您在本书中发现任何谬误或疑问，或对本书有任何意见或建议，敬请回复此帖。小至标点符号，大至思想观点，但凡有疑，尽可畅所欲言，笔者将不胜感谢！</p>
<p>本着对读者负责的精神，若发现书中错误，本博将及时勘正；若发现不够详尽之处，本博将另行补充。</p>
</div>
<div>
<p style="text-align: right;">郑晖</p>
<p style="text-align: right;">2009年11月7日</p>
</div>
<p><strong>后记</strong>：</p>
<p>本帖已成<em>deprecated</em> API，<em>不建议</em>读者继续在此回复。对本书如有疑问或建议，请移步<a href="http://bbs.zhenghui.org/">冒号论坛</a>。</p> 
 <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%2F11%2F07%2Fcolonclass-suggestions%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E6%84%8F%E8%A7%81%E6%94%B6%E9%9B%86" id="wpa2a_32">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=NlcBFfY1Uig:fqWK0rpg3Ik:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/NlcBFfY1Uig" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/feed/</wfw:commentRss>
		<slash:comments>58</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/</feedburner:origLink></item>
		<item>
		<title>《冒号课堂》上市</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/71ofaXqnhq0/</link>
		<comments>http://blog.zhenghui.org/2009/10/26/colonclass-sales/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 03:44:41 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[上市]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=527</guid>
		<description><![CDATA[公告：《冒号课堂》已正式上市 [...]]]></description>
			<content:encoded><![CDATA[
<h2><strong>公告</strong>：《冒号课堂》已正式上市。</h2>
<p>&nbsp;</p>
<p><strong>基本信息</strong>：</p>
<div style="float: left; padding-right: 5px;"><img src="http://blog.zhenghui.org/img/colonclass/bookcover_3D_small.jpg" alt="" /></div>
<p>书名：冒号课堂——编程范式与OOP思想</p>
<p>书号：978-7-121-09545-0</p>
<p>出版社： 电子工业出版社</p>
<p>出版日期：2009年10月</p>
<p>字数：570千字</p>
<p>定价：￥65.00</p>
<p>页码：476</p>
<p>开本：16</p>
<p>&nbsp;</p>
<p><strong><span style="font-family: Georgia,&amp;quot;">网站主页：</span></strong></p>
<p>博客：<a href="http://blog.zhenghui.org/">http://blog.zhenghui.org/</a></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">豆瓣：<a href="http://www.douban.com/subject/4031906/">http://www.douban.com/subject/4031906/</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">51CTO读书频道：<a href="http://book.51cto.com/art/200910/155991.htm">http://book.51cto.com/art/200910/155991.htm</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;"><br /></span></span></p>
<p><strong>网上书店</strong>：</p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">互动网：<a href="http://www.china-pub.com/196068">http://www.china-pub.com/196068</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">当当网：<a href="http://product.dangdang.com/product.aspx?product_id=20707892">http://product.dangdang.com/product.aspx?product_id=20707892</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">卓越网：<a href="http://www.amazon.cn/detail/product.asp?prodid=bkbk992557">http://www.amazon.cn/detail/product.asp?prodid=bkbk992557</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">京东商城：<a href="http://book.360buy.com/10067468.html">http://book.360buy.com/10067468.html</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">北发图书网：<a href="http://book.beifabook.com/product/BookDetail.aspx?Plucode=712109545">http://book.beifabook.com/product/BookDetail.aspx?Plucode=712109545</a> </span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">新华书店.com：<a href="http://www.xinhuabookstore.com/product/1252467/">http://www.xinhuabookstore.com/product/1252467/</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">金桥书城：<a href="http://book.jqcq.com/product/1310853.html">http://book.jqcq.com/product/1310853.html</a> </span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">电子工业出版社：<a href="http://www.phei.com.cn/bookshop/bookinfo.asp?booktype=main&amp;bookcode=TP095450">http://www.phei.com.cn/bookshop/bookinfo.asp?booktype=main&amp;bookcode=TP095450</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">中国图书网：<a href="http://www.bookschina.com/4344485.htm">http://www.bookschina.com/4344485.htm</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">99网上书城：<a href="http://www.99read.com/product/detail.aspx?proid=541928">http://www.99read.com/product/detail.aspx?proid=541928</a><br /></span></span></p>
<p>华储网：<a href="http://www.huachu.com.cn/itbook/bookinfo.asp?lbbh=10110053">http://www.huachu.com.cn/itbook/bookinfo.asp?lbbh=10110053</a></p>
<p>新风雨：<a href="http://www.cnforyou.com/query/bookdetail1.asp?viBookCode=6625">http://www.cnforyou.com/query/bookdetail1.asp?viBookCode=6625</a></p>
<p>拓普网：<a href="http://www.toopoo.com/cmml/cmmlwb/query/query_info_det.asp?sdh=1138291">http://www.toopoo.com/cmml/cmmlwb/query/query_info_det.asp?sdh=1138291</a></p>
<p>百度有啊：<a href="http://youa.baidu.com/item/42141d9fcd0e3a9696ea5021">http://youa.baidu.com/item/42141d9fcd0e3a9696ea5021</a></p>
<p>儒商书库：<a href="http://www.sijibook.com/BookDetails.aspx?bookid=135505">http://www.sijibook.com/BookDetails.aspx?bookid=135505</a></p>
<p>金书网：<a href="http://www.golden-book.com/booksinfo/12/1200741.html">http://www.golden-book.com/booksinfo/12/1200741.html</a></p>
<p>蔚蓝网：<a href="http://www.wl.cn/5951937">http://www.wl.cn/5951937</a></p>
<p>中教图书：<a href="http://www.book1993.com/pro-821338.html">http://www.book1993.com/pro-821338.html</a></p>
<p>ANI图书网：<a href="http://www.anibook.cn/ProductDetail/2009/10/27/PBA97871210954500001.html">http://www.anibook.cn/ProductDetail/2009/10/27/PBA97871210954500001.html</a></p>
<p>希望书店：<a href="http://www.hopebook.net/tushumulu/301117/">http://www.hopebook.net/tushumulu/301117/</a></p>
<p>拍拍网：<a href="http://auction1.paipai.com/2419A70400000000002438DF0568A984">http://auction1.paipai.com/2419A70400000000002438DF0568A984</a></p>
<p>天下书城：<a href="http://book110.cn/bookshow/200911/442565/">http://book110.cn/bookshow/200911/442565/</a></p>
<p>广购中心：<a href="http://www.gzbookcenter.com/search/view.jsp?proID=1214051">http://www.gzbookcenter.com/search/view.jsp?proID=1214051</a></p>
<p>&nbsp;</p>
<p><strong>全国各地书店有售</strong></p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px;">
<table width="100%" border="0" cellspacing="1" cellpadding="1">
<tbody>
<tr>
<td valign="bottom" bgcolor="#fbf9f5" height="25"><strong>字数：</strong>570千字</td>
<td valign="bottom" bgcolor="#fbf9f5" width="21%" height="25"></td>
<td valign="bottom" bgcolor="#fbf9f5" width="36%"><strong>定价：</strong>￥65.00元</td>
</tr>
<tr>
<td align="center" valign="top"></td>
<td valign="bottom" bgcolor="#fbf9f5" width="19%" height="25"><strong>页码：</strong>476</td>
<td valign="bottom" bgcolor="#fbf9f5"></td>
<td valign="bottom" bgcolor="#fbf9f5"><strong><span style="color: #ff0000;">会员价：</span></strong><span style="color: #ff0000;">￥52.00元</span></td>
</tr>
<tr>
<td align="center" valign="top"></td>
<td valign="bottom" bgcolor="#fbf9f5" height="25"><strong>开本：</strong>16</td>
</tr>
</tbody></table>
</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%2F10%2F26%2Fcolonclass-sales%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E4%B8%8A%E5%B8%82" id="wpa2a_34">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (58)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=71ofaXqnhq0:FjSIzmg8w2E:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/71ofaXqnhq0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/26/colonclass-sales/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2009/10/26/colonclass-sales/</feedburner:origLink></item>
		<item>
		<title>冒号课堂§10.2：抽象类型</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/UGNoe35T114/</link>
		<comments>http://blog.zhenghui.org/2009/10/24/colon-class-10_2/#comments</comments>
		<pubDate>Sat, 24 Oct 2009 01:37:53 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[mixin]]></category>
		<category><![CDATA[trait]]></category>
		<category><![CDATA[抽象数据类型]]></category>
		<category><![CDATA[抽象类]]></category>
		<category><![CDATA[抽象类型]]></category>
		<category><![CDATA[接口]]></category>
		<category><![CDATA[继承]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=518</guid>
		<description><![CDATA[<b>抽象类型</b>——实中之虚（<em>介绍抽象类型的种类、意义及其用法</em>）<br/> • 浅显的比方只是门槛前的台阶，借之或可拾级入门，却无法登堂入室<br/> • 具体类型是创建对象的模板，抽象类型是创建类型的模块<br/> • 抽象数据类型的核心是数据抽象，而抽象类型的核心是多态抽象<br/> • 必先以术养道，而后以道御术<br/> • 以社会身份而非个人身份作为公民之间联系的纽带，正是针对接口而非实现来编程的社会现实版<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="id603878"></a>抽象类型——实中之虚</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>
                    个体身份对应的规范抽象借助封装，以数据抽象的形式出现
                </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>接口是为了克服（Java或C#中）抽象类不能多重继承的缺点吗？</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>
            冒号调整了焦点：“鉴于目前专注的范式是OOP，参数多态最好放在以后的GP专题再作探讨。除非特别说明，下面提到的多态专指子类型多态。谈到这类多态，就不得不提及抽象类型。谁来说说，究竟什么是抽象类型？”
        </p><p>
            冒号抬手内扬，摆出了对练的姿势。
        </p><p>
            叹号率先抢攻：“抽象类型指的是至少含有一个抽象方法的类型。”
        </p><p>
            冒号轻松化解：“在C++中这句话尚可勉强成立，但在Java和C#中则大不尽然：一个类即使没有一个抽象方法也可以被申明为抽象的；一个没有任何成员的空接口或称<span class="term">标记接口</span>同样属于抽象类型。”
        </p><p>
            “抽象类型是指无法实例化的类型。”逗号发起二次进攻。
        </p><p>
            冒号见招拆招：“Java中的Math类也不能实例化，原因是它只有private构造器，并且没有一个能返回实例的静态方法。C#中的Math类是静态类，同样不能实例化。”
        </p><p>
            问号纵身而上：“抽象类型指能且只能通过继承来实例化的类型。Math类是final类，无法被继承。最主要的是，它的价值体现在它的静态方法上，压根儿就没有实例化的必要。”
        </p><p>
            冒号借力反打：“为什么要强调无法实例化呢？”
        </p><p>
            引号一旁助攻：“一个抽象类型代表着一个抽象概念，而抽象概念自然是无法具化的。比如你无法实例化抽象的形状，但可以实例化长方形、三角形等具体的形状；无法实例化抽象的水果，但可以实例化苹果、桔子等具体的水果。”
        </p><p>
            “很官方的说法。这就好比将继承关系说成‘is-a’关系一样，理论上虽通俗易懂，实践上却不足为训。”冒号收起架势，“要说抽象，Java和C#中的Object类可谓包罗万象，该够抽象了吧？不照样实例化？<span class="term">列表</span>（list）与<span class="term">映射</span>（map）是抽象的还是具体的？在C++中它们是具体类型，而在Java和C#中它们却是抽象类型<a class="link" href="#note1"><sup>[1]</sup></a>。这又是为什么？”
        </p><p>
            一连串的反问让大家陷入沉思。
        </p><p>
            “相比其他编程范式，OOP更贴合客观世界，人们经常用打比方的形式来描述和理解OOP的一些概念和思想。这本身并无不妥，但一定要保持清醒的头脑：浅显的比方只是门槛前的台阶，借之或可拾级入门，却无法登堂入室。”冒号谆戒道，“天下之理皆同，天下之人皆同，故凡学问殿堂之前皆一般景象：入门者众，入室者寡。本班的目的便是，引导诸位从徘徊于编程之门左右的人群中越众而出，早达内室。”
        </p><p>
            “那就成了传说中的内室弟子吧？大伙在门边转悠很久了，头都发晕了，师父还是快些领我等入室吧。” 逗号近乎戏谑地恳求。
        </p><p>
            冒号一笑：“我可算不得你们的师父，只不过是个闻道在先的师兄而已。”
        </p><p>
            一直没有出手的句号忽然开腔：“抽象是个相对概念，一个类型是否是抽象的完全取决于设计者对它的角色定位。如果想用它来创建对象，它就是可实例化的具体类型；如果想用它来作为其他类型的基类，它就是不可实例化的抽象类型。”
        </p><p>
            “这才击中了要害！”冒号不禁喝彩道，“整理一下你的观点：<span class="strong"><strong>具体类型是创建对象的模板，抽象类型是创建类型的模块</strong></span>。一个是为对象服务的，一个是为类型服务的。显然，后者的抽象性正是源自其服务对象的抽象性。就拿刚才的实例来说，模板方法模式中的Authenticator类是抽象的，是为创建子类型SimpleAuthenticator、Sha1Authenticator等服务的；策略模式中的Authenticator类是具体的，是为创建对象服务的，但它合成的两个接口KeyValueKeeper和Encrypter又是为创建算法类型服务的。值得注意的是，不要把抽象类型与抽象数据类型（ADT）混为一谈，后者的抽象指的是类型的接口不依赖其实现。或者说，<span class="strong"><strong>抽象数据类型的核心是数据抽象，而抽象类型<a class="link" href="#note2"><sup>[2]</sup></a>的核心是多态抽象</strong></span>。”
        </p><p>
            问号想让概念更明确些：“抽象类型就只有<span class="term">接口</span>（interface）和<span class="term">抽象类</span>（abstract class）两种吗？”
        </p><p>
            “在Java和C#中基本上是这样，但在C++中这两种类型没有显式的区别<a class="link" href="#note3"><sup>[3]</sup></a>。”冒号，“此外，动态OOP语言如Ruby、Python、Perl、Scala、Smalltalk等还至少支持mixin和trait中的一种类型。mixin直译为‘混入’，trait直译为‘特质’，为避免翻译上的问题，今后我们还是采用英文术语。这两种类型大同小异，为简便起见，下面以mixin类型为代表<a class="link" href="#note4"><sup>[4]</sup></a>。它们的出现是为了弥补接口与抽象类的一些不足，更好地实现代码重用。我们知道，接口的主要目的是创建多态类型，本身不含任何实现。子类型通过接口继承只能让代码被重用，却无法重用超类型的实现代码。抽象类可以重用代码，可又有多重继承的问题。Java和C#不支持这种机制，C++虽支持但有不少弊端。”
        </p><p>
            引号奇道：“这个问题上节课不是已经解决了吗？用合成来代替继承啊。”
        </p><p>
            冒号解释：“合成是一种解决办法，但也不是没有缺陷。首先，合成的用法不如继承那么简便优雅，这也是许多人喜欢用继承的主要原因；其次，合成不能产生子类型，而有时这正是设计者所需要的；再次，合成无法覆盖基础类的方法，也无法访问它的protected成员；最后，却可能是最大的缺点是：合成的基础类只能是具体类型，不能是抽象类型<a class="link" href="#note5"><sup>[5]</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>，倾向它包含尽可能少的功能。”冒号擘肌分理，“妥协的结果是，一个新类型往往只用到基础类型的部分功能，却可能受到其他功能变动的影响。虽然这种影响在良好的封装之下会大大削弱，但也难以完全消弭。”
        </p><p>
            句号思索片刻，已明其意：“换句话说，以具体类型为代码重用的基本单位，难免颗粒度过大？”
        </p><p>
            “然也！”冒号的手在空中挽了个花，“其实作为抽象类型的接口也有类似的尴尬：对它的客户类来说，它承诺的服务是多多益善；对它的实现类来说，承诺越多负担却越重。如果能有这样一种可重用的模块，既不像具体类型那样面面俱到，又不像接口那样有名无实，也没有抽象类的多重继承之弊，岂不妙哉？”
        </p><p>
            “想必就是mixin了！”叹号眼中闪过一道光芒，旋即又暗淡下来，“只可惜Java并不支持啊。”。
        </p><p>
            “Java不支持就没兴趣了？” 冒号听出他的话里有话，“要成为优秀的程序员，千万不能画地为牢、自我禁锢。始终要保持一颗开放的心，不要拘于某些语言或范式，也不要囿于某些概念或技术。”
        </p><p>
            叹号的耳根有点发热。
        </p><p>
            “陌生的理论和技术开始总是拒人千里，不过一旦你了解其问题来源，它们会慢慢变得和蔼可亲起来。”冒号循循善诱，“既然具体类型和现存的两种抽象类型均有不足之处，mixin的产生便合情合理了。它是具体类型与接口类型的一种折衷，既可有抽象方法，也可有具体方法。这一点类似抽象类，但又没有抽象类的多重继承问题。举例来说，Ruby中的Comparable就是一个简单却很典型的mixin。”
        </p><p>
            问号插话：“Java中也有Comparable接口啊。”
        </p><p>
            冒号道出其中差异：“Java中的Comparable和C#中的IComparable只有一个抽象的比较方法，而Ruby中的除了有类似的抽象方法——比较（&lt;=&gt;）之外，还提供了小于（&lt;）、小于等于（&lt;=）、等于（==）、大于（&gt;）、大于等于（&gt;=）和介于（between?）等六种具体方法。显而易见，多出的方法均可通过唯一抽象的比较方法来实现。”
        </p><p>
            引号一点即通：“如此一来，重用Comparable的类只需实现一个抽象方法，便可自动拥有另外六个有用的功能。这既满足了客户类的需求，又不增加实现类的负担。”
        </p><p>
            “买一送六，这买卖划算！”逗号来劲了。
        </p><p>
            冒号双眼微眯：“更划算的买卖是Ruby中Enumerable。任何包含该mixin的类只要实现一个遍历方法each，便可免费得到二十多个有关遍历和搜寻的方法。如果再实现比较方法&lt;=&gt;，还可获赠排序和最值方法。相比Java中Enumeration和Iterator接口，优势历然。”
        </p><p>
            问号很好奇：“为什么称为mixin呢？”
        </p><p>
            冒号述说由来：“冰淇淋中经常会掺混一些薄荷、香草、巧克力之类的调味料和花生、坚果之类的小零碎，人们管它们叫mix-in。后来被借用来表示一种抽象类型，主要有如下特点：一、抽象性和依赖性：本身没有独立存在的意义，必须融入主体类型才能发挥作用；二、实用性和可重用性：不仅提供接口，还提供部分实现；三、专一性和细粒度性：提供的接口职责明确而单一；四、可选性和边缘性：为主体类型提供非核心的辅助功能。”
        </p><p>
            “这些特点与风味添加料还真的颇为神似。”叹号想着想着，嘴里不自觉地咂摸了一下。
        </p><p>
            “虽然C++、Java和C#在语法上尚不支持mixin，但C++可通过多重继承、Java和C#可通过合成和接口来分别模拟mixin。不仅如此，借助<span class="term">切面式编程</span>（AOP），Java和C#甚至可完全实现mixin；借助<span class="term">泛型式编程</span>（GP），C++也能通过<span class="term">模板</span>更好地实现mixin<a class="link" href="#note6"><sup>[6]</sup></a>。”冒号点到为止，“就此我们重温前面提到的两个观点。一是编程范式之间的合作性：mixin属于OOP的范畴，但其他编程范式如切面式、泛型式以及二者背后的元编程都能与之相通；二是设计与语言的相关性：C++、Java和C#以及其他诸如Ruby、Python等动态语言对mixin有着不同的支持方式，这在一定程度上会影响系统的OOP设计。”
        </p><p>
            引号憧憬道：“语言是在发展的，说不定哪天Java也会支持mixin的。”
        </p><p>
            冒号以实相应：“Java的动态小兄弟Groovy在1.6版已经开始支持mixin ，而C#3.0也新引入了对mixin更友好的语法特性<a class="link" href="#note7"><sup>[7]</sup></a>。”
        </p><p>
            逗号提了一个长期困惑大家的问题：“每当一个新技术出现，我就觉得很矛盾：不追怕落伍，追吧又怕落空。如何判断一个它是昙花一现，还是大势所趋呢？”
        </p><p>
            “任何技术都是在赞美与批判中成长起来的，预测它们是流星还是恒星绝非易事。就拿OOP来说，上个世纪六十年代就出现了支持OOP的语言<a class="link" href="#note8"><sup>[8]</sup></a>，但直到九十年代中后期它才真正成为主流的编程范式。这段时间恐怕比大多数人的程序员生涯还长吧。再说mixin，其实并非今日的重点，介绍它的目的不是盲目追新，而是希望透过其背后的需求驱动点，重新审视现有技术。至于它今后会不会为主流语言所接纳，反倒不是那么重要了。如果一定要我给个建议，那就是八个字：‘<span class="strong"><strong>不执一法，不舍一法</strong></span>’。”冒号以禅语作答，“软件技术这棵大树经过多年的快速成长，早已枝蔓丛生。欲臻不执不舍之境，当如开班导言中所说：究其根本以知过去，握其主干以知现在，察其生长点以知未来。我之所以倾向于用抽象的方式来谈论技术，正是因为抽象的东西更接近根、更接近干、更接近生长点，从而更普泛深刻，也更稳定持久。”
        </p><p>
            句号借机问道：“您认为抽象比具体更重要？”
        </p><p>
            “抽象与具体无所谓孰高孰低，它们只是功用不同而已。”冒号轻轻晃了晃脑袋，“正所谓：<span class="strong"><strong>必先以术养道，而后以道御术</strong></span>。也就是说，在学习时应注重从具体知识中领悟抽象思想，在应用时应注重用抽象理论来指导具体实践。类似地，软件开发也是如此：从具体需求中构建出抽象模型，再根据抽象模型来完成具体实现。因此，在设计阶段抽象类型尤为关键，而在实现阶段则是具体类型更为重要。”
        </p><p>
            问号表示理解：“假如从具体需求直接跨到具体实现，省去中间的抽象建模过程，那还用得着架构师和分析师吗？”
        </p><p>
            “话虽不错，但疑似倒果为因。”冒号洞若观火，“是否有必要抽象建模，关键看项目需求。如果需求简单而稳定，一步到位又何尝不可？甚至软件的开发效率和运行效率还更高——为劈几根细柴而磨刀，值吗？如果需求复杂而多变，引入抽象方有‘磨刀不误砍柴工’之效。毕竟抽象不是目的而是手段，对它片面的追求反会导致过度的设计。”
        </p><p>
            众人这才发现，给老冒戴顶“抽象派”的帽子是有些冤枉他了，应该是“抽象现实派”的。
        </p><p>
            冒号续道：“为进一步认识抽象类型，我举个非常实用的例子。它只适用于C++，而不适用于Java和C#。如果你对这一点感到遗憾的话，不要忘记我们的原则：具体实例永远是为抽象思想服务的。”
        </p><p>
            幻灯一闪，现出一段C++代码——
        </p><div class="informalexample"><pre class="programlisting">
/** 一个不可复制的类 */
class NonCopyable
{
protected:
    // 非公有构造函数防止创建对象
    NonCopyable() {}  
    // 非公有非虚析构函数建议子类非公有继承
    ~NonCopyable() {}
private: 
    // 私有复制构造函数防止直接的显式复制和通过参数传递的隐式复制
    NonCopyable(const NonCopyable&amp;);
    // 私有赋值运算符防止通过赋值来复制
    const NonCopyable&amp; operator=(const NonCopyable&amp;); // copy assignment
};

/** NonCopyable的一个私有继承类 */
class SingleCopy : private NonCopyable {};

/** 测试代码 */
int main()
{
    SingleCopy singleCopy1;
    SingleCopy copy(singleCopy1); // 编译器报错：企图复制singleCopy1

    SingleCopy singleCopy2;
    singleCopy2 = singleCopy1; // 编译器报错：企图复制singleCopy1
    return 0;
}</pre></div><p>
            冒号讲解道：“有些对象是不希望被复制的。比如一些代表网络连接、数据库连接的资源对象，它们的复制要么意义不大，要么实现困难。由于C++的编译器为每个类提供了默认的<span class="term">复制构造函数</span>（copy constructor）和<span class="term">赋值运算符</span>（assignment operator），要想阻止对象的复制，通常做法是将这两个函数私有化。引入NonCopyable后，它的任何子类将自动拥有不可复制的特性。这样为开发者节省了代码编写量，还免掉了相应的文档说明，使用者也一望而知其意，可说是一石三鸟。虽然NonCopyable从语法上说不是抽象类，但从本质上看是一种类似mixin功能的抽象类型。”
        </p><p>
            引号考量一番后说道：“单就它的功效而言，的确非常符合mixin的四大特点，只是它的子类用的是私有继承，而不是类继承或接口继承。”
        </p><p>
            “你说得很对。可问题是，我们并没有要求mixin或者trait一定要通过继承的方式来重用啊？事实上，有些mixin甚至可在运行期间产生，还能克服继承的静态缺陷。即使采用继承，一般也不满足‘is-a’关系。你总不能说草莓冰淇淋是一种草莓吧？”冒号淡淡地说，“先前你们总结出抽象类型有两个特征：需要继承和无法实例化，但它们并非本质，关键还是它的目的——为类型服务。提供可被继承的超类型只是一种服务方式，却非唯一的方式；无法实例化只因它不是为对象服务的，禁止实例化不过是语法上的加强，目的是让用户在编译期间就能发现用法错误。其实，即便NonCopyable类的构造函数是公有的，也不会有人去实例化。原因很简单，它的价值只有通过子类才能体现，这是由其抽象的本性所决定的。”
        </p><p>
            逗号有些奇怪：“为什么在Java中就没有类似的对象复制问题呢？”
        </p><p>
            “这是一个非常基础的问题，请容我下次再回答你。”冒号破天荒地没有立即解疑，“以下重点还是放在接口和抽象类上面，我们称之为基本抽象类型，以别于mixin、trait等其他抽象类型。我们先从语法上简单地对比一下这两种类型。”
        </p><p>
            屏幕上显示出一张表格（如表10-1所示）——
        </p><div class="table"><a name="id604285"></a><p class="title"><b>表 10-1. Java/C#的抽象类与接口在语法上的区别</b></p><div class="table-contents"><table summary="Java/C#的抽象类与接口在语法上的区别" border="1"><colgroup><col><col><col></colgroup><thead><tr><th> </th><th>抽象类</th><th>接口</th></tr></thead><tbody><tr><td>提供实现代码</td><td>能</td><td>否</td></tr><tr><td>多重继承</td><td>否</td><td>能</td></tr><tr><td>拥有非public成员</td><td>能</td><td>否</td></tr><tr><td>拥有域成员</td><td>能</td><td>否（Java中的static final域成员除外）</td></tr><tr><td>拥有static成员</td><td>能</td><td>否（Java中的static final域成员除外）</td></tr><tr><td>拥有非abstract方法成员</td><td>能</td><td>否</td></tr><tr><td>方法成员的默认修饰符</td><td>无</td><td>public abstract（Java：可选；C#：不能含有任何修饰符）</td></tr><tr><td>域成员的默认修饰符</td><td>无</td><td>Java：public static final；C#：不允许域成员</td></tr></tbody></table></div></div><br class="table-break"><p>
            冒号简明扼要地总结：“C#的语法与Java的稍有不同，但二者在接口与抽象类的关键区别上还是一致的：接口不能提供实现但能多重继承，抽象类则正相反；接口只能包含<span class="emphasis"><em>公有</em></span>的、<span class="emphasis"><em>非静态</em></span>的、<span class="emphasis"><em>抽象</em></span>的方法成员<a class="link" href="#note9"><sup>[9]</sup></a>，抽象类则无此限制。”
        </p><p>
            问号言明难处：“从语法上区分它们并不难，难的是从设计上区分它们。”
        </p><p>
            逗号实话实说：“按照上节课‘提倡接口继承，慎用实现继承’的方针，应该倾向用接口而非抽象类。但总觉得接口太虚了，没有抽象类实在。”
        </p><p>
            引号反驳：“要说实在，具体类型更实在啊。”
        </p><p>
            叹号坦言：“在编程中经常需要用到标准的或第三方的类库，可查起API来经常是左一个接口右一个接口的，迟迟不见具体类型现身，心里哪个急啊！”
        </p><p>
            冒号打了个比方：“如果到包子铺买包子，作为客户你也许会认为包子是具体类型，但对提供包子的人来说它却是抽象类型。他一定会问你：是要肉包、菜包还是豆沙包？是要蒸包、煎包还是小笼包？他的铺子开得越专业，给你出的选择题越多，众口难调嘛。同样道理， 要建一个高度可重用的类库，一些接口是必不可少的。”
        </p><p>
            句号悟道：“接口的意义就在于：提供者不是擅作主张，而是推迟决定，让客户选择实现方式。”
        </p><p>
            “言之有理！类似地，抽象类的意义就在于：父类推迟决定，让子类选择实现方式。‘推迟’二字道出了抽象类型除创建类型之外的另一功用：<span class="strong"><strong>提供动态节点</strong></span>。如果是具体类型，节点已经固定，没有太多变化的余地<a class="link" href="#note10"><sup>[10]</sup></a>。反过来，要使节点动态化，一般通过多态来实现。由此，抽象类型常常与多态机制形影不离。”冒号稍加引申，“就说前面的验证类吧，用模板方法模式实现的Authenticator类将关键的方法交给子类SimpleAuthenticator或Sha1Authenticator处理，用策略模式实现的Authenticator类将关键的方法交给内嵌接口KeyValueKeeper和Encrypter的实现类处理。后者的两次接口继承比前者的一次实现继承多了一个动态节点，因而更加灵活。这也是为什么一个需要（M×N）个实现类，一个只要（M+N）个的原因。当然，这也不是完全没有代价的。比如要创建一个用SHA-1算法加密的验证类实例，两种方法对比如下——”
        </p><div class="informalexample"><pre class="programlisting">
模板方法模式：new Sha1Authenticator()
策略模式：    new Authenticator(new MemoryKeeper(), new Sha1Encrypter())
</pre></div><p>
            冒号指点着黑板：“显然，后者无论是使用上还是性能上都比前者稍有不如。但权衡利弊，多数时候它仍是更好的选择。” 
        </p><p>
            “包子铺的包子用料种类越多、做法越多，买一个包子越费事。但只要不到饿得发昏的地步，大家还是更喜欢花样更多的包子铺。看来我也不该再抱怨类库的接口过多了。”叹号心下释然。
        </p><p>
            “大家再看看这个电脑主板，开过机箱攒过机的人应该对它并不陌生。”冒号终于亮出了蓄藏已久的道具， “上面密密麻麻地布满了各种元件，那是它的实部，而我们关注的是它的虚部——各种插槽和接口，包括CPU插槽、内存插槽、PCI插槽、AGP插槽、ATA接口、PS/2接口、USB接口以及其他林林总总的扩展插槽等等。这些接口的存在，使得主板与CPU、内存条、外围设备以及扩展卡等不必硬性焊接在一起，大大增强了电脑主机的可定制性。”
        </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>
            “比喻非常到位！” 冒号很满意，“不过准确地说，与接口类型对应的不是物理接口，而是<span class="strong"><strong>接口规范</strong></span>。如果仅仅是物理接口，只能保证该接口适用于某种特定型号的硬件产品，却不能保证同时适用于其他型号或者其他类型的硬件。以大家熟悉的USB（Universal Serial Bus）接口为例，它能接入各种外部设备，包括鼠标、键盘、打印机、外置硬盘、闪存和形形色色的数码产品。这当然不是偶然的，因为所有厂家在生产这些硬件时均遵循了相同的业界标准——USB协议规范。换言之，任何一个与USB接口兼容的设备，都可看作是实现了此接口的具体类型，而主机对该设备的自动识别能力则可看作一种多态机制。”
        </p><p>
            “这下我更深刻地理解那句话了：接口继承不是为了重用，而是为了被重用。”句号品味道，“比如一个鼠标，可以有串行接口、PS/2接口、USB接口或者无线接口，还可以同时拥有多个不同类型的接口。无论怎样，它本身都是完整的产品，根本不需要重用主机上的其他硬件，它实现某些接口的目的完全是为了能被主机所用。”
        </p><p>
            逗号意识到：“看样子，硬件设计也需要OOP思想呢。”
        </p><p>
            “相比软件设计师，硬件设计师往往能更好地贯彻OOP的理念。”冒号加强了语气，“他们的对象化概念更清晰更自然，因为硬件模块比软件模块更实在更具体；他们更注重设计，因为硬件比软件的修改成本大得多；他们更注重设计重用，因为硬件重新发明轮子的成本普遍很高；他们更注重实现重用，因为无法在举手之间完成‘复制-粘贴’工作；他们更注重接口明确、封装完好，因为把内部的接口或结构暴露在外不仅难看，还容易带来缠绕、磨损、短路等问题；他们采用合成和接口来组装模块，因为硬件没有类似实现继承的机制。”
        </p><p>
            “看起来我们真得向硬件设计师取经了。”叹号有些信服了。
        </p><p>
            冒号旧话重提：“我们曾对OOP有过这样的描述：如果把OOP系统看作民主制社会，每个对象是独立而平等的公民，那么封装使得公民拥有个体身份，继承使得公民拥有家庭身份，多态使得公民拥有社会身份。补充一下，其中的继承主要指类继承，多态主要指接口继承带来的多态。经过这段时间的学习，大家对此有何见解？”
        </p><p>
            问号发表看法：“广义封装让每个类成为独立的模块，从而让每个对象具备了个体身份。狭义封装又进一步地把类的接口与实现分离，从而让每个对象具有显著的外在行为和隐藏的内在特性。继承机制可使一个类成为其他类的子类或父类，从而确立了对象在类型家族中的身份。至于多态嘛，嗯。。。”
        </p><p>
            问号努力想抓住若隐若现的头绪。
        </p><p>
            句号接过话头：“一个公民的社会身份是指他在社会中所处的地位和扮演的角色。比如，一个人在学校里是学生，在公司里是职员，在商店里是顾客，他真正的个体身份往往是被掩盖的。同样地，一个对象在与外界联系时，通常不以其实际类型的身份出现，而是在不同的场合下以不同的抽象类型的身份出现。我想，这大概就是多态带来的社会身份吧。”
        </p><p>
            “这种社会身份的意义何在？”冒号不动声色地问。
        </p><p>
            句号接着回答：“社会身份既是一种资格也是一种义务。比如在列车上有人得了急病，可以通过广播找医生。人们不用事先知道来者的具体个人身份，只要他是医生，就会放心地让他第一时间去救人。”
        </p><p>
            “这个比喻很恰当。”冒号赞道，“不用<span class="emphasis"><em>事先</em></span>知道个人身份，不正说明广播呼叫的对象是一个多态的抽象类型吗？同理，当一个具体类型显式继承了一个接口，它的对象便拥有了个体身份之外社会身份：有资格以该接口的形式与外界打交道，也有义务履行该接口的职责。”
        </p><p>
            “咦，那为什么把社会身份归功于多态而不是继承呢？”问号发出疑问。
        </p><p>
            冒号释疑：“继承自然有功劳，毕竟子类型多态要建立在它的基础上。但如果没有多态机制，要确保一个对象的实际方法而不是其超类型的方法被调用，必须将其还原为具体类型，从而使社会身份变得几乎有名无实。”
        </p><p>
            问号憬然醒悟。
        </p><p>
            冒号继续深入：“对象每多一种社会身份，便多一条与外界交流的渠道。为什么遮遮掩掩地不肯以本来面目示人呢？非是羞于见人，盖因一般的具体类型在公共场合是不为人知的，只有少数核心库里的核心类是例外。即使侥幸被认识，也难被认可，因为那会以代码的复杂度和耦合度为代价。社会身份则不然，它远比一般的个体身份更容易被接受。”
        </p><p>
            逗号举出例证：“这就好比上课得有学生证，上班得有工作证，上火车得有火车票，上飞机得有登机牌。只要不是炙手可热的公众人物，很多场合都是认牌认证不认人的。”
        </p><p>
            “道理人人都懂，可总有不少人以为自己编写的类都是明星大腕，大有‘天下谁人不识我’的豪迈，无牌无证就敢到处乱窜。更有甚者，不用多态就算了，连封装也不要，简直是在裸奔嘛。”冒号揶揄道。
        </p><p>
            全班笑不可仰。
        </p><p>
            冒号恢复肃容：“谈到这里，我们不能不再次提到‘针对接口编程’的基本原则。它有一种建立于数据抽象之上的形式，能让用户只关心抽象数据类型的API接口而无视其具体实现。不过，它至少有两大局限。其一，虽然在接口不变的情况下，实现代码的改变不会影响客户代码，但仍需要重新编译，对于需要头文件的C++来说则需要更多的编译链接时间。其二，虽然相同的接口可以有多种实现方法，但它们不能同时并存，更无法动态切换。于是，另一种建立于多态抽象之上的形式应运而生。它把抽象数据类型隐藏在抽象类型的背后，从而提升了抽象接口。同一个抽象接口允许有多种实现并存，且能动态切换，新增、删除或修改某种实现也不会导致其他代码的修改或重新编译。方才我们从主体类的角度来看，它的对象尽量以社会身份参与社会活动；现在再从客户类的角度看，它会尽量召集有社会身份的对象。两相结合，以社会身份而非个人身份作为公民之间联系的纽带，正是针对接口而非实现来编程的<span class="strong"><strong>社会现实版</strong></span>。”
        </p><p>
            问号有所顾虑：“可是，有不少具体类型并没有实现任何接口，也就没有社会身份。”
        </p><p>
            “排除设计不良的因素，没有抽象超类型的具体类型最常见的有两种可能。一种是与世隔绝，一辈子几乎足不出户，至多在小圈子里活动。典型的有非公有类、内部类、局部类等等。一种是名满天下，他的脸就是一张天然名片，他的个人身份也就是社会身份。典型的有基本数据类型、字符串类型、日期类型等通用数据类型以及特定领域的通用数据类型。可见，个人身份与社会身份并无绝对的界限。同样，家庭身份与社会身份也有交合之处，正如名门望族也可成为社会身份一样。典型的有Java IO库中的InputStream和OutputStream、Reader和Writer，以及UI库中的Component和JComponent等等。”冒号信手拈来，“因此我们谈到的社会身份，不必拘泥于接口，甚至不必限于抽象类型，关键是该类型是否具备了足够的<span class="strong"><strong>通用性和规范性、稳定性和独立性、灵活性和专业性</strong></span>。还是应了那句话：抽象不是目的而是手段。再拿现实社会说事，每种社会身份都代表了个体与社会缔结的一种契约，它有如下的特点：<span class="emphasis"><em>独立而稳定</em></span>——先于个体而存在，且不随个体的变化而变化；<span class="emphasis"><em>公开而权威</em></span>——为人所知、为人所信；<span class="emphasis"><em>规范而开放</em></span>——制定的协议标准明确，且允许个体在遵守协议的前提下百花齐放。毫无疑问，推行契约制将使社会大受其惠。首先，相同身份的个体可相互替换、新型个体可随时加入，而且不会影响整体框架和流程，保证了系统的灵活性和扩展性。其次，整体不因某一个体的变故而受冲击，保证了系统的稳定性和可靠性；最后，个体角色清晰、分工明确，保证了系统的规范性和可读性。”
        </p><p>
            引号非常注重概念：“社会身份所代表的契约对应的正是<span class="term">规范抽象</span>吧。”
        </p><p>
            “每种身份都是规范抽象的结果。” 冒号推而广之，“具体地说，个体身份对应的规范抽象借助封装，以<span class="term">数据抽象</span>（data abstraction）的形式出现；家庭身份对应的规范抽象借助继承，以<span class="term">类型层级</span>（type hierarchy）的形式出现；社会身份对应的规范抽象借助多态，以<span class="term">多态抽象</span>（polymorphic abstraction）的形式出现。至此，我们分别从行为和规范两个角度分别诠释了OOP的三大特征与公民的三大身份之间的关系。这也非常合乎情理：一个合理设计和实现的类，其对象的行为与规范本应保持一致。”
        </p><p>
            句号欲印证自己的想法：“我的理解是，接口是一个携带契约的角色标签，接口继承的作用就是<span class="emphasis"><em>静态地</em></span>为对象贴上该标签，而多态机制的作用就是<span class="emphasis"><em>动态地</em></span>让对象发挥该角色。因此，要赋予对象某个角色，就应该让相应的类去继承相应的接口。”
        </p><p>
            “你的前半部分表述得非常精当，后半部分则稍有瑕疵。”冒号评论道，“接口可用来代表角色，但角色却不一定要通过接口。正如你提到的，接口继承是静态的，而角色却可能是动态的。比如学生毕业后变成职员，职员升迁后变成经理等等。对于静态类型语言来说，这类问题的解决单靠接口继承是不够的，还需要利用合成等手段，或者利用前面提到的其他抽象类型如mixin或trait。”
        </p><p>
            叹号仍有疑惑：“接口的意义已经很清楚了，那抽象类呢？它们的区别真的很大吗？”
        </p><p>
            “我们已经从语法上比较了它们的区别，那些只是表象的东西。如果对语言规则的理解仅仅停留于语法层面，那么它更多体现为<span class="strong"><strong>对实现的束缚</strong></span>。只有提升到语义层面，它才更多体现为<span class="strong"><strong>对设计的保障</strong></span>。”冒号保持一贯高举高打的风格，“从语义上看，抽象类与接口的区别，并不比它与具体类的区别小多少。”
        </p><p>
            叹号错愕不已：“怎么可能？抽象类与接口好歹都是抽象类型啊。”
        </p><p>
            冒号反诘：“为什么不说抽象类与具体类好歹都是类呢？”
        </p><p>
            叹号一时无语。
        </p><p>
            “先看段历史吧。”冒号幽幽地说，“开始C++是没有抽象类型的，直到1989年C++ Release 2.0发布前的最后一刻，Bjarne Stroustrup才力排众议引入抽象类。从C++的前身C with Classes 开始算起，其间已经整整十年了。即便如此，它的意义在当时仍不为大多数人所认识。推出一个看似小小的语法特征竟会如此艰难，恐怕远远超出诸位的想象吧！有人幻想只通过看语法书就能完全领会语言的精髓，又与痴人说梦何异？”
        </p><p>
            冒号的声音渐渐激昂起来。
        </p><p>
            逗号为自己找到了安慰：“难怪当初学到抽象类时，总感到只知其意而不知其用。”
        </p><p>
            冒号紧接着说：“抽象类的出现，让两种不同角色的类在语法上有了明确的界定：具体类<span class="strong"><strong>描述对象</strong></span>，重在实现；抽象类<span class="strong"><strong>描述规范</strong></span>，重在接口。这种分工降低了用户与实现者之间的耦合度，大大减少了代码的维护成本以及编译时间<a class="link" href="#note11"><sup>[11]</sup></a>。由于抽象类不是为了创建对象，它的实例化自然是没有意义的。又由于它是接口规范，在子类没有实现其所有规范之前，是不能实例化的，否则规范岂不成了一纸空文？在没有抽象类的语法之前，要实现类似的功能，唯一的办法是：在本该抽象的方法被调用时强行中止程序。烦琐丑陋不说，还只能在运行期间捕捉错误。在<span class="term">纯虚函数</span>（pure virtual function）——相当于Java和C#中的抽象方法——被引入之后，任何含有抽象方法的类都是抽象类，编译器将保证它不会被实例化。”
        </p><p>
            问号连连点头：“从这个角度来理解抽象类的语法，一切都顺理成章了。不过，抽象类与接口的区别好像还是没有看到。”
        </p><p>
            谈到兴头，冒号出言更如下阪走丸：“从具体类中分离出抽象类是一次质的飞跃，从抽象类中进一步地分离出接口则是另一次飞跃。Java推出接口类型之时同样饱受质疑，最终还是经受了实践的考验，后又为C#所采纳。其实最初C++的抽象类是为了定义一组协议并强令各子类遵守，实质上正是Java和C#中的接口所起的作用。但在协议规范的实现过程中，可能会产生一些不完全实现类。允许这种类的存在固然是一种灵活的举措，但必须认识到它们与纯规范的抽象类已判若云泥。打个比方，如果把对象看作产品，把具体类看作一个制作产品的模具，那么接口就是模具的规格标准，而抽象类是在模具加工过程中产生的半成品。接口与抽象类无法实例化，模具规格与模具半成品也不能直接制作产品；一个具体类可以有多个接口，一个模具也可有多个不同方面的规格；一个具体类至多只能继承一个抽象类，一个模具也至多只能在一种模具半成品的基础上直接加工。”
        </p><p>
            引号细加回味：“如果具体类、抽象类和接口分别对应于模具、模具半成品和模具规格，那后两者的区别的确比前两者的区别还大。可是假如一个抽象类完全没有任何实现呢？抛开多重继承的限制，它与接口又有何区别呢？”
        </p><p>
            冒号辨析其别：“一个抽象类可以没有任何实现，但也随时可以加入实现。接口则不同，永远都不能有实现代码。这正是引入关键字interface的目的，明明白白地表明：此乃规范集合所在，杜绝任何自以为是、画蛇添足的实现。初看似乎不合常理：这不是自缚手脚、自废武功吗？殊不知<span class="strong"><strong>自由源于自制</strong></span>。许多人为了贪恋一点点代码重用，总忍不住把一些实现放在本该只是规范的地方。一来，这模糊了规范与实现的界限，背离了接口与实现相分离的设计初衷。要知道，再完美的实现都有改动的余地，将其捆绑到规范中只会增加不稳定因素；再完美的实现也不应该影响其他的实现，先入为主只会降低灵活性。二来，带有实现的抽象类无法用于合成，必须通过类继承才能起作用，而实现继承的弊端我们已经见识过了。在有些情况下，规范的实现比较复杂，需要渐进实现，保留一些中间状态的抽象类也是合理的，但最初的接口最好保留。总不能因为有了模具半成品，就抛弃模具规格吧？以Java Collections Framework为例，既规范了Collection、Set、List、Map等接口，又为这些接口提供了抽象类和具体类，从而给了用户三种选择：直接利用具体类、扩展抽象类、直接实现接口，方便程度递减而灵活程度递增。”
        </p><p>
            句号进行反思：“我在想，为什么以前对接口总有本能的排斥心理？原因在于：满脑子更多想的是怎么让程序工作，而不是想怎么让程序工作得更好。因此更重视代码实现，比较忽视规范设计。”
        </p><p>
            众人皆有同感。
        </p><p>
            “确实，在缺乏设计观念的人看来，使用接口和脱裤放屁差不多。”冒号轻笑道，“特别需要注意一种常见的说法：接口是为了克服Java或C#中抽象类不能多重继承的缺点。这句话具有相当大的误导性，因为该处的多重继承是指多重实现继承，而接口甚至连单重实现继承都做不到！许多人对接口与抽象类的认识之所以模糊不清，原因是他们习惯于从<span class="emphasis"><em>定义和语法</em></span>中寻找<span class="emphasis"><em>表象的答案</em></span>，不习惯从<span class="emphasis"><em>本源和语义</em></span>上进行<span class="emphasis"><em>本质的分析</em></span>。然而不可否认，毕竟接口与抽象类提供了相似的抽象机制，在实践中往往确难抉择。因此光从语法上对比二者的差别是远远不够的，需要进一步在语义上进行对比（如表10-2所示）——”
        </p><div class="table"><a name="id604782"></a><p class="title"><b>表 10-2. Java/C#的抽象类与接口在语义上的区别</b></p><div class="table-contents"><table summary="Java/C#的抽象类与接口在语义上的区别" border="1"><colgroup><col><col><col><col><col><col><col><col><col></colgroup><thead><tr><th> </th><th>关系</th><th>共性</th><th>特征</th><th>联系</th><th>重用</th><th>实现</th><th>重点</th><th>演变</th></tr></thead><tbody><tr><td><span class="strong"><strong>接口</strong></span></td><td>can-do</td><td>相同功能</td><td>边缘特征</td><td>横向联系</td><td>规范重用</td><td>多种实现</td><td>可置换性</td><td>新增类型</td></tr><tr><td><span class="strong"><strong>抽象类</strong></span></td><td>is-a</td><td>相同种类</td><td>核心特征</td><td>纵向联系</td><td>代码重用</td><td>多级实现</td><td>可扩展性</td><td>新增成员</td></tr></tbody></table></div></div><br class="table-break"><p>
            冒号展开叙述：“先从本性上看：接口是一套功能规范集合，因此相同的接口代表相同的功能，多表示‘can-do’关系，常用后缀为‘-able’的形容词命名，如Comparable、Runnable、Cloneable等等。接口一般表述的是对象的边缘特征<a class="link" href="#note12"><sup>[12]</sup></a>，或者说一个对象在某一方面的特征，因此能在本质不同的类之间建立起横向联系。由于一个对象可拥有多方面的角色特征，故而可有多种接口。与之相对地，抽象类是一类对象的本质属性的抽象，因此相同的抽象基类代表相同的种类，多表示‘is-a’关系，常用名词命名。抽象类一般表述的是对象的核心特征，只能在本质相同的类之间沿着继承树建立起纵向联系。由于一个对象通常只有一个核心，故而只能有一种基类。再从目的上看：接口是为了规范重用，让一个规范有多种实现，看重的是<span class="emphasis"><em>可置换性</em></span>；抽象类主要是为了代码重用<a class="link" href="#note13"><sup>[13]</sup></a>，能逐级分步实现基类的抽象方法，看重的是<span class="emphasis"><em>可扩展性</em></span>。”
        </p><p>
            叹号追问：“演变指的又是什么呢？”
        </p><p>
            冒号答道：“严格说来，演变不属语义范畴，属于语法规则的一个推论。在系统演变过程中，接口与抽象类的表现差异很大。接口由于是被广泛采用的规范，相当于行业标准，一经确立不能轻易改动。一旦被广泛采用，它的任何改动——包括增减接口、修改接口的签名或规范——将波及整个系统，必须慎之又慎。抽象类的演变则没有那么困难，一则它在系统中用得没有接口那么广泛，更多地是家庭身份而非社会身份；二则它可随时新增域成员或有默认实现的方法成员<a class="link" href="#note14"><sup>[14]</sup></a>，所有子类将自动得以扩充。这是抽象类的最大优点之一。不过接口也有抽象类所不具备的优点，虽然自身难以演化，但很容易让其他类型演化为该接口的子类型。例如，JDK5.0之前的StringBuffer、CharBuffer、Writer和PrintStream本是互不相关的，在引进了接口Appendable并让以上类实现该接口后，它们便有了横向联系，均可作为格式化输出类Formatter的输出目标。”
        </p><p>
            问号还留有一个疑点：“现在接口与抽象类之间的差异是越来越清晰了，我只是有一点一直没想通：标记接口究竟有什么用？它一个方法都没有，也就谈不上规范，也无法利用多态机制，继承这类接口又有何意义呢？”
        </p><p>
            逗号随口说：“这就好比有些社会身份是光挂名头不干事的虚衔，不足为奇。”
        </p><p>
            冒号回应道：“先需澄清一点，一个类型的规范不限于单个的方法，类型整体上也有规范，比如主要目的、适用场合、限定条件、类不变量等等。另外，接口的目的是为了产生<span class="emphasis"><em>多态类型</em></span>，不能只看到‘多态’而忽略‘类型’。一个接口哪怕没有一个方法，也是有意义的。首先，接口是一种类型，有严格的语法保障和明确的语义提示，这也是静态类型的优势所在。让一个具体类继承特定接口，既凸显了设计者的用意，也授予用户针对性地处理该类型的权力。比如java.util.EventListener接口为所有的事件监听器提供了统一的根类型。其次，有时需要对某些类型提出特殊要求、提供特殊服务或进行特殊处理，而这些并不能通过公有方法来办到，也没有其他有效的语言支持。标记接口可担此任，成为类型<span class="term">元数据</span>（metadata）的载体。比如给一个类贴上一个java.io.Serializable的标签，它的对象便能被序列化<a class="link" href="#note15"><sup>[15]</sup></a>，具体工作由JVM来完成。用户也可以通过自定义私有的writeObject 、readObject等方法来定制序列化方式。值得指出的是，当标记接口仅仅用于元数据时，更好的办法是采用<span class="term">属性导向式编程</span>（@OP），Java中的annotation、C#中的attribute即作此用。”
        </p><p>
            逗号摸了摸后脑勺：“原来标记接口并非虚有其名，还是在偷偷地干实事呢。”
        </p><p>
            冒号见时候已到，准备落下帷幕：“至此，我们探讨了OOP中最基本的机制——封装、最独特的机制——继承、最重要的机制——多态。在今天的课结束之前，请大家每人用一个关键词来形容自己眼中的OOP，并作简要说明。”
        </p><p>
            引号说：“责任——在契约化的公民社会中，最重要的是对自己、对家庭、对社会的责任感。”
        </p><p>
            问号说：“变化——采用封装以防个人之变，慎用继承以防家庭之变，采用多态以防社会之变。”
        </p><p>
            逗号说：“分合——数据与运算结合，接口与实现分离。”
        </p><p>
            句号说：“抽象——无论是封装、继承还是多态，都是施诸众对象之上的抽象机制。”
        </p><p>
            叹号说：“虚伪——用封装来掩盖内心，用多态来掩盖外表，提倡继承责任却不提倡继承财富！”
        </p><p>
            冒号欣赞道：“不错不错，虽然角度各异，但均深中肯綮。我也大可安心下课了！”
        </p><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>
                    C#中表示列表和映射的抽象类型（具体类型）分别是IList（List）和IDictionary（Dictionary）。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    此处主要指以继承为基础的抽象类型，如接口与抽象类。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    在C++中，如果一个类不含数据只含抽象的成员函数（即pure virtual function），则该类有时被称为纯抽象类（pure abstract class），与Java和C#中的interface的功用大致相当。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    参考文献【4】和【5】对trait有深入的介绍，并与mixin作了详细的比较。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    该问题的一个解决方式是依赖注射，即把创建被合成对象的职责交给外界。但严格说来这不是合成，而是聚合或关联。它们之间的详细区别请参见§11.2。
                </p></li><li class="listitem"><p><a name="note6"></a>
                    C++可利用CRTP（<span class="strong"><strong>C</strong></span>uriously <span class="strong"><strong>R</strong></span>ecurring <span class="strong"><strong>T</strong></span>emplate <span class="strong"><strong>P</strong></span>attern）的惯用法来实现mixin。
                </p></li><li class="listitem"><p><a name="note7"></a>
                    指C#3.0的扩展方法（extension method）。
                </p></li><li class="listitem"><p><a name="note8"></a>
                    第一个支持OOP的语言是Simula 67（1967年）。
                </p></li><li class="listitem"><p><a name="note9"></a>
                    例外情形：Java的interface可含static final域成员，C#的interface还可含property、event或indexer成员。
                </p></li><li class="listitem"><p><a name="note10"></a>
                    虽然具体类型有可能被继承，但通常并不提倡。
                </p></li><li class="listitem"><p><a name="note11"></a>
                    据参考文献【1】中介绍，一些大型系统在引入抽象类后，编译时间少了一个数量级。
                </p></li><li class="listitem"><p><a name="note12"></a>
                    接口也可能描述对象的核心特征，但一个类至多一个这样的接口。
                </p></li><li class="listitem"><p><a name="note13"></a>
                    由于类继承同时也继承了接口，抽象类也能规范重用，但更侧重代码重用。
                </p></li><li class="listitem"><p><a name="note14"></a>
                    前提是新增的方法成员不与子类型的方法发生冲突。
                </p></li><li class="listitem"><p><a name="note15"></a>
                    严格说来，还要求该类所有非static非transient的域都是可序列化的。
                </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>
                    抽象类型除了接口和抽象类外，还有mixin、trait等，它们用来克服以下问题——
                </p><p class="simpara"><span class="emphasis"><em>合成的缺陷：</em></span></p><p class="simpara">用法不如继承那么简便优雅；</p><p class="simpara">不能产生子类型；</p><p class="simpara">无法覆盖基础类的方法，也无法访问它的protected成员；</p><p class="simpara">不能以抽象类型为基础类。</p><p class="simpara"><span class="emphasis"><em>具体类型的矛盾与缺陷：</em></span></p><p class="simpara">作为创造对象的单位，功能越多越好；</p><p class="simpara">作为可重用的单位，功能越少越好。</p><p class="simpara">不宜被继承。</p><p class="simpara"><span class="emphasis"><em>接口的矛盾与缺陷：</em></span></p><p class="simpara">客户类希望它提供尽可能多的服务；</p><p class="simpara">实现类希望尽可能少的实现代码。</p><p class="simpara">无法代码重用。</p><p class="simpara"><span class="emphasis"><em>抽象类的缺陷：</em></span></p><p class="simpara">多重类继承或复杂晦涩或未获支持。</p></li><li class="listitem"><p>
                    mixin的特点：抽象性和依赖性；实用性和可重用性；专一性和细粒度性；可选性和边缘性。
                </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>
                    接口与抽象类的语义区别：接口是一套功能规范集合，相同的接口代表相同的功能，多表示“can-do”关系，一般是对象的边缘特征，在本质不同的类型之间建立横向联系；抽象类是一类对象的本质属性的抽象，相同的抽象基类代表相同的种类，多表示“is-a”关系，一般是对象的核心特征，在本质相同的类型之间建立纵向联系。接口看重规范重用和可置换性；抽象类看重代码重用和可扩展性。
                </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="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Bjarne Stroustrup．The Design and Evolution of C++．Reading, MA：Addison-Wesley，1994．277-281
                </p></li><li class="listitem"><p>
                    Joshua Bloch．Effective Java: Programming Language Guide．Boston, MA：Addison-Wesley，2001．84-88
                </p></li><li class="listitem"><p>
                    Nathanael Schärli，Stéphane Ducasse，Oscar Nierstrasz，Andrew P. Black．Traits: Composable Units of Behaviour．ECOOP，2003，LNCS 2743：248–274
                </p></li><li class="listitem"><p>
                    Stéphane Ducasse，Oscar Nierstrasz，Nathanael Schärli，Roel Wuyts，Andrew P. Black．Traits: A Mechanism for Fine-grained Reuse．ACM Transactions，2006，28(2)：331-388
                </p></li><li class="listitem"><p>
                    Wikipedia．Mixin．<a class="link" href="http://en.wikipedia.org/wiki/Mixin" target="_top">http://en.wikipedia.org/wiki/Mixin</a>
                </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>10-01 多态类型在何种程度上解放了静态类型的束缚？</li>
    <li>10-02 请总结参数多态与子类型多态的特点和适用场合。</li>
    <li>10-03 为什么抽象类型如此重要？</li>
    <li>10-04 你认为有必要引入mixin或trait类型吗？</li>
    <li>10-05 区分接口与抽象类的意义何在？</li>
    <li>10-06 你常有往接口中放置代码的冲动吗？</li>
    <li>10-07 如何理解文中“多态使得公民拥有社会身份”这句话？</li>
    <li>10-08 “针对接口编程”与“公民之间以社会身份互相交流”有何相似之处？</li>
    <li>10-09 你是如何理解OOP中抽象、封装、继承和多态的？</li>
    <li>10-10 每当一项新技术出现时，你通常抱什么态度？</li>
    <li>10-11 你会在编程中对某些语法上的限制感到恼火吗？</li>
    <li>10-12  在理解或比较一些编程概念时，你是更习惯从定义和语法的角度，还是更习惯从本源和语义的角度？</li>
    <li>10-13  本课与前课均提到了编程与武术相通之处：攻守兼备，动静得宜，刚柔并济，虚实结合。对此你有何心得体会？</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%2F10%2F24%2Fcolon-class-10_2%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A710.2%EF%BC%9A%E6%8A%BD%E8%B1%A1%E7%B1%BB%E5%9E%8B" id="wpa2a_36">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年10月20日 -- <a href="http://blog.zhenghui.org/2009/10/20/colon-class-10_1/" title="冒号课堂§10.1：多态类型">冒号课堂§10.1：多态类型</a> (8)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=UGNoe35T114:bQRQ7n0s608:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/UGNoe35T114" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/24/colon-class-10_2/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2009/10/24/colon-class-10_2/</feedburner:origLink></item>
		<item>
		<title>冒号课堂§10.1：多态类型</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/g6gjzH_McMA/</link>
		<comments>http://blog.zhenghui.org/2009/10/20/colon-class-10_1/#comments</comments>
		<pubDate>Tue, 20 Oct 2009 09:37:45 +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>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=506</guid>
		<description><![CDATA[<b>多态类型</b>——静中之动（<em>通过实例展示多态类型的三种用法</em>）<br/> • 继承是多态的基础，多态是继承的目的<br/> • 多态是动静结合的产物，将静态类型的安全性和动态类型的灵活性融为一体<br/> • 前者（参数多态）是发散式的，让相同的实现代码应用于不同的场合<br/> • 后者（包含多态）是收敛式的，让不同的实现代码应用于相同的场合<br/> • 模板方法模式突出的是稳定坚固的骨架，策略模式突出的是灵活多变的手腕 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center;"><span style="font-family: 宋体;">冒号课堂</span></h1>
<p><strong><span style="font-size: 13pt; font-family: 宋体;">第十课 多态机制（1）</span></strong></p>
<p><strong><span style="font-family: 宋体;">课前导读</span></strong></p>
<p style="text-indent: 18pt;"><span style="font-family: 宋体;">本课通过实例编程和对抽象类型的解读，显示了OOP中多态机制和抽象类型的重要性，有助于培养和加深读者的OOP语感。</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>
<!-- below comes from generated html -->
<p>&nbsp;</p>

<div class="article" title="多态类型" lang="zh-CN">
<div class="titlepage">
<div>
<div>
<h1 class="title"><a name="id525234"></a>10.1 多态类型——静中之动</h1>
</div>
<div>
<div class="author">
<h3 class="author">郑晖</h3>
</div>
</div>
<div>
<div class="abstract" title="摘要">
<p class="title"><strong>摘要</strong></p>
<p>通过实例展示多态类型的三种用法</p>
</div>
</div>
</div>
<hr /></div>
<div class="toc">
<p><strong>目录</strong></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>
</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>
</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>（polymorphism）机制。因此不妨说，对于静态类型语言来说，<span class="strong"><strong>继承是多态的基础，多态是继承的目的</strong></span>。”</p>
<p>问号忍不住问：“为什么要强调静态类型呢？”</p>
<p>“还记得<span class="term">鸭子类型</span><a class="link" href="#note1"><sup>[1]</sup></a>吗？那就是一种不依赖于继承的多态类型，也是动态类型语言一大优劣参半的特色。”冒号提醒道，“静态类型语言中的多态是动静结合的产物，将静态类型的安全性和动态类型的灵活性融为一体。它一般有两种实现方式：一种利用GP（泛型编程）中的<span class="term">参数多态</span>（parametric polymorphism），一种利用OOP中的<span class="term">包含多态</span>（inclusion polymorphism）或称<span class="term">子类型多态</span>（subtyping polymorphism）。从实现机制上看，二者的不同之处在于何时将一个变量与其实际类型所定义的行为挂钩。前者在编译期，属于<span class="term">早绑定 </span>（early binding）或<span class="term">静态绑定</span>（static binding）<a class="link" href="#note2"><sup>[2]</sup></a>；后者在运行期，属于<span class="term">迟绑定 </span>（late binding）或<span class="term">动态绑定</span>（dynamic binding）。从应用形式上看，前者是<span class="emphasis"><em>发散式</em></span>的，让相同的实现代码应用于不同的场合；后者是<span class="emphasis"><em>收敛式</em></span>的，让不同的实现代码应用于相同的场合。从思维方式上看，前者是泛型式编程风格，看重的是算法的普适性；后者是对象式编程风格，看重的是接口与实现的分离度。尽管二者从范式到语法、语义都大相径庭，但都是为着同一个目的：在保证必要的类型安全的前提下，突破编译期间过于严苛的类型限制。对于既是静态类型语言又是静态语言、既支持OOP又支持GP的C++、Java和C#而言，多态机制是保证代码的灵活性、可维护性和可重用性的<span class="emphasis"><em>终极武器</em></span>。为了说明问题，我们看一个简单而实用的例子：编写一个类，让它能储存用户名和密码，以作今后验证之用。”</p>
<p>叹号一愣：“这题是不是太简单了？还有别的要求吗？”</p>
<p>冒号摇摇头。</p>
<p>引号却认为：“要求太少反而不好做。比如是把数据放在内存、还是文件或者数据库？密码以明文还是密文的形式存储？”</p>
<p>句号提出：“无论是数据的存放方式还是密码的加密方式，都不应该硬编码。”</p>
<p>“循此思路，我们就来编写一个可重用的抽象类。”冒号投放了一段Java代码——</p>
<div class="informalexample">
<pre class="programlisting">/** 一个可以验证用户名和密码的类  */
abstract class Authenticator
{
    /** 保存用户名和密码  */
    final public void save(String user, String password)
    {
        if (password == null)
            password = "";
        store(user, encrypt(password));
    }

    /** 验证用户名和密码  */
    final public boolean authenticate(String user, String password)
    {
        String storedPassword = retrieve(user);
        if (storedPassword == null) return false; // 无此用户
            
        if (password == null)
            password = "";
        return storedPassword.equals(encrypt(password));
    }

    /** 保存用户名和加密过的密码  */
    protected abstract void store(String user, String encryptedPassword);

    /** 从用户名获取相应的加密过的密码  */
    protected abstract String retrieve(String user);

    /** 给明文单向（one-way）加密，默认不加密  */
    protected String encrypt(String text) { return text; }
}</pre>
</div>
<p>冒号解说道：“该抽象类有两个public接口，一个用来保存，一个用来验证。它们用final修饰符来禁止子类覆盖，因为真正的扩展点是三个protected方法。其中store和retrieve是抽象的，encrypt有一个平凡实现。以此为基础，再根据实际需要来编写子类，具体实现这三个方法。”</p>
<p>幻灯片转到下一页——</p>
<div class="informalexample">
<pre class="programlisting">import java.util.Map;
import java.util.HashMap;

/** 一个简单的验证类，数据放在内存，密码保持明文  */
class SimpleAuthenticator extends Authenticator
{
    private Map&lt;String, String&gt; usrPwd = new HashMap&lt;String, String&gt;();

    @Override protected void store(String user, String encryptedPassword)
    {
        usrPwd.put(user, encryptedPassword);  
    }

    @Override protected String retrieve(String user)
    {
        return usrPwd.get(user);  
    }
}</pre>
</div>
<p>“我们利用HashMap来储存数据，密码保持明文。这大概是最简单的一种子类了。”冒号仿佛在轻轻地把玩着一件小物什，“为安全起见，最好还是将密码加密。于是我们设计了稍微复杂一点的子类——”</p>
<div class="informalexample">
<pre class="programlisting">import java.security.MessageDigest;

/** 一个安全的验证类，数据放在内存，密码经过SHA-1加密  */
class Sha1Authenticator extends SimpleAuthenticator
{
    private static final String ALGORITHM = "SHA-1"; // SHA-1算法
    private static final String CHARSET = "UTF-8"; // 避免依赖平台

    @Override protected String encrypt(String plainText)
    {
        try
        {
            MessageDigest md = MessageDigest.getInstance(ALGORITHM);
            md.update(plainText.getBytes(CHARSET));
            byte digest[] = md.digest();
            // BASE64编码比十六进制编码节省空间
           //为简便起见用到了非标准的API，因此以下代码有警告 
            return (new sun.misc.BASE64Encoder()).encode(digest); 
        }
        catch (java.security.NoSuchAlgorithmException e)
        {
            throw new InternalError(e.getMessage());  // 不可能发生
        }
        catch (java.io.UnsupportedEncodingException e)
        {
            throw new InternalError(e.getMessage());  // 不可能发生
        }
    }
}</pre>
</div>
<p>逗号质疑道：“不是具体类不宜被继承的吗？怎么Sha1Authenticator类却继承了具体类SimpleAuthenticator？”</p>
<p>冒号略表赞许：“很高兴你没有忘记这个原则。不过考虑到Sha1Authenticator类需要覆盖父类的encrypt方法，这么做也是情有可原的。当然最好选择让该类直接继承抽象类Authenticator，但作为示例代码，我们还是希望它简洁一些，不想让过多的细枝末节掩盖核心主干。下面是测试代码——”</p>
<div class="informalexample">
<pre class="programlisting">public class TestAuthenticator 
{  // 为避免额外依赖，没有采用JUnit等单元测试工具
    public static void main(String[] args)
    {
        test(new SimpleAuthenticator());
        test(new Sha1Authenticator());
    }

    // 测试给定的Authenticator 
    private static void test(Authenticator authenticator) // 子类型多态
    {
        test(authenticator, "user", "password");
        test(authenticator, "user", "newPassword");
        test(authenticator, "admin", "admin");
        test(authenticator, "guest", null);
        test(authenticator, null, "pass");

        authenticator.save("scott", "tiger");
        assert(!authenticator.authenticate("scott", "TIGER")); // 大小写敏感
        assert(!authenticator.authenticate("SCOTT", "tiger")); // 大小写敏感
    }

    private static void test(Authenticator authenticator, String user, String password)
    {
        authenticator.save(user, password);
        assert(authenticator.authenticate(user, password));
    }
}</pre>
</div>
<p>引号觉得眼熟：“这不是上节课讲的<span class="term">模板方法模式</span>吗？”</p>
<p>“正是此公。”冒号确认，“该模式的核心思想是：固定整体框架和流程以保证可重用性，留出一些子类定制点以保证可扩展性。在测试代码的两个test方法中，传入的参数是Authenticator类，但数据存放和密码加密的方式是在运行中才确定的，即先后遵照SimpleAuthenticator类和Sha1Authenticator类的实现。这就是我们所说的子类型多态的效果——让不同的实现代码应用于相同的场合。假设没有多态机制，这种效果就只能靠if/else或switch之类的条件语句才能实现，非常地痛苦。”</p>
<p>冒号的眉头皱成了粗体的“川”字。</p>
<p>“还有更好的方法吗？”句号察言观色，断定老冒还留有后手。</p>
<p>果不其然，冒号的眉毛立刻又舒展开来，中气充沛地应道：“有！诸位请看——”</p>
<div class="informalexample">
<pre class="programlisting">// 键值对的存取接口
interface KeyValueKeeper
{
    public void store(String key, String value);
    public String retrieve(String key);
}

// 加密接口
interface Encrypter
{
    public String encrypt(String plainText);
}

class Authenticator
{
    private KeyValueKeeper keeper;
    private Encrypter encrypter;

    public Authenticator(KeyValueKeeper keeper, Encrypter encrypter)
    {
        this.keeper = keeper;
        this.encrypter = encrypter;
    }

    public void save(String user, String password)
    {
        if (password == null)
            password = "";
        keeper.store(user, encrypter.encrypt(password));
    }

    public boolean authenticate(String user, String password)
    {
        String storedPassword = keeper.retrieve(user);
        if (storedPassword == null) return false;

        if (password == null)
            password = "";
        return storedPassword.equals(encrypter.encrypt(password));
    }
}</pre>
</div>
<p>冒号加以引导：“如果仔细比较两种设计，就会发现它们很相似。后者只不过把前者对子类开放的接口合成为自己的两个成员。再看接口的实现类——”</p>
<div class="informalexample">
<pre class="programlisting">class MemoryKeeper implements KeyValueKeeper
{
     private Map&lt;String, String&gt; keyValue = new HashMap&lt;String, String&gt;();

    @Override public void store(String key, String value)
    {
        keyValue.put(key, value);  
    }

    @Override public String retrieve(String key)
    {
        return keyValue.get(key);  
    }
}

class PlainEncrypter implements Encrypter
{
    @Override public String encrypt(String plainText)
    {
        return plainText;
    }
}

class Sha1Encrypter implements Encrypter
{
    private static final String ALGORITHM = "SHA-1";
    private static final String CHARSET = "UTF-8"; 

    @Override public String encrypt(String plainText)
    {
        try
        {
            MessageDigest md = MessageDigest.getInstance(ALGORITHM);
            md.update(plainText.getBytes(CHARSET));
            byte digest[] = md.digest();
            return (new sun.misc.BASE64Encoder()).encode(digest); 
        }
        catch (java.security.NoSuchAlgorithmException e)
        {
            throw new InternalError(e.getMessage());
        }
        catch (java.io.UnsupportedEncodingException e)
        {
            throw new InternalError(e.getMessage());
        }
    }
}</pre>
</div>
<p>逗号比较后得出结论：“MemoryKeeper与SimpleAuthenticator、Sha1Encrypter与Sha1Authenticator除了超类型和方法访问修饰符外，其他毫无二致。”</p>
<p>屏幕滚动出另一段代码——</p>
<div class="informalexample">
<pre class="programlisting">public class TestAuthenticator
{
    public static void main(String[] args)
    {
        test(new Authenticator(new MemoryKeeper(), new PlainEncrypter()));
        test(new Authenticator(new MemoryKeeper(), new Sha1Encrypter()));
    }	

    private static void test(Authenticator authenticator) // 隐含子类型多态
    { /* 同上，略 */}
}</pre>
</div>
<p>“测试代码区别也不大，只是Authenticator的多态性更加隐蔽。”冒号如是说。</p>
<p>叹号挑剔说：“后一种创建实例稍显麻烦一些。”</p>
<p>“但它是以小弊换大利。”冒号朗声而道，“首先，后者用的是合成与接口继承，比前者的实现继承更值得推荐，理由在上堂课业已阐明。其次，假设共有M种数据存取方式，包括内存、文件、数据库等等；共有N种加密方式，包括明文、SHA-1、SHA-256、MD5等等。按第一种设计，需要（M×N）个实现类；按第二种设计，只要（M+N）个实现类。这还只是两种变化因素，假如需要考虑更多的因素，二者差距将更大。比如增加编码方式：加密后的数据可以选择费空间省时间的十六进制编码、费时间省空间的BASE64编码、省时间省空间却包含非打印字符的原始形式等；比如增加安全强度：引入salt、nonce或IV等<a class="link" href="#note3"><sup>[3]</sup></a>；比如增加密码状态：已生效密码、未生效密码、已过期密码等等。对比下面的UML类图，孰优孰劣更加一目了然。”</p>
<p>众人眼前出现了两幅图——</p>
<div class="figure"><a name="id525517"></a>
<p class="title"><strong>图10-1. Authenticator的UML类图（模板方法模式）</strong></p>
<div class="figure-contents">
<div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure10-1.jpg" alt="Authenticator的UML类图（模板方法模式）" /></div>
</div>
</div>
<br class="figure-break" />
<div class="figure"><a name="id525535"></a>
<p class="title"><strong>图10-2. Authenticator的UML类图（策略模式）</strong></p>
<div class="figure-contents">
<div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure10-2.jpg" alt="Authenticator的UML类图（策略模式）" /></div>
</div>
</div>
<br class="figure-break" />
<p>冒号指着屏幕问：“图二不仅比图一少了三个实现类，而且可重用性也更高。大家说是为什么？”</p>
<p>引号应答：“图一中的九个Authenticator的子类只能作为验证类来重用，而图二中六个实现类不仅可以合作完成验证类的功能，还能分别单独提供键值存储和加密字符串的功能。”</p>
<p>冒号作出肯定：“这就是职责分离的好处。存储与加密本是两样不相干的工作，必要时可以合作，但平时最好分开管理，符合‘低耦合、高内聚’的原则。”</p>
<p>问号注意到图中的注释，遂问：“第二种采用的是策略模式？”</p>
<p>冒号颔首：“简单地说，<span class="term">策略模式</span>（strategy pattern或policy pattern）的基本思想是：把一个模块所依赖的某类算法委交其他模块实现。比如Java中的Comparable和Comparator、C#中的IComparer就是比较算法的接口，当一个类的某个方法接收了此种类型的参数，实质上就采用了策略模式。”</p>
<p>逗号不以为奇：“这岂非很平常？”</p>
<p>“你认为设计模式真的高不可攀吗？”冒号反问道，“包括模板方法模式，你们很可能也在编程实践中采用过，只不过相交不相识罢了。”</p>
<p>句号看出：“模板方法模式与策略模式非常神似，都是把一个类的可变部分移交给其他类处理。”</p>
<p>“照你这么说，绝大多数设计模式都是神似的，这也是为什么我们不专门谈设计模式的缘故。GoF设计模式是OOP大树上结出的硕果，在你心中培养的OOP成熟之前，匆忙缔结的果实多半是青涩弱小的。”冒号忠告，“我们也不会对设计模式避而不谈，但凡提及都是水到渠成的产物。再说回这两种设计模式，虽然有相通的思想，也能解决相同的问题，在稳定性与灵活性之间都取得了某种平衡，但还是各有侧重的。模板方法模式突出的是稳定坚固的骨架，策略模式突出的是灵活多变的手腕。不妨拿国家政策作比：一个强调对内要稳，老一辈制订了大政方针，下一代必须在坚持原则的前提下进行完善；一个强调对外要活，不能或不便自行开发的技术不妨从国外引进。”</p>
<p>叹号一乐：“哈！设计模式上升到了政策模式。”</p>
<p>冒号抽丝剥茧：“正如模板方法模式可看作控制反转的特例，策略模式与<span class="term">依赖注射</span>（Dependency Injection）也异曲同工。第二个Authenticator所依赖的两个功能KeyValueKeeper和Encrypter，就是是通过构造方法‘注射’进来的<a class="link" href="#note4"><sup>[4]</sup></a>。当然策略只是一种特殊的依赖，是自内而外的——将算法抽出来外包；依赖注射的机制更复杂、涵盖面更广，是自外而内的——从外部嵌入定制功能。后者被广泛地用于框架应用之中，尤以Spring Framework和Google Guice为代表。”</p>
<p>引号听得起劲：“这下热闹了，设计模式、框架与OOP范式全搅和到一块了。”</p>
<p>“还有GP范式呢。”冒号顺接话题，“让我们再用C++的模板来实现一下Authenticator类吧。没有继续采用Java，是因为它的泛型仍离不开子类型多态。”</p>
<p>说着，他换上了C++代码——</p>
<div class="informalexample">
<pre class="programlisting">#include &lt;string&gt;
#include &lt;map&gt;

using namespace std;

template &lt;typename KeyValueKeeper, typename Encrypter&gt;
class Authenticator
{
private:
    KeyValueKeeper keeper;
    Encrypter encrypter;
public:
    void save(const string&amp; user, const string&amp; password)
    {
        keeper.store(user, encrypter.encrypt(password));
    }

    bool authenticate(const string&amp; user, const string&amp; password) const
    {
        string storedPassword;
        if (!keeper.retrieve(user, storedPassword)) return false;

        return storedPassword == encrypter.encrypt(password);
     }
};

class MemoryKeeper
{
private:
    map&lt;string, string&gt; keyValue;
public:
    void store(const string&amp; key, const string&amp; value)
    {
        keyValue[key] = value;  
    }

    bool retrieve(const string&amp; key, string&amp; value) const
    {
        map&lt;string, string&gt;::const_iterator itr = keyValue.find(key);
        if (itr == keyValue.end()) return false;

        value = itr-&gt;second;  
        return true;
    }
};

class PlainEncrypter
{
public:
    string encrypt(const string&amp; plainText) const { return plainText; }
};

class Sha1Encrypter
{
public:
    string encrypt(const string&amp; plainText) const { /* 省略代码  */ }
};

namespace
{
    template &lt;typename K, typename E&gt;
    void test(Authenticator&lt;K, E&gt; authenticator) // 参数多态
    { /* 省略代码  */ }
}

int main()
{ 
    test(Authenticator&lt;MemoryKeeper, PlainEncrypter&gt;());
    test(Authenticator&lt;MemoryKeeper, Sha1Encrypter&gt;());
    return 0;
}</pre>
</div>
<p>“以上代码与Java版的策略模式代码很相似，主要的区别是把KeyValueKeeper和Encrypter两个接口换成了模板参数。由于模板是在编译期间实例化的，因此没有动态绑定的运行开销，但缺点是不能动态改变策略<a class="link" href="#note5"><sup>[5]</sup></a>。”冒号分析道，“至此，我们通过一个验证类的三种解法，分别展示了三种形式的多态：基于类继承的多态、基于接口继承的多态和基于模板的多态。它们殊途同归，都能让代码更简洁、更灵活、可重用性更高、更易维护和扩展。”</p>
<p>问号想到一个问题：“C语言既没有子类型多态也没有参数多态，又如何保证高质量的C程序呢？”</p>
<p>冒号眉梢轻挑：“C语言有指针啊，C++、Java和C#的多态在底层就是用指针实现的。C中的函数指针比Java中的接口更加灵活高效，当然对程序员的要求也更高。”</p>
<p>引号蓦地记起：“重载不也是一种多态吗？”</p>
<p>“刚才所说的多态都属于<span class="term">通用多态</span>（universal polymorphism）。此外，还有一类<span class="term">特别多态</span>（ad-hoc polymorphism），常见有两种形式。一种是<span class="term">强制多态</span>（coercion polymorphism），即一种类型的变量在作为参数传递时隐式转换成另一种类型，比如一个整型变量可以匹配浮点型变量的函数参数。另一种就是<span class="term">重载多态</span>（overloading polymorphism），它允许不同的函数或方法拥有相同的名字。特别多态浅显易懂，其重要性与通用多态也不可同日而语，故不在我们关注之列。只是要注意一点，正如子类型应遵守超类型的规范，同名的函数或方法也应遵守相同的规范。如果为贪图取名方便而滥用重载，早晚因小失大。”冒号告诫道。</p>
<p>逗号突发奇论：“一个多态类型的对象可以在不同的类型之间变来变去，是不是叫‘变态类型’更生动些？”</p>
<p>“我看你就属于典型的变态类型。”句号乘机拿他开涮。</p>
<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> 参见§5.2。</p></li>
	<li class="listitem">
<p><a name="note2"></a> 虽然C#具体的泛型类型是在运行期间实例化的，但每类泛型对应相同的实现代码，故变量的行为仍是在编译期间决定的。</p></li>
	<li class="listitem">
<p><a name="note3"></a> salt、nonce和IV都是密码学中的术语，是在加密过程中混入的一次性数据，以增加预计算攻击（如字典攻击）的难度。</p></li>
	<li class="listitem">
<p><a name="note4"></a> 这被称为constructor injection，另外两种常用的注射方法是setter injection和interface injection。</p></li>
	<li class="listitem">
<p><a name="note5"></a> 对用Java实现的Authenticator类（策略模式版）稍作修改，就能让客户动态改变策略。</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>合理地运用基于类继承的多态、基于接口继承的多态和基于模板的多态，能增强程序的简洁性、灵活性、可维护性、可重用性和可扩展性。</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>Erich Gamma，Richard Helm，Ralph Johnson，John Vlissides．Design Patterns: Elements of Reusable Object-Oriented Software．Boston, MA：Addison-Wesley，1994．315-323</p></li>
	<li class="listitem">
<p>Luca Cardelli，Peter Wegner．On understanding types, data abstraction, and polymorphism．Computing Surveys，1985，17(4)：471-522</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%2F10%2F20%2Fcolon-class-10_1%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A710.1%EF%BC%9A%E5%A4%9A%E6%80%81%E7%B1%BB%E5%9E%8B" id="wpa2a_38">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年10月24日 -- <a href="http://blog.zhenghui.org/2009/10/24/colon-class-10_2/" title="冒号课堂§10.2：抽象类型">冒号课堂§10.2：抽象类型</a> (7)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=g6gjzH_McMA:x6j0_tvznGM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/g6gjzH_McMA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/20/colon-class-10_1/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2009/10/20/colon-class-10_1/</feedburner:origLink></item>
		<item>
		<title>《冒号课堂》自序</title>
		<link>http://feedproxy.google.com/~r/huizheng/~3/PPvurroHW5o/</link>
		<comments>http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/#comments</comments>
		<pubDate>Fri, 16 Oct 2009 04:58:31 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[IT]]></category>
		<category><![CDATA[数学]]></category>
		<category><![CDATA[简历]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=493</guid>
		<description><![CDATA[《冒号课堂》一书的序言 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">序</span></h1>

<!-- 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><div class="author"><h3 class="author">郑晖</h3></div></div></div><hr /></div><div class="toc"><dl><dt><span class="section"><a href="#content"></a></span></dt></dl></div><div class="section"><div class="titlepage"></div><p>
            去年3月的一个下午，过于明媚的春光唤醒了一份久违的情怀，书摊上的一本《青年文摘》便成了合宜的载体。与其说是为了阅读，不如说是为了回忆——对20年前读书心境的回忆。孰料读罢开篇，怀旧之窗随即悄然关掩，一扇求新之门却戛然开启。那是一篇人物介绍，讲述一位籍籍无名的年轻人是如何因撰写博客而声名鹊起的。抚卷思之，网络平台已成大众舞台，人人皆可登台献技，自己何不前去一试？心念甫动，顿感技痒难耐，当晚寝不安席，于辗转反侧之中磨出了一本书的轮廓。
        </p><p>
            尽管钟书先生认为鸡与蛋应为松耦合关系，但一只来历不明的鸡确会招致人们对其产品可靠性的怀疑。故而在介绍《冒号课堂》的创作思路之前，先自我介绍一番。1986年我怀揣着成为数学家的梦想，考入武汉大学数学系。7年的大学生涯在浑浑噩噩中度过，毕业后在广州一所高校教了3年的高等数学。在混沌与迷茫中挣扎了10年，终于不堪蹉跎，1996年赴美攻读数学博士。始料不及的是，在大洋彼岸不仅没能一圆数学之梦，反倒从一个未曾碰过鼠标的电脑排斥者变成了一名IT工作者。“罪魁祸首”正是电脑和Internet，它们潜移默化地改变了人们的生活和思维方式，我亦未能幸免。1998年开始选修计算机课程，两年后拿到硕士学位，并在华尔街的一家软件公司找到了工作。2004年年底，选择回国发展，再度主导了人生的一次急转弯。回到广州后，顺利地进入了一家著名的外企。平淡而安逸的生活似乎注定与我无缘，不久又转去一家小公司作技术总监。如果用一句话来描述自己的职业生涯，那便是：数学是我的初恋情人，计算机是我的终生伴侣。无论成败，都是命运与人生双向选择的结果。
        </p><p>
            本书的创作虽出偶然，却也有其必然性。一方面，市面上的计算机书籍多为拼凑之作，且不少带有应试教育的痕迹。另一方面，论坛上充斥着各种谬言妄论，人们或目空一切，或人云亦云；每当争论一起，常常硝烟弥漫，出言无状者甚众。如此诸般，不忍卒睹。深感激浊扬清之必要，此念一直郁积于心，终至一朝爆发。自知虽无澄清玉宇之力，唯奢念带来一缕清风。
        </p><p>
            《冒号课堂》采用对话体，是为了借不同背景、不同水平、不同性格的人物之口，多层次、多维度、多角度地展现知识的内涵与活性。人物皆以标点符号命名，是为了塑造让人过目不忘的形象：冒号善解释，引号善引用，问号善提问，逗号善缓冲，叹号善感叹，句号善总结。此外，6个标点符号还对应着每小节的6个部分：冒号是正文讲解，引号是文献参考，问号是问题列表，逗号是补充插语，叹号是精华预览，句号是本节总结。与一般纯技术类图书不同，本书非常强调学习方法和学习精神的重要性。在内容组织上也一反常规，以思想为主、以知识为辅，以抽象为主、以具体为辅，以范式为主、以语言为辅。人们常把书籍比作一种食物，其实书籍也是一种药物。一本书应当同时提供两种价值：一种是让人获取正确知识的食用价值，一种是让人抛弃错误观点的药用价值。《冒号课堂》更侧重后者，这多少给读者带来一定的阅读障碍，因为抛弃往往比获取更加困难。此外，全书涉及的知识点较多，覆盖的知识面较广，一些流行的语言或技术反被刻意地淡化。假如读者没有足够的计算机理论和实践基础，难免会感到一些困难和不适。古语有云：“学然后知不足”，认识到不足何尝不是学习的一种收获呢？从另一面说，假如读者发现书中疏谬，还请不吝赐教，本人将不胜感激。坦而言之，随着写作的深入，自得之心日敛，惴惴之心日甚，正应了上面古语的后半句：“教然后知困”。
        </p><p>
            本书的完成首先需要感谢3位母亲：我的母亲、我太太的母亲、我女儿的母亲。没有她们默默无闻的支持和帮助，冒号课堂只能在梦中开班。还要感谢博文视点的周筠老师对本书的大力支持和关怀；白爱萍编辑负责而又耐心，不厌其烦地和我讨论一个个文字和版式的细节；博文的陈宜、杨小勤、陈琼、徐定翔、许莹、胡文佳等编辑也以同样的热情带给我很好的出版体验。我从其他亲友和网友那里也得到了许多热情的鼓励和有益的启示，是他们让虚拟的课堂变得真实和生动。
        </p><p dir="rtl">郑晖</p><p dir="rtl">2009年8月24日于广州</p></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%2F10%2F16%2Fpreface-of-colonclass%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E8%87%AA%E5%BA%8F" id="wpa2a_40">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年09月20日 -- <a href="http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/" title="论思维的刚性与柔性（科学的迷信-3）">论思维的刚性与柔性（科学的迷信-3）</a> (0)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (58)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/huizheng?a=PPvurroHW5o:Dx0NFMnrl3s:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/huizheng?d=yIl2AUoC8zA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/huizheng/~4/PPvurroHW5o" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		<feedburner:origLink>http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/</feedburner:origLink></item>
	</channel>
</rss><!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Served from: blog.zhenghui.org @ 2012-04-17 22:56:38 -->

