﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-loop_in_codes-随笔分类-Lisp</title><link>http://www.cppblog.com/kevinlynx/category/16250.html</link><description>低调做技术__
C/C++\MMORPG服务器\模块架构__ TODO：linux env/read more books __Kevin Lynx</description><language>zh-cn</language><lastBuildDate>Thu, 29 Sep 2011 12:53:16 GMT</lastBuildDate><pubDate>Thu, 29 Sep 2011 12:53:16 GMT</pubDate><ttl>60</ttl><item><title>使用Lisp搭建独立博客</title><link>http://www.cppblog.com/kevinlynx/archive/2011/09/29/157144.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 29 Sep 2011 09:19:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2011/09/29/157144.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/157144.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2011/09/29/157144.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/157144.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/157144.html</trackback:ping><description><![CDATA[<table frame="void" rules="none">
<col>
<col>
<tbody valign="top">
<tr><th>Author:</th>
<td>Kevin Lynx</td></tr>
<tr><th>Date:</th>
<td>9.29.2011</td></tr>
<tr><th>Contact:</th>
<td>kevinlynx at gmail dot com</td></tr>
</tbody>
</table>
<p>本文描述如何使用Lisp工具集搭建一个完整的个人博客站点。一个搭建好的例子站点可以参看我的个人博客：<a title="http://codemacro.com" href="http://codemacro.com">http://codemacro.com</a>。</p>
<p>要搭建一个独立博客，需要两方面的支持。一是博客软件，二是根据选择的博客软件取得必须的&#8220;硬件&#8220;。例如我这里使用的是Lisp工具集，就需要一个可以完全控制的服务器，所以这里我需要一个VPS。当然，购买一个合适的域名也是必须的。以下将针对这些内容做描述。</p>
<h3>获取VPS及域名</h3>
<p>VPS提供商国内国外都有很多。我选择的是 <a href="http://www.rapidxen.net/" target="_blank">rapidxen</a> ，128M内存，1年70来美元，算是国外比较便宜的，速度上还过得去。</p>
<p>购买了VPS后，可以进入后台管理页面安装VPS操作系统。同样，因为我使用的是Lisp，我选择安装了Debian 6.0 squeeze (minimal)64位。实际上我更倾向于32位，因为我的PC系统就是32位，方便测试。安装系统非常简单，基本随意设置下即可。值得注意的是，除了修改root密码外，最好修改下ssh端口，具体设置方法可以另行搜索。此外，因为后面我会使用nginx作为HTTP前端服务器，为了方便安装nginx，最好更新下软件源列表，编辑etc/apt/source.list:</p>
<pre class="brush:bash">deb http://ftp.us.debian.org/debian squeeze main<br />deb http://packages.dotdeb.org stable all<br />deb-src http://packages.dotdeb.org stable all<br />deb http://php53.dotdeb.org stable all<br />deb-src http://php53.dotdeb.org stable all<br /></pre>
<p>购买VPS最主要的，就是获取到一个独立IP，如图：</p>
<img alt="imgs/vps.png" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/cl-writer/20119291758/vps.png" />
<p>然后可以去购买域名。同样，也有很多域名服务商。这里我选择的是 <a href="http://www.godaddy.com/" target="_blank">godaddy</a> ，我选择的域名codemacro.com一年11美元。购买了域名后，就需要将域名和VPS IP关联起来。详细设置也可以另行搜索。这里简要提下：在成功登入godaddy后，选择My Account，进入自己的域名，选择DNS Manager，然后添加域名映射即可，如图：</p>
<img alt="imgs/domain.png" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/cl-writer/20119291758/domain.png" />
<p>通过以上设置后，你购买的域名就成功指向你购买的VPS地址了。可以通过ping来观察是否指向成功。</p>
<h3>使用Lisp构建博客系统</h3>
<p>要在VPS上安装软件，首先需要SSH上你的VPS，例如：ssh -p 1234 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#114;&#111;&#111;&#116;&#38;&#35;&#54;&#52;&#59;&#99;&#111;&#100;&#101;&#109;&#97;&#99;&#114;&#111;">root@codemacro</a>.com。</p>
<p>这里使用的软件集包括：</p>
<ul>
<li>nginx，Web服务器</li>
<li><a href="http://www.sbcl.org" target="_blank">SBCL</a> ，Lisp编译器实现</li>
<li><a href="http://www.quicklisp.org/" target="_blank">quicklisp</a> ，可以方便自动下载、安装Lisp库的工具</li>
<li><a href="http://weitz.de/hunchentoot/" target="_blank">hunchentoot</a> ，Lisp实现的Web服务器（不用特意安装）</li>
<li><a href="http://codemacro.com/view/8" target="_blank">ext-blog</a> ，Lisp实现的博客系统</li>
</ul>
<p>实际上，可以完全使用Lisp作为Web服务器，但我担心效率问题（对个人博客而言完全没这回事），所以使用了nginx作为Web服务器前端，将hunchentoot放在后面。</p>
<h4>安装nginx</h4>
<p>在设置好debian软件源后，安装非常简单:</p>
<pre class="brush:bash">apt-get install nginx<br /></pre>
<p>安装完后，因为要将HTTP请求转发给Lisp服务器，所以需要修改下配置:</p>
<pre class="brush:bash">vi /etc/nginx/sites-avaiable/default<br /></pre>
<p>将/请求派发给Lisp服务器（假设监听于8000端口）:</p>
<pre class="brush:bash">location / {<br />    proxy_pass http://127.0.0.1:8000;<br />    proxy_set_header Host $host;<br />    proxy_set_header X-Real-IP $remote_addr;<br />}<br /></pre>
<p>然后可以启动nginx了:</p>
<pre class="brush:bash">nginx<br /></pre>
<p>这个时候通过浏览器访问，会得到503 bad gateway的错误提示，因为hunchentoot还没开启。</p>
<h4>安装Lisp相关软件</h4>
<p><a href="http://www.sbcl.org" target="_blank">SBCL</a> 同样可以通过apt直接安装:</p>
<pre class="brush:bash">apt-get instal sbcl<br /></pre>
<p>装好SBCL后，就可以进一步安装 <a href="http://www.quicklisp.org/" target="_blank">quicklisp</a> 。可以完全遵守quicklisp官方给的安装方法进行安装。大概就是先获取quicklisp.lisp文件，然后在SBCL中载入，根据提示即可。这里不再赘述。</p>
<p>安装好quicklisp后，就可以使用它安装很多Lisp软件/库了。quicklisp在安装一个Lisp库时，会自动下载并安装依赖库，就像apt-get一样。因为ext-blog并未收入到quicklisp的软件列表里，所以ext-blog需要手动安装。首先，在本地（非VPS上）获取ext-blog源码:</p>
<pre class="brush:bash">git clone git://github.com/kevinlynx/klprj.git<br /></pre>
<p>上面的git是我个人存东西用的，暂时没将ext-blog单独放置。进入到ext-blog目录。该目录下有几个方便的脚本可以用于博客管理。首先将ext-blog打包并上传到VPS上，例如:</p>
<pre class="brush:bash">./upload-dist.sh root@codemacro.com 1234 /home/test<br /></pre>
<p>该脚本会调用make-dist.sh将ext-blog全部源码打包，然后使用scp拷贝该文件及update-blog.sh到VPS指定的目录里（这里是/home/test），然后ssh上VPS。期间会两次输入VPS系统的密码。然后以下操作将在VPS上完成。</p>
<p>首先进入到刚才拷贝文件的目录:</p>
<pre class="brush:bash">cd /home/test<br /></pre>
<p>解压ext-blog.tar.gz:</p>
<pre class="brush:bash">tar xvf ext-blog.tar.gz<br /></pre>
<p>然后ext-blog被解压到/home/test/dist目录。进入此目录运行SBCL:</p>
<pre class="brush:bash">cd dist<br />sbcl<br /></pre>
<p>ext-blog目录下dep.lisp会使用quicklisp安装依赖库，进入SBCL后，载入该文件即可安装所有依赖库，这可能需要一点时间:</p>
<pre class="brush:bash">(load "dep.lisp")<br /></pre>
<p>在没有其他问题下，可以暂时退出SBCL完成一些其他准备工作。</p>
<p>ext-blog在最近的版本中加入了验证码生成功能，这需要一个pcf字体文件。因为字体文件一般较大，所以upload-dist.sh脚本并没有将该字体文件打包，所以这里需要手动复制，同样在本地的ext-blog目录下:</p>
<pre class="brush:bash">scp -P 1234 data/wenquanyi_12ptb.pcf root@codemacro.com:/home/test/dist/data/<br /></pre>
<p>另外，因为需要将Lisp解释器放置在系统后台执行，避免关掉SSH会话后终止SBCL进程，所以这里需要个工具gnu screen。可以使用apt-get来安装:</p>
<pre class="brush:bash">apt-get install screen<br /></pre>
<p>然后，一切就OK了。在VPS上可以使用ext-blog目录下的run-blog.sh来运行这个博客（首先确定VPS上的nginx开启）:</p>
<pre class="brush:bash">./run-blog.sh<br /></pre>
<p>该脚本会使用screen在后台开启一个SBCL进程，并自动载入ext-blog，然后在8000端口上开启HTTP服务。这个启动过程可能会使用几十秒的时间，直接ctrl+z退出screen，这并不终止SBCL。一段时间后便可在浏览器里测试。</p>
<h4>设置博客</h4>
<p>如果一切正常，此时通过浏览器访问你的站点时，会被重定向到一个博客初始化页面，如下：</p>
<img alt="imgs/initblog.png" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/cl-writer/20119291759/initblog.png" />
<p>上图中我是在本机测试的，所以域名是localhost，希望不至于产生误解。初始化仅需输入用户名和密码即可，以后可通过该用户名和密码进入博客后台管理页面。完成这一步后，就可以进入博客后台管理页面做更多的设置，例如博客标题等。</p>
<p>ext-blog的管理页面使用了emlog博客系统的CSS及其他资源，因此有同学觉得管理页面很面熟就不奇怪了。ext-blog提供在线编辑博客功能，同时也支持简单的metaweblog API，因此可以使用一些博客客户端来发表文章（仅测过我自己写的博客客户端cl-writer）。</p>
<h3>最后</h3>
<p>本文描述较为粗略，主要是很多细节我自己也记不清。如有问题可以发邮件给我。</p><img src ="http://www.cppblog.com/kevinlynx/aggbug/157144.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2011-09-29 17:19 <a href="http://www.cppblog.com/kevinlynx/archive/2011/09/29/157144.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Lisp开发博客系统（WordPress马甲）</title><link>http://www.cppblog.com/kevinlynx/archive/2011/08/05/152563.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Fri, 05 Aug 2011 08:43:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2011/08/05/152563.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/152563.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2011/08/05/152563.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/152563.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/152563.html</trackback:ping><description><![CDATA[<p>4月份的时候基于nuclblog写过一个简单的博客系统，但是因为写得丑，代码耦合度高，又有很多硬编码。当然nuclblog本身就写得不怎么样，所以6月分的时候就用Lisp写了新版的ext-blog。支持自定义主题，套个马甲上去像模像样。</p>
<p>ext-blog是一个使用Common Lisp编写的博客系统。基于之前基于nuclblog修改的经验，新的ext-blog最大程度地将博客本身的逻辑与前台渲染分离开，并且添加了对主题 (theme)的支持。制作新的主题可以随便找一个WordPress的主题，然后将php代码翻译成Lisp代码即可。</p>
<p>ext-blog底层代码非常少，其实基本的博客系统功能本来就不多。大部分功能都是在6月初完成。那个时候公司每天加班，下班回去后还写点Lisp代码。后来越整越累，就实在没那完善它的心情，一拖就拖到7月底，功能都还不算完善（至少还得加个rss导出吧？）。</p>
<h3>关于主题开发</h3>
<p>ext-blog主要有几个页面派发，对每个页面都派发给具体的主题模块，让其完成渲染。编写一个主题本质上就是生成html页面。在Lisp的世界中有很多库可以生成html。ext-blog的主题也不限制你使用哪一个html生成库。目前我自己移植的2个WordPress主题，使用的都是google的closure-template的Lisp移植版本，即cl-closure-template。closure-template会从模板产生出 Lisp函数，这一点是比同类库中的html-template方便一点。当然，作为一个模板语言，内置判断、循环则是必须的。</p>
<h3>关于网络框架</h3>
<p>世界上很多流行的语言都有流行的Web开发框架。Lisp方面，我最开始选用的是Weblocks，我甚至用它为公司写了个简单的订餐系统（这让一个程序员颇有自豪感）。但终究觉得Weblocks太难用，复杂，但没有实际功能。我甚至阅读了它80%的源代码，但依然获取不到如何更好使用它的思想。然后恰好我看了些Rails例子，虽然我不懂Ruby语言（依然可以看到很多语言特性有Lisp的影子），但看懂例子还不是大问题。后来我决定自己写个 Web框架，因为其实我主要需要的就是一个url派发(route)，就像Rails那样。我甚至为此做了些详细设计，结果后来不幸发现Lisp里已经有一个类似的框架了，这就是Restas。ext-blog基于Restas。</p>
<h3>关于后台管理</h3>
<p>后台管理这东西其实可要可不要。就算没有后台管理，也可以通过增强RPC来实现。但并不是每个人都是Lisper，相信想了解ext-blog的人很大一部分都是想学习Lisp的人。综合来看，拥有一个后台管理功能，提供更友好的操作界面，也是非常有必要的。但我确实不擅长做前台美化的工作。幸运地是我将渲染和逻辑分离开了，后台管理也算是主题的一种。然后，我抄了emlog博客系统的后台管理，如前所说，也就是把php代码（虽然我也不懂php）翻译成lisp代码。</p>
<h3>关于开源</h3>
<p>ext-blog是完全有理由发布到common-lisp.net上的，甚至还可以加入到quicklisp的库列表里。但前提是排除尽可能多的 bug，写一系列英文文档，以及最重要的，对其进行长期维护。不幸的是我目前没有这个时间和精力。所以，只能暂时在这里发布下了。</p><p><br /></p><p>要围观效果的请移步至我的独立博客：<a title="http://codemacro.com" href="http://codemacro.com">http://codemacro.com</a>。关于ext-blog更正式的介绍请移步此篇：<a title="http://codemacro.com/view/8" href="http://codemacro.com/view/8">http://codemacro.com/view/8</a>。</p><p>ps，之前订阅我独立博客的TX麻烦更换下rss地址：<a title="http://codemacro.com/feed" href="http://codemacro.com/feed">http://codemacro.com/feed</a>，而博客主页也最好换成<a title="http://codemacro.com" href="http://codemacro.com">http://codemacro.com</a>。</p><p><br /></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/152563.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2011-08-05 16:43 <a href="http://www.cppblog.com/kevinlynx/archive/2011/08/05/152563.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lisp实践：开发RSS阅读器</title><link>http://www.cppblog.com/kevinlynx/archive/2011/03/30/142991.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 30 Mar 2011 01:32:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2011/03/30/142991.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/142991.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2011/03/30/142991.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/142991.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/142991.html</trackback:ping><description><![CDATA[<div id="lisp-rss" class="document"><h1 class="title">Lisp&#x5B9E;&#x8DF5;&#xFF1A;&#x5F00;&#x53D1;RSS&#x9605;&#x8BFB;&#x5668;</h1><table rules="none" frame="void" class="docinfo"><col class="docinfo-name"></col><col class="docinfo-content"></col><tbody valign="top"><tr><th class="docinfo-name">Author:</th><td>Kevin Lynx</td></tr><tr><th class="docinfo-name">Date:</th><td>3.30.2011</td></tr><tr><th class="docinfo-name">Contact:</th><td>kevinlynx at gmail dot com</td></tr></tbody></table><div class="tip"><p class="first admonition-title">Tip</p><p class="last">&#x672C;&#x6587;&#x7B80;&#x8981;&#x4ECB;&#x7ECD;&#x4E86;&#x5982;&#x4F55;&#x4F7F;&#x7528;Lisp&#x5B9E;&#x73B0;&#x4E00;&#x4E2A;&#x7B80;&#x5355;&#x7684;RSS&#x9605;&#x8BFB;&#x5668;&#xFF0C;&#x5BF9;Lisp&#x65E0;&#x5174;&#x8DA3;&#x7684;TX&#x53EF;&#x4EE5;
&#x53EA;&#x5BF9;RSS&#x9605;&#x8BFB;&#x5668;&#x7684;&#x5B9E;&#x73B0;&#x601D;&#x8DEF;&#x7A0D;&#x4F5C;&#x4E86;&#x89E3;&#x5373;&#x53EF;&#x3002;</p></div><div id="rss" class="section"><h1>&#x4E00;&#x3001;RSS&#x9605;&#x8BFB;&#x5668;&#x7684;&#x5B9E;&#x73B0;</h1><p>RSS Reader&#x7684;&#x5B9E;&#x73B0;&#x5E76;&#x4E0D;&#x50CF;&#x5B83;&#x770B;&#x4E0A;&#x53BB;&#x90A3;&#x4E48;&#x590D;&#x6742;&#x3002;&#x5F53;&#x521D;&#x5728;&#x51B3;&#x5B9A;&#x5199;&#x8FD9;&#x4E2A;&#x4F5C;&#x4E3A;Lisp&#x7EC3;&#x4E60;&#x65F6;&#xFF0C;&#x751A;&#x81F3;&#x89C9;&#x5F97;
&#x6CA1;&#x6709;&#x591A;&#x5C11;&#x5185;&#x5BB9;&#x53EF;&#x505A;&#x3002;&#x5176;&#x7B80;&#x5355;&#x7A0B;&#x5EA6;&#x751A;&#x81F3;&#x7528;&#x4E0D;&#x4E86;&#x4F60;&#x542F;&#x52A8;&#x4E00;&#x4E2A;&#x6162;&#x901F;IDE&#x7684;&#x65F6;&#x95F4;:D&#x3002;&#x5BF9;Lisp&#x65E0;&#x5174;&#x8DA3;&#x7684;
TX&#x53EA;&#x9700;&#x8981;&#x8BFB;&#x5B8C;&#x8FD9;&#x4E00;&#x8282;&#x5373;&#x53EF;&#xFF0C;</p><div id="id1" class="section"><h2>&#x4EC0;&#x4E48;&#x662F;RSS&#x9605;&#x8BFB;&#x5668;?</h2><p>RSS&#x5728;&#x5B9E;&#x73B0;&#x4E0A;&#xFF0C;&#x53EF;&#x4EE5;&#x8BF4;&#x662F;XML&#x7684;&#x53C8;&#x4E00;&#x6B21;&#x6269;&#x5F20;&#x5F0F;&#x7684;&#x5E94;&#x7528;&#x3002;&#x56E0;&#x4E3A;RSS&#x6700;&#x91CD;&#x8981;&#x7684;&#x4E1C;&#x897F;&#x5C31;&#x662F;&#x4E00;&#x4E2A;XML&#x6587;&#x4EF6;
&#x3002;RSS&#x4E3B;&#x8981;&#x7528;&#x4E8E;Web&#x4E2D;&#x7684;&#x5185;&#x5BB9;&#x540C;&#x6B65;&#x3002;&#x4F8B;&#x5982;&#x6211;&#x4EEC;&#x5199;&#x7684;&#x535A;&#x5BA2;&#xFF0C;&#x95E8;&#x6237;&#x7F51;&#x7AD9;&#x7684;&#x65B0;&#x95FB;&#xFF0C;&#x90FD;&#x662F;&#x5185;&#x5BB9;&#x3002;Web&#x670D;
&#x52A1;&#x5668;&#x5C06;&#x8FD9;&#x4E9B;&#x5185;&#x5BB9;&#x7EC4;&#x7EC7;&#x6210;XML&#xFF0C;&#x7136;&#x540E;&#x6211;&#x4EEC;&#x901A;&#x8FC7;&#x4E00;&#x4E2A;&#x5BA2;&#x6237;&#x7AEF;&#x6765;&#x89E3;&#x6790;&#x8FD9;&#x4E9B;XML&#xFF0C;&#x5C31;&#x53EF;&#x4EE5;&#x5728;&#x4E0D;&#x7528;&#x76F4;&#x63A5;&#x8BBF;
&#x95EE;&#x7F51;&#x7AD9;&#x7684;&#x60C5;&#x51B5;&#x4E0B;&#x83B7;&#x53D6;&#x4FE1;&#x606F;&#xFF1A;</p><img src="http://www.cppblog.com/images/cppblog_com/kevinlynx/cl-writer/201133093232/rss-overview.png" alt="imgs/rss-overview.png"></img><p>RSS&#x9605;&#x8BFB;&#x5668;&#x5C31;&#x662F;&#x8FD9;&#x6837;&#x4E00;&#x4E2A;&#x4ECE;Web&#x670D;&#x52A1;&#x5668;&#x901A;&#x8FC7;RSS&#xFF08;&#x8868;&#x73B0;&#x5F62;&#x5F0F;&#x4E3A;XML&#xFF09;&#x6765;&#x83B7;&#x53D6;&#x4FE1;&#x606F;&#x5185;&#x5BB9;&#x7684;&#x5DE5;&#x5177;&#x3002;&#x5B83;
&#x53EF;&#x4EE5;&#x88AB;&#x5B9E;&#x73B0;&#x4E3A;&#x4E00;&#x4E2A;&#x72EC;&#x7ACB;&#x7684;&#x5BA2;&#x6237;&#x7AEF;&#x7A0B;&#x5E8F;&#xFF0C;&#x4E5F;&#x53EF;&#x4EE5;&#x5B9E;&#x73B0;&#x4E3A;&#x50CF;Google Reader&#x8FD9;&#x79CD;&#x7F51;&#x9875;&#x5F62;&#x5F0F;&#x3002;&#x540E;&#x8005;
&#x5176;&#x6838;&#x5FC3;&#x529F;&#x80FD;&#x5176;&#x5B9E;&#x662F;Google&#x670D;&#x52A1;&#x5668;&#x5728;&#x505A;&#xFF0C;&#x53D6;&#x5F97;&#x4FE1;&#x606F;&#x540E;&#x518D;&#x53D1;&#x7ED9;&#x7528;&#x6237;&#x3002;</p></div><div id="id2" class="section"><h2>RSS&#x6587;&#x4EF6;</h2><p>&#x4E0A;&#x5DF2;&#x63D0;&#x53CA;&#xFF0C;RSS&#x7684;&#x5B9E;&#x73B0;&#x5176;&#x5B9E;&#x5C31;&#x662F;&#x4E2A;XML&#x6587;&#x4EF6;&#x3002;&#x8FD9;&#x4E2A;XML&#x6587;&#x4EF6;&#x683C;&#x5F0F;&#x975E;&#x5E38;&#x7B80;&#x5355;&#xFF0C;&#x4F8B;&#x5982;:</p><pre class="literal-block">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;rss version=&quot;2.0&quot;&gt;
   &lt;channel&gt;
      &lt;title&gt;Liftoff News&lt;/title&gt;
      &lt;link&gt;http://liftoff.msfc.nasa.gov/&lt;/link&gt;
      &lt;description&gt;Liftoff to Space Exploration.&lt;/description&gt;
      &lt;item&gt;
         &lt;title&gt;Star City&lt;/title&gt;
         &lt;link&gt;http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp&lt;/link&gt;
         &lt;description&gt;Oh no, you wrote another blog!&lt;/description&gt;
      &lt;/item&gt;
    &lt;/channel&gt;
&lt;/rss&gt;
</pre><p>&#x6211;&#x4EEC;&#x8EAB;&#x8FB9;&#x5230;&#x5904;&#x90FD;&#x662F;RSS&#x6587;&#x4EF6;&#xFF0C;&#x4F8B;&#x5982; <a href="http://www.cppblog.com/rss.aspx" class="reference external">http://www.cppblog.com/rss.aspx</a> &#x3002;RSS&#x6587;&#x4EF6;&#x7684;&#x6846;&#x67B6;&#x5927;
&#x81F4;&#x4E3A;:</p><pre class="literal-block">
&lt;rss&gt;
    &lt;channel&gt;
        &lt;item&gt;
        &lt;/item&gt;
        &lt;item&gt;
        &lt;/item&gt;
        ...
    &lt;/channel&gt;
&lt;/rss&gt;
</pre><p>&#x5BF9;&#xFF0C;&#x5176;&#x6846;&#x67B6;&#x5C31;&#x662F;&#x8FD9;&#x6837;&#xFF0C;&#x4E00;&#x4E2A;channel&#x8282;&#x70B9;&#xFF0C;&#x5176;&#x4E0B;&#x82E5;&#x5E72;&#x4E2A;item&#x8282;&#x70B9;&#x3002;&#x4E3E;&#x4F8B;&#x6765;&#x8BF4;&#xFF0C; <strong>CPPBLOG&#x9996;&#x9875;&#x5C31;
&#x662F;&#x4E00;&#x4E2A;channel&#xFF0C;&#x8BE5;channel&#x4E0B;&#x6709;&#x82E5;&#x5E72;&#x539F;&#x521B;&#x6587;&#x7AE0;&#xFF0C;&#x6BCF;&#x7BC7;&#x6587;&#x7AE0;&#x5C31;&#x662F;&#x4E00;&#x4E2A;item&#x3002;</strong> &#x65E0;&#x8BBA;&#x662F;channel
&#xFF0C;&#x8FD8;&#x662F;item&#xFF0C;&#x90FD;&#x4F1A;&#x6709;&#x5F88;&#x591A;&#x5C5E;&#x6027;&#xFF0C;&#x4F8B;&#x5982;title/description/link&#xFF0C;&#x6709;&#x4E9B;&#x5C5E;&#x6027;&#x662F;RSS&#x89C4;&#x8303;&#x91CC;&#x8981;&#x6C42;
&#x5FC5;&#x987B;&#x6709;&#x7684;&#xFF0C;&#x6709;&#x7684;&#x662F;&#x53EF;&#x9009;&#x7684;&#x3002;</p></div><div id="id3" class="section"><h2>&#x4EA4;&#x4E92;&#x8FC7;&#x7A0B;</h2><p>&#x90A3;&#x4E48;&#xFF0C;&#x670D;&#x52A1;&#x5668;&#x548C;&#x5BA2;&#x6237;&#x7AEF;&#x662F;&#x5982;&#x4F55;&#x4EA4;&#x4E92;&#x7684;&#x5462;&#xFF1F;&#x9996;&#x5148;&#xFF0C;&#x670D;&#x52A1;&#x5668;&#x4E0A;&#x7684;&#x7A0B;&#x5E8F;&#x9488;&#x5BF9;&#x5176;&#x67D0;&#x4E2A;&#x9875;&#x9762;&#xFF0C;&#x751F;&#x6210;&#x5BF9;&#x5E94;
&#x7684;RSS&#x6587;&#x4EF6;&#x3002;&#x8FD9;&#x4E2A;RSS&#x6587;&#x4EF6;&#x57FA;&#x672C;&#x4E0A;&#x662F;&#x6709;&#x56FA;&#x5B9A;&#x7684;URL&#x7684;&#x3002;&#x5BA2;&#x6237;&#x7AEF;&#x6BCF;&#x6B21;&#x83B7;&#x53D6;&#x5185;&#x5BB9;&#x65F6;&#xFF0C;&#x5C31;&#x4ECE;&#x8FD9;&#x4E2A;&#x56FA;&#x5B9A;
&#x7684;URL&#x83B7;&#x53D6;&#x8FD9;&#x4E2A;RSS&#x6587;&#x4EF6;&#x3002;&#x5BA2;&#x6237;&#x7AEF;&#x83B7;&#x53D6;&#x5230;&#x8FD9;&#x4E2A;RSS&#x6587;&#x4EF6;&#x540E;&#xFF0C;&#x89E3;&#x6790;&#x4E4B;&#xFF0C;&#x518D;&#x5448;&#x73B0;&#x7ED9;&#x7528;&#x6237;&#x3002;&#x8FD9;&#x5C31;&#x662F;&#x6574;
&#x4E2A;&#x8FC7;&#x7A0B;&#x3002;&#x8FD9;&#x4E2A;&#x8FC7;&#x7A0B;&#x4E2D;&#x5BA2;&#x6237;&#x7AEF;&#x4E0E;&#x670D;&#x52A1;&#x5668;&#x7684;&#x4EA4;&#x4E92;&#xFF0C;&#x5168;&#x90E8;&#x662F;&#x6B63;&#x5E38;&#x7684;HTTP&#x8BF7;&#x6C42;&#x3002;</p><p>&#x800C;RSS&#x9605;&#x8BFB;&#x5668;&#xFF0C;&#x5982;&#x679C;&#x505A;&#x5F97;&#x8DB3;&#x591F;&#x7B80;&#x5355;&#xFF0C;&#x5219;&#x53EA;&#x9700;&#x8981;&#x4ECE;&#x6307;&#x5B9A;&#x7684;&#x5730;&#x65B9;&#x83B7;&#x53D6;&#x5230;RSS&#x6587;&#x4EF6;&#xFF0C;&#x7136;&#x540E;&#x89E3;&#x6790;&#x8FD9;&#x4E2A;
XML&#x6587;&#x4EF6;&#xFF0C;&#x7136;&#x540E;&#x4EE5;&#x76F8;&#x5BF9;&#x53CB;&#x597D;&#x7684;&#x5F62;&#x5F0F;&#x663E;&#x793A;&#x5373;&#x53EF;&#x3002;</p></div><div id="id4" class="section"><h2>&#x6269;&#x5C55;</h2><p>&#x867D;&#x7136;RSS&#x9605;&#x8BFB;&#x5668;&#x5728;&#x6838;&#x5FC3;&#x529F;&#x80FD;&#x4E0A;&#x5F88;&#x7B80;&#x5355;&#xFF0C;&#x4F46;&#x662F;&#x4F5C;&#x4E3A;&#x4E00;&#x4E2A;&#x53EF;&#x4EE5;&#x4F7F;&#x7528;&#x7684;&#x5DE5;&#x5177;&#xFF0C;&#x4F9D;&#x7136;&#x6709;&#x5F88;&#x591A;&#x529F;&#x80FD;&#x70B9;&#x9700;
&#x8981;&#x5B9E;&#x73B0;&#x3002;&#x57FA;&#x672C;&#x529F;&#x80FD;&#x5305;&#x62EC;&#xFF1A;</p><ul class="simple"><li>&#x8BB0;&#x5F55;&#x7528;&#x6237;&#x5173;&#x6CE8;&#x7684;RSS</li><li>&#x7F13;&#x5B58;&#x67D0;&#x4E2A;RSS&#x8FC7;&#x53BB;&#x4E00;&#x6BB5;&#x65F6;&#x95F4;&#x66F4;&#x65B0;&#x7684;&#x5185;&#x5BB9;</li><li>&#x5BF9;HTTP&#x56DE;&#x5E94;&#x7684;&#x5904;&#x7406;&#xFF0C;&#x6700;&#x91CD;&#x8981;&#x7684;&#x5C31;&#x662F;&#x91CD;&#x5B9A;&#x5411;&#x5904;&#x7406;</li></ul><p>&#x6211;&#x4EEC;&#x8FD8;&#x53EF;&#x4EE5;&#x505A;&#x5F88;&#x591A;&#x6269;&#x5C55;&#xFF0C;&#x4F8B;&#x5982;Google Reader&#x4E4B;&#x7C7B;&#x7684;&#x5728;&#x7EBF;RSS&#x9605;&#x8BFB;&#x5668;&#x3002;&#x8FD9;&#x4E9B;&#x9605;&#x8BFB;&#x5668;&#x7684;RSS&#x6293;&#x53D6;
&#x529F;&#x80FD;&#x505A;&#x5728;&#x670D;&#x52A1;&#x5668;&#x7AEF;&#xFF0C;&#x5B83;&#x4EEC;&#x9664;&#x4E86;&#x4E0A;&#x9762;&#x63D0;&#x5230;&#x7684;&#x57FA;&#x7840;&#x529F;&#x80FD;&#x5916;&#xFF0C;&#x8FD8;&#x4F1A;&#x5305;&#x542B;&#x5185;&#x5BB9;&#x5206;&#x7C7B;&#xFF0C;&#x7ED9;&#x5185;&#x5BB9;&#x6253;&#x4E00;&#x4E9B;
&#x6807;&#x7B7E;&#xFF0C;&#x5206;&#x6790;&#x7528;&#x6237;&#x7684;&#x8BA2;&#x9605;&#x4E60;&#x60EF;&#x7136;&#x540E;&#x63A8;&#x8350;&#x7C7B;&#x4F3C;&#x7684;&#x5185;&#x5BB9;&#x7B49;&#x7B49;&#x3002;</p></div></div><div id="lisp" class="section"><h1>&#x4E8C;&#x3001;Lisp&#x5B9E;&#x73B0;</h1><p>&#x672C;&#x8282;&#x63CF;&#x8FF0;&#x5728;Lisp&#x4E2D;&#x5B9E;&#x73B0;&#x4E0A;&#x6587;&#x7684;&#x5185;&#x5BB9;&#x3002;&#x4E3B;&#x8981;&#x5305;&#x62EC;&#xFF1A; <strong>&#x901A;&#x8FC7;HTTP&#x8BF7;&#x6C42;&#x83B7;&#x53D6;&#x5230;RSS&#x6587;&#x4EF6;&#x3001;&#x89E3;&#x6790;RSS&#x6587;&#x4EF6;
&#x3002;</strong></p><div id="id5" class="section"><h2>&#x83B7;&#x53D6;RSS&#x6587;&#x4EF6;</h2><p>Lisp&#x867D;&#x7136;&#x5386;&#x53F2;&#x60A0;&#x4E45;&#xFF0C;&#x4F46;&#x5176;&#x6269;&#x5C55;&#x5E93;&#x6807;&#x51C6;&#x5374;&#x505A;&#x5F97;&#x5F88;&#x62D9;&#x52A3;&#x3002;&#x504F;&#x5E94;&#x7528;&#x7EA7;&#x7684;&#x6269;&#x5C55;&#x5E93;&#x8981;&#x4E48;&#x7531;&#x7F16;&#x8BD1;&#x5668;&#x5B9E;&#x73B0;&#x63D0;
&#x4F9B;&#xFF0C;&#x8981;&#x4E48;&#x5C31;&#x5F97;&#x81EA;&#x5DF1;&#x5728;&#x7F51;&#x4E0A;&#x627E;&#x3002;&#x4E00;&#x65B9;&#x9762;&#x4F7F;&#x7528;&#x8005;&#x5E0C;&#x671B;&#x5E93;&#x4F7F;&#x7528;&#x8D77;&#x6765;&#x65B9;&#x4FBF;&#xFF0C;&#x53E6;&#x4E00;&#x65B9;&#x9762;&#x5E93;&#x5F00;&#x53D1;&#x8005;&#x5728;&#x8DE8;&#x7F16;
&#x8BD1;&#x5668;&#x5B9E;&#x73B0;&#x65B9;&#x9762;&#x4E5F;&#x5934;&#x75BC;&#x4E0D;&#x5DF2;&#x3002;&#x6240;&#x5E78;&#x73B0;&#x5728;&#x6709;&#x4E86;quick lisp&#xFF0C;&#x5B89;&#x88C5;&#x7B2C;&#x4E09;&#x65B9;&#x5E93;&#x5C31;&#x50CF;Ubuntu&#x91CC;&#x5B89;&#x88C5;&#x8F6F;&#x4EF6;
&#x4E00;&#x6837;&#x7B80;&#x5355;&#xFF08;&#x5927;&#x90E8;&#x5206;&#xFF09;&#x3002;</p><p>socket&#x652F;&#x6301;&#x5C31;&#x662F;&#x9762;&#x4E34;&#x7684;&#x7B2C;&#x4E00;&#x4E2A;&#x95EE;&#x9898;&#x3002;&#x4E0D;&#x8FC7;&#x6211;&#x8FD9;&#x91CC;&#x5E76;&#x4E0D;&#x8003;&#x8651;&#x8DE8;&#x7F16;&#x8BD1;&#x5668;&#x5B9E;&#x73B0;&#x7684;&#x95EE;&#x9898;&#xFF0C;&#x76F4;&#x63A5;&#x4F7F;&#x7528;
SBCL&#x91CC;&#x7684;socket&#x63A5;&#x53E3;&#x3002;</p><p>&#x8981;&#x83B7;&#x53D6;RSS&#x6587;&#x4EF6;&#xFF0C;&#x53EA;&#x9700;&#x8981;&#x8FDE;&#x63A5;Web&#x670D;&#x52A1;&#x5668;&#xFF0C;&#x53D1;&#x8D77;HTTP&#x7684;GET&#x8BF7;&#x6C42;&#x5373;&#x53EF;&#x3002;&#x5F53;&#x7136;&#xFF0C;&#x5EFA;&#x7ACB;TCP&#x8FDE;&#x63A5;&#xFF0C;&#x7EC4;
&#x5EFA;HTTP&#x8BF7;&#x6C42;&#x5305;&#xFF0C;&#x5C31;&#x4E0D;&#x662F;&#x8FD9;&#x91CC;&#x7684;&#x8BA8;&#x8BBA;&#x4E86;&#x3002;&#x6211;&#x4EEC;&#x8FD8;&#x662F;&#x62FF;CPPBLOG&#x9996;&#x9875;&#x7684;RSS&#x4E3A;&#x4F8B;&#xFF0C;&#x8BE5;RSS&#x7684;URL&#x4E3A;:</p><pre class="literal-block">
http://www.cppblog.com/rss.aspx
</pre><p>&#x62C6;&#x5206;&#x4E00;&#x4E0B;&#xFF0C;&#x5F97;&#x5230;host&#x4E3A;www.cppblog.com&#xFF08;&#x5373;&#x6211;&#x4EEC;&#x8981;connect&#x7684;&#x5730;&#x5740;&#xFF09;&#xFF0C;rss&#x7684;uri&#x4E3A;
/rss.aspx&#xFF08;&#x5373;HTTP&#x8BF7;&#x6C42;&#x91CC;&#x7684;&#x6587;&#x4EF6;URI&#xFF09;&#xFF0C;&#x4E8E;&#x662F;&#x5EFA;&#x7ACB;HTTP&#x8BF7;&#x6C42;&#x5305;:</p><pre class="literal-block">
GET /rss.aspx HTTP/1.0
Host: www.cppblog.com
</pre><p>&#x5173;&#x4E8E;HTTP&#x8BF7;&#x6C42;&#x7684;&#x4E00;&#x4E9B;&#x57FA;&#x7840;&#x77E5;&#x8BC6;&#xFF0C;&#x53EF;&#x4EE5;&#x53C2;&#x8003;&#x6211;&#x5F88;&#x65E9;&#x524D;&#x5199;&#x7684;&#x4E00;&#x7BC7;&#x535A;&#x5BA2;&#xFF1A;&lt;<a href="http://www.cppblog.com/kevinlynx/archive/2008/07/30/57521.aspx" class="reference external">&#x5B9E;&#x73B0;&#x81EA;&#x5DF1;&#x7684;http&#x670D;&#x52A1;&#x5668;</a>&gt;&#x3002;
&#x6B63;&#x5E38;&#x60C5;&#x51B5;&#x4E0B;&#xFF0C;Web&#x670D;&#x52A1;&#x5668;&#x5C31;&#x4F1A;&#x8FD4;&#x56DE;RSS&#x7684;&#x6587;&#x4EF6;&#x5185;&#x5BB9;&#x3002;&#x7136;&#x540E;&#x6211;&#x4EEC;&#x5C31;&#x53EF;&#x4EE5;&#x7EE7;&#x7EED;&#x89E3;&#x6790;&#x3002;</p></div><div id="id6" class="section"><h2>&#x89E3;&#x6790;RSS</h2><p>RSS&#x672C;&#x8EAB;&#x662F;&#x4E00;&#x4E2A;XML&#x683C;&#x5F0F;&#x7684;&#x6587;&#x4EF6;&#x3002;&#x4E4B;&#x524D;&#x8FDE;&#x63A5;Web&#x670D;&#x52A1;&#x5668;&#x53D1;&#x8D77;HTTP&#x8BF7;&#x6C42;&#x6CA1;&#x6709;&#x7528;&#x5230;&#x7B2C;&#x4E09;&#x65B9;&#x5E93;&#xFF0C;&#x4F46;&#x662F;
&#x89E3;&#x6790;XML&#x6587;&#x4EF6;&#x4E0D;&#x662F;&#x51E0;&#x5341;&#x6765;&#x884C;&#x4EE3;&#x7801;&#x80FD;&#x641E;&#x5B9A;&#x7684;&#x4E8B;&#x60C5;&#xFF0C;&#x6240;&#x4EE5;&#x8FD9;&#x91CC;&#x9700;&#x8981;&#x9009;&#x7528;&#x4E00;&#x4E2A;&#x7B2C;&#x4E09;&#x65B9;&#x5E93;&#x3002;</p><p>&#x6211;&#x7528;&#x7684;&#x662F;s-xml&#xFF0C;&#x8FD9;&#x4E2A;&#x5E93;&#x5728;&#x6211;&#x4E4B;&#x524D;&#x7684; <a href="http://www.cppblog.com/kevinlynx/archive/2011/03/13/141713.aspx" class="reference external">&#x5173;&#x4E8E;Lisp&#x7684;&#x6587;&#x7AE0;</a> &#x4E2D;&#x63D0;&#x5230;&#x8FC7;&#x3002;s-xml&#x4E0E;&#x6211;&#x4E4B;&#x524D;&#x5728;C++
&#x9886;&#x57DF;&#x89C1;&#x5230;&#x7684;XML&#x89E3;&#x6790;&#x5E93;&#x6700;&#x5927;&#x7684;&#x4E0D;&#x540C;&#x70B9;&#x5728;&#x4E8E;&#xFF0C;&#x5B83;&#x63D0;&#x4F9B;&#x7684;API&#x662F;&#x57FA;&#x4E8E;&#x4E8B;&#x4EF6;&#x6A21;&#x5F0F;&#x7684;&#x3002;&#x610F;&#x601D;&#x662F;&#x8BF4;&#xFF0C;&#x4F60;&#x4E0D;
&#x8981;&#x53BB;&#x67E5;&#x8BE2;&#x67D0;&#x4E2A;element&#x7684;&#x503C;&#x662F;&#x591A;&#x5C11;&#xFF0C;&#x5F53;&#x6211;&#x89E3;&#x6790;&#x5230;&#x7684;&#x65F6;&#x5019;&#x4F1A;&#x544A;&#x8BC9;&#x4F60;&#x3002;&#x4E8B;&#x4EF6;&#x6A21;&#x5F0F;&#x7684;&#x7F16;&#x7A0B;&#x65B9;&#x5F0F;&#x81EA;&#x7136;
&#x79BB;&#x4E0D;&#x5F00;&#x56DE;&#x8C03;&#x51FD;&#x6570;:</p><pre class="literal-block">
(s-xml:start-parse-xml
  stream
  (make-instance 's-xml:xml-parser-state
                 :new-element-hook #'decode-rss-new-element
                 :finish-element-hook #'decode-rss-finish-element
                 :text-hook #'decode-rss-text)))
</pre><p>&#x4E0E;s-xml&#x4EA4;&#x4E92;&#x7684;&#x4E5F;&#x5C31;&#x662F;&#x4E0A;&#x9762;&#x4EE3;&#x7801;&#x91CC;&#x63D0;&#x5230;&#x7684;&#x4E09;&#x4E2A;&#x51FD;&#x6570;&#xFF1A;new-element-hook, finish-element-hook
, text-hook&#x3002;&#x8FD9;&#x79CD;&#x7C7B;&#x578B;&#x7684;&#x63A5;&#x53E3;&#x5BFC;&#x81F4;&#x89E3;&#x6790;&#x4EE3;&#x7801;&#x5927;&#x91CF;&#x51CF;&#x5C11;&#xFF0C;&#x4F46;&#x4E0D;&#x5229;&#x4E8E;&#x7406;&#x89E3;&#x3002;&#x6211;&#x4EEC;&#x8981;&#x5728;&#x6574;&#x4E2A;&#x89E3;&#x6790;
&#x8FC7;&#x7A0B;&#x4E2D;&#x4F20;&#x9012;&#x6570;&#x636E;&#xFF0C;&#x9700;&#x8981;&#x901A;&#x8FC7;&#x4E0E;s-xml&#x4EA4;&#x4E92;&#x7684;&#x51FD;&#x6570;&#x53C2;&#x6570;&#xFF08;&#x5F53;&#x7136;&#x4E0D;&#x4F1A;&#x8822;&#x5230;&#x53BB;&#x7528;&#x5168;&#x5C40;&#x53D8;&#x91CF;&#xFF09;&#x3002;</p><p>&#x89E3;&#x6790;&#x8FC7;&#x7A0B;&#x4E2D;&#x901A;&#x8FC7;&#x5F80;&#x51FD;&#x6570;&#x53C2;&#x6570;&#x6307;&#x5B9A;&#x7684;&#x5BF9;&#x8C61;&#x8EAB;&#x4E0A;&#x585E;&#x6570;&#x636E;&#x5B8C;&#x6210;&#xFF0C;&#x6574;&#x4E2A;&#x89E3;&#x6790;&#x5B9E;&#x73B0;&#x4E5F;&#x5C31;&#x51E0;&#x5341;&#x884C;&#x4EE3;&#x7801;&#x3002;
&#x6587;&#x7AE0;&#x5C3E;&#x53EF;&#x4E0B;&#x8F7D;&#x4EE3;&#x7801;&#x770B;&#x770B;&#x3002;</p></div><div id="id7" class="section"><h2>&#x663E;&#x793A;&#x51FA;&#x6765;</h2><p>&#x901A;&#x8FC7;&#x4E0A;&#x9762;&#x4E24;&#x6B65;&#xFF0C;&#x6211;&#x4EEC;&#x5F97;&#x5230;&#x4E86;RSS&#x6587;&#x4EF6;&#x3001;&#x89E3;&#x6790;&#x51FA;&#x4E86;&#x5177;&#x4F53;&#x5185;&#x5BB9;&#xFF0C;&#x6700;&#x540E;&#x4E00;&#x6B65;&#x5C31;&#x662F;&#x5448;&#x73B0;&#x51FA;&#x6765;&#x770B;&#x770B;&#x3002;RSS
&#x6587;&#x4EF6;&#x91CC;&#x6BCF;&#x4E2A;Item&#x90FD;&#x662F;&#x4E00;&#x7BC7;&#x6587;&#x7AE0;&#xFF08;&#x65B0;&#x95FB;&#x4E4B;&#x7C7B;&#xFF09;&#xFF0C;&#x8FD9;&#x4E2A;&#x6587;&#x7AE0;&#x5185;&#x5BB9;&#x53EF;&#x76F4;&#x63A5;&#x5305;&#x542B;HTML&#x6807;&#x8BB0;&#xFF0C;&#x8BF4;&#x767D;&#x4E86;&#xFF0C;
&#x8FD9;&#x4E9B;&#x5185;&#x5BB9;&#x5C31;&#x662F;&#x76F4;&#x63A5;&#x7684;HTML&#x5185;&#x5BB9;&#x3002;&#x8981;&#x663E;&#x793A;&#x8FD9;&#x4E9B;&#x5185;&#x5BB9;&#xFF0C;&#x6700;&#x7B80;&#x5355;&#x7684;&#x65B9;&#x6CD5;&#x5C31;&#x662F;&#x628A;&#x4E00;&#x4E2A;RSS&#x8F6C;&#x6362;&#x6210;&#x4E00;&#x79CD;
&#x7B80;&#x5355;&#x7684;HTML&#x6587;&#x4EF6;&#xFF0C;&#x4EE5;&#x4F9B;&#x9605;&#x8BFB;&#x3002;</p><p>&#x8FD9;&#x91CC;&#x5C31;&#x6D89;&#x53CA;&#x5230;HTML generator&#xFF0C;&#x51E0;&#x4E4E;&#x6240;&#x6709;&#x7684;Lisper&#x90FD;&#x4F1A;&#x5199;&#x4E00;&#x4E2A;HTML&#x4EA7;&#x751F;&#x5668;&#xFF08;&#x5E93;&#xFF09;&#xFF08;&#x867D;&#x7136;&#x76EE;&#x524D;
&#x6211;&#x8FD8;&#x6CA1;&#x5199;&#xFF09;&#x3002;&#x8FD9;&#x79CD;&#x5E93;&#x7684;&#x4F5C;&#x7528;&#x5C31;&#x662F;&#x65B9;&#x4FBF;&#x5730;&#x8F93;&#x51FA;HTML&#x6587;&#x4EF6;&#x3002;</p><p>Lisp&#x76F8;&#x5BF9;&#x4E8E;&#x5176;&#x4ED6;&#x8BED;&#x8A00;&#x5F88;&#x5927;&#x7684;&#x4E00;&#x4E2A;&#x7279;&#x70B9;&#xFF0C;&#x6216;&#x8005;&#x8BF4;&#x662F;&#x4F18;&#x70B9;&#xFF0C;&#x5C31;&#x662F;&#x5176;&#x8BED;&#x8A00;&#x672C;&#x8EAB;&#x7684;&#x6269;&#x5C55;&#x80FD;&#x529B;&#x3002;&#x8FD9;&#x79CD;&#x6269;
&#x5C55;&#x4E0D;&#x662F;&#x7B80;&#x5355;&#x7684;&#x6DFB;&#x52A0;&#x51E0;&#x4E2A;&#x51FD;&#x6570;&#xFF0C;&#x4E5F;&#x4E0D;&#x662F;&#x7C7B;&#xFF0C;&#x800C;&#x662F;&#x63D0;&#x4F9B;&#x4E00;&#x4E9B;&#x5C31;&#x50CF;&#x8BED;&#x8A00;&#x672C;&#x8EAB;&#x63D0;&#x4F9B;&#x7684;&#x7279;&#x6B8A;&#x64CD;&#x4F5C;&#x7B26;&#x4E00;&#x6837;
&#x7684;&#x4E1C;&#x897F;&#x3002;&#x800C;HTML generator&#x6B63;&#x662F;&#x8FD9;&#x79CD;&#x4E1C;&#x897F;&#x5927;&#x653E;&#x5F02;&#x5F69;&#x7684;&#x5730;&#x65B9;&#x3002;&#x8FD9;&#x79CD;&#x611F;&#x89C9;&#x6709;&#x70B9;&#x50CF;&#x5728;C++&#x4E2D;&#x901A;&#x8FC7;&#x6A21;
&#x677F;&#x9020;&#x51FA;&#x5404;&#x79CD;&#x589E;&#x5F3A;&#x8BED;&#x8A00;&#x7279;&#x6027;&#x7684;&#x4E1C;&#x897F;&#x4E00;&#x6837;&#xFF08;&#x4F8B;&#x5982;boost/loki&#xFF09;&#x3002;</p><p>&#x56E0;&#x4E3A;&#x6211;&#x8FD9;&#x91CC;&#x53EA;&#x662F;&#x8F93;&#x51FA;&#x7B80;&#x5355;&#x7684;HTML&#x6587;&#x4EF6;&#xFF0C;&#x4F55;&#x51B5;&#x6211;&#x5BF9;HTML&#x7684;&#x6807;&#x8BB0;&#x4E86;&#x89E3;&#x7684;&#x4E5F;&#x4E0D;&#x591A;&#xFF0C;&#x4E5F;&#x61D2;&#x5F97;&#x518D;&#x82B1;&#x7ECF;&#x5386;
&#x3002;&#x6240;&#x4EE5;&#x6211;&#x6682;&#x65F6;&#x4E5F;&#x5C31;&#x5C06;&#x5C31;&#x4E86;&#x4E9B;&#x571F;&#x65B9;&#x6CD5;:</p><pre class="literal-block">
(with-output-to-string (stream)
  (let ((channel (rss-channel rss))) ;&#x53D6;&#x51FA;channel&#x5BF9;&#x8C61;
   (format stream &quot;&lt;html&gt;&lt;head&gt;&lt;title&gt;~a&lt;/title&gt;&lt;/head&gt;&quot;
           (get-property channel :|title|)) ;&#x53D6;&#x51FA;channel&#x7684;title
</pre><p>&#x6700;&#x540E;&#x7EC4;&#x5408;&#x4E00;&#x4E9B;&#x63A5;&#x53E3;&#xFF0C;&#x5373;&#x53EF;&#x5C06;&#x6574;&#x4E2A;&#x8FC7;&#x7A0B;&#x8054;&#x7CFB;&#x8D77;&#x6765;&#xFF0C;&#x5BFC;&#x51FA;html&#x6587;&#x4EF6;:</p><pre class="literal-block">
(cl-rss-test:test-rss-http :uri &quot;/news/newshot/hotnewsrss.xml&quot;
                           :host &quot;cd.qq.com&quot;)
</pre><p>&#x7136;&#x540E;&#x5728;&#x6D4F;&#x89C8;&#x5668;&#x91CC;&#x67E5;&#x770B;&#xFF0C;&#x5982;&#x56FE;:</p><img src="http://www.cppblog.com/images/cppblog_com/kevinlynx/cl-writer/201133093232/screenshot.png" alt="imgs/screenshot.png"></img></div><div id="id8" class="section"><h2>&#x5176;&#x4ED6;</h2><p>&#x5F53;&#x4E00;&#x4E9B;&#x4EE3;&#x7801;&#x53EF;&#x4EE5;&#x5DE5;&#x4F5C;&#x8D77;&#x6765;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x5C31;&#x53EF;&#x4EE5;&#x7740;&#x624B;&#x6D4B;&#x8BD5;&#x8FD9;&#x6279;&#x4EE3;&#x7801;&#x3002;&#x7136;&#x540E;&#x6211;&#x5C31;&#x7528;&#x8FD9;&#x4E2A;&#x5DE5;&#x5177;&#x6D4B;&#x8BD5;&#x6211;
Google Reader&#x91CC;&#x8BA2;&#x9605;&#x7684;&#x4E00;&#x4E9B;RSS&#x3002;&#x6700;&#x5927;&#x7684;&#x95EE;&#x9898;&#xFF0C;&#x5C31;&#x662F;&#x5173;&#x4E8E;HTTP&#x91CD;&#x5B9A;&#x5411;&#x7684;&#x95EE;&#x9898;&#x3002;</p><p>&#x5F53;&#x670D;&#x52A1;&#x5668;&#x8FD4;&#x56DE;301&#x6216;&#x8005;302&#x7684;&#x9519;&#x8BEF;&#x4FE1;&#x606F;&#x65F6;&#xFF08;HTTP&#x56DE;&#x5E94;&#xFF09;&#xFF0C;&#x5C31;&#x6807;&#x793A;&#x8BF7;&#x6C42;&#x7684;URI&#x88AB;&#x79FB;&#x52A8;&#x5230;&#x4E86;&#x5176;&#x4ED6;&#x5730;
&#x65B9;&#xFF0C;&#x5BA2;&#x6237;&#x7AEF;&#x9700;&#x8981;&#x8BBF;&#x95EE;&#x65B0;&#x7684;&#x5730;&#x5740;&#x3002;&#x8FD9;&#x4E2A;&#x5176;&#x5B9E;&#x67E5;&#x67E5; <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html" class="reference external">HTTP&#x7684;&#x89C4;&#x8303;</a> &#x5C31;&#x53EF;&#x4EE5;&#x8F7B;&#x6613;&#x89E3;&#x51B3;&#x3002;&#x91CD;&#x5B9A;&#x5411;&#x65F6;&#xFF0C;
&#x65B0;&#x7684;URI&#x88AB;&#x6307;&#x5B9A;&#x5728;Response Header&#x91CC;&#x7684;Location&#x57DF;&#xFF0C;&#x53D6;&#x51FA;&#x6765;&#x53D1;&#x8D77;&#x7B2C;&#x4E8C;&#x6B21;&#x8BF7;&#x6C42;&#x5373;&#x53EF;&#x3002;</p><p><a href="http://www.cppblog.com/Files/kevinlynx/cl-rss.tar.gz" class="reference external">&#x4E0B;&#x8F7D;&#x4EE3;&#x7801;</a></p></div></div><div id="id9" class="section"><h1>&#x53C2;&#x8003;&#x6587;&#x6863;</h1><ul class="simple"><li>HTTP&#x89C4;&#x8303;:  <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html" class="reference external">http://www.w3.org/Protocols/rfc2616/rfc2616.html</a></li><li>RSS2.0&#x89C4;&#x8303;: <a href="http://feed2.w3.org/docs/rss2.html" class="reference external">http://feed2.w3.org/docs/rss2.html</a></li></ul><p>;;EOF;;</p></div></div><img src ="http://www.cppblog.com/kevinlynx/aggbug/142991.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2011-03-30 09:32 <a href="http://www.cppblog.com/kevinlynx/archive/2011/03/30/142991.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lisp一瞥：增强型变量Symbol</title><link>http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 22 Mar 2011 03:33:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/142462.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/142462.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/142462.html</trackback:ping><description><![CDATA[<div id="lisp-symbol" class="document">
<h1 class="title">Lisp一瞥：增强型变量Symbol</h1>
<table class="docinfo" frame="void" rules="none">
    <col class="docinfo-name"><col class="docinfo-content">
    <tbody valign="top">
        <tr>
            <th class="docinfo-name">Author:</th>
            <td>Kevin Lynx</td>
        </tr>
        <tr>
            <th class="docinfo-name">Date:</th>
            <td>3.21.2011</td>
        </tr>
        <tr>
            <th class="docinfo-name">Contact:</th>
            <td>kevinlynx at gmail dot com</td>
        </tr>
    </tbody>
</table>
<div class="note">
<p class="first admonition-title">Note</p>
<p class="last">本文描述的Lisp主要指Lisp的方言Common Lisp。</p>
</div>
<p>变量，是所有编程语言里都有的语法概念。在C/C++中，变量用于标示一个内存地址，而变
量名则在语法层面上代表这个地址。当链接器最终链接我们的程序时，就将这些名字替换
为实际的地址。在其他语言中，变量虽然或多或少有其他不同的含义，但也大致如此。</p>
<p>Lisp中的变量也差不多这样，但若将variable和Lisp中的 <strong>symbol</strong> 放在一起，则多少会
带来些困惑。</p>
<div id="lisp" class="section">
<h1>Lisp中的&#8220;变量"</h1>
<p>很多教授Lisp的书中，大概会简单地告诉我们可以使用如下的方式定义一个全局变量 <a id="id1" href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id12" class="footnote-reference">[1]</a>.</p>
<pre class="literal-block">(defparameter *var* 1)
</pre>
<p>如上代码，我们便定义了一个全局变量 <tt class="docutils literal">*var*</tt><a id="id2" href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id13" class="footnote-reference">[2]</a> ，它被初始化为数值1。同样，我们
还可以使用另一种基本相同的方式:</p>
<pre class="literal-block">(defvar *var* 1)
</pre>
<p>除了全局变量，我们还可以定义局部变量。但局部变量的定义稍显麻烦（却可能是另一种
设计考虑）。定义局部变量需要使用一些宏，或者特殊运算符，例如:</p>
<pre class="literal-block">(let ((var 1))
(format t "~a" var))
</pre>
<p>好了，就这些了。Lisp中关于变量的细节，也就这些。你甚至能用你在C/C++中的经验来窥
探一切。但是，我们很快就看到了很多困惑的地方。</p>
<p>我遇到的第一个困惑的地方来源于函数，那么等我讲讲函数再来分享下坎坷。</p>
</div>
<div id="id3" class="section">
<h1>Lisp中的函数</h1>
<p>Lisp中的函数绝对不复杂，你绝对不用担心我在忽悠你 <a id="id4" href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id14" class="footnote-reference">[3]</a> 。作为一门函数式语言，其首要
任务就是加强函数这个东西在整个语言里的功能。如果你喜欢广阅各种与你工作不相干的
技术，你肯定已经对很多函数式语言世界中的概念略有耳闻。例如闭包，以及first class
type <a id="id5" href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id15" class="footnote-reference">[4]</a> 。</p>
<p>Lisp中的函数就是first class type。这什么意思呢？直白来说， <strong>Lisp中的函数和变量
没什么区别，享有同等待遇</strong> 。进一步来说，变量fn的值可以是数值1，也可以是字符串
"hello"，甚至是某个函数。这其实就是C++程序员说的functor。</p>
<p>Lisp中定义函数非常简单:</p>
<pre class="literal-block">(defun add2 (x)
(+ 2 x))
</pre>
<p>这样，我们就定义了一个名为add2，有1个参数，1个返回值的函数。要调用该函数时，只需
要 <tt class="docutils literal">(add2 2)</tt> 即可。这简直和我们在Lisp中完成一个加法一模一样:<tt class="docutils literal">(+ 2 3)</tt></p>
<p>Lisp作为一门函数式语言，其函数也能作为另一个函数的参数和返回值 <a id="id6" href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id16" class="footnote-reference">[5]</a></p>
<pre class="literal-block">(defun apply-fn (fn x)
(funcall fn x))
</pre>
<p>apply-fn函数第一个参数是一个函数，它使用funcall函数间接地调用fn指向的函数。作为
一个C++程序员，这简直太好理解了，这完全就是一个函数指针的语法糖嘛。于是，假设我
们要使用apply-fn来间接调用add2函数:</p>
<pre class="literal-block">(apply-fn add2 2) ;; wrong
</pre>
<p>可是这是不对的。我们需要通过另一个特殊操作符来完成这件事:</p>
<pre class="literal-block">(apply-fn #'add2 2) ;; right
</pre>
<p>#'操作符用于将add2对应的函数取出来，这么说当然不大准确。Again，作为一个C++程序员
，这简直就是个取地址操作符&amp;的语法糖嘛。好吧，这么理解起来似乎没问题了。</p>
<p>Lisp中能甚至能在任何地方定义一个函数，例如我们创建一个函数，该函数返回创建出来的
函数，这是一个典型的讲解什么是 <strong>闭包</strong> 的例子:</p>
<pre class="literal-block">(defun get-add-n (n)
#' (lambda (x)
(+ x n)))
</pre>
<p>无论如何，get-add-n函数返回一个函数，该函数是add2函数的泛型实现。它可以将你传入
的参数加上n。这些代码里使用了lambda表达式。lambda表达式直白来说，就是创建一个字
面上的函数。这又是什么意思呢？就像我们在代码中写出2，写出"hello"一样，2就是个字
面上的数字，"hello"就是个字面上的字符串 <a id="id7" href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id17" class="footnote-reference">[6]</a> 。</p>
<p>那么，总而言之，通过lambda创建一个函数体，然后通过#'操作符即可得到一个函数，虽然
没有名字。有了以上知识后，Again and again，作为一个C++程序员，很快我们就能得到一
个程序：定义变量，用变量去保存一个函数，然后通过这个变量来调用这个函数。这是多么
天经地义的事，就像之前那个通过参数调用其指向的函数一样:</p>
<pre class="literal-block">;; wrong
(defvar fn #' (lambda (x) (+ x 2)))
(fn 3)
</pre>
<p>这样的代码是不对的，错误发生于第二行，无论你使用的Lisp实现是哪种，大概会得到如下
的错误信息:</p>
<pre class="literal-block">"The function FN is undefined."
</pre>
<p>老实说，这已经算是多么有迹可循的错误提示了啊。将以上代码和之前的apply-fn对比，是
多么得神似啊，可惜就是错的。这是我们遇到的第一个理解偏差导致的问题。如果你还不深
入探究，你将会在这一块遇到更多麻烦。及时地拿出你的勇气，披荆斩棘，刨根究底，绝对
是学习编程的好品质。</p>
</div>
<div id="symbol" class="section">
<h1>&#8220;万恶之源&#8220;：SYMBOL</h1>
<p>上文中提到的变量函数之类，之所以会在某些时候与我们的理解发生偏差，并且总是存在些
神秘的地方无法解释。这完全是因为我们理解得太片面导致。Lisp中的Symbol可以说就是某
个变量，或者某个函数，但这太片面。Lisp中的Symbol拥有更丰富的含义。</p>
<div id="id8" class="section">
<h2>Symbol的名字</h2>
<p>就像很多语言的变量、函数名一样，Lisp中的Symbol比其他语言在命名方面更自由： <strong>只
要位于'|'字符之间的字符串，就表示一个合法的Symbol名。</strong> 我们可以使用函数
symbol-name来获取一个Symbol的名字，例如:</p>
<pre class="literal-block">(symbol-name '|this is a symbol name|)
输出："this is a symbol name"
</pre>
<p>'(quote)操作符告诉Lisp不要对其修饰的东西进行求值(evaluate)。但假如没有这个操作符
会怎样呢？后面我们将看到会怎样。</p>
</div>
<div id="id9" class="section">
<h2>Symbol本质</h2>
<p>&lt;ANSI Common Lisp&gt;一书中有句话真正地揭示了Symbol的本质： <strong>Symbols are real
objects</strong> 。是的，Symbols是对象，这个对象就像我们理解的C++中的对象一样，它是一个
复合的数据结构。该数据结构里包含若干域，或者通俗而言：数据成员。借用&lt;ANSI Common
Lisp&gt;中的一图：</p>
<blockquote><img src="http://www.cppblog.com/images/cppblog_com/kevinlynx/cl-writer/2011322113248/symbol-obj.png" alt="imgs/symbol-obj.png"></blockquote>
<p>通过这幅图，可以揭开所有谜底。一个Symbol包含至少图中的几个域，例如Name、Value、
Function等。在Lisp中有很多函数来访问这些域，例如上文中使用到的symbol-name，这个
函数本质上就是取出一个Symbol的Name域。</p>
</div>
<div id="symbolvariablefunction" class="section">
<h2>Symbol与Variable和Function的联系</h2>
<p>自然而然地，翻阅Lisp文档，我们会发现果然还有其他函数来访问Symbol的其他域，例如:</p>
<pre class="literal-block">symbol-function
symbol-value
symbol-package
symbol-plist
</pre>
<p>但是这些又与上文提到的变量和函数有什么联系呢？真相只有一个， <strong>变量、函数粗略来
说就是Symbol的一个域，一个成员。变量对应Value域，函数对应Function域。一个Symbol
这些域有数据了，我们说它们发生了绑定(bind)。</strong>  而恰好，我们有几个函数可以用于判
定这些域是否被绑定了值:</p>
<pre class="literal-block">boundp ;判定Value域是否被绑定
fboundp;判定Function域是否被绑定
</pre>
<p>通过一些代码来回味以上结论:</p>
<pre class="literal-block">(defvar *var* 1)
(boundp '*var*) ; 返回真
(fboundp '*var*) ; 返回假
(defun *var* (x) x) ; 定义一个名为*var*的函数，返回值即为参数
(fboundp '*var*) ; 返回真
</pre>
<p>上面的代码简直揭秘了若干惊天地泣鬼神的真相。首先，我们使用我们熟知的defvar定义了
一个名为 <tt class="docutils literal">*var*</tt> 的变量，初值为1，然后使用boundp去判定 <tt class="docutils literal">*var*</tt> 的Value域是否
发生了绑定。这其实是说： <strong>原来定义变量就是定义了一个Symbol，给变量赋值，原来就
是给Symbol的Value域赋值！</strong></p>
<p><strong>其实，Lisp中所有这些符号，都是Symbol。</strong> 什么变量，什么函数，都是浮云。上面的
例子中，紧接着用fboundp判断Symbol <tt class="docutils literal">*var*</tt> 的Function域是否绑定，这个时候为假。
然后我们定义了一个名为 <tt class="docutils literal">*var*</tt> 的函数，之后再判断，则已然为真。这也是为什么，
<strong>在Lisp中某个函数可以和某个变量同名的原因所在。</strong>  从这段代码中我们也可以看出
defvar/defun这些操作符、宏所做事情的本质。</p>
</div>
<div id="more-more-more" class="section">
<h2>More More More</h2>
<p>事情就这样结束了？Of course not。还有很多上文提到的疑惑没有解决。首先，Symbol是
如此复杂，那么Lisp如何决定它在不同环境下的含义呢？Symbol虽然是个对象，但它并不像
C++中的对象一样，它出现时并不指代自己！不同应用环境下，它指代的东西也不一样。这
些指代主要包括变量和函数，意思是说： <strong>Symbol出现时，要么指的是它的Value，要么是
它的Function。</strong>  这种背地里干的事情，也算是造成迷惑的一个原因。</p>
<p>当一个Symbol出现在一个List的第一个元素时，它被处理为函数。这么说有点迷惑人，因为
它带进了Lisp中代码和数据之间的模糊边界特性。简单来说，就是当Symbol出现在一个括号
表达式(s-expression)中第一个位置时，算是个函数，例如:</p>
<pre class="literal-block">(add2 3) ; add2位于第一个位置，被当作函数处理
(*var* 3) ; 这里*var*被当作函数调用，返回3
</pre>
<p>除此之外，我能想到的其他大部分情况，一个Symbol都被指代为它的Value域，也就是被当
作变量，例如:</p>
<pre class="literal-block">(*var* *var*) ; 这是正确的语句，返回1
</pre>
<p>这看起来是多么古怪的代码。但是运用我们上面说的结论，便可轻易解释：表达式中第一个
<tt class="docutils literal">*var*</tt> 被当作函数处理，它需要一个参数；表达式第二部分的 <tt class="docutils literal">*var*</tt> 被当作变量
处理，它的值为1，然后将其作为参数传入。</p>
<p>再来说说'(quote)操作符，这个操作符用于防止其操作数被求值。而当一个Symbol出现时，
它总是会被求值，所以，我们可以分析以下代码:</p>
<pre class="literal-block">(symbol-value *var*) ; wrong
</pre>
<p>这个代码并不正确，因为 <tt class="docutils literal">*var*</tt> 总是会被求值，就像 <tt class="docutils literal">(*var* *var*)</tt> 一样，第二
个 <tt class="docutils literal">*var*</tt> 被求值，得到数字1。这里也会发生这种事情，那么最终就等同于:</p>
<pre class="literal-block">(symbol-value 1) ; wrong
</pre>
<p>我们试图去取数字1的Value域，而数字1并不是一个Symbol。所以，我们需要quote运算符:</p>
<pre class="literal-block">(symbol-value '*var*) ; right
</pre>
<p>这句代码是说，取Symbol <tt class="docutils literal">*var*</tt> 本身的Value域！而不是其他什么地方。至此，我们
便可以分析以下复杂情况:</p>
<pre class="literal-block">(defvar *name* "kevin lynx")
(defvar *ref* '*name*) ; *ref*的Value保存的是另一个Symbol
(symbol-value *ref*) ; 取*ref*的Value，得到*name*，再取*name*的Value
</pre>
<p>现在，我们甚至能解释上文留下的一个问题:</p>
<pre class="literal-block">;; wrong
(defvar fn #' (lambda (x) (+ x 2)))
(fn 3)
</pre>
<p>给fn的Value赋值一个函数， <tt class="docutils literal">(fn 3)</tt> 当一个Symbol作为函数使用时，也就是取其
Function域来做调用。但其Function域什么也没有，我们试图将一个Symbol的Value域当作
Function来使用。如何解决这个问题？想想，symbol-function可以取到一个Symbol的
Function域:</p>
<pre class="literal-block">(setf (symbol-function 'fn) #' (lambda (x) (+ x 2)))
(fn 3)
</pre>
<p>通过显示地给fn的Function域赋值，而不是通过defvar隐式地对其Value域赋值，就可以使
<tt class="docutils literal">(fn 3)</tt> 调用正确。还有另一个问题也能轻易解释:</p>
<pre class="literal-block">(apply-fn add2 2) ; wrong
</pre>
<p>本意是想传入add2这个Symbol的function域，但是直接这样写的话，传入的其实是add2的
Value域 <a id="id10" href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id18" class="footnote-reference">[7]</a> ，这当然是不正确的。对比正确的写法，我们甚至能猜测#'运算符就是一个
取Symbol的Function域的运算符。进一步，我们还可以给出另一种写法:</p>
<pre class="literal-block">(apply-fn (symbol-function 'add2) 2)
</pre>
<p>深入理解事情的背后，你会发现你能写出多么灵活的代码。</p>
</div>
</div>
<div id="end" class="section">
<h1>END</h1>
<p>关于Symbol的内容还有更多，例如Package。正确理解这些内容以及他们之间的关系，有助
于更深刻地理解Lisp。</p>
</div>
<div id="id11" class="section">
<h1>注解</h1>
<table id="id12" class="docutils footnote" frame="void" rules="none">
    <colgroup><col class="label"><col></colgroup>
    <tbody valign="top">
        <tr>
            <td class="label"><a href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id1" class="fn-backref">[1]</a></td>
            <td>在Lisp中全局变量又被称为dynamic variables</td>
        </tr>
    </tbody>
</table>
<table id="id13" class="docutils footnote" frame="void" rules="none">
    <colgroup><col class="label"><col></colgroup>
    <tbody valign="top">
        <tr>
            <td class="label"><a href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id2" class="fn-backref">[2]</a></td>
            <td>Lisp中按照习惯通常在为全局变量命名时会加上星号，就像我们习惯使用g_一样</td>
        </tr>
    </tbody>
</table>
<table id="id14" class="docutils footnote" frame="void" rules="none">
    <colgroup><col class="label"><col></colgroup>
    <tbody valign="top">
        <tr>
            <td class="label"><a href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id4" class="fn-backref">[3]</a></td>
            <td>因为我确实在忽悠你</td>
        </tr>
    </tbody>
</table>
<table id="id15" class="docutils footnote" frame="void" rules="none">
    <colgroup><col class="label"><col></colgroup>
    <tbody valign="top">
        <tr>
            <td class="label"><a href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id5" class="fn-backref">[4]</a></td>
            <td>first class type，有人翻译为&#8220;一等公民&#8221;，我觉得压力巨大</td>
        </tr>
    </tbody>
</table>
<table id="id16" class="docutils footnote" frame="void" rules="none">
    <colgroup><col class="label"><col></colgroup>
    <tbody valign="top">
        <tr>
            <td class="label"><a href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id6" class="fn-backref">[5]</a></td>
            <td>即高阶函数</td>
        </tr>
    </tbody>
</table>
<table id="id17" class="docutils footnote" frame="void" rules="none">
    <colgroup><col class="label"><col></colgroup>
    <tbody valign="top">
        <tr>
            <td class="label"><a href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id7" class="fn-backref">[6]</a></td>
            <td>&#8220;字面&#8220;主要是针对这些信息会被词法分析程序直接处理</td>
        </tr>
    </tbody>
</table>
<table id="id18" class="docutils footnote" frame="void" rules="none">
    <colgroup><col class="label"><col></colgroup>
    <tbody valign="top">
        <tr>
            <td class="label"><a href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#id10" class="fn-backref">[7]</a></td>
            <td>这可能导致更多的错误</td>
        </tr>
    </tbody>
</table>
</div>
</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/142462.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2011-03-22 11:33 <a href="http://www.cppblog.com/kevinlynx/archive/2011/03/22/142462.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用lisp开发博客客户端</title><link>http://www.cppblog.com/kevinlynx/archive/2011/03/13/141713.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 13 Mar 2011 05:19:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2011/03/13/141713.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/141713.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2011/03/13/141713.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/141713.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/141713.html</trackback:ping><description><![CDATA[<div class="document" id="lisp">
<h1 class="title">用lisp开发博客客户端</h1>
<table class="docinfo" frame="void" rules="none">
<col class="docinfo-name" />
<col class="docinfo-content" />
<tbody valign="top">
<tr><th class="docinfo-name">Author:</th>
<td>Kevin Lynx</td></tr>
<tr><th class="docinfo-name">Date:</th>
<td>3.13.2011</td></tr>
</tbody>
</table>
<!-- -*- coding: utf-8 -*- -->
<p>最近一直在学习Lisp这门语言。回头一看，基本上接近1个月了。刚开始接触Lisp是因为看
了&lt;Lisp本质&gt;，然后我发现有很多人宗教般地忠诚这门语言，于是就来了兴趣。</p>
<blockquote>
<img alt="imgs/lisp_believer.png" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/cl-writer/201131313192/lisp_believer.png" />
</blockquote>
<p>当然并不是每次因为某篇写得很geek技术文章就去学习某个新的技术点。一个月时间对我来
说还是很珍贵了。但是Lisp绝对是大部分程序员都值得一学的语言（就像Haskell一样）。
我能给出的简单理由包括：</p>
<ul class="simple">
<li>大部分程序员只会命令式语言（C/C++/C Like etc)，缺乏函数式语言解决编程问题的思
想（当然Lisp不是纯函数式)</li>
<li>Lisp是仅次于Fortran的古老语言，很多优秀的语言设计思想在现代的一些语言里都找得
到</li>
<li>装B党必备</li>
</ul>
<p>另一方面，结合我一个月以来的读书和两个练习工程的实践经历，我觉得也有些理由值得你
不去学习Lisp：</p>
<ul class="simple">
<li>你会Haskell或者其他函数式语言</li>
<li>我目前还是觉得Lisp学习曲线高(大概是因为我读到的书都在应用语法层兜圈子，事实上
Lisp的语法之统一，全特么的是s-expression)，你不愿意花费这些成本</li>
<li>you are too old bo to be a B</li>
</ul>
<div class="section" id="id1">
<h1>关于这篇文档</h1>
<p>这篇博客我使用reStructuredText格式编写，然后用docutls导出为html，再然后使用这回
用lisp开发的基于metaweblog API的博客客户端，自动发布到CPPBLOG。</p>
</div>
<div class="section" id="id2">
<h1>他们怎么说Lisp</h1>
<p>我就摘录些书上的观点(历史)：</p>
<ul class="simple">
<li>1958年，John McCarthy和他的学生搞出了Lisp，包括其第一个实现，最初貌似也是以一
篇论文起头</li>
<li>Lisp可以让你做其他语言里无法做的事情(&lt;ANSI common Lisp&gt;)</li>
<li>大部分编程语言只会告诉你不能怎样做，这限制了你解决问题的思路，Lisp not (&lt;ANSI
Common Lisp&gt;)</li>
<li>Lisp让你以Lisp的思维思考问题，换到其他语言你会说：为什么X语言就不支持这个特性
呢(Once you've leanred Lisp, you'll even dream in Lisp) (&lt;Land Of Lisp&gt;)</li>
<li>Lisp代码更清晰地体现你的想法(&lt;Practical Common Lisp&gt;)</li>
</ul>
</div>
<div class="section" id="and-my-opinion">
<h1>And my opinion</h1>
<p>我可还没到把Lisp捧上天的地步。如果Lisp如此之好，为什么用的人不多？&lt;Land Of Lisp&gt;
里作者恰好对这个问题做了回答(bla bla bla，懒得细读)。</p>
<ul class="simple">
<li>Lisp也是一门杂和型风格的语言，函数式、命令式、面向对象，以及最被人吹捧的宏编程
--程序自己写自己</li>
<li>Lisp的语句全部以(xxx xxx)的形式出现，被称为s-expression，我看称为括号表达式还
差不多</li>
<li>Lisp每条语句都有返回值，没基础过函数式编程的同学，if语句也是有返回值的</li>
<li>函数式编程语言的一个重要特性就是闭包(closure)，这个东西用来避免全局变量实在太
geek了</li>
</ul>
</div>
<div class="section" id="id3">
<h1>开始学习Lisp</h1>
<p>Lisp不像有些语言，有个直接的机构来维护。感觉它更像C/C++一样，只有个标准，然后有
若干编译器（解释器）实现。Lisp在几十年的发展中，产生了很多种方言。方言也就是形变
神不变的语言变种，本文说的Lisp均指Lisp的方言Common Lisp。另一个比较有名的方言是
Scheme，关于各个方言的特点，&lt;Land Of Lisp&gt;里也给了一个图片：</p>
<blockquote>
<img alt="imgs/dialect.png" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/cl-writer/201131313195/dialect.png" />
</blockquote>
<p>其中，最左边那只wolf就是Common Lisp，右边那只sheep就是Scheme。</p>
<p>要学习Lisp，首先就是选择方言。然后最重要的就是选择一个编译器实现。世界上知名的有
十几种实现（也许更多）。一些商业版本非常强大，甚至能编译出很小的本地代码执行文件
，不过价格也不菲。当然也有很多开源免费的实现，例如CLISP、SBCL。我选用的是SBCL。</p>
<p>SBCL交互式命令行不支持括号匹配，甚至没有输入历史。要实现这两个功能，可以装一个
lisp工具：linedit。在lisp的世界中，要获得一个lisp的库实在不是件方便的事。尤其是
这些免费的编译器实现，并不像有些语言一样，直接随编译器带个几十M的库。</p>
<p>然后就有了quicklisp这个工具。该工具就像Ubuntu系统里的软件管理器一样，你可以在
lisp里直接获取某个库。quicklisp检查该库是否存在，不存在直接从它的服务器上下载人
然后自动安装。</p>
<p>此外，在lisp的世界里，写出来的程序不再是跨OS。OS的差异由编译器实现来解决。但是，
写lisp程序却需要考虑跨编译器实现（egg hurt）。这也是个无比伤神的事，比跨OS更伤
神。因为OS就那么几个，但lisp的编译器实现，流行的也有好几个。</p>
<p>lisp的世界里，工程组织也有特殊的一套，就像makefile一样，这就是asdf。</p>
</div>
<div class="section" id="id4">
<h1>博客客户端如何实现</h1>
<p>像我们这种基本没接触过Web开发的人，可能完全没有思路去实现一个博客客户端。事实上
实现起来非常简单。</p>
<p>使用过其他博客客户端（例如Windows Live writer）的人肯定知道metaweblog API，在配
置客户端的时候需要填入。例如CPPBLOG的这个地址就是
<a class="reference external" href="http://www.cppblog.com/kevinlynx/services/metaweblog">http://www.cppblog.com/kevinlynx/services/metaweblog</a>.aspx。这个页面展示了一些API
说明。这些API就是博客客户端和服务器进行操作通信的接口。意思是说，服务器端提供这
这些接口，我们的客户端调用这些接口即可。例如:</p>
<pre class="literal-block">
blogger.deletePost，调用该接口即可删除一篇博客文章
</pre>
<p>但是客户端如何调用到这个接口呢？这需要通过一种新的技术（或者说标准），即 <strong>xml rpc</strong>
。rpc大家应该清楚，xml rpc其实说白了， <strong>就是把接口调用的细则塞进</strong> <strong>http
请求发给web服务器，服务器接收请求完成操作后再把结果以http回应的形式丢给客户端，
即完成了一次接口调用</strong> 。</p>
<p>至于http请求回应的细则就不提了，无非就是一些特殊格式的数据，通过tcp连接与服务器
交互这些数据。</p>
<p>所以，基本上，整个过程还是非常简单。如何来将调用细节塞进http请求，则是以xml rpc
标准来做，其格式正好是xml格式。举个例子吧:</p>
<pre class="literal-block">
&lt;?xml version='1.0'?&gt;
&lt;methodCall&gt;
    &lt;methodName&gt;title_or_id&lt;/methodName&gt;
        &lt;params&gt;
        &lt;/params&gt;
&lt;/methodCall
</pre>
<p>当然这部分数据之前就是若干http请求的数据。服务器回应也是以xml格式组织:</p>
<pre class="literal-block">
&lt;?xml version='1.0'?&gt;
&lt;methodResponse&gt;
    &lt;params&gt;
        &lt;param&gt;
            &lt;value&gt;&lt;string&gt;Welcome to Zope.org&lt;/string&gt;&lt;/value&gt;
        &lt;/param&gt;
    &lt;/params&gt;
&lt;/methodResponse&gt;
</pre>
<p>我们的博客客户端所要做的，就是把这些博客发布相关的操作封装起来提供给使用者。底层
实现主要包括http请求、xml-rpc的组织等。何况，这两部分在各个语言里都有大量的库存
在，lisp自然也有。</p>
<p>我这里直接选取了lisp的一个xml-rpc库：s-xml-rpc，基本上百来行代码就可以把各个功
能跑一遍。例如以下lisp代码就实现了通过s-xml-rpc删除CPPBLOG的一篇文章:</p>
<pre class="literal-block">
(defun delete-post (postid)
  (rpc-call
    &quot;blogger.deletePost&quot;
    postid
    &quot;kevinlynx&quot;
    &quot;password&quot;
    t))
</pre>
<p>发布博客也很简单，根据metaweblog API接口的说明，发布博客时需要填充一个结构体。但
主要涉及到的数据仅包括：文章内容、文章标题、文章分类（可选）:</p>
<pre class="literal-block">
(defun new-post (title context &amp;optional (cates))
  (rpc-call
    &quot;metaWeblog.newPost&quot;
    &quot;&quot;
    &quot;kevinlynx&quot;
    &quot;password&quot;
    (new-post-struct title context cates)
    t))
</pre>
<p>值得注意的是，如果文章中有贴图，则需要事先将图片文件上传到服务器。CPPBLOG的
metaweblog API里恰有API提供:</p>
<pre class="literal-block">
(defun new-media-object (filename)
  (rpc-call
    &quot;metaWeblog.newMediaObject&quot;
    &quot;&quot;
    &quot;kevinlynx&quot;
    &quot;password&quot;
    (new-media-object-struct filename)))
</pre>
<p>该函数读入图片文件，然后调用metaWeblog.newMediaObject接口，即可完成上传。上传成
功后，服务器会返回该图片的URL。然后在我们的文章中就可以使用该图片了。</p>
</div>
<div class="section" id="id5">
<h1>完整实现方案</h1>
<p>仅仅将metaweblog的一些接口做封装，对于一个可以使用的博客客户端来说还远远不够。大
部分同类工具都有一个友好的GUI编辑界面。我并不打算弄一个编辑界面出来，吃力不讨好
的事情。</p>
<p>我的打算是先用其他工具对文章做排版处理，最后导出为html格式。因为CPPBLOG支持直接
发布一个html文件。然后在用这个lisp工具将整个文件作为博客文章内容发布。</p>
<p>恰好公司最近打算用reStructureText(rst)格式来编辑文档，作为熟悉手段，我决定拿这个
来练手。rst格式非常简单，同wiki命令很相似。在vim里编辑该文件非常合适，因为默认支
持。见图:</p>
<blockquote>
<img alt="imgs/rst.png" src="http://www.cppblog.com/images/cppblog_com/kevinlynx/cl-writer/2011313131910/rst.png" />
</blockquote>
<p>由图即可看出，rst是一种半所见即所得的格式。即：它遵循你在编辑器里的排版，同时也
通过一些tag（例如image）来控制更丰富的输出。</p>
<p>rst有很多前端工具，可以将rst文件输出，例如rst2html.py就可以输出为html。好吧，最
最终我们得到了html格式的博客文章。</p>
<p>但是如果文章中出现了图片，而图片基本上在本地，转成html后也是相对路径。我需要我的
lisp writer(cl-writer)能自动扫描文章，发现有图片的地方，就自动将图片上传。最恶心
的是上传后还得替换图片引用路径。这个工作可以在rst格式上做，也可以在结果格式html
上做。通过xml解析库解析html比直接解析rst格式更简单，并且在扩展性上更好。</p>
<p>最终这个html中图片路径替换工作只消耗了不到100行lisp代码。这在很大程度上也依赖于
s-xml库的接口设计。</p>
<p>最终封装好的发布接口如下，从这里也可以看出，函数式语言锻炼我们写出功能单一代码度
短小的接口:</p>
<pre class="literal-block">
(defun writer-post-new (post-file &amp;key (u (get-default-user))(cates))
  (read-post-file u post-file context title
                  (new-post u title context cates)))
</pre>
</div>
<div class="section" id="end">
<h1>END</h1>
<p>别指望我发布的代码能够让你一键在你的博客上留下&quot;this is a test&quot;，你甚至别指望它能
能够工作。但如果你本来就是一个资深的lisper，或者虽然不是lisper但却执意想看看结果
。这里我就简要说说如何让这些代码欢乐起来:</p>
<ol class="arabic">
<li><p class="first">OS Ubuntu10.04，下载安装SBCL，不会有问题；</p>
</li>
<li><p class="first">下载安装quicklisp，官方文档hand by hand，简单不会有问题；</p>
</li>
<li><p class="first">SBCL交互环境中使用quicklisp安装s-xml-rpc:</p>
<pre class="literal-block">
(ql:quickload &quot;s-xml-rpc&quot;)
</pre>
</li>
<li><p class="first">装载我的代码:</p>
<pre class="literal-block">
(asdf:load-system :cl-writer)
</pre>
</li>
<li><p class="first">在home下添加配置文件.cl-writer.lisp，配置你博客信息，例如:</p>
<pre class="literal-block">
(in-package cl-writer)
(setf *default-user* (make-cppblog-user &quot;账户名&quot; &quot;密码&quot;))
</pre>
<p>如果你的博客不在CPPBLOG，虽然也许也是metaweblog，但我不能保证成功，配置文件则
要复杂点:</p>
<pre class="literal-block">
(setf *default-user* (make-user-info :name &quot;帐户名&quot;
                      :password &quot;密码&quot; :host &quot;www.cppblog.com&quot;
                      :url &quot;/kevinlynx/services/metaweblog.aspx&quot;))
</pre>
</li>
<li><p class="first">SBCL交互环境下测试:</p>
<pre class="literal-block">
(in-package cl-writer)
(new-post (get-default-user) &quot;this is a test&quot; &quot;title&quot;)
</pre>
</li>
</ol>
<p><a class="reference external" href="http://www.cppblog.com/Files/kevinlynx/cl-writer.tar.gz">下载代码</a></p>
<p>最后，终于敲完这篇文章，我需要通过以下步骤来发表它:</p>
<pre class="literal-block">
in shell:
rst2html.py lisp_xml_rpc.rst lisp_xml_rpc.html
in SBCL:
(writer-post-new &quot;lisp_xml_rpc.html&quot;)
</pre>
<div class="section" id="eof">
<h2>;;EOF;;</h2>
</div>
</div>
</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/141713.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2011-03-13 13:19 <a href="http://www.cppblog.com/kevinlynx/archive/2011/03/13/141713.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>