<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8229657610060087793</id><updated>2011-12-21T08:35:27.472-08:00</updated><category term='曹雷'/><category term='Python'/><category term='烹饪'/><category term='翻译'/><category term='postgres'/><category term='流水帐'/><category term='杂谈'/><category term='科学'/><category term='web'/><category term='閑記'/><category term='latex'/><category term='IT'/><category term='sphinx'/><category term='Postgres Story'/><category term='技术'/><category term='志趣'/><category term='web2py'/><category term='计算机'/><category term='Java'/><category term='流水帳'/><category term='Haskell'/><category term='CPP'/><category term='trac'/><category term='时间帐单 postgres jquery trac web.py mercury opensource'/><category term='很欠贬'/><category term='学习'/><category term='PostgreSQL'/><category term='吾友旧作'/><category term='搭訕'/><category term='偷窺'/><category term='ubuntu'/><category term='xelatex'/><category term='Scheme'/><category term='歌词收藏'/><title type='text'>挖坑不填是一种美德</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>91</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-105203657921078353</id><published>2010-11-21T10:51:00.000-08:00</published><updated>2010-11-21T10:51:36.181-08:00</updated><title type='text'>haskell shell prototype</title><content type='html'>&lt;span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana,Helvetica,sans-serif; font-size: 15px; line-height: 17px; text-align: left;"&gt;只是一个非常土的原型……&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: Verdana,Helvetica,sans-serif; font-size: 15px; line-height: 17px; text-align: left;"&gt;效果：&lt;br /&gt;&lt;pre class="src src-plain" style="background-color: #eeeeee; border: 1px solid rgb(85, 85, 85); font-family: courier,monospace; font-size: 9pt; overflow: auto; padding: 1em;"&gt;*Funs&amp;gt; select (from &lt;span style="color: #008b00;"&gt;"/home/march/temp/text/\\.(tex|csv)$"&lt;/span&gt;) &amp;gt;&amp;gt;= whereLines &lt;span style="color: #008b00;"&gt;"end"&lt;/span&gt;&lt;br /&gt;[&lt;span style="color: #008b00;"&gt;"\\end{center}"&lt;/span&gt;,&lt;span style="color: #008b00;"&gt;"\\end{document}"&lt;/span&gt;]&lt;/pre&gt;代码：&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="src src-haskell" style="background-color: #eeeeee; border: 1px solid rgb(85, 85, 85); font-family: courier,monospace; font-size: 9pt; overflow: auto; padding: 1em;"&gt;&lt;span style="color: mediumblue;"&gt;module&lt;/span&gt; &lt;span style="color: steelblue;"&gt;Funs&lt;/span&gt; &lt;span style="color: mediumblue;"&gt;where&lt;/span&gt;&lt;br /&gt;&lt;span style="color: mediumblue;"&gt;import&lt;/span&gt; &lt;span style="color: steelblue;"&gt;Data.List&lt;/span&gt;&lt;br /&gt;&lt;span style="color: mediumblue;"&gt;import&lt;/span&gt; &lt;span style="color: steelblue;"&gt;Monad&lt;/span&gt;&lt;br /&gt;&lt;span style="color: mediumblue;"&gt;import&lt;/span&gt; &lt;span style="color: steelblue;"&gt;System.Directory&lt;/span&gt;&lt;br /&gt;&lt;span style="color: mediumblue;"&gt;import&lt;/span&gt; &lt;span style="color: steelblue;"&gt;System.FilePath&lt;/span&gt;&lt;br /&gt;&lt;span style="color: mediumblue;"&gt;import&lt;/span&gt; &lt;span style="color: steelblue;"&gt;System.IO&lt;/span&gt;&lt;br /&gt;&lt;span style="color: mediumblue;"&gt;import&lt;/span&gt; &lt;span style="color: steelblue;"&gt;Text.Regex.PCRE&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: mediumblue;"&gt;data&lt;/span&gt; &lt;span style="color: steelblue;"&gt;Store&lt;/span&gt; t p &lt;span style="color: black;"&gt;=&lt;/span&gt; &lt;span style="color: steelblue;"&gt;Text&lt;/span&gt; p &lt;br /&gt;               &lt;span style="color: black;"&gt;|&lt;/span&gt; &lt;span style="color: steelblue;"&gt;Csv&lt;/span&gt; p&lt;br /&gt;               &lt;span style="color: black;"&gt;|&lt;/span&gt; &lt;span style="color: steelblue;"&gt;Bin&lt;/span&gt; p&lt;br /&gt;               &lt;span style="color: black;"&gt;|&lt;/span&gt; &lt;span style="color: steelblue;"&gt;File&lt;/span&gt; p&lt;br /&gt;&lt;br /&gt;&lt;span style="color: darkorange;"&gt;selectFrom&lt;/span&gt; (&lt;span style="color: steelblue;"&gt;Text&lt;/span&gt; store) &lt;span style="color: black;"&gt;=&lt;/span&gt; &lt;span style="color: mediumblue;"&gt;do&lt;/span&gt;&lt;br /&gt;  content &lt;span style="color: black;"&gt;&amp;lt;-&lt;/span&gt; readFile store&lt;br /&gt;  return&lt;span style="color: black;"&gt;$&lt;/span&gt;lines content&lt;br /&gt;&lt;br /&gt;&lt;span style="color: darkorange;"&gt;unionIt&lt;/span&gt; &lt;span style="color: black;"&gt;::&lt;/span&gt; &lt;span style="color: steelblue;"&gt;IO&lt;/span&gt; [[&lt;span style="color: steelblue;"&gt;String&lt;/span&gt;]] &lt;span style="color: black;"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: steelblue;"&gt;IO&lt;/span&gt;[&lt;span style="color: steelblue;"&gt;String&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: darkorange;"&gt;unionIt&lt;/span&gt; &lt;span style="color: black;"&gt;=&lt;/span&gt; liftM concat&lt;br /&gt;&lt;br /&gt;&lt;span style="color: darkorange;"&gt;select&lt;/span&gt; &lt;span style="color: black;"&gt;::&lt;/span&gt; &lt;span style="color: steelblue;"&gt;IO&lt;/span&gt; [&lt;span style="color: steelblue;"&gt;FilePath&lt;/span&gt;] &lt;span style="color: black;"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: steelblue;"&gt;IO&lt;/span&gt; [&lt;span style="color: steelblue;"&gt;String&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: darkorange;"&gt;select&lt;/span&gt; paths &lt;span style="color: black;"&gt;=&lt;/span&gt; paths &lt;br /&gt;               &lt;span style="color: black;"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span style="color: black;"&gt;\&lt;/span&gt;ps &lt;span style="color: black;"&gt;-&amp;gt;&lt;/span&gt; unionIt(filterM doesFileExist ps &lt;br /&gt;                              &lt;span style="color: black;"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; mapM (&lt;span style="color: black;"&gt;\&lt;/span&gt;p &lt;span style="color: black;"&gt;-&amp;gt;&lt;/span&gt; selectFrom (&lt;span style="color: steelblue;"&gt;Text&lt;/span&gt; p)))&lt;br /&gt;&lt;br /&gt;&lt;span style="color: darkorange;"&gt;from&lt;/span&gt; &lt;span style="color: black;"&gt;::&lt;/span&gt; &lt;span style="color: steelblue;"&gt;FilePath&lt;/span&gt; &lt;span style="color: black;"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: steelblue;"&gt;IO&lt;/span&gt; [&lt;span style="color: steelblue;"&gt;FilePath&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: darkorange;"&gt;from&lt;/span&gt; path &lt;span style="color: black;"&gt;=&lt;/span&gt; &lt;span style="color: mediumblue;"&gt;let&lt;/span&gt; (dir, files) &lt;span style="color: black;"&gt;=&lt;/span&gt; splitFileName path&lt;br /&gt;            &lt;span style="color: mediumblue;"&gt;in&lt;/span&gt; liftM(&lt;span style="color: black;"&gt;\&lt;/span&gt;lines&lt;span style="color: black;"&gt;-&amp;gt;&lt;/span&gt;[dir&lt;span style="color: black;"&gt;++&lt;/span&gt;line &lt;span style="color: black;"&gt;|&lt;/span&gt; line &lt;span style="color: black;"&gt;&amp;lt;-&lt;/span&gt; lines, line&lt;span style="color: black;"&gt;=~&lt;/span&gt;files])&lt;br /&gt;                   ((getDirectoryContents&lt;span style="color: black;"&gt;.&lt;/span&gt;takeDirectory) dir)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: darkorange;"&gt;whereLines&lt;/span&gt; &lt;span style="color: black;"&gt;::&lt;/span&gt; &lt;span style="color: steelblue;"&gt;String&lt;/span&gt; &lt;span style="color: black;"&gt;-&amp;gt;&lt;/span&gt; [&lt;span style="color: steelblue;"&gt;String&lt;/span&gt;] &lt;span style="color: black;"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: steelblue;"&gt;IO&lt;/span&gt; [&lt;span style="color: steelblue;"&gt;String&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: darkorange;"&gt;whereLines&lt;/span&gt; reg lines &lt;span style="color: black;"&gt;=&lt;/span&gt; return &lt;span style="color: black;"&gt;$&lt;/span&gt;filter (&lt;span style="color: black;"&gt;\&lt;/span&gt;line &lt;span style="color: black;"&gt;-&amp;gt;&lt;/span&gt; line &lt;span style="color: black;"&gt;=~&lt;/span&gt; reg &lt;span style="color: black;"&gt;::&lt;/span&gt; &lt;span style="color: steelblue;"&gt;Bool&lt;/span&gt;) lines&lt;/pre&gt;这段代码已经包含了基本的 select from where 模式，甚至可以说已经是一个实 用的实现，但是对于我来说，期待它有进一步的功能：&lt;br /&gt;&lt;ul&gt;&lt;li&gt;有真正的前端脚本&lt;/li&gt;&lt;li&gt;可以在 select 后设定输出字段，如匹配文件字和行号，以及进一步用正则对行 文件分组&lt;/li&gt;&lt;li&gt;将 where 嵌入 select 过程，真正实现流式过滤&lt;/li&gt;&lt;li&gt;支持 limit offset&lt;/li&gt;&lt;li&gt;支持CSV&lt;/li&gt;&lt;li&gt;支持 group by … having&lt;/li&gt;&lt;li&gt;支持 order by&lt;/li&gt;&lt;/ul&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-105203657921078353?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/105203657921078353/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=105203657921078353' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/105203657921078353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/105203657921078353'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/11/haskell-shell-prototype.html' title='haskell shell prototype'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-1312662141917773862</id><published>2010-11-18T06:22:00.000-08:00</published><updated>2010-11-18T06:22:57.930-08:00</updated><title type='text'>《Python速成讲座》使用的新幻灯设置</title><content type='html'>&lt;span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: 文泉驿等宽微米黑; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New',Courier,宋体; font-size: 12px;"&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-size: 12px;"&gt;&lt;a href="http://slideshare.net/marchliu/python-5722206"&gt;上次的幻灯&lt;/a&gt;中，我对平时使用的 latex 代码做了一些调整，新的引言区如下：&lt;/span&gt;&lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-size: 12px;"&gt;&lt;br style="font-size: 10pt;" /&gt;&lt;/span&gt;&lt;/span&gt;&lt;table bgcolor="#f1f1f1" border="1" cellpadding="0" cellspacing="0" style="border-collapse: collapse;"&gt;&lt;tbody&gt;&lt;tr style="font-size: 10pt;"&gt;&lt;td style="font-size: 10pt;"&gt;&lt;pre style="line-height: 19px; margin: 5px;"&gt;\documentclass[utf8x, notes=hide]{beamer}&lt;br /&gt;&lt;br /&gt;%\usepackage[bars]{beamerthemetree} % Beamer Theme v 2.2&lt;br /&gt;\usetheme{boxes} % Beamer theme&lt;br /&gt;\usecolortheme{seahorse} % Beamer color theme&lt;br /&gt;&lt;br /&gt;\usepackage[boldfont,slantfont]{xeCJK}&lt;br /&gt;\usepackage{fontspec}&lt;br /&gt;\setmainfont{DejaVu Serif}&lt;br /&gt;\setsansfont{DejaVu Sans}&lt;br /&gt;\setmonofont{DejaVu Sans Mono}%{Monaco}&lt;br /&gt;\setCJKmainfont{文泉驿正黑}&lt;br /&gt;\setCJKsansfont{文泉驿微米黑}&lt;br /&gt;\setCJKmonofont{WenQuanYi Micro Hei Mono}&lt;br /&gt;\setCJKfamilyfont{tt}{Monaco}&lt;br /&gt;&lt;br /&gt;\usepackage{color}&lt;br /&gt;\definecolor{listinggray}{gray}{0.9} &lt;br /&gt;\usepackage{xcolor}&lt;br /&gt;&lt;br /&gt;\usepackage{listings}&lt;br /&gt;\lstset{language=Python,&lt;br /&gt;numbers=left,&lt;br /&gt;backgroundcolor=\color{listinggray},&lt;br /&gt;frame=single,&lt;br /&gt;framexleftmargin=7mm,&lt;br /&gt;frameshape={RYN}{y}{y}{RYN}}&lt;br /&gt;\usepackage{hyperref}&lt;br /&gt;\usepackage{graphicx}&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;其中最主要的改动是利用xeCJK 对中西文字体做了定制，以及 lstlisting 代码区的定制（圆角、显示行号）&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-1312662141917773862?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/1312662141917773862/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=1312662141917773862' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1312662141917773862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1312662141917773862'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/11/python_18.html' title='《Python速成讲座》使用的新幻灯设置'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-1325597694162526962</id><published>2010-11-09T19:26:00.001-08:00</published><updated>2010-11-09T19:26:28.381-08:00</updated><title type='text'>Python速成指南</title><content type='html'>今晚将要在公司内部进行的 Python 语言速成讲座&lt;div style="width:425px" id="__ss_5722206"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/marchliu/python-5722206" title="Python速成指南"&gt;Python速成指南&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5722206" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=python-101109212231-phpapp02&amp;stripped_title=python-5722206&amp;userName=marchliu" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5722206" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=python-101109212231-phpapp02&amp;stripped_title=python-5722206&amp;userName=marchliu" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/marchliu"&gt;March Liu&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-1325597694162526962?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/1325597694162526962/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=1325597694162526962' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1325597694162526962'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1325597694162526962'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/11/python.html' title='Python速成指南'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-6702602705482694067</id><published>2010-10-26T01:39:00.000-07:00</published><updated>2010-10-26T01:39:14.570-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='翻译'/><title type='text'>[翻译]如何阅读数学</title><content type='html'>&lt;span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: 文泉驿等宽微米黑; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: sans-serif; font-size: 16px;"&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;This article is part of the book Rediscovering Mathematics, which is due out in early 2011. - Rediscovering Mathematics: Patriot Ledger&lt;/div&gt;&lt;div class="section" id="how-to-read-mathematics"&gt;&lt;h1 style="background-color: #f2f2f2; border-bottom: 1px solid rgb(204, 204, 204); color: #20435c; font-family: 'Trebuchet MS',sans-serif; font-size: 32px; font-weight: normal; margin: 0px -20px 10px; padding: 3px 0px 3px 10px;"&gt;How to Read Mathematics 如何阅读数学&lt;/h1&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;Mathematics&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;is&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;“a&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;language&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;that&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;can&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;neither&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;be&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;read&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;nor&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;understood&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;without&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;initiation.”&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;未经启蒙的话，数学是门既不可读又无法理解的语言。&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;a class="footnote-reference" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id7" id="id1" style="color: #355f7c; text-decoration: none;"&gt;[1]&lt;/a&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;A reading protocol is a set of strategies that a reader must use in order to benefit fully from reading the text. Poetry calls for a different set of strategies than fiction, and fiction a different set than non-fiction. It would be ridiculous to read fiction and ask oneself what is the author’s source for the assertion that the hero is blond and tanned; it would be wrong to read non-fiction and not ask such a question. This reading protocol extends to a viewing or listening protocol in art and music. Indeed, much of the introductory course material in literature, music and art is spent teaching these protocols.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;阅读的原则是一些可以让读者受益的方法。诗歌和小说有不同的方法，小说与非 虚构有不同的方法。读了小说以后就要求证主人公的原型和作者写的一样金发碧眼而 且有褐色皮肤就太可笑了。读了非虚构的文学以后不对这些有质疑也不对。这是 欣赏艺术和音乐的原则。的确，很多文学、音乐和艺术作品已经过时了，不再适 合这个原则。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Mathematics has a reading protocol all its own, and just as we learn to read literature, we should learn to read mathematics. Students need to learn how to read mathematics, in the same way they learn how to read a novel or a poem, listen to music, or view a painting. Ed Rothstein’s book, Emblems of Mind, a fascinating book emphasizing the relationship between mathematics and music, touches implicitly on the reading protocols for mathematics.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;数学有其自己的阅读原则，就像我们学习文学阅读，我们也应该学习数学阅读。 就像学生需要学会如何阅读长篇小说和诗歌，学会听音乐和观赏画作，他们也同 样需要学会阅读数学。Ed Rothstein 的《心灵的象征》，是本迷人的书，强调 数学和音乐之间的关系，触及了数学阅读的原则。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;When we read a novel we become absorbed in the plot and characters. We try to follow the various plot lines and how each affects the development of the characters. We make sure that the characters become real people to us, both those we admire and those we despise. We do not stop at every word, but imagine the words as brushstrokes in a painting. Even if we are not familiar with a particular word, we can still see the whole picture. We rarely stop to think about individual phrases and sentences. Instead, we let the novel sweep us along with its flow and carry us swiftly to the end. The experience is rewarding, relaxing and thought provoking.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;当我们阅读长篇小说时，会被情节和人物吸引。我们试图理解情节线索，搞明白 它们如何影响人物的各方面。我们会想像那些被我们赞赏和厌恶的人物成为真实 存在的人物。我们欲罢不能，用文字在脑海中描述图景。甚至我们不必关注每个 具体的字眼，仍可以洞察整个画卷。我们很少停下来思考某个段落或句子。相 反，我们任由小说将我们抛出剧情的激流，裹挟着我们直达结尾。这种体验很 棒，放松而剌激。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Novelists frequently describe characters by involving them in well-chosen anecdotes, rather than by describing them by well-chosen adjectives. They portray one aspect, then another, then the first again in a new light and so on, as the whole picture grows and comes more and more into focus. This is the way to communicate complex thoughts that defy precise definition.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;小说家经常将角色置于精心构造的桥段中以描写他们，而不是用精挑细选的形容 词来表达。他们描述一个面孔，然后是另一个，然后第一个又出现在新的瞬间。 反复如此直到整个画卷越来越清晰。这是不使用精确定义而表达复杂含义的方法。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Mathematical ideas are by nature precise and well defined, so that a precise description is possible in a very short space. Both a mathematics article and a novel are telling a story and developing complex ideas, but a math article does the job with a tiny fraction of the words and symbols of those used in a novel. The beauty in a novel is in the aesthetic way it uses language to evoke emotions and present themes which defy precise definition. The beauty in a mathematics article is in the elegant efficient way it concisely describes precise ideas of great complexity.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;数学思想天然就是定义完全而精确的，故可以在很短的容量中精确的描述。数学 论文和小说都是讲述一个故事以展显复杂的思想，但是数学论文只用小说词语和 符号的一小部分数量就完成这件事。小说之美在于优美的运用语言来唤起情绪但 拒绝精确定义。数学之美在于它优雅高效的表达方式，简洁的描述出复杂事物中 精确的思想。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;What are the common mistakes people make in trying to read mathematics? How can these mistakes be corrected?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;想要阅读数学的人通常会犯什么错误？如何避免它们？&lt;/div&gt;&lt;div class="section" id="dont-miss-the-big-picture"&gt;&lt;h2 style="background-color: #f2f2f2; border-bottom: 1px solid rgb(204, 204, 204); color: #20435c; font-family: 'Trebuchet MS',sans-serif; font-size: 26px; font-weight: normal; margin: 20px -20px 10px; padding: 3px 0px 3px 10px;"&gt;Don’t Miss the Big Picture 不要忘记大局&lt;/h2&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;“Reading&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;Mathematics&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;is&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;not&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;at&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;all&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;a&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;linear&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;experience&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;...&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;Understanding&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;the&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;text&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;requires&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;cross&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;references,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;scanning,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;pausing&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;and&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;revisiting”&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;数学阅读不是线性的体验……理解文本需要交叉引用、扫读、停顿和重读。&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;a class="footnote-reference" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id8" id="id2" style="color: #355f7c; text-decoration: none;"&gt;[2]&lt;/a&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Don’t assume that understanding each phrase, will enable you to understand the whole idea. This is like trying to see a portrait painting by staring at each square inch of it from the distance of your nose. You will see the detail, texture and color but miss the portrait completely. A math article tells a story. Try to see what the story is before you delve into the details. You can go in for a closer look once you have built a framework of understanding. Do this just as you might reread a novel.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;不要想当然的认为理解每一个段落会有助于你理解整个思想。这就像想要从每一 个平方英寸入手去尝试看清你鼻尖前的一幅肖像油画。你可以看到细节，纹理和 颜色，但是看不到整个肖像。每篇数学论文都讲一个故事。在你深入细节前，尝 试了解故事。你可以在构建起理解的框架后再深入。这就像你读小说时一样。&lt;/div&gt;&lt;/div&gt;&lt;div class="section" id="dont-be-a-passive-reader"&gt;&lt;h2 style="background-color: #f2f2f2; border-bottom: 1px solid rgb(204, 204, 204); color: #20435c; font-family: 'Trebuchet MS',sans-serif; font-size: 26px; font-weight: normal; margin: 20px -20px 10px; padding: 3px 0px 3px 10px;"&gt;Don’t be a Passive Reader 不要做一个被动的读者&lt;/h2&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;“A&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;three-line&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;proof&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;of&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;a&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;subtle&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;theorem&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;is&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;the&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;distillation&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;of&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;years&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;of&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;activity.&lt;/span&gt;&amp;nbsp;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;Reading&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;mathematics…&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;involves&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;a&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;return&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;to&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;the&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;thinking&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;that&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;went&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;into&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;the&lt;/span&gt;&lt;span class="pre"&gt;writing”&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;某个巧妙的定理的三段证明可以是经年思考的升华。阅读数学……将我们带回写&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;下它们的那个时刻。&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;a class="footnote-reference" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id9" id="id3" style="color: #355f7c; text-decoration: none;"&gt;[3]&lt;/a&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Explore examples for patterns. Try special cases.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;探索模式的示例，尝试特定的例子。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;A math article usually tells only a small piece of a much larger and longer story. The author usually spends months discovering things, and going down blind alleys. At the end, he organizes it all into a story that covers up all the mistakes (and related motivation), and presents the completed idea in clean neat flow. The way to really understand the idea is to re-create what the author left out. Read between the lines.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;数学论文通常只讲述更大更长的故事中的一个片段。作者通常花费几个月发现事 物，走入死胡同。最终，他将这一切写入一个故事，讲述所有的错误（及相关的 动机），然后在清晰的情节中给出完整的思想。阅读字里行间，这种方式确实可 以重现作者表达的思想。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Mathematics says a lot with a little. The reader must participate. At every stage, he/she must decide whether or not the idea being presented is clear. Ask yourself these questions:&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;数学在很短的篇幅里讲述很多。读者必须参与其中。在每个章节，他／她必须判 断是否明白了其中的思想。自省以下的问题：&lt;/div&gt;&lt;div class="highlight-python"&gt;&lt;pre style="background-color: #eeffcc; border-bottom: 1px solid rgb(170, 204, 153); border-style: solid none; border-top: 1px solid rgb(170, 204, 153); color: #333333; line-height: 15px; overflow: auto; padding: 5px;"&gt;Why is this idea true?&lt;br /&gt;为什么这个想法是对的？&lt;br /&gt;Do I really believe it?&lt;br /&gt;我是否确信它？&lt;br /&gt;Could I convince someone else that it is true?&lt;br /&gt;我能说服别人也信服它吗？&lt;br /&gt;Why didn't the author use a different argument?&lt;br /&gt;为什么作者没有用一个不同的论据？&lt;br /&gt;Do I have a better argument or method of explaining the idea?&lt;br /&gt;我有没有一个更好的论据或方法来说明这个思想？&lt;br /&gt;Why didn't the author explain it the way that I understand it?&lt;br /&gt;为什么作者不用我理解到的方式去阐述它？&lt;br /&gt;Is my way wrong?&lt;br /&gt;我的方法错了吗？&lt;br /&gt;Do I really get the idea?&lt;br /&gt;我确实理解这些方法了吗？&lt;br /&gt;Am I missing some subtlety?&lt;br /&gt;我是否搞错了某些细节？&lt;br /&gt;Did this author miss a subtlety?&lt;br /&gt;作者是否错失了某些细节？&lt;br /&gt;If I can't understand the point, perhaps I can understand a similar&lt;br /&gt;but simpler idea?&lt;br /&gt;如果我不能理解这个观点，我是否能理解类似的更简单的思想？&lt;br /&gt;Which simpler idea?&lt;br /&gt;哪个思想更简单？&lt;br /&gt;Is it really necessary to understand this idea?&lt;br /&gt;对于理解这个思想，它确实是必要的吗？&lt;br /&gt;Can I accept this point without understanding the details of why it&lt;br /&gt;is true?&lt;br /&gt;在不理解它成立的细节依据时，我能接受这个观点吗？&lt;br /&gt;Will my understanding of the whole story suffer from not&lt;br /&gt;understanding why the point is true?&lt;br /&gt;在容忍一些我不理解的内容后，我能理解整个故事吗？&lt;/pre&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Putting too little effort into this participation is like reading a novel without concentrating. After half an hour, you wake up to realize the pages have turned, but you have been daydreaming and don’t remember a thing you read.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;在参与过程中投入的努力太少，就会像阅读一本没有精要的长篇小说。半小时过 去，你清醒过来，意识到翻页了，但是完全不记得刚刚的白日梦中读到了什么。&lt;/div&gt;&lt;/div&gt;&lt;div class="section" id="dont-read-too-fast"&gt;&lt;h2 style="background-color: #f2f2f2; border-bottom: 1px solid rgb(204, 204, 204); color: #20435c; font-family: 'Trebuchet MS',sans-serif; font-size: 26px; font-weight: normal; margin: 20px -20px 10px; padding: 3px 0px 3px 10px;"&gt;Don’t Read Too Fast 别读的太快&lt;/h2&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Reading mathematics too quickly results in frustration. A half hour of concentration in a novel might net the average reader 20-60 pages with full comprehension, depending on the novel and the experience of the reader. The same half hour in a math article buys you 0-10 lines depending on the article and how experienced you are at reading mathematics. There is no substitute for work and time. You can speed up your math reading skill by practicing, but be careful. Like any skill, trying too much too fast can set you back and kill your motivation. Imagine trying to do an hour of high-energy aerobics if you have not worked out in two years. You may make it through the first class, but you are not likely to come back. The frustration from seeing the experienced class members effortlessly do twice as much as you, while you moan the whole next day from soreness, is too much to take.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;数学阅读过快会导致挫败。在阅读小说时，视读者的经历和小说而异，半小时内可以 读 20 到 60 页并完全理解。同样阅读数学论文半小时，视你数学阅读的经验和 论文而异，可以读 0 到 10行。这些劳作和时间无可取代。但是要记住，你可以 通过实践提升数学阅读技巧，加快速度。和任何其它技巧一样，如果你做的过快， 可能会徒劳无功，浪费自己的积极性。设想一下，如果你兩年没有做过高强度的健 身操，现在来它一个小时。你也许做的一极棒，但是不会想再来一次。当你第二 天在痛苦中抱怨的时候，老兵级别的轻松达到你的两倍进度，这是你望尘莫及的 程度。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;For example, consider the following theorem from Levi Ben Gershon’s manuscript Maaseh Hoshev (The Art of Calculation), written in 1321.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;例如，考虑 Levi Ben Gershon 1321 年的 Maaseh Hoshev 手稿（计算的艺术）。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;“When you add consecutive numbers starting with 1, and the number of numbers you add is odd, the result is equal to the product of the middle number among them times the last number.” It is natural for modern day mathematicians to write this as:&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;“当你从 1 开始连续的累加数值，累加奇数个，其结果等于中位数与最后一个数 的积。”现代数学写作:&lt;/div&gt;&lt;img alt="_images/image002.gif" class="align-center" src="http://zerolabrary.appspot.com/static/mathread/html/_images/image002.gif" style="border-width: 0px; clear: both; text-align: center;" /&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;A reader should take as much time to unravel the two-inch version as he would to unravel the two-sentence version. An example of Levi’s theorem is that 1 + 2 + 3 + 4 + 5 = 3×5.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;读者花在理解两英寸版本上的时间应该与两句版本一样。例如，Levi’s 定理的 一个示例如下：&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;1&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;+&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;2&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;+&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;3&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;+&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;4&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;+&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;5&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;=&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;3×5&lt;/span&gt;&lt;/tt&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;。&lt;/div&gt;&lt;/div&gt;&lt;div class="section" id="make-the-idea-your-own"&gt;&lt;h2 style="background-color: #f2f2f2; border-bottom: 1px solid rgb(204, 204, 204); color: #20435c; font-family: 'Trebuchet MS',sans-serif; font-size: 26px; font-weight: normal; margin: 20px -20px 10px; padding: 3px 0px 3px 10px;"&gt;Make the Idea your Own 建立你自己的思想&lt;/h2&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;The best way to understand what you are reading is to make the idea your own. This means following the idea back to its origin, and rediscovering it for yourself. Mathematicians often say that to understand something you must first read it, then write it down in your own words, then teach it to someone else. Everyone has a different set of tools and a different level of “chunking up” complicated ideas. Make the idea fit in with your own perspective and experience.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;阅读时最好的理解方式是建立你自己的思想。这意味着追寻思想的脉络，重 新发现它。数学家们经常讲你要理解某事就要先去读它，然后用自己的语言写下 来，再去向别人传授。每个人都有独特的工具集，“归纳”复杂事物的层次也不同。 让这个思想符合你自己的想法和体验。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;“When I use a word, it means just what I choose it to mean”&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;“当我选择一个词语，它就是我为它选择的含义”&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;(Humpty Dumpty to Alice in Through the Looking Glass by Lewis Carroll)&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;“The&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;meaning&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;is&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;rarely&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;completely&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;transparent,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;because&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;every&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;symbol&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;or&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;word&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;already&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;represents&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;an&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;extraordinary&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;condensation&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;of&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;concept&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;and&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;reference”&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;这意味着很少有完整的说明，因为每一个符号和词语都代表了一个章节的缩写或引用参考&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;a class="footnote-reference" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id10" id="id4" style="color: #355f7c; text-decoration: none;"&gt;[4]&lt;/a&gt;&lt;/blockquote&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;A well-written math text will be careful to use a word in one sense only, making a distinction, say, between combination and permutation (or arrangement). A strict mathematical definition might imply that “yellow rabid dog” and “rabid yellow dog” are different arrangements of words but the same combination of words. Most English speakers would disagree. This extreme precision is utterly foreign to most fiction and poetry writing, where using multiple words, synonyms, and varying descriptions is de rigueur.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;书写良好的数学文本会非常注意用语无岐义，区分明显，如组合与排列（或放 置）。严格的数学定义可以说明“黄疯狗”和“疯黄狗”是不同的词语排列方 式，但它们是同样的组合。大多数说英语的人都不会同意这个。这种极端精确的 用法对大多数小说和诗绝对都是陌生的，它们出于礼仪需要使用复合词，同义词 和各种描述。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;A reader is expected to know that an absolute value is not about some value that happens to be absolute, nor is a function about anything functional.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;对读者来说，他应该知道绝对值不是绝对会出现的一些值，也不是某种有实用价 值的函数。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;A particular notorious example is the use of “It follows easily that” and equivalent constructs. It means something like this:&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;有个特别臭名昭著的例子是滥用“显然可以得到”及其等价物，它相当于下面 的意思：&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;One&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;can&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;now&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;check&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;that&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;the&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;next&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;statement&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;is&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;true&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;with&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;a&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;certain&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;amount&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;of&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;essentially&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;mechanical,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;though&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;perhaps&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;laborious,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;checking.&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;I,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;the&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;author,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;could&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;do&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;it,&lt;/span&gt;&lt;span class="pre"&gt;but&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;it&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;would&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;use&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;up&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;a&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;large&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;amount&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;of&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;space&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;and&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;perhaps&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;not&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;accomplish&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;much,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;since&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;it'd&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;be&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;best&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;for&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;you&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;to&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;go&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;ahead&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;and&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;do&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;the&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;computation&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;to&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;clarify&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;for&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;yourself&lt;/span&gt;&lt;span class="pre"&gt;what's&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;going&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;on&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;here.&lt;/span&gt;&amp;nbsp;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;I&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;promise&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;that&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;no&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;new&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;ideas&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;are&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;involved,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;though&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;of&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;course&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;you&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;might&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;need&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;to&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;think&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;a&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;little&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;in&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;order&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;to&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;find&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;just&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;the&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;right&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;combination&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;of&lt;/span&gt;&lt;span class="pre"&gt;good&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;ideas&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;to&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;apply.&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;现在可以用一系列明确的步骤机械的判明下一表述的正确性，即使可能是很繁重&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;的步骤。我，即作者，可以做到它，但是它可能会占用大量的空间，而这可能无&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;法接受，而由你来完成这些计算，去验证它更好。我承诺不会有什么新的思想再&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;引入进来，当然你还是需要稍动一下脑子，把好点子正确的组合起来。&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;In other words, the construct, when used correctly, is a signal to the reader that what’s involved here is perhaps tedious and even difficult, but involves no deep insights. The reader is then free to decide whether the level of understanding he/she desires requires going through the details or warrants saying “Okay, I’ll accept your word for it.”&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;另一方面，使用得当的话，这种形式很有用。它给读者一个提示，这里可以参与 进来，也许很复杂，很困难，但是不需要更深入的洞见。读者可以自己决定其理 解层次，可以深入细节，也可以讲“OK，我相信你的说法。”&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Now, regardless of your opinion about whether that construct should be used in a particular situation, or whether authors always use it correctly, you should understand what it is supposed to mean. “It follows easily that” does not mean&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;于是，不管你怎么想，要么这种形式用在非常特殊的情况，要么作者总是很得体 的使用它，你应该能理解它的含义。“显然可以得到”不意味着&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;if&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;you&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;can’t&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;see&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;this&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;at&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;once,&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;you’re&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;a&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;dope,&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;如果你一下子看不懂，你就是个笨蛋，&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;neither does it mean&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;也不表示&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;this&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;shouldn’t&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;take&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;more&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;than&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;two&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;minutes,&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;这玩意儿用不了两分钟就能看明白，&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;but a person who doesn’t know the lingo might interpret the phrase in the wrong way, and feel frustrated. This is apart from the issue that one person’s tedious task is another person’s challenge, so the author must correctly judge the audience.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;但是不懂这些隐语的人会因为误解了内容而感到挫折。甲之废话，乙之真言，这 一直是争议所在，作者要恰当的评估读者的水平。&lt;/div&gt;&lt;div class="section" id="know-thyself"&gt;&lt;h3 style="background-color: #f2f2f2; border-bottom: 1px solid rgb(204, 204, 204); color: #20435c; font-family: 'Trebuchet MS',sans-serif; font-size: 22px; font-weight: normal; margin: 20px -20px 10px; padding: 3px 0px 3px 10px;"&gt;Know Thyself 了解你自己&lt;/h3&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Texts are written with a specific audience in mind. Make sure that you are the intended audience, or be willing to do what it takes to become the intended audience.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;文章总是为由假想中的读者写就，请确认你正是这样的读者，或者至少想要变成一个 适宜的读者。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;T.S.Eliot’s&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;A Song for Simeon:&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;西面颂歌&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;a class="footnote-reference" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id11" id="id5" style="color: #355f7c; text-decoration: none;"&gt;[5]&lt;/a&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;：&lt;/div&gt;&lt;div class="highlight-python"&gt;&lt;pre style="background-color: #eeffcc; border-bottom: 1px solid rgb(170, 204, 153); border-style: solid none; border-top: 1px solid rgb(170, 204, 153); color: #333333; line-height: 15px; overflow: auto; padding: 5px;"&gt;Lord, the Roman hyacinths are blooming in bowls and&lt;br /&gt;The winter sun creeps by the snow hills;&lt;br /&gt;The stubborn season has made stand.&lt;br /&gt;My life is light, waiting for the death wind,&lt;br /&gt;Like a feather on the back of my hand.&lt;br /&gt;Dust in sunlight and memory in corners&lt;br /&gt;Wait for the wind that chills towards the dead land.&lt;/pre&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;For example, Eliot’s poem pretty much assumes that its readers are going to either know who Simeon was or be willing to find out. It also assumes that its reader will be somewhat experienced in reading poetry and/or is willing to work to gain such experience. He assumes that they will either know or investigate the allusions here. This goes beyond knowledge of things like who Simeon was. For example, why are the hyacinths “Roman?” Why is that important?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;举个例子，艾略特这首诗就做了不少假设：读者要么知道西面是谁，要么愿意去 了解他是谁；读者要么有一定诗歌鉴赏基础，要么愿意提高这方面的能力；读者 要么知晓诗中的典故，要么愿意研究个中精妙，比如为什么是“罗马”风信子， 为什么这个意象很重要。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Elliot assumes that the reader will read slowly and pay attention to the images: he juxtaposes dust and memory, relates old age to winter, compares waiting for death with a feather on the back of the hand, etc. He assumes that the reader will recognize this as poetry; in a way, he’s assuming that the reader is familiar with a whole poetic tradition. The reader is supposed to notice that alternate lines rhyme, but that the others do not, and so on.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;艾略特还认定读者们会慢慢阅读这首诗，并注意到那些意象：回忆与灰尘的并 置，年老与冬日的关联，将等待死亡降临比作手背上的羽毛等等。读者应该能够 读到这首诗的本质，换句话说，读者应该通晓诗歌的创作传统；读者还应注意到 这首诗押隔行韵等等……这些都是艾略特对自己读者做的假设。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Most of all, he assumes that the reader will read not only with the mind, but also with his/her emotions and imagination, allowing the images to summon up this old man, tired of life but hanging on, waiting expectantly for some crucial event, for something to happen.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;大多数情况，他假设读者不仅仅用思想，也用感情和想像去阅读，让已经对人生 感到疲惫，但是仍在坚持，在期待着某些重要的大事将会发生的老人形像鲜明起来。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Most math books are written with assumptions about the audience: that they know certain things, that they have a certain level of “mathematical maturity,” etc. Before you start to read, make sure you know what the author expects you to know.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;大多数数学书假定读者为：他们有一定程度的基础，已经有一个确定的“数学基 础”等等。在你开始阅读之前，请确认你已经了解作者希望你了解的知识。&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="section" id="an-example-of-mathematical-writing"&gt;&lt;h2 style="background-color: #f2f2f2; border-bottom: 1px solid rgb(204, 204, 204); color: #20435c; font-family: 'Trebuchet MS',sans-serif; font-size: 26px; font-weight: normal; margin: 20px -20px 10px; padding: 3px 0px 3px 10px;"&gt;An Example of Mathematical Writing　一个数学写作的例子&lt;/h2&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;To allow an opportunity to experiment with the guidelines presented here, I am including a small piece of mathematics often called the birthday paradox. The first part is a concise mathematical article explaining the problem and solving it. The second is an imaginary Reader’s attempt to understand the article by using the appropriate reading protocol. This article’s topic is probability and is accessible to a bright and motivated reader with no background at all.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;为了做个实验来展示这里介绍的能力和方法，我引入了名为“生日悖论”的数学 讨论。第一分部分是是一篇数学论文，讲解这个问题并解决它，第二部分是一个 虚构的读者尝试使用之前的阅读规则以理解它。该论文的主题是概率论，它可以 为完全没有知识背景但聪明、积极的读者所理解。&lt;/div&gt;&lt;div class="section" id="the-birthday-paradox"&gt;&lt;h3 style="background-color: #f2f2f2; border-bottom: 1px solid rgb(204, 204, 204); color: #20435c; font-family: 'Trebuchet MS',sans-serif; font-size: 22px; font-weight: normal; margin: 20px -20px 10px; padding: 3px 0px 3px 10px;"&gt;The Birthday Paradox　生日悖论&lt;/h3&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;A professor in a class of 30 random students offers to bet that there are at least two people in the class with the same birthday (month and day, but not necessarily year). Do you accept the bet? What if there were fewer people in the class? Would you bet then?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;有个教授打赌说他班上的30个学生中至少有两个生日在同一天（同月同日，可以 是不同年）。你要不要跟他打赌？如果班上的人再少些呢？还要不要赌？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Assume that the birthdays of n people are uniformly distributed among 365 days of the year (assume no leap years for simplicity). We prove that, the probability that at least two of them have the same birthday (month and day) is equal to:&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;假设 n 个人的生日平均分布在一年的 365 天中（简单起见，不考虑闰年）。我 们证明，至少有两人生日相同的概率为：&lt;/div&gt;&lt;img alt="_images/image004.gif" class="align-center" src="http://zerolabrary.appspot.com/static/mathread/html/_images/image004.gif" style="border-width: 0px; clear: both; text-align: center;" /&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;What is the chance that among 30 random people in a room, there are at least two or more with the same birthday? For n = 30, the probability of at least one matching birthday is about 71%. This means that with 30 people in your class, the professor should win the bet 71 times out of 100 in the long run. It turns out that with 23 people, she should win about 50% of the time.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;同一个班中随机的 30 人，至少有两人生日相同的机率是多少？令 n = 30 ，至 少两人生日想同的概率是 71% 。这意味着教授一直在找 30 人的班打这个赌， 长期来讲他 100 次里能赢 71 次。23 个人的话，她的赢面是 50% 。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Here is the proof: Let P(n) be the probability in question. Let Q(n) = 1 – P(n) be the probability that no two people have a common birthday. Now calculate Q(n) by calculating the number of n birthdays without any duplicates and divide by the total number of n possible birthdays. Then solve for P(n).&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这里有个修正：令 P(n) 为问题中的概率。令 Q(n) = 1 - P(n) 为不存在两个 生日相同的人的概率。现在计算所有 n 个生日的可能性中，全部不重复的概率 Q(n)。然后求得 P(n)。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;The total number of n birthdays without duplicates is:&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;n 个生日的所有不重复的组合是：&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;tt class="docutils literal" style="background-color: #ecf0f3; font-size: 0.95em; padding: 0px 1px;"&gt;&lt;span class="pre"&gt;365&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;×&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;364&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;×&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;363&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;×&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;...&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;×&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;(365&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;–&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;n&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;+&lt;/span&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;span class="pre"&gt;1).&lt;/span&gt;&lt;/tt&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;This is because there are 365 choices for the first birthday, 364 for the next and so on for n birthdays. The total number of n birthdays without any restriction is just 365&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;sup&gt;n&lt;/sup&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;because there are 365 choices for each of n birthdays. Therefore, Q(n) equals&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这因为第一个人有365种可能的选择，第二个人有 364 种可能，依次递推直至第 n 个生日。所有 n 种无限制的生日就是 365&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;sup&gt;n&lt;/sup&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;。因此，Q(n) 等于&lt;/div&gt;&lt;img alt="_images/image004.gif" class="align-center" src="http://zerolabrary.appspot.com/static/mathread/html/_images/image004.gif" style="border-width: 0px; clear: both; text-align: center;" /&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Solving for P(n) gives P(n) = 1 – Q(n) and hence our result.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;根据 P(n) = 1 - Q(n) ，求得 P(n)，解之可得前述答案。&lt;/div&gt;&lt;/div&gt;&lt;div class="section" id="our-reader-attempts-to-understand-the-birthday-paradox"&gt;&lt;h3 style="background-color: #f2f2f2; border-bottom: 1px solid rgb(204, 204, 204); color: #20435c; font-family: 'Trebuchet MS',sans-serif; font-size: 22px; font-weight: normal; margin: 20px -20px 10px; padding: 3px 0px 3px 10px;"&gt;Our Reader Attempts to Understand the Birthday Paradox 阅读理解&lt;/h3&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;In this section, a naive Reader tries to make sense out of the last few paragraphs. The Reader’s part is a metaphor for the Reader thinking out loud, and the Professional’s comments represent research on the Reader’s part. The appropriate protocols are centered and bold at various points in the narrative.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这一节中，一个新手读者想要读懂最后几段。下面的“读者”表示我们虚构的那位读 者，“教授”的注解表示与读者的探讨。在叙述中涉及的几点规则居中加粗表 示。&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;a class="footnote-reference" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id12" id="id6" style="color: #355f7c; text-decoration: none;"&gt;[6]&lt;/a&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;My Reader may seem to catch on to things relatively quickly. However, be assured that in reality a great deal of time passes between each of my Reader’s comments, and that I have left out many of the Reader’s remarks that explore dead-end ideas. To experience what the Reader experiences requires much more than just reading through his/her lines. Think of his/her part as an outline for your own efforts.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这位读者看起来好像很快找到了事物的相关性。然而，为了确认这一点，要在他 的评注中花去大量的时间，我会略过那些他钻牛角尖的讨论。为了体验到读者的 经验，就要深入到他／她的字里行见。通过你的努力去想像他／她的思路。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;strong&gt;Know Thyself 自省&lt;/strong&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Reader (R): I don’t know anything about probability, can I still make it through?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我一点儿也不了解概率，我还能读懂它吗？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Professional (P): Let’s give it a try. We may have to backtrack a lot at each step.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;让我们试试看，我们可能要回溯好多步。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: What does the phrase “30 random students” mean?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;“任意的” 30 个学生是什么意思？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;strong&gt;“When I use a word, it means just what I choose it to mean 当我使用一个词，它就是我为它选择的意思”&lt;/strong&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Good question. It doesn’t mean that we have 30 spacy or scatter-brained people. It means we should assume that the birthdays of these 30 people are independent of one another and that every birthday is equally likely for each person. The author writes this more technically a little further on: “Assume that the birthdays of n people are uniformly distributed among 365 days of the year.”&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;好问题，它不是说我们有 30 个宽广的或者心不在焉的人。它的意思是我们假定 这 30 个人每个人的生日都独立于其他人，选择机会都完全平等。更技术化一点 的说法是：“假设这 n 个人的生日平均分布于一年的 365 天中。”&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Isn’t that obvious? Why bother saying that?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这不是很明显么？为什么要说的这么罗嗦？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Yes the assumption is kind of obvious. The author is just setting the groundwork. The sentence guarantees that everything is normal and the solution does not involve some imaginitive fanciful science-fiction.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;是的，某种意义上来讲这个假设很明显。作者只是做一个背景设定。这个声明确 认每件事都是平凡的，这个解答不会引入什么科幻小说式的想像。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: What do you mean?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;你的意思是？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: For example, the author is not looking for a solution like this: everyone lives in Independence Land and is born on the 4th of July, so the chance of two or more people with the same birthday is 100%. That is not the kind of solution mathematicians enjoy. Incidentally, the assumption also implies that we do not count leap years. In particular, nobody in this problem is born on February 29. Continue reading.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;例如，作者没有做这样的解答：每个人都生活在一个独立的大陆，生于七月四 日，所以每两个或更多的人生日相同的机率为 100%。这不是数学家喜欢的答案。 顺便说一下，这个假设还意味着我们不需要计算闰年，特别是这个问题中没有人 生于二月二十九日。下一个。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: I don’t understand that long formula, what’s n?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我不理解那个很长的公式，什么是 n？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: The author is solving the problem for any number of people, not just for 30. The author, from now on, is going to call the number of people n.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;作者解决了任意多个人的问题，不止是 30，于是作者称人数为 n。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: I still don’t get it. So what’s the answer?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我不太理解，所以那个答案是？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;strong&gt;Don’t Be a Passive Reader - Try Some Examples 不要做一个被动的读者，尝试一下&lt;/strong&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Well, if you want the answer for 30, just set n = 30.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;好的，如果你想知道 30 人时的答案，n 就是 30。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Ok, but that looks complicated to compute. Where’s my calculator? Let’s see: 365 × 364 × 363 × ... × 336. That’s tedious, and the final exact value won’t even fit on my calculator. It reads:&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;OK，不过看起来计算过程很复杂。我的计算器哪儿去了？我们看看： 365 × 364 × 363 × ... × 336 。这也太可怕了，我的计算器都显示不全结果了。 它读作：&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;2.1710301835085570660575334772481e+76&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;If I can’t even calculate the answer once I know the formula, how can I possibly understand where the formula comes from?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;如果我所知的这个公式都不能拿来让我算一次答案，我怎么理解它呢？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: You are right that this answer is inexact, but if you actually go on and do the division, your answer won’t be too far off.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;没错，答案并不准确。不过，如果你用除法去消元，就根本不会有这么复杂。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: The whole thing makes me uncomfortable. I would prefer to be able to calculate it more exactly. Is there another way to do the calculation?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这事儿让我很感棘手。我想把它算得更精确一点。有没有其它的算法？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: How many terms in your product? How many terms in the product on the bottom?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;你乘了几顶？底部的式子总共要多少项相乘？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: You mean 365 is the first term and 364 is the second? Then there are 30 terms. There are also 30 terms on the bottom, (30 copies of 365).&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;你的意思是 365 是第一项，364 是第二项？那么有 30 项，下面也有 30 项 ，（30 个 365）.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Can you calculate the answer now?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;现在你能计算答案了吗？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Oh, I see. I can pair up each top term with each bottom term, and do 365/365 as the first term, then multiply by 364/365, and so on for 30 terms. This way the product never gets too big for my calculator. (After a few minutes)... Okay, I got 0.29368, rounded to 5 places.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;哦，我看看。我可以将每一个分子和每一个分母因数配对，第一顶是 365/365， 然后乘 364/365，类推 30 项。这样对我的计算器来说这个乘法不是很大了。 （过几分钟后）……OK，我求得 0.29368。5位精度。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: What does this number mean?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;那么这个整数意味着什么？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;strong&gt;Don’t Miss the Big Picture 不要失去大局观&lt;/strong&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: I forgot what I was doing. Let’s see. I was calculating the answer for n = 30. The 0.29368 is everything except for subtracting from 1. If I keep going I get 0.70632. Now what does that mean?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我忘了我要干啥了。让我想想，我计算了 n=30 时的答案。从一中减去所求之后 为 0.29368 。接下去我可以求得 0.70632。那么这说明了什么？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Knowing more about probability would help, but this simply means that the chance that two or more out of the 30 people have the same birthday is 70,632 out of 100,000 or about 71%.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;知道多一点更好理解，不过也可以简单的理解为 100,000 次实验中，有 70,632 次机会，30 个人中有至少两个生日相同，这个机率约为 71%。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: That’s interesting. I wouldn’t have guessed that. You mean that in my class with 30 students, there’s a pretty good chance that at least two students have the same birthday?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这很有趣，我没有想到。你的意思是我班上有 30 个学生的话，有很大的机会至 少有两个学生生日想同？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Yes that’s right. You might want to take bets before you ask everyone their birthday. Many people don’t thinkthat a duplicate will occur. That’s why some authors call this the birthday paradox.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;没错，就是这意思。你在问过他们生日前可能很想打这个赌。很多人都没有想到 会有重复出现。这就是为什么有些作者称之为生日悖论。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: So that’s why I should read mathematics, to make a few extra bucks?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;所以我应该读些数学，可以弄点儿外快？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: I see how that might give you some incentive, but I hope the mathematics also inspires you without the monetary prospects.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这事儿看来给了你一些鼓励，不过我希望数学还能激励你多一些非功利的想法。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: I wonder what the answer is for other values of n. I will try some more calculations.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我对 n 为其它值的答案很有兴趣，我想再多算几个。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: That’s a good idea. We can even make a picture out of all your calculations. We could plot a graph of the number of people versus the chance that a duplicate birthday occurs, but maybe this can be left for another time.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;是个好主意，我们甚至可以把你所有的计算结果画成图。我们可以来个人数与生 日重复事件之意的关系曲线，不过这事儿可以下次再弄。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Oh look, the author did some calculations for me. He says that for n = 30 the answer is about 71%; that’s what I calculated too. And, for n = 23 it’s about 50%. Does that make sense? I guess it does. The more people there are, the greater the chance of a common birthday. Hey, I am anticipating the author. Pretty good. Okay, let’s go on.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;瞧，作者为我做了一些计算，他说 n=30 时答案约为 71%，我验算过了。并且， n=23 时答案约为 50%。这可信吗？我觉得可以。选更多的人数，生日重复的机 会总是会更大。嘿，我会抢答了。太棒了。OK，我们继续。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Good, now you’re telling me when to continue.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;很好，现在你觉得我们可以继续了。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;strong&gt;Don’t Read Too Fast 不要读的太快&lt;/strong&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: It seems that we are up to the proof. This must explain why that formula works. What’s this Q(n)? I guess that P stands for probability but what does Q stand for?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;看起来我们做出了证明。我们一定要弄清公式的原理。什么是 Q(n) ？我猜 P 代表概率的意思，不过 Q 代表什么？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: The author is defining something new. He is using Q just because it’s the next letter after P, but Q(n) is also a probability, and closely related to P(n). It’s time to take a minute to think. What is Q(n) and why is it equal to 1 – P(n)?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;作者定义了很多东西，这里使用 Q 只是因为它是 P 的下一个字母。Q(n) 也是 个概率，它强相关于 P(n)。我们该花几分钟想想了。什么是 Q(n)？为什么它等 于 1 - P(n)？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Q(n) is the probability that no two people have the same birthday. Why does the author care about that? Don’t we want the probability that at least two have the same birthday?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Q(n) 是没有生日相同的概率，为什么作者强调这个？我们不能去考虑至少两个 人在同一个生日的概率？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Good point. The author doesn’t tell you this explicitly, but between the lines, you can infer that he has no clue how to calculate P(n) directly. Instead, he introduces Q(n) which supposedly equals 1 – P(n). Presumably, the author will proceed next to tell us how to compute Q(n). By the way, when you finish this article, you may want to deal with the problem of calculating P(n) directly. That’s a perfect follow up to the ideas presented here.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;很好。作者没有明确的告诉你，但是在文中你可以看出他没办法直接计算 P(n)。 相反他引入了相当于 1-P(n) 的 Q(n)。大概作者接下来会告诉我们如何计算 Q(n)。顺便，当你读完论文，可能想解决直接计算 P(n) 的方法。这是个很好的 想法。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: First things first.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;那就尽快动手吧。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Ok. So once we know Q(n), then what?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;OK，我们知道 Q(n) 以后，接下来干什么？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Then we can get P(n). Because if Q(n) = 1 – P(n), then P(n) = 1 – Q(n). Fine, but why is Q(n) = 1 – P(n)? Does the author assume this is obvious?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我们可以得到 P(n)。因为如果 Q(n) = 1 - P(n)，那么 P(n) = 1 - Q(n)。很 好，但是为什么 Q(n) = 1 - P(n)？作者认为这很明显么？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Yes, he does, but what’s worse, he doesn’t even tell us that it is obvious. Here’s a rule of thumb: when an author says clearly this is true or this is obvious, then take 15 minutes to convince yourself it is true. If an author doesn’t even bother to say this, but just implies it, take a little longer.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;是啊，他这么想，更糟糕的是他甚至没说这是显然的。有一个基本守则：如果某 作者对你说这是显然的或肯定为真，那就应该能在 15 分钟内向你说明。如果该 作者甚至懒得说出这一点，只是暗示了一下，大概会花更长时间。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: How will I know when I should stop and think?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我怎么知道我应该在哪里停下来？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Just be honest with yourself. When in doubt, stop and think. When too tired, go watch television.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;诚实的面对自己就好了。因惑的时候就停下来想一想。太累了就去看看电视。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: So why is Q(n) = 1 – P(n)?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;那么为什么 Q(n) = 1 – P(n) ？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Let’s imagine a special case. If the chance of getting two or more of the same birthdays is 1/3, then what’s the chance of not getting two or more?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;让我们想像一个特殊的情况。如果有两个或更多的人同一生日的机率是 1/3。那 么没有两个或更多重复的机率是多少？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: It’s 2/3, because the chance of something not happening is the opposite of the chance of it happening.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;是 2/3 。因为没发生某事的机率是发生此事件的机率的互斥数。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;&lt;strong&gt;Make the Idea Your Own 建立你自己的思想&lt;/strong&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Well, you should be careful when you say things like opposite, but you are right. In fact, you have discovered one of the first rules taught in a course on probability. Namely, that the probability that something will not occur is 1 minus the probability that it will occur. Now go on to the next paragraph.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;很好，讨论对互斥性的时候要小心一些，不过你答对了。事实上你已经在概率论 的道路上发现了第一个法则。即某事件发生的概率是 1 减它没有发生的概率。 现在我们讨论下一部分。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: It seems to be explaining why Q(n) is equal to long complex-looking formula shown. I will never understand this.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;看来这解释了为什么 Q(n) 等于看起来这么复杂的公式。我可能永远也理解不了。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: The formula for Q(n) is tough to understand and the author is counting on your diligence, persistence, and/or background here to get you through.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;Q(n) 的公式确实很难理解，作者也是像你一样坚持不懈的计算和推导中才做到。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: He seems to be counting all possibilities of something and dividing by the total possibilities, whatever that means. I have no idea why.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;不管怎么说，看起来他计算了某事所有的可能性再除总的可能性。我不太理解。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Maybe I can fill you in here on some background before you try to check out any more details. The probability of the occurrence of a particular type of outcome is defined in mathematics to be: the total number of possible ways that type of outcome can occur divided by the total number of possible outcomes. For example, the probability that you throw a four when throwing a die is 1/6. Because there is one possible 4, and there are six possible outcomes. What’s the probability you throw a four or a three?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;可能在你发掘出更多细节前，我可以给你一些背景知识。某一特定类型的事件发 生的概率在数学上定义为：此类事件所有可能发生的结果总和除以此类型事件可 能发生的总数。例如，你扔骰子时扔出 4 的概率是 1／6.因为有六种可能，4 是其中一种。你扔出 4 或 3 的概率是多少？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Well I guess 2/6 (or 1/3) because the total number of outcomes is still six but I have two possible outcomes that work.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我猜是 2/6 （即 1/3）。因为所有可能结果的总数是6，但是我有两种可能的结 果。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Good. Here’s a harder example. What about the chance of throwing a sum of four when you roll two dice? There are three ways to get a four (1-3, 2-2, 3-1) while the total number of possible outcomes is 36. That is 3/36 or 1/12. Look at the following 6 by 6 table and convince yourself.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;很好，有个更难一点儿的例子。当你扔两个骰子的时候，扔出 4 的概率是多大 呢？在所有36种可能中，有三种可能会扔出四（1-3，2-2，3-1）。即3／36，也 就是 1／12。你可以根据下面的 6x6 表格验证一下。&lt;/div&gt;&lt;div class="highlight-python"&gt;&lt;div class="highlight" style="background-color: #eeffcc;"&gt;&lt;pre style="background-color: #eeffcc; border-bottom: 1px solid rgb(170, 204, 153); border-style: solid none; border-top: 1px solid rgb(170, 204, 153); color: #333333; line-height: 15px; overflow: auto; padding: 5px;"&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;br /&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;br /&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;br /&gt;&lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;br /&gt;&lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;br /&gt;&lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;What about the probability of throwing a 7?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;扔出 7 的机率是多少？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Wait. What does 1-1 mean? Doesn’t that equal 0?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;等等， 1-1 是什么意思？不应该是0么？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Sorry, my bad. I was using the minus sign as a dash, just to mean a pair of numbers, so 1-1 means a roll of one on each die - snake eyes.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;对不起，我的错。我推进的太快了。这里只代表一对数字， 1-1 表示扔出两个 1.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Couldn’t you have come up with a better notation?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;你不能找个更好的标注法么？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Well maybe I could/should have, but commas would look worse, a slash would look like division, and anything else might be just as confusing. We aren’t going to publish this transcript anyway.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;说不定有更好的方法，不过逗号看起来好浪费，斜杠看起来像除法，每一种看起 来都有些岐义。不管怎么说，我们又不会拿这个手抄本去出版。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: That’s a relief. Well, I know what you mean now. To answer your question, I can get a seven in six ways via 1-6, 2-5, 3-4, 4-3, 5-2, or 6-1. The total number of outcomes is still 36, so I get 6/36 or 1/6. That’s weird, why isn’t the chance of rolling a 4 the same as for rolling a 7?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;好的，我知道你的意思了。为了回答你的问题，我找到了六种可能的组合 1-6, 2-5, 3-4, 4-3, 5-2, 和 6-1 。结果的总数还是36，所以我求得 6/36，即 1/6。这太奇怪了，为什么 扔出 4 的机率和 扔出 7 的不一样？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Because not every sum is equally likely. The situation would be very different if we were simply spinning a wheel with the sums 2 through 12 listed in equally spaced intervals. In that case, each one of the 11 sums would have probability 1/11.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;因为不是每种总数都一样。这种情况跟我们简单的在 12 种平均分布的样本中任 取两个不一样。这种情况下，11 个样本中任选一个的机率是 1／11。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Okay, now I am an expert. Is probability just about counting?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;OK，现在我也是专家了。概率就是数数嘛。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Sometimes, yes. But counting things is not always so easy.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;某种意义上是这样，不过有时候数数也很难啊。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: I see, let’s go on. By the way, did the author really expect me to know all this? My friend took Probability and Statistics and I am not sure he knows all this stuff.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我懂，咱们继续。顺便问一句，作者真的认为我应该对这些都懂吗？我的朋友就 是搞概率和统计的，我觉得他也未必都懂。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: There’s a lot of information implied in a small bit of mathematics. Yes, the author expected you to know all this, or to discover it yourself just as we have done. If I hadn’t been here, you would have had to ask yourself these questions and answer them by thinking, looking in a reference book, or consulting a friend.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这些东西只是数学知识的沧海一粟。没错，作者希望你都懂，或者就像你刚才那 样去学习它们。如果我不在这儿，你可以向自己提问，然后通过思考，查阅参考 书或请教朋友去弄懂它们。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: So the chance that there are no two people with the same birthday is the number of possible sets of n birthdays without a duplicate divided by the total number of possible sets of n birthdays.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;所以没有两人生日相同的机率是n个人所有可能的生日的总数除以所有不重复生 日的组合数目的商。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Excellent summary.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;完全正确。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: I don’t like using n, so let me use 30. Perhaps the generalization to n will be easy to see.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我不喜欢用 n，那我用 30。可能用 n 来表示更为通用。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Great idea. It is often helpful to look at a special case before understanding the general case.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;太对了。通常在理解通用情况时我们会先尝试特定的情景。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: So how many sets of 30 birthdays are there total? I can’t do it. I guess I need to restrict my view even more. Let’s pretend there are only two people.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;那么 30 人生日总共有多少种组合？我算不出来，大概得再加一些限制。让我们 假设只有两个人的情况。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Fine. Now you’re thinking like a mathematician. Let’s try n = 2. How many sets of two birthdays are there total?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;很好，现在你开始像个数学家一样思考了，让我们考虑一下 n = 2。有多少种生 日组合？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: I number the birthdays from 1 to 365 and forget about leap years. Then these are the all the possibilities:&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;忽略闰年，记可能的生日为1到365.下面是所有的可能：&lt;/div&gt;&lt;div class="highlight-python"&gt;&lt;div class="highlight" style="background-color: #eeffcc;"&gt;&lt;pre style="background-color: #eeffcc; border-bottom: 1px solid rgb(170, 204, 153); border-style: solid none; border-top: 1px solid rgb(170, 204, 153); color: #333333; line-height: 15px; overflow: auto; padding: 5px;"&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o" style="color: #666666;"&gt;...&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;365&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o" style="color: #666666;"&gt;...&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;365&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class="o" style="color: #666666;"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class="mi" style="color: #208050;"&gt;365&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;365&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;365&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mi" style="color: #208050;"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o" style="color: #666666;"&gt;...&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi" style="color: #208050;"&gt;365&lt;/span&gt;&lt;span class="o" style="color: #666666;"&gt;-&lt;/span&gt;&lt;span class="mf" style="color: #208050;"&gt;365.&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: When you write 1-1, do you mean 1-1 = 0, as in subtraction?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这里你写的 1-1 不是 1-1=0？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Stop teasing me. You know exactly what I mean.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;别想糊弄我，你知道我的意思。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Yes I do, and nice choice of notation I might add. Now how many pairs of birthdays are there?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;好的，看来我选的这个标记还挺好用。现在有多少对生日组合？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: There are 365 × 365 total possibilities for two people.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;两个人的话共有 365 x 365 种可能。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: And how many are there when there are no duplicate birthdays?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;那么有多少种不重复的组合？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: I can’t use 1-1, or 2-2, or 3-3 or ... 365-365, so I get&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;去掉 1-1 或 2-2 或 3-3……那么得到&lt;/div&gt;&lt;dl class="docutils" style="margin-bottom: 15px;"&gt;&lt;dt&gt;::&lt;/dt&gt;&lt;dd style="line-height: 20px; margin-bottom: 10px; margin-left: 30px; margin-top: 3px; text-align: justify;"&gt;1-2, 1-3, ... , 1-365, 2-1, 2-3, ... , 2-365, ... 365-1, 365-2, ... , 365-364&lt;/dd&gt;&lt;/dl&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;The total number here is 365 × 364 since each row now has 364 pairs instead of 365.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;总共有 365 x 364 种，因为现在每行有 364 对而不是 365。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Good. You are going a little quickly here, but you’re 100% right. Can you generalize now to 30? What is the total number of possible sets of 30 birthdays? Take a guess. You’re getting good at this.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;很好。这次你做的很快，不过完全弄对了。30 人生日的总的样本数是多少？猜 猜看，你很擅长这个的。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Well if I had to guess, (it’s not really a guess, after all, I already know the formula), I would say that for 30 people you get 365 × 365 ×... × 365, 30 times, for the total number of possible sets of birthdays.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;OK让我猜一下，（也不算真的猜，毕竟我知道公式），30 人生日的所有可能样 本是 365 x 365 x 365 ... x365，共 30 次。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Exactly. Mathematicians write 365&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;sup&gt;30&lt;/sup&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;. And what is the number of possible sets of 30 birthdays without any duplicates?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;很好，数学家会将其写做 365&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;sup&gt;30&lt;/sup&gt;&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;。那么 30 个不重复的生日 组合会是多少？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: I know the answer should be 365 × 364 × 363 × 362 × ... × 336, (that is, start at 365 and multiply by one less for 30 times), but I am not sure I really see why this is true. Perhaps I should do the case with three people first, and work my way up to 30?&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;我知道这个答案应该是 365 × 364 × 363 × 362 × ... × 336 ,（即从 365 开始逐项递减相乘，共 30 项），但是我不确信它是对的。或许我应该先算 一下三个人的，然后逐项增加到 30？&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;P: Splendid idea. Let’s quit for today. The whole picture is there for you. When you are rested and you have more time, you can come back and fill in that last bit of understanding.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;这个想法非常好。今天先到这里。你已经有了自己的构思。等你休息一下，有了 时间，可以回来完成最终的学习和理解。&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;R: Thanks a lot; it’s been an experience. Later.&lt;/div&gt;&lt;div style="line-height: 20px; text-align: justify;"&gt;非常感谢，我学到了很多知识。再见。&lt;/div&gt;&lt;table class="docutils footnote" frame="void" id="id7" rules="none" style="border-collapse: collapse; border-width: 0px;"&gt;&lt;colgroup&gt;&lt;col class="label"&gt;&lt;/col&gt;&lt;col&gt;&lt;/col&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr&gt;&lt;td class="label" style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;&lt;a class="fn-backref" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id1" style="color: #355f7c; text-decoration: none;"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;Emblems of Mind, Edward Rothstein, Avon Books, page 15.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table class="docutils footnote" frame="void" id="id8" rules="none" style="border-collapse: collapse; border-width: 0px;"&gt;&lt;colgroup&gt;&lt;col class="label"&gt;&lt;/col&gt;&lt;col&gt;&lt;/col&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr&gt;&lt;td class="label" style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;&lt;a class="fn-backref" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id2" style="color: #355f7c; text-decoration: none;"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;ibid, page 16.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table class="docutils footnote" frame="void" id="id9" rules="none" style="border-collapse: collapse; border-width: 0px;"&gt;&lt;colgroup&gt;&lt;col class="label"&gt;&lt;/col&gt;&lt;col&gt;&lt;/col&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr&gt;&lt;td class="label" style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;&lt;a class="fn-backref" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id3" style="color: #355f7c; text-decoration: none;"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;ibid, page 38&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table class="docutils footnote" frame="void" id="id10" rules="none" style="border-collapse: collapse; border-width: 0px;"&gt;&lt;colgroup&gt;&lt;col class="label"&gt;&lt;/col&gt;&lt;col&gt;&lt;/col&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr&gt;&lt;td class="label" style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;&lt;a class="fn-backref" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id4" style="color: #355f7c; text-decoration: none;"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;ibid, page 16.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table class="docutils footnote" frame="void" id="id11" rules="none" style="border-collapse: collapse; border-width: 0px;"&gt;&lt;colgroup&gt;&lt;col class="label"&gt;&lt;/col&gt;&lt;col&gt;&lt;/col&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr&gt;&lt;td class="label" style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;&lt;a class="fn-backref" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id5" style="color: #355f7c; text-decoration: none;"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;《西面颂歌》是大诗人艾略特所作，西面是圣经故事中的先知。能力所限，此处不译——译者。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table class="docutils footnote" frame="void" id="id12" rules="none" style="border-collapse: collapse; border-width: 0px;"&gt;&lt;colgroup&gt;&lt;col class="label"&gt;&lt;/col&gt;&lt;col&gt;&lt;/col&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr&gt;&lt;td class="label" style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;&lt;a class="fn-backref" href="http://zerolabrary.appspot.com/static/mathread/html/index.html#id6" style="color: #355f7c; text-decoration: none;"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(170, 170, 170); border-bottom-style: solid; border-width: 0px ! important; padding: 1px 8px 1px 5px; text-align: left;"&gt;由于使用的文档工具问题，您看到的可能只是加粗，我尽量在 HTML 格式中还原原文的格式，并单独制作一份 Latex，直接写 HTML 固然可以完全控制格式，但是这对我太影响翻译的效率了——译者。&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-6702602705482694067?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/6702602705482694067/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=6702602705482694067' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6702602705482694067'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6702602705482694067'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/10/blog-post.html' title='[翻译]如何阅读数学'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-8506991565442554133</id><published>2010-10-09T02:32:00.000-07:00</published><updated>2010-10-09T02:32:15.511-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xelatex'/><category scheme='http://www.blogger.com/atom/ns#' term='latex'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='sphinx'/><title type='text'></title><content type='html'>&lt;span class="Apple-style-span" style="border-collapse: separate; color: black; font-family: 文泉驿等宽微米黑; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: arial,sans-serif; font-size: 13px;"&gt;&lt;span class="z19Dle" id="col-z130edyoix2atlsgd04cjphrplmatnwwv5w"&gt;&lt;span class="zo" style="margin-bottom: 0.4em;"&gt;按如下内容 sphinx 项目的 conf.py ，就可以使其生成的.tex在xelatex命名下正确输出中文PDF&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;latex_preamble = '''\usepackage{xunicode}&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;\usepackage{xltxtra}&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;\usepackage{verbatim}&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;\usepackage{fontspec}&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;\setsansfont{UMingCN}&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;\setromanfont{UKaiCN}&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;\XeTeXlinebreaklocale "zh"&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;\XeTeXlinebreakskip = 0pt plus 1pt&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;'''&lt;span class="Apple-converted-space"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;字体设置根据自己的机器和爱好去修改就可以了。关键是这样已经达到了一个尽可能小的配置集，不需要xeCJK或CJKutf8，这个配置集同样可以用于其它xelatex配置，这样，基于sphinx、xelatex(texlive)，可以形成一个比较理想的html+pdf文档编写环境&lt;/span&gt;&lt;/span&gt;&lt;span class="Ia dm2Ocf" style="color: black; cursor: pointer; margin-left: 10px; text-decoration: underline; white-space: nowrap;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-8506991565442554133?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/8506991565442554133/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=8506991565442554133' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8506991565442554133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8506991565442554133'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/10/sphinx-conf.html' title=''/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-2219995144060983797</id><published>2010-09-01T02:51:00.001-07:00</published><updated>2010-09-01T02:51:09.306-07:00</updated><title type='text'>Python 数据库技术讲座（二）</title><content type='html'>数据库访问技术&lt;div style="width:425px" id="__ss_5095718"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/marchliu/python-5095718" title="Python 数据库技术讲座（二）"&gt;Python 数据库技术讲座（二）&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5095718" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=database2-100831064924-phpapp02&amp;stripped_title=python-5095718" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5095718" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=database2-100831064924-phpapp02&amp;stripped_title=python-5095718" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/marchliu"&gt;March Liu&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-2219995144060983797?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/2219995144060983797/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=2219995144060983797' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2219995144060983797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2219995144060983797'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/09/python_01.html' title='Python 数据库技术讲座（二）'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-3353919979493960180</id><published>2010-09-01T02:48:00.001-07:00</published><updated>2010-09-01T02:48:17.383-07:00</updated><title type='text'>Python 数据库技术第三讲</title><content type='html'> Python 数据库技术第三讲，从SQLAchemy谈面向数据库的架构 &lt;div style="width:425px" id="__ss_5104191"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/marchliu/python-5104191" title="Python 数据库技术第三讲"&gt;Python 数据库技术第三讲&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5104191" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=database3-100901043509-phpapp02&amp;stripped_title=python-5104191" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5104191" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=database3-100901043509-phpapp02&amp;stripped_title=python-5104191" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/marchliu"&gt;March Liu&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-3353919979493960180?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/3353919979493960180/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=3353919979493960180' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3353919979493960180'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3353919979493960180'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/09/python.html' title='Python 数据库技术第三讲'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-8699188317467734180</id><published>2010-08-30T23:21:00.001-07:00</published><updated>2010-08-30T23:21:09.734-07:00</updated><title type='text'>Python 数据库技术讲座（一）</title><content type='html'>Python 内训教材。数据库讲座的第一部分&lt;div style="width:425px" id="__ss_5093577"&gt;&lt;strong style="display:block;margin:12px 0 4px"&gt;&lt;a href="http://www.slideshare.net/marchliu/python-5093577" title="Python 数据库技术讲座（一）"&gt;Python 数据库技术讲座（一）&lt;/a&gt;&lt;/strong&gt;&lt;object id="__sse5093577" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=database1-100830224834-phpapp01&amp;stripped_title=python-5093577" /&gt;&lt;param name="allowFullScreen" value="true"/&gt;&lt;param name="allowScriptAccess" value="always"/&gt;&lt;embed name="__sse5093577" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=database1-100830224834-phpapp01&amp;stripped_title=python-5093577" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="padding:5px 0 12px"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/marchliu"&gt;March Liu&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-8699188317467734180?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/8699188317467734180/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=8699188317467734180' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8699188317467734180'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8699188317467734180'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/08/python.html' title='Python 数据库技术讲座（一）'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-6101560869547594326</id><published>2010-06-12T19:54:00.001-07:00</published><updated>2010-06-12T19:54:40.909-07:00</updated><title type='text'>关于UML的个人见解——答周筠和霍炬两位老师</title><content type='html'>谢谢周老师和霍总的抬爱，我觉得很惭愧，&lt;br /&gt;因为IT知识体系的大局观，我一直感觉不够。&lt;br /&gt;UML在02-04年的时候，非常爱用。但是在后来到北大青鸟做教师，上了一遍UML课程下来，备课过程中，甚至是在批改学生作业的时候，才深感以前对很多基础的UML组成，根本就理解错了。也就是说那个时候所有的同事、客户都在乱用。我抛弃了UML之后，不但没觉得损失，还觉得写代码就写代码，直接、清晰了很多。在这样一个领悟后，我彻底戒除了IDE和UML。反而感觉工作能力有所提升。&lt;br /&gt;对UML的立场，我也处于一个反复和深入的过程。一方面觉得像以前那样错用UML，真的是有害无益。但是经历很多同行企业，确实就是在这样误用。大家仅仅是处于对工具的生产力迷信，这其实是跟我早年盲目相信用VS或Delphi等RAD工具就一定比手写代码高效一样。特别是IT业涉及的生产领域非常广大，管理方式也越来越丰富。很多场合根本不依赖UML这样的图例工具，文本就足够整个团队进行沟通了。但是另一方面，看到一些企业也确实成功的使用UML进行工作。客观上来说，所有这样的团队都是在假装一种获得满足的状态？似乎不可能。我很困惑。&lt;br /&gt;从自己意识到UML被自己误用的体验来看。我认为UML作为软件工程的一个发展成果，肯定还是有它的意义。特别是大型团队中的内外沟通。有一些软件开发工作，特别是我们通常说的大型企业应用项目。有些工作还很难靠开发人员的才华来突破，而是要靠一个团队去完成客观的工作量。这个时候，如果整个团队有一个大家都能理解的，可以图形化的，比较直观的沟通方式，确实是有积极作用。单纯个图形和线段。我想能完成的作用还是有限。但是围绕UML，是有一套基于文本的，清晰的定义方式和描述标准。只是了解这样一个标准，同样需要学习代价。更糟糕的是往往UML在团队中的使用价值，取决于平均甚至最差的那个使用者，而不是最好的那个。&lt;br /&gt;从我的经验来说，我教过的ACCP的学生，几个班里能正确理解Use Case的不超过10人。能很漂亮的运用Use Case的，不超过3个。从我个人来讲，我用过各种UML工具，没有一个让我觉得很方便的可以帮助我快速画出我的想法。所有的图例工具都让我有一种跟不上思路的感觉。类似类图和代码之间互相转换的功能，更是让我觉得画蛇添足，华而不实。&lt;br /&gt;要发挥UML的作用，我想首先应该推广学习最容易，对工作最有价值的那部分，如用例图，泳道图等。作为工作团队，不应该追求尽可能多的使用UML，相反，只在UML比文本好的时候才用它。尽可能的在纸上、白板上画图，哪怕画完再拍照或扫描。不必推崇完备的UML工具。特别是听过一些讲软件工程的课，老师们总是很推崇从ROSE之类的工具中，用UML生成代码，这种技术的实用价值我持怀疑态度。对于我所经历过的所有的工作场景，会用到的UML知识都只是非常非常小的一个子集。UML的最大价值应该是帮助人理解问题，把文本不易描述的问题直观化，让技术能力不同，知识背景不同的人形成共识。而不是帮助机器去构建。或许在J2EE风格鼎盛的年代这样的能力很好很强大。但是在现代这些动态语言面前，无论使用怎样的开发工具，Java/C#这样的静态、强OO、编译语言与之相差一两个数量级的开发效率，总是很难弥补。&lt;br /&gt;另一点说，有个题外话。我有个朋友，他是个DIY发烧友，从我五岁认识他起，他就热衷各种手工制作。有次他给我看一个欧洲论坛上的文章。一个德国朋友讲如何在自家车库里制作涡轮风扇发动机。燃烧室腔体形状如何，可以从那些日用品中找到替代材料，涡扇要有几片桨叶，要扭多大的角度，为什么这样，数学公式如何，三视图如何。都一清二楚。这位德国老兄并不是克虏伯或奔驰的工程师，更不是什么航空专家。他就是一个普通的老百姓，一个单纯的机械爱好者。做这样的 DIY，不是单靠说我喜欢或者我手巧就可以达成的，它需要一种工程师的思维能力，一种可以把想法实现出来，一步步的变成现实的思维和实践训练。想到欧洲，特别是德国英国这样的老牌工业国家，遍地是这样的普通人。就让我产生一种复杂的感情。我们和他们之间的差距，不是一个问题两个问题的解决方法，而是更大的能力差距。当这种差距从一个人两个人扩大到国民这么大的一个团体，发达国家的竞争优势就体现出来了。&lt;br /&gt;在软件开发的领域，这样的差距同样表现的非常明显。为什么人家的团队可以用的很好的各种开发模式和工具，包括敏捷、UML等，到我们这里来就一塌糊涂。这个并非我们不聪明，但是我们缺少真正的工程师的训练。&lt;br /&gt;这些方面我见识少，说的比较混乱。再不打住，就不知道跑出十万八千里了。简单的说，UML，我承认他有用，但是应该在不增加负担的情况下有限使用。UML 是一个中间过程，不是IT开发团队最终的产物，不应该因为它给团队带来负担。大家能读懂到什么程度，能用到什么程度，就到那个程度为止。因地制宜非常重要。不能削足适履。特别是对于我们身边常见的开发团队，和项目领域，如果不是复杂的，需要长期维护、需要大量的跟客户深度沟通的企业应用项目，UML的意义并不大。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-6101560869547594326?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/6101560869547594326/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=6101560869547594326' title='20 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6101560869547594326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6101560869547594326'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/06/uml.html' title='关于UML的个人见解——答周筠和霍炬两位老师'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-8155458116962417638</id><published>2010-06-10T05:00:00.000-07:00</published><updated>2010-06-10T05:03:08.713-07:00</updated><title type='text'>【搬家旧闻】 如果我们的行业充满这样的人和事</title><content type='html'>江湖笑话，诸君不必当真：&lt;br /&gt;&lt;br /&gt;话说当年3721周鸿祎周总跟百度李彦宏斗得不可开交。两人公堂对薄。周总赢了，出来时喜形于色，冲着李总招呼：怎么样，我赢了，你打我啊，你来打我啊？&lt;br /&gt;&lt;br /&gt;李彦宏无语，过去伸手打了他几拳，一脸黑线的跟周围的记者说，你们都看到了吧，是他要我打他的……&lt;br /&gt;&lt;br /&gt;这事儿怎么说呢，真要是有这么喜剧的段子发生在一群新闻记者面前，居然一点风声都没走漏出来，我是不信的。何况李彦宏这么相貌堂堂的一位大叔跟男人打粉拳……这场面太难接受了……呃……&lt;br /&gt;&lt;br /&gt;然则艺术总是来源于生活，想高于生活却未必能成功。人的想像力有多强大，用想像是永远达不到的。例如就有一位0bug 老师 ，别人帮他指出书里有错误，他就能把人想像成是仇家派来玩儿他的。路过的酱油众发表一下意见，他就能把大家都想像成枪手。当然真正让人绝倒的还是他能想像的出一种称为O(7)的时间复杂度……&lt;br /&gt;&lt;br /&gt;现在不要问我出处，0bug老师删帖的本事出神入化的说。我只能就近给各位展示一个喜感的段子。就是Milo Yip撰文（http://www.cnblogs.com/miloyip/archive/2010/05/27 /reply_discrete.html）讨论相关的问题后，肖老师居然能想像出这么伟大的由头：你侵权！（http://blog.csdn.net /tonyxiaohome/archive/2010/05/28/5631256.aspx）。&lt;br /&gt;&lt;br /&gt;且不说这是不是讨论问题的态度吧，也不说这Milo Yip说的是不是在理，能想出来这么个“罪名”，这得是什么样的想像力啊……喂同学你的逻辑在哪里啊……尤其在与肖老师亲切交流之后，我已经吐槽无力了：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;tonyxiaohome 博客专家 发表于2010年5月28日 23:08:43 IP: 举报 回复 删除&lt;br /&gt;回复 missdeer：举个例子吧，我现在就、能让你闭嘴。&lt;br /&gt;&lt;br /&gt;missdeer 博客专家 发表于2010年5月28日 23:13:49 IP: 举报 回复 删除&lt;br /&gt;回复 tonyxiaohome：在说呢，没闭啊&lt;br /&gt;&lt;br /&gt;ccat 博客专家 发表于2010年5月28日 23:15:00 IP: 举报 回复 删除&lt;br /&gt;回复 tonyxiaohome：激动，赶紧占个“现在”的座儿&lt;br /&gt;&lt;br /&gt;ccat 博客专家 发表于2010年5月28日 23:17:33 IP: 举报 回复 删除&lt;br /&gt;回复 missdeer：被事主儿抢了沙发了……&lt;br /&gt;&lt;br /&gt;tonyxiaohome 博客专家 发表于2010年5月28日 23:20:43 IP: 举报 回复 删除&lt;br /&gt;回复 ccat：被闭嘴还能再说，这份脸皮，佩服。嗯，不是枪手的话，一般愤青做不出来。看来，一个人为了钱，什么都干得出来。有个东西叫做廉耻知道不？不知道，回去问问爸爸和妈妈。&lt;br /&gt;&lt;br /&gt;ccat 博客专家 发表于2010年5月28日 23:22:16 IP: 举报 回复 删除&lt;br /&gt;回复 tonyxiaohome：您指望对墙角画小人儿就能控制言论了么？出版署情何以堪吖……&lt;br /&gt;&lt;br /&gt;唉……太那啥的话我就不想说了，这个对着电脑屏幕幻想能让对方闭嘴的强大头脑，难不成是用啥特殊材料做成的？&lt;br /&gt;&lt;br /&gt;忘了这哥们儿啥公司就职来着，但是我越来越看不懂，一个求平均数都不会，时间复杂度能算出O(7)，每天对着电脑把不同意见者幻想成各色仇家的人，到底是怎么混到今天的？这年头难道真是只有数盲才能做领导？&lt;br /&gt;&lt;br /&gt;其实今天本来没肖老师啥事儿的（呃您真无辜）。我想谈的是另一个问题，就是周鸿祎老大对金山的持续性抹黑。&lt;br /&gt;&lt;br /&gt;周总是老江湖，自然不会像肖老师这么没水准。实力在那个摆着，掩耳盗铃每天冲着求伯君先生的头像说“你已经被我卸载”了之类的蠢事想也不会去做。人家老周想到，直接去做就是了。&lt;br /&gt;&lt;br /&gt;于是，360就真的把金山的一系列安全产品都当做木马什么的卸了。然后还要假模假式的摆着提示在那里，跟用户说：这个很危险，我是为你好。&lt;br /&gt;&lt;br /&gt;事至于此，360已经迈过了安全行业大家都视作底线的地方：全面的，无任何理由的将竞争对手的产品列入黑名单。拆台的事儿我不敢说大家从来都没干过 ——实际上很多互不兼容的糗事儿确实是无意造成的——，但是干的这么明目张胆的，真是平生仅见。当然360过底线的事也不止这一件了，其它我就不多说了，免得跑题……&lt;br /&gt;&lt;br /&gt;也不能说以前这事儿从来没出过，3721也曾经跟人在IE的地址栏上打得不亦乐乎。呃，那也是周总大作。&lt;br /&gt;&lt;br /&gt;更搞笑的是周鸿祎突然间正义感大作，跑出来在微博上不断揭批金山种种“黑幕”。每天到微博上上班的敬业精神，真是肖舸之流忘尘莫及啊。&lt;br /&gt;&lt;br /&gt;周总的高明之处在于，有据可查的谣绝对不造，百分百的假话绝对不说。深得韦小宝真传。例如说金山用DDOS攻击了批评金山产品的国外评测网站，这事儿我们听得这个莫名其妙。哪家网站？莫非AV-C？可是人家总裁刚刚来信说金山的产品很不错啊囧……&lt;br /&gt;&lt;br /&gt;然则这种事情你去跟周总对质是不行的，人家又没说是哪个网站被黑了，而且更高明的是绝对不说消息来源是哪里，一口咬定是充满了正义感的内幕人士打电话来爆的料……&lt;br /&gt;&lt;br /&gt;说金山踩微点和瑞星也是同样的路数。证据的不要找我，反正我就是知道，我消费我自己的信用还不行么？周总高明……&lt;br /&gt;&lt;br /&gt;同样的事情，当年3721也不是没对百度做过。现在3721在哪儿？百度在哪儿？尔曹身与名俱裂，不废江河万古流。&lt;br /&gt;&lt;br /&gt;（开复老师：咳咳，这楼主你真会瞎联系……）&lt;br /&gt;&lt;br /&gt;境界决定成就，哪怕你只有五十步，至少也可以笑百步……&lt;br /&gt;&lt;br /&gt;给人扣帽子的本事，肖老师您真是得学一学。你没听说过Milo Yip这不是你的错，但是你总不会说周总也是我们派来的枪手吧。&lt;br /&gt;&lt;br /&gt;周总有逻辑，肖舸没逻辑；周总有目的，肖舸……这个目的的问题，或许有，或许……&lt;br /&gt;&lt;br /&gt;境界啊……所以人家叱咤风云，李彦宏何等人物也为之无可奈何（开复老师要内牛了吧……）。肖老师就只好跟我们这种小人物比一比CSDN谁注册早啊什么的……金麟岂是池中物，肖老师您要雄起啊。&lt;br /&gt;&lt;br /&gt;其实肖老师今天挺无辜，原本是上来吐槽一下周总，想着说万一写精彩了，周总过来骂两句，兄弟我也就红了不是。可是肖老师这段子真把我逗乐了。就是那个说人侵权的文章，我回了一句：&lt;br /&gt;&lt;br /&gt;智拙 评论了博客：请博客园立即停止侵权行为的公开信 4小时前&lt;br /&gt;作人别这么颠倒是非行么？人家指出你的程序有问题，你要是不服可以从技术上正当的回复，居然说人侵权，要不要脸？欢迎删除，今儿我心情好，你删除的话我转手就给你找地方再发出来。&lt;br /&gt;&lt;br /&gt;肖老师真是配合，删了这个回复不说还加了句：&lt;br /&gt;&lt;br /&gt;tonyxiaohome 博客专家 发表于2010年5月28日 22:10:46 IP: 举报 回复 删除&lt;br /&gt;回复 ccat：我呢，先回你再删，留个印记。我就删了，你咬我？&lt;br /&gt;&lt;br /&gt;ccat 博客专家 发表于2010年5月28日 23:03:05 IP: 举报 回复 删除&lt;br /&gt;回复 tonyxiaohome：老肖这么给面子，这坑我不填都不好意思啊，周末开心些哈，等我弄完正事儿慢慢聊，介时一定要捧场哈。&lt;br /&gt;&lt;br /&gt;啧啧啧……话说大家都看到了吧……这是他自己要求的……&lt;br /&gt;&lt;br /&gt;IT业原本应该是一个相对比较干净的行业。自己能骗，编译器没法骗。一个客户能骗，整个互联网没办法骗。一年两年能骗，历史潮流不能骗。广泛的民众化参与，机械精准刻板的行为，决定了我们对真实和逻辑的天然亲近。然而我却看到的是越来越多的无逻辑，越来越多的敏感词。从江湖大佬到隐士高人。总有那么一群人，想的不是如何走正途做正事。而是如何用谣言、抹黑干掉竞争对手，和反对他的人。如何用欺骗和恶意的技术抢占用户的终端。只要没有对手，没有反对，删帖是可以的，在网页里插代码安装3721也是可以。他们努力把这个行业变成泥潭，好在里面可以打个滚。&lt;br /&gt;&lt;br /&gt;当年微点事发的时候，求总多余的话什么也没有说，只在内部讲讲“做自己的事，不看别人笑话”之类的。今天的360事件爆发出来，几位老总也仍然是 “安心做好产品，我们不做恶，公司支持你们”。&lt;br /&gt;&lt;br /&gt;你要说我来金山，困难遇到过，不顺利也有过，但是从这个方面，我从来不后悔来这个企业。&lt;br /&gt;&lt;br /&gt;不在其位不谋其政。我不是什么官方发言人，死Coder而已。我不代表传说中我背后的枪手大佬Milo Yip（要按肖老师的逻辑Milo可以组个洋枪队了，还搞什么IT啊……），也不代表我东家，谢谢。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-8155458116962417638?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/8155458116962417638/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=8155458116962417638' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8155458116962417638'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8155458116962417638'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/06/blog-post_10.html' title='【搬家旧闻】 如果我们的行业充满这样的人和事'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-8752755747422135019</id><published>2010-06-10T04:53:00.000-07:00</published><updated>2010-06-10T05:00:43.866-07:00</updated><title type='text'>[文章搬个家] 关于0bug事件，表个态吧</title><content type='html'>&lt;p&gt;热心读者指点，“下划线下划线下划线”应为“减号减号减号”，特此更正。&lt;br /&gt;&lt;/p&gt;&lt;p&gt;===========================================&lt;/p&gt;&lt;p&gt;最近网络上闹出这个0bug事件，我一直只是个围观的酱油党，最多背地里招呼朋友一起&lt;a href="http://www.douban.com/group/topic/9651108/"&gt;围观一下&lt;/a&gt;&lt;br /&gt;。不过这件事现在看来是没完没了了。&lt;/p&gt;&lt;p&gt;其实事情原本不复杂，这位&lt;a href="http://student.csdn.net/space.php?uid=39028"&gt;肖舸&lt;/a&gt;&lt;br /&gt;先生写了本《&lt;span style="WIDOWS: 2; TEXT-TRANSFORM: none; TEXT-INDENT: 0px; BORDER-COLLAPSE: separate; FONT: medium Simsun; WHITE-SPACE: normal; ORPHANS: 2; LETTER-SPACING: normal; WORD-SPACING: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0pxcolor:#000000;" &gt;&lt;span style="LINE-HEIGHT: 18px;font-family:Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif;color:#333333;"  &gt;&lt;a href="http://book.douban.com/subject/4149139/"&gt;&lt;span style="font-size:100%;"&gt;0bug – C/C++商用工程之道&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;》，然后一些读了这本书的业界人士来写了一些书评，评论了其中的一些问题。前面的链接指向豆瓣，大家还可以看到那些书评。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;这在IT界，本来是再正常不过的事情，一来再好的书或者软件也会有错误；二来有人帮你找出错误，这说明你的书有人读，而且这个读者具有一定的水准，可以帮你和你的其他读者提高。所以侯捷老师就曾经撰文大力推崇为技术书籍勘误。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;在事情的开始，确实如我认为的那样，陈硕先生写了一篇&lt;a href="http://book.douban.com/review/2919204/"&gt;书评&lt;/a&gt;&lt;br /&gt;，肖舸客客气气的接待了，气氛还算融洽。我当时还在豆瓣上推荐了这篇书评，因为我觉得宾主相和，难得看到这么精彩的讨论。C++语言我虽然已经多年不用了，但是看到有这样的思想碰撞，还是很开心的。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;然而接下来的事情发展越来越让我看不懂，这篇&lt;a href="http://book.douban.com/review/2949973/"&gt;评论&lt;/a&gt;&lt;br /&gt;，无论内容的水准，还是作者 Milo 先生的态度，都是非常让人感动的。肖舸同学却一跃而起，跳出来污言秽语，大骂不止。原想看到一场更精彩的讨论，却遇到了泼皮耍赖。围观群众当场就惊呆了。其前倨而后恭，判若两人。让人大惑不解。某位同学的评语或许道出了个中秘密：肖舸知道陈硕是谁，却不认识 Milo。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;这事情就让人觉得有点搞笑了。连围观的各位都看得出， Milo 无论学识人品，都不知道甩出肖舸几条街。难道肖舸待人，只看听没听说过？江湖之大，用这种态度做事，就算只读过武侠小说的也知道会杯具。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;嗯，观众最爱看的就是高手出山，遇上强人剪径，“纳尼？王重阳的干活？不认识！找打！”&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;而最具戏剧性的是，第二天早上肖舸同学他，他，他……他竟然把自己的回复都删了！&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;这可就搞笑了，我对此的评价是：“你当你是卓越？说删就删？”（不懂的同学请搜索“卓越 二十五元 二十四史”等关键词）。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;于是闷笑后，我也忍不住搞了一个收集贴，不过收集的不全，大家不妨猛击以下&lt;a href="http://book.douban.com/review/2963028/"&gt;链接&lt;/a&gt;&lt;br /&gt;。还有减号减号减号老师的精彩表演哦~&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;是的，肖舸先生已经干脆的把自己的ID改成了---。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;卓越也没有这样的事啊！&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;至于肖老师后面的种种表演，大部分集中在CSDN上他自己的专栏，那里他可以删帖可以关评论，只留下排山倒海的拥护声，尽可以关起门来自慰。我就不多引介了，大家有兴趣可以自行围观。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;关于这件事，这个人，我的看法是逐渐在改变的。最初是欣赏，然后是惊讶，再是哑然，然后就开始越来越厌恶了。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;在事情发展的中期，曾经有位老师私下问我，我说，自问有人为我的写作挑毛病，我能不能欣然接受？忐忑不安。我们在潜移默化中接受的危机教育，就是不择手段，决不可被人打倒。我不是个心胸开阔的人，不知道能不能做得比肖老师好。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;而后我却读到了肖舸的&lt;a href="http://student.csdn.net/space.php?uid=39028&amp;amp;do=blog&amp;amp;id=21604"&gt;神作&lt;/a&gt;&lt;br /&gt;。当时忍不住取笑了一句：“满清大员初见蒸汽船无风自行，以为牛拉”。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;时至今日，我只有含笑不语了，朽人粗鄙，但总还是知耻的。再不济你关起门来自省几天还不行么？&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;倒是 Milo Yip 从始至终，一直在对事不对人，对书不对人。人家肖舸污言秽语对他，他几乎没有过一句回嘴。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;桃李不言，下自成蹊。你肖舸搞个闭关自守，不和谐者莫入，固然在你一亩三分地上，Bug的没有，批评的没有，毒奶粉的没有，伟光正的一塌糊涂。天下之大，你还准备封众人之口么？&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;大清也没有这样的事啊。&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;鄙人既非名家，亦远离C++多年。原本与此事无干。单纯只是看个热闹。然则你写了这么一本名目吓人的书，却半点错误都不许人指正，无礼无智，徒增人笑耳。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-8752755747422135019?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/8752755747422135019/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=8752755747422135019' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8752755747422135019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8752755747422135019'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/06/blog-post.html' title='[文章搬个家] 关于0bug事件，表个态吧'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-6720331947248873971</id><published>2010-03-25T10:47:00.001-07:00</published><updated>2010-04-17T16:04:07.923-07:00</updated><title type='text'>不许幻想</title><content type='html'>&lt;div class="gmail_quote"&gt;&lt;blockquote style="BORDER-LEFT: #ccc 1px solid; MARGIN: 0px 0px 0px 0.8ex; PADDING-LEFT: 1ex" class="gmail_quote"&gt;全文见链接：&lt;br /&gt;&lt;a href="http://www.google.com/buzz/108550080008031214508/Hp9HMP2m5UG/%E4%B8%8D%E8%AE%B8%E5%B9%BB%E6%83%B3-%E8%B7%AF%E8%A5%BF%E6%B3%95-%E6%99%A8%E6%98%9F-%E8%AF%B7%E4%BD%A0%E9%97%AE"&gt;http://www.google.com/buzz/108550080008031214508/Hp9HMP2m5UG/%E4%B8%8D%E8%AE%B8%E5%B9%BB%E6%83%B3-%E8%B7%AF%E8%A5%BF%E6%B3%95-%E6%99%A8%E6%98%9F-%E8%AF%B7%E4%BD%A0%E9%97%AE&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;上午1:37 &lt;b&gt;刘鑫:&lt;/b&gt; 不许幻想&lt;br /&gt;&lt;br /&gt;路西法·晨星，请你问问你自己，问问这里的所有人……如果十万天使军不再梦想天堂，地狱又有何伟力？——尼尔·盖曼《睡魔》&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-6720331947248873971?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/6720331947248873971/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=6720331947248873971' title='8 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6720331947248873971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6720331947248873971'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/03/blog-post.html' title='不许幻想'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-5132843599840819940</id><published>2010-01-13T04:38:00.000-08:00</published><updated>2010-01-13T04:43:47.081-08:00</updated><title type='text'>【ZT】百度孙云丰：Google市侩，我感到恶心</title><content type='html'>&lt;div class="artInfo"&gt;&lt;br /&gt;&lt;/div&gt;   &lt;p&gt;今天下午1时20分，百度首席产品设计师孙云丰在自己的博客中撰文关于谷歌退出中国，直指Google退出中国的姿态证明自己是市侩分子，对此感到恶心。&lt;/p&gt; &lt;p&gt;博客全文如下：&lt;/p&gt; &lt;p&gt;&lt;span style="font-family: Arial; line-height: 20px;"&gt;google宣称要退出中国，所证明的，恰恰不是市面上的那些g粉所宣称的那样，google是个人权斗士，而刚好反了过来，正好证明google是个市侩分子。&lt;/span&gt;&lt;/p&gt; &lt;p&gt;&lt;span style="font-family: Arial; line-height: 20px;"&gt;google的首席法律顾问的调调 让我感到恶心。因经济利益退出，就直白白的说好了，把自己涂脂抹粉一番，还煞有介事的提到google被中国人攻击，中国异议分子的Gmail信箱被攻 击，把这些事情作为退出中国的铺垫，这种论调是侮辱中国普通老百姓的智商，但还真有可能迎合那帮目空一切，但从未到过中国、对中国没有丝毫了解，却又喜欢 对中国说三道四的西方人的假想。&lt;br /&gt;&lt;br /&gt;只提一个假设，如果谷歌占据了中国80%的搜索市场份额，google的高管，还会这么高调的宣称要do no evil，从中国退出吗？&lt;br /&gt;&lt;br /&gt;整个事情给我的唯一感受，就是恶心。&lt;br /&gt;--------------&lt;br /&gt;&lt;span style="font-family: Verdana,Arial,Helvetica,sans-serif; line-height: normal;"&gt;&lt;span style="font-family: Arial; line-height: 20px;"&gt;以上是作为一个曾经的忠实google用户而说的，和百度无关。市面上沾沾自喜于了解一点google的产品技术细节将google奉为道德楷模而自封G粉的兄弟，请勿跟帖瞎喷，你们根本不懂什么叫搜索引擎，什么叫自由人权。&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;span style="font-family: Arial; line-height: 20px;"&gt;&lt;span style="font-family: Verdana,Arial,Helvetica,sans-serif; line-height: normal;"&gt;&lt;span style="font-family: Arial; line-height: 20px;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Arial; line-height: 20px;"&gt;&lt;span style="font-family: Verdana,Arial,Helvetica,sans-serif; line-height: normal;"&gt;&lt;span style="font-family: Arial; line-height: 20px;"&gt;历史应该记住这一天，在这位百度首席产品设计师发表此文之后几小时，Google取消Google中国的敏感词过滤，随之百度的敏感词过滤即刻失效，事实证明了最懂搜索引擎，最懂自由人权，最懂中文的百度如何盗用Google服务为自己牟利。&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-5132843599840819940?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/5132843599840819940/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=5132843599840819940' title='5 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5132843599840819940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5132843599840819940'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/01/ztgoogle.html' title='【ZT】百度孙云丰：Google市侩，我感到恶心'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-527784866126665727</id><published>2010-01-09T19:19:00.000-08:00</published><updated>2010-01-09T19:20:39.253-08:00</updated><title type='text'>为 PyCon Asia Pacific 2010 准备的——基于三元语义的关系数据库建模工具Socrates</title><content type='html'>&lt;h2&gt;基于三元语义的关系数据库动态建模工具Socrates&lt;/h2&gt;&lt;h2&gt;Socrates, Relational Database Dynamic Models Make Tools&lt;/h2&gt;&lt;h3&gt;摘要 Summary&lt;/h3&gt;&lt;p&gt;Socrates 是一个基于关系型数据库和ORM的通用数据库工具。它通过三元语义表达了对动态数据结构的存储和管理功能。使得开发人员可以在关系数据库中管理结构可变的 数据。三元语义可以方便的表达单一信息之间的关系，因此它很适合用来表达有向图，甚至有向有环的网状模型，或路径长度很长的关系。&lt;/p&gt;&lt;ul&gt;&lt;li&gt;预计时长：25分钟&lt;/li&gt;&lt;li&gt;难度  Level ：面向中级听众&lt;/li&gt;&lt;li&gt;类别 Categories：数据库 databases&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;详细内容 Description&lt;/h3&gt;&lt;p&gt;Outline&lt;br /&gt;=======&lt;br /&gt;&lt;br /&gt;.. contents:: :local:&lt;br /&gt;&lt;br /&gt;项目背景 Background on project&lt;br /&gt;---------------------------------------&lt;br /&gt;&lt;br /&gt;* 需要建立一个数据库，支持多个统计报表输出，每个报表都是临时性的，数据量不大，但是报表的格式和内容变化很大&lt;/p&gt;&lt;p&gt;* 需要支持当前不能预期的数据内容&lt;/p&gt;&lt;p&gt;* 需要支持一些网状关系模型，节点间的关系变更频繁，经常需要沿节点间的路径进行访问&lt;/p&gt;&lt;p&gt;* 游戏中的人物、道具等信息，展开为关系表会非常庞大，但是往往只有一部分字段有信息，此类稀疏数据结构用关系数据库表达比较浪费&lt;br /&gt;&lt;br /&gt;需要解决的问题 Problems to solve&lt;br /&gt;------------------------------------&lt;/p&gt;&lt;p&gt;* 数据表的业务信息有内在的一致性，为每种变化建立一个新的数据表不经济&lt;/p&gt;&lt;p&gt;* 频繁变化结构的数据，每次调整表结构不现实&lt;/p&gt;&lt;p&gt;* 关系数据库的模型适合表示大量同类数据的关系，但表达深度的递归关系，如树状结构，是比较难操作的&lt;/p&gt;&lt;p&gt;* 一些应用，如游戏中的人物、道具等信息，展开为关系表会非常庞大，但是往往只有一部分字段有信息，此类稀疏数据结构用关系数据库表达比较浪费&lt;/p&gt;&lt;p&gt;项目介绍 What's socrates?&lt;br /&gt;------------------------------------&lt;/p&gt;&lt;p&gt; * Socrates 基于三元语义模型，可以方便的表达动态的数据结构和关系&lt;/p&gt;&lt;p&gt; * 模型分为两个层次：条目和子句&lt;/p&gt;&lt;p&gt;  * 每个子句是一个（主语，谓语，宾语）三元结构&lt;/p&gt;&lt;p&gt;  * 每个主语表示一个条目，每一到多个同一主语的子句描述一个完整的条目&lt;/p&gt;&lt;p&gt;  * 谓语表示主语拥有的特征&lt;/p&gt;&lt;p&gt;  * 宾语表示主语在当前谓语下表达的信息内容&lt;/p&gt;&lt;p&gt; * 每个条目可以看成一个类字典的结构&lt;/p&gt;&lt;p&gt; * 谓语是可读的文本，它的命名是其唯一标识特性&lt;/p&gt;&lt;p&gt; * 宾语可以使用数据库平台所支持的任意数据类型&lt;/p&gt;&lt;p&gt; * Socrates封装关系数据库的静态强类型本质，在使用接口上表现为动态数据模型&lt;/p&gt;&lt;p&gt; * 访问层上，Socrates基于Python的ORM工具SQLAlchemy，可以使用SQLAlchemy的强大功能进行操作&lt;/p&gt;&lt;p&gt; * Socrates 在数据库层表现为一个统一序列器，若干个存储表，十个元语命令的数据行，存储表可以由用户动态添加&lt;br /&gt;&lt;br /&gt;动机 Why socrates?&lt;br /&gt;------------------------&lt;br /&gt;&lt;br /&gt;* 小粒度数据，可以方便的对单一属性精确的访问&lt;/p&gt;&lt;p&gt;* 支持对数据进行类似字典操作的访问&lt;br /&gt;&lt;br /&gt; * 每一个数据条目就是一个类似字典的结构，可以独立的增删数据，不影响其它条目&lt;/p&gt;&lt;p&gt; * 条目之间可以通过子句方便的建立关系&lt;/p&gt;&lt;p&gt; * &lt;/p&gt;&lt;p&gt;支持多种不同数据库，允许使用具体平台的特定数据类型&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;简单的演示 Basic recurrence&lt;br /&gt;----------------------------------&lt;br /&gt;&lt;br /&gt;* 在不同数据库平台上“On Command”建立一个Socrates环境&lt;br /&gt;* 注册新类型、新谓语&lt;br /&gt;* 添加条目，建立关系&lt;br /&gt;&lt;br /&gt;进一步的演示 More sophisticated recurrence&lt;br /&gt;-----------------------------&lt;br /&gt;&lt;br /&gt;* 查询子句和条目&lt;/p&gt;&lt;p&gt;* 沿条目关系进行访问&lt;/p&gt;&lt;p&gt;* 访问和操作条目中的子句&lt;/p&gt;&lt;p&gt;* 谓词表达式&lt;br /&gt;&lt;br /&gt;Other capabilities of socrates&lt;br /&gt;------------------------------&lt;/p&gt;&lt;p&gt;* 性能讨论&lt;/p&gt;&lt;p&gt;* 并发和分布&lt;/p&gt;&lt;p&gt;* 异构数据库的多节点集群&lt;br /&gt;&lt;br /&gt;结尾&lt;br /&gt;----------&lt;br /&gt;&lt;br /&gt;* Socrates  提供了灵活的数据管理方式，为动态和复杂数据的管理提供了一种通用的方式&lt;/p&gt;&lt;p&gt;* Socrates 在DSL和分布式集群方面还可以有所发展&lt;/p&gt;* Socrates 的性能还有很大的提升空间&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-527784866126665727?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/527784866126665727/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=527784866126665727' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/527784866126665727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/527784866126665727'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/01/pycon-asia-pacific-2010-socrates.html' title='为 PyCon Asia Pacific 2010 准备的——基于三元语义的关系数据库建模工具Socrates'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-11305490100497652</id><published>2010-01-09T00:34:00.000-08:00</published><updated>2010-01-09T00:36:20.334-08:00</updated><title type='text'>IT宅男们，有没有什么技术让你觉得相见恨晚？</title><content type='html'>&lt;div class="content clear-block"&gt;     &lt;p&gt;无意中想起SQLite，于是发起了这么一个话题，感谢各位朋友的参与，我把大家的话收集一下，放在这里。&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545076076" favorited="false" class="tweet_text_d" id="tweet_text_d_7545076076"&gt;&lt;span class="message_text" id="msg_text_7545076076"&gt;@sshg&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="shhgs" msg_id="7545185394" favorited="false" class="tweet_text_d" id="tweet_text_d_7545185394"&gt;&lt;span class="message_text" id="msg_text_7545185394"&gt;PY算一个吧。当初看了一个下午就决定ditch Perl了。Haskell算另一个吧。看到type class的时候，也是怦然心动。不过Haskell比Py门槛高，用的机会也相对少。&lt;/span&gt;&lt;/span&gt;&lt;span tab="friends_tab" sender="shhgs" msg_id="7545417632" favorited="false" class="tweet_text_d" id="tweet_text_d_7545417632"&gt;&lt;span class="message_text" id="msg_text_7545417632"&gt;呵呵，我资格比你老。我上手的时候才是2.1。查资料的时候，Internet上还有很多1.6的东西。此外，还被逼无奈，用1.4写过代码。呵呵，1.4才叫那个痛苦呢。连自省都很不完善。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="shhgs" msg_id="7545243913" favorited="false" class="tweet_text_d" id="tweet_text_d_7545243913"&gt;&lt;span class="message_text" id="msg_text_7545243913"&gt;硬件方面，Nokia的maemo设备。此外，3M的迷你投影仪，以及CU上介绍的OLPC&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="shhgs" msg_id="7545264306" favorited="false" class="tweet_text_d" id="tweet_text_d_7545264306"&gt;&lt;span class="message_text" id="msg_text_7545264306"&gt;75没有的第二代OLPC平板电脑。Sexy！&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="shhgs" msg_id="7545849219" favorited="false" class="tweet_text_d" id="tweet_text_d_7545849219"&gt;&lt;span class="message_text" id="msg_text_7545849219"&gt;还有虚拟化。用过很多虚拟化的产品，包括vmware, VBox, Xen, KVM。具体哪个产品好，不好说，但是第一次看到vmware的时候，那个激动呀。用一个词形容，sexy！&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="shhgs" msg_id="7546038850" favorited="false" class="tweet_text_d" id="tweet_text_d_7546038850"&gt;&lt;span class="message_text" id="msg_text_7546038850"&gt;这样说吧。技术分几种。一种是一看就喜欢，然后越研究越喜欢。比如Py，比如虚拟化。有的，一看喜欢，一眼就就放下了。比如Zope。有的一看没觉得怎样，研究一番之后，喜欢上了。比如DNS。还有，一看就不喜欢，越用越不喜欢，还很恶心地得天天用。比如Perl。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545076076" favorited="false" class="tweet_text_d"&gt;&lt;span class="message_text"&gt;@laiyonghao&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="laiyonghao" msg_id="7545705600" favorited="false" class="tweet_text_d" id="tweet_text_d_7545705600"&gt;&lt;span class="message_text" id="msg_text_7545705600"&gt;cassandra/thrift/scribe&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545076076" favorited="false" class="tweet_text_d"&gt;&lt;span class="message_text"&gt;@xiaoxiolu&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="xiaoxiaolu" msg_id="7545279884" favorited="false" class="tweet_text_d" id="tweet_text_d_7545279884"&gt;&lt;span class="message_text" id="msg_text_7545279884"&gt;pypy吧&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545076076" favorited="false" class="tweet_text_d"&gt;&lt;span class="message_text"&gt;@smallfish&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="smallfish19" msg_id="7545622007" favorited="false" class="tweet_text_d" id="tweet_text_d_7545622007"&gt;&lt;span class="message_text" id="msg_text_7545622007"&gt;perl和pg。可惜现在一直未有大规模应用，还只是自己的试验田&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="smallfish19" msg_id="7545622007" favorited="false" class="tweet_text_d"&gt;&lt;span class="message_text"&gt;@neruont&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="neuront" msg_id="7545732040" favorited="false" class="tweet_text_d" id="tweet_text_d_7545732040"&gt;&lt;span class="message_text" id="msg_text_7545732040"&gt;函数式编程. 话说我学函数式编程思想入门还是靠 CPP 泛型...&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;@marchliu&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545158525" favorited="false" class="tweet_text_d" id="tweet_text_d_7545158525"&gt;&lt;span class="message_text" id="msg_text_7545158525"&gt;一 度觉得，我对Python的接触并不算很晚，毕竟从Python2.3以后，它才逐渐完善了多编码文本、修饰、异常等等有用的特性，但是想了想，即使只是 自由软件和动态类型这两个特色，已经足可以改变我的世界观了。如果时间可以倒流，我希望1991年就开始学习Python。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545336823" favorited="false" class="tweet_text_d" id="tweet_text_d_7545336823"&gt;&lt;span class="message_text" id="msg_text_7545336823"&gt;Postgres， 其实从04年就想要学习，但不得其门而入。从去年正式的使用它，这个产品从未让我失望过一次。我是一个“重”数据库爱好者。PG足够强大完备，它的无限嵌 入语言、正则语法集成、丰富的contrib，使我如鱼得水。这个产品可称伟大。它让我入行十年来很多未实现的构思得以成真。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545385185" favorited="false" class="tweet_text_d" id="tweet_text_d_7545385185"&gt;&lt;span class="message_text" id="msg_text_7545385185"&gt;Perl对我来说也是非常重要的一项，它使我的文本处理技能提升了一个台阶。如果早几年学会它，相信我获益会更多。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545526287" favorited="false" class="tweet_text_d" id="tweet_text_d_7545526287"&gt;&lt;span class="message_text" id="msg_text_7545526287"&gt;Emacs， 这个软件对我来说太重要了。它使我真正摆脱了对IDE的依赖，让我从一个拖控件的家伙，真正进入到对代码和软件开发的理解。它改变了我的工作习惯和思维方 式。不过在Emacs23之前，我大概不能接受它对中文的支持度，事实上也是因为这一点，我才拖到这几年学会它。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545576336" favorited="false" class="tweet_text_d" id="tweet_text_d_7545576336"&gt;&lt;span class="message_text" id="msg_text_7545576336"&gt;Unicode，Unicode如果早十年推广该多好！不需要更多的解释了！&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545656598" favorited="false" class="tweet_text_d" id="tweet_text_d_7545656598"&gt;&lt;span class="message_text" id="msg_text_7545656598"&gt;曾经我有很多次机会，如果可以快速交付实用的软件，就可以赚一些钱，但是我找不到顺手的组合，甚至出现过两次对我打击比较大的失败。如果早几年接触Drupal和SQLite多好！我想写这个专题，正是因为刚才作饭的时候想起了SQLite，这是一个伟大的小东西！&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545704659" favorited="false" class="tweet_text_d" id="tweet_text_d_7545704659"&gt;&lt;span class="message_text" id="msg_text_7545704659"&gt;SQLAlchemy，作为一个数据库编程工具，SQLAlchemy还不够完美，但是我再没有用过比它好的了！早年我不该相信“SQLAlchemy笨重复杂，难学难用"的谣传。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span tab="friends_tab" sender="marchliu" msg_id="7545795191" favorited="false" class="tweet_text_d" id="tweet_text_d_7545795191"&gt;&lt;span class="message_text" id="msg_text_7545795191"&gt;各种VCS，作为一个IT工程师，我应该一入行就学会它们中至少一个的使用，但是一直到05年我才有机会去学习和使用CVS，甚至我身边的同事对VCS的兴趣都很小，有很长时间只有我自己去尝试SVN和CVS的使用。真是个悲剧。&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;  &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-11305490100497652?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/11305490100497652/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=11305490100497652' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/11305490100497652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/11305490100497652'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/01/it.html' title='IT宅男们，有没有什么技术让你觉得相见恨晚？'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-2170793642922591051</id><published>2010-01-06T08:56:00.000-08:00</published><updated>2010-01-06T09:00:14.066-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><title type='text'>Java Play! 学习体验</title><content type='html'>Java平台上有大量优秀的资源，但是其基础Web框架标准J2EE非常沉重复杂，严重 影响了小团队使用Java技术实现快速开发。而 Java Play!( &lt;a href="http://www.playframework.org/"&gt;http://www.playframework.org/&lt;/a&gt; ) 有力提升了Java开发的敏捷能力。&lt;br /&gt;&lt;br /&gt;这几天我一直在下班时间学习这个框架。总的来说，它非常的易学易懂，而且架构分层很接近ROR的模型（或者，对于我，很接近Web2py）。其架构设计非常清晰易懂。另外，使用中我体会到的几点优点如下：&lt;br /&gt;&lt;br /&gt;- 在线热开发，不必考虑“Web-app”之类的部署问题，可以在命令行方便的建立和启动项目站点进行开发，源码保存后自动热编译更新，这一点比 Web2py好多了。&lt;br /&gt;- 框架驱动模式合理，几乎不用操心接口继承、注入、AOP什么的，也不需要写XML 配置文件，最多为特定功能的结构进行标记。&lt;br /&gt;- 文档翔实，质量高。这是在任何一个Python框架中都没有达到的水准，包括 Django和ZOPE。甚至就算ROR我觉得与之文档相比也有相当差距。仅 Tutorial一项就比web2py所有文档加一起的质和量都好很多了。&lt;br /&gt;- 结构透明，无魔术。可以方便的使用任何开发工具进行开发，不依赖于某个 IDE，我是用的emacs。&lt;br /&gt;- route 配置非常简单，纯文本，每个配置一行完成&lt;br /&gt;- 模板功能丰富，特别是URL生成，非常智能。&lt;br /&gt;- 有丰富完整和强大的测试支持，从基础的Java UnitTest到前端页面的“硒”测试，还允许用YAML编写测试数据环境，注入到测试或开发环境使用。&lt;br /&gt;- 提供了CRUD、图片验证码等功能支持。&lt;br /&gt;&lt;br /&gt;说了这么多好的，再说说我认为不足的：&lt;br /&gt;- 限于Java语言，它再怎么智能、动态，也是有限的，Java语法不允许的，它不会变魔术。比如SQLAlchemy的动态记录类型，这个在Play的 JPA里，如果不反射的的 话，应该是没办法的。&lt;br /&gt;- 模板功能很丰富，但是标记稍有些烦琐，这个不算大问题。&lt;br /&gt;- 结构比J2EE/EJB简化了，自然也就有简化的代价，估计再像传统方式那样集合大 团队作战不太容易，例如模型层想做多层的代理转发是不行的。但是另一方面说， 肯定也比过去节省人力，开发效率高。&lt;br /&gt;- 还没找到有没有XMLRPC等服务的提供方式，如果真的没有，就需要自己弄一个嵌进去了。&lt;br /&gt;&lt;br /&gt;Play 一个有趣的地方是大量使用动态语言技术进行混合开发，框架里有不少 Python（jython?）和groovy代码。&lt;br /&gt;&lt;br /&gt;在以下两个方面，Play可能会对有贡献：&lt;br /&gt;&lt;br /&gt;一些Java成熟的技术和资源，如Flash、商业应用模型，可以通过这个平台引入 一些高性能要求的应用，目前测试结果Play好过Django等框架。&lt;br /&gt;&lt;br /&gt;Play是我至今遇到最易学易用的Java框架，首次将Java Web开发的门槛降到了 ROR、Django、Web2py这个层次。以前我们难以引入的一些Java资源，现在有希望了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-2170793642922591051?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/2170793642922591051/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=2170793642922591051' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2170793642922591051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2170793642922591051'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2010/01/java-play.html' title='Java Play! 学习体验'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-4091459278377794954</id><published>2009-11-22T22:45:00.001-08:00</published><updated>2010-04-17T16:00:07.028-07:00</updated><title type='text'>数据库技术人员的能力评估建议与培养规划</title><content type='html'>&lt;span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: &amp;#39;Times New Roman&amp;#39;; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: arial,sans-serif; font-size: small;"&gt;&lt;h2&gt; 数据库技术人员的能力评估建议与培养规划&lt;/h2&gt;&lt;p&gt;&lt;a href="http://zerolab.co.cc/?q=node/41" target="_blank" style="font-family: arial,sans-serif; color: rgb(0, 62, 168);"&gt;http://zerolab.co.cc/?q=node/41&lt;span class="__wave_paste"&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/a&gt;&lt;/p&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: &amp;#39;Times New Roman&amp;#39;; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: arial,sans-serif; font-size: small;"&gt;&lt;ul&gt; &lt;li&gt;自挖坑，存档&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;岗位职能区分&lt;/h2&gt;&lt;p&gt;数据库技术在现代软件技术领域有广泛的应用，与数据相关的技术岗位和职能，也可以区分为若干个不同的类别。根据在一个理想的开发团队中不同的分工和知识掌握的不同，可以区分为：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;程序员&lt;/li&gt;&lt;li&gt;分析师&lt;/li&gt;&lt;li&gt;设计师&lt;/li&gt;&lt;li&gt;架构师管理员&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;对于数据库领域，分析与设计岗位、架构与运维岗位（管理员）的重叠较高。特别是分析与设计岗位通常在团队中由同一（组）人承担。在更多的团队中架构师也与分析和设计人员的岗位重叠。因此，从项目开发周期考虑，可以简单的分为三类：&lt;/p&gt; &lt;ul&gt;&lt;li&gt;分析与设计岗位（含架构）&lt;/li&gt;&lt;li&gt;开发岗位&lt;/li&gt;&lt;li&gt;运维岗位&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;个人能力发展路线&lt;/h2&gt;&lt;h3 style="font-family: arial,sans-serif; font-size: small;"&gt;基本路线图&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;初级程序员&lt;/li&gt;&lt;li&gt;中级技术岗位&lt;/li&gt;&lt;li&gt;初级设计人员&lt;/li&gt;&lt;li&gt;初级管理员&lt;/li&gt;&lt;li&gt;中级开发人员&lt;/li&gt; &lt;li&gt;高级技术岗位&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;架构师&lt;/li&gt;&lt;li&gt;设计师&lt;/li&gt;&lt;li&gt;高级管理员&lt;/li&gt;&lt;/ul&gt;&lt;h3 style="font-family: arial,sans-serif; font-size: small;"&gt;入门&lt;/h3&gt;&lt;p&gt;通&lt;/p&gt;&lt;p&gt;常来说，新手接触数据库技术，从程序员岗位做起。这是因为对于数据库技术，此岗位比较容易切入。在软件开发团队中，通常允许初学数据库技术的开发人员，从&lt;/p&gt; &lt;p&gt;事受限的数据库开发工作，此类工作内容可以是基本的增删改查，其数据结构已经由设计人员固定。初级程序员岗位是练习 SQL&lt;/p&gt;&lt;p&gt;语言基本技能、理解关系模型的很好的切入点。&lt;/p&gt;&lt;p&gt;近 年来，此岗位工作通常使用 ORM&lt;/p&gt;&lt;p&gt;工具来完成工作，以提高工作效率和质量。但是对数据库的了解仍然对这个岗位有重要意义。对数据库知识有深入了解的开发人员，可以编写出更高质量的&lt;/p&gt;&lt;p&gt;ORM 代码，可以在必要的时候越过 ORM 的功能局限，编写扩展代码，可以定位性能瓶颈进行优化，等等。&lt;/p&gt; &lt;p&gt;因&lt;/p&gt;&lt;p&gt;此，关系型数据库研发岗位，在当今应用层封装技术非常成熟的前提下，入门更为容易。程序员可以先从 ORM&lt;/p&gt;&lt;p&gt;入手，通常自己比较熟悉的应用层编程环境学习数据访问知识，然后再进一步了解 SQL 语言的知识。另一方面， ORM&lt;/p&gt;&lt;p&gt;并不能代替关系数据库本身。关系数据库知识的掌握程度直接影响程序员的职业发展。&lt;/p&gt;&lt;h3 style="font-family: arial,sans-serif; font-size: small;"&gt; 提升&lt;/h3&gt;&lt;h4&gt;分析/设计岗位&lt;/h4&gt;&lt;p&gt;技&lt;/p&gt;&lt;p&gt;术人员掌握了基本的数据库访问知识后，通常在数据库技术领域，可以有三个不同的发展方向。常见的一个发展方向是转入分析与设计领域。UML&lt;/p&gt;&lt;p&gt;的用例分析、类图、需求分析等知识，与关系数据库的基本设计工具 E-R&lt;/p&gt;&lt;p&gt;图有直接的关系。由此也可以看出，关系数据库的设计和分析工作其实是整合在项目开发过程中的。因为，向此方向发展的技术人员应该提升综合能力，转向软件分&lt;/p&gt; &lt;p&gt;析设计知识。&lt;/p&gt;&lt;p&gt;良好的项目分析准备，能更好的厘清数据库设计所需要的信息。良好的数据库设计，是编写出优质应用层代码的基础。个人前期工作中积累的数据库编程经验，能够使得设计师理解数据库设计中的关键点，了解基本泛式和常见设计模式的运用方法。&lt;/p&gt;&lt;p&gt;===== 中级开发人员 ====&lt;/p&gt;&lt;p&gt;面向数据库的编程工作，本身存在较深入的技术要求。高水平的数据库开发人员，面对各种问题，可以更快的给出解决方案，提供高质量的软件产品。&lt;/p&gt; &lt;p&gt;通常在复杂查询、复杂事务、数据迁移、业务报表定制等方面，需要高水平的开发人员支持。&lt;/p&gt;&lt;h4&gt;初级管理员&lt;/h4&gt;&lt;p&gt;初级数据库管理员可能由开发人员转岗，也可能独立学习，或由系统管理员转岗。除了掌握系统管理员的基本能力，还要了解关系数据的知识。&lt;/p&gt;&lt;p&gt;作为数据库管理员，要能够实现数据库服务的部署、备份、恢复。能够为开发人员和客户提供问题诊断、预警支持。能够找出运营过程中发生的常见问题，予以解决。&lt;/p&gt;&lt;h3 style="font-family: arial,sans-serif; font-size: small;"&gt; 高级技术岗位&lt;/h3&gt;&lt;h4&gt;架构师&lt;/h4&gt;&lt;p&gt;对于大型项目，系统架构成为独立的、重要的岗位。架构师要求综合性的知识和能力，有关数据库领域，主要在于集群设计，备份方案的实现，这需要对操作系统、网络工程、数据库自身的特性都有深入的了解。要求能够解决项目中数据库层的性能和可靠性问题。&lt;/p&gt;&lt;h4&gt;设计师&lt;/h4&gt;&lt;p&gt;通常来说设计师的工作与架构师有一定的重叠。相对来说设计师的工作更偏"软"，需要与开发团队有更多的交流。往往设计师要肩负高级数据库开发人员的职责。同时要有对业务的深入了解。能够设计出应对海量数据、高可靠性、复杂业务的数据库方案。&lt;/p&gt; &lt;h4&gt;高级管理员&lt;/h4&gt;&lt;p&gt;高级数据库管理员是通常概念中数据库领域的高端岗位。这个岗位要求有很强的运维技术能力。要能够参与数据库架构方案的设计，并将其实现。要求能够完成复杂集群的实施、备份恢复方案实现、预警和问题诊断。&lt;/p&gt;&lt;h2&gt;技术能力评佑&lt;/h2&gt;&lt;h3 style="font-family: arial,sans-serif; font-size: small;"&gt;初级岗位&lt;/h3&gt;&lt;h4&gt;预备要求&lt;/h4&gt;&lt;ul&gt;&lt;li&gt; - 掌握基本的编程知识，能够用项目使用的编程语言参与开发&lt;/li&gt;&lt;li&gt;文档阅读能力，能够读懂项目文档，使用术语与团队成员交流&lt;/li&gt;&lt;li&gt;有内外存的基本知识&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;达标指标&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;能够使用 ORM 工具（对我部门建议 SQLAlchemy ）进行基本的增删改查操作&lt;/li&gt;&lt;li&gt;能够理解 ORM 生成的 SQL 脚本&lt;/li&gt;&lt;li&gt;能够手工编写简单的单表和两表关联操作&lt;/li&gt;&lt;li&gt;学会使用客户端工具&lt;/li&gt; &lt;/ul&gt;&lt;h3 style="font-family: arial,sans-serif; font-size: small;"&gt;中级岗位&lt;/h3&gt;&lt;h4&gt;中级开发人员&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;能够使用 SQL 语言解决复杂的查询&lt;/li&gt;&lt;li&gt;熟练使用工作数据库平台，包括特定的编程功能&lt;/li&gt;&lt;li&gt;熟练使用 ORM 工具，可以灵活的使用 ORM 和 SQL 解决工作中的技术问题&lt;/li&gt;&lt;li&gt;可以判断出程序运行中的数据性能问题、设计问题，提出解决方案&lt;/li&gt; &lt;/ul&gt;&lt;h4&gt;分析/设计人员&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;能够理解业务模型，将其转化为数据模型&lt;/li&gt;&lt;li&gt;能够使用 UML 完成从需求分析到数据库设计的思考和工具&lt;/li&gt;&lt;li&gt;能够设计出合理的数据库结构，编程方便，维护容易，避免性能和安全隐患&lt;/li&gt;&lt;li&gt;能够指导初级开发人员解决技术问题 - 熟练阅读和编写复杂的 SQL 语句&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;管理员&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;熟悉 SQL 编程，能够编写复杂的报表查询和数据操作 - 熟悉 *nix 系统环境能够&lt;/li&gt; &lt;li&gt;了解数据库安装和部署方法 - 能够为架构师和开发人员提供问题诊断支持&lt;/li&gt;&lt;li&gt;能够设计和实现基本的备份方案 - 能够使用系统脚本和 SQL 快速解决常见的管理问题包括：&lt;/li&gt;&lt;li&gt;数据备份恢复&lt;/li&gt;&lt;li&gt;服务器状态查询&lt;/li&gt;&lt;li&gt;解决死锁、死链&lt;/li&gt;&lt;li&gt;存储迁移&lt;/li&gt;&lt;li&gt;授权管理&lt;/li&gt;&lt;/ul&gt;&lt;h3 style="font-family: arial,sans-serif; font-size: small;"&gt; 高级岗位&lt;/h3&gt;&lt;h4&gt;架构师&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;熟悉特定数据库的特性&lt;/li&gt;&lt;li&gt;能够理解需求分析，掌握客户需求&lt;/li&gt;&lt;li&gt;能够设计出合理的服务架构和运维方案&lt;/li&gt;&lt;li&gt;能够为开发和设计人员提供数据库技术的咨询支持&lt;/li&gt;&lt;li&gt;能够为运维人员提供数据库运维问题的解决方案&lt;/li&gt;&lt;li&gt;能够为客户提供良好的售前咨询&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;设计师&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;熟悉项目设计&lt;/li&gt;&lt;li&gt;了解业务需求，能够与分析人员和客户进行良好的沟通&lt;/li&gt; &lt;li&gt;了解数据特性，能够根据使用的技术进行设计&lt;/li&gt;&lt;li&gt;能够将业务需求转化为良好的设计方案&lt;/li&gt;&lt;li&gt;能够为开发和设计人员提供数据库技术的咨询支持&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;管理员&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;熟悉机房环境&lt;/li&gt;&lt;li&gt;熟悉操作系统和服务器的运维技术&lt;/li&gt;&lt;li&gt;熟悉数据库特性&lt;/li&gt;&lt;li&gt;能够制定数据库服务器整体运维方案&lt;/li&gt;&lt;li&gt;能够为数据库运营提供灾备、预警、优化、扩展服务&lt;span class="__wave_paste"&gt;&lt;/span&gt;&lt;span&gt; &lt;br&gt; &lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/span&gt;&lt;/span&gt;&lt;br clear="all"&gt;&lt;br&gt;-- &lt;br&gt;每一个成功都是不可复制的。&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-4091459278377794954?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/4091459278377794954/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=4091459278377794954' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4091459278377794954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4091459278377794954'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/11/blog-post.html' title='数据库技术人员的能力评估建议与培养规划'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-6090294391283262490</id><published>2009-10-07T23:32:00.001-07:00</published><updated>2010-04-17T15:44:20.918-07:00</updated><title type='text'>Bitmap 的数据库持久化实现</title><content type='html'>以下是基于数据覆盖的一个初步讨论，实用中，位映射有广泛的应用领域，也有大量的算法实现需求。在经过初步的推演后，我发现这不是可以一蹴而就的。所以先给出一个简单的实现和应用算法讨论。&lt;br&gt;&lt;h1&gt;Bitmap 的数据库持久化实现&lt;/h1&gt;      &lt;p&gt;Bitmap 是一个常见的数据类型，常用于优化大量数值的存储和查询。这个概念对于数据 库程序员应该不陌生，Oracle 等数据库通过这种技术优化查询。通常程序员会在一些算法 或数据结构书籍上读到以 C 语言 char* 实现的 bitmap 。这里，我们考虑一个在数据库中 存储的 bitmap 实现。&lt;/p&gt;  &lt;h3&gt;PG 上的数据结构实现&lt;/h3&gt;  &lt;p class="first"&gt;bitmap 的实现不难，PostgreSQL 中提供了 bitstring 类型，可以直接存储位串。但是单行 bitstring 能提供的数据长度终归有限。为了存储和性能的需要，我们将其设计为一个表。 通过索引值将 bitmap 分片为 bitstring 集。&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;create&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;table&lt;/span&gt; &lt;span style="color: rgb(135, 206, 250);"&gt;bitmap&lt;/span&gt;(idx bigint, segment &lt;span style="color: rgb(152, 251, 152);"&gt;bit&lt;/span&gt;(32));&lt;br&gt; &lt;/pre&gt;   &lt;h3&gt;分析&lt;/h3&gt;  &lt;p class="first"&gt;计算时我们将一个bitmap表视为一个完整的bitmap数据集，设 segment 的宽度为 w ，索引 值为 i 。将第行的 idx 置为0，则第 i 个索引行上的第 b 位对应整个 bitmap 的第 b+i*w 位。逆推之，bitmap 第 x 位为 x div w 行，第 x%w 位。&lt;/p&gt;  &lt;p&gt;因为数据分行存储，位计算操作也需要重新定义，要考虑跨界定位问题。Postgres 的 bit 本身支持左移右移操作。这点并不复杂。可以利用掩码技巧将计算参数分解为区片。具体算 法几乎与 C 语言版本一致。当然针对一些有规则操作时，可以充分的利用 SQL 和 PLSQL 的语法特色进行简化。&lt;/p&gt;   &lt;h3&gt;实例&lt;/h3&gt;  &lt;h4&gt;覆盖统计&lt;/h4&gt;  &lt;p class="first"&gt;详细内容介绍见下一段中的链接。这里简单介绍一下：每个数据样本是一个由大量一维区间 值组成的文本。每一行是一个（起点，终点）区间。要计算二到N个样本件的覆盖率。而数 据本身是未经整理的，可能有重复。&lt;/p&gt;  &lt;p&gt;事实上，这个&lt;a href="http://nodex.javaeye.com/blog/379598"&gt;例子&lt;/a&gt;是我最初着手实现 bitmap 的起因。可以说 bitmap 可以直接优化此类问 题，首先bitmap以至少8倍以上的比例节省存储空间（以 c 语言 char* 每字节存储位置状 态来计）。而空间缩减后对查询性能也有直接提升。&lt;/p&gt;  &lt;p&gt;这个应用的操作非常好实现，将收集来的数据区间段(s, e)转换为二进制数 2^s+2^(s+1)+...+2^e ，即从 s 到 e 之间都为1的二进制位串。然后与整个 bitmap 做或 运算。示例代码如下：&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;create&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;or&lt;/span&gt; replace &lt;span style="color: rgb(0, 255, 255);"&gt;function&lt;/span&gt; &lt;span style="color: rgb(135, 206, 250);"&gt;segment_over&lt;/span&gt;(s bigint, e bigint) &lt;span style="color: rgb(0, 255, 255);"&gt;returns&lt;/span&gt; void &lt;span style="color: rgb(0, 255, 255);"&gt;as&lt;/span&gt; $$&lt;br&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;declare&lt;/span&gt;&lt;br&gt;    sidx bigint;&lt;br&gt;    eidx bigint;&lt;br&gt;    d_data &lt;span style="color: rgb(152, 251, 152);"&gt;bit&lt;/span&gt;(32) := 4294967295::&lt;span style="color: rgb(152, 251, 152);"&gt;bit&lt;/span&gt;(32); &lt;span style="color: rgb(255, 69, 0);"&gt;-- pow(2, 32) -1&lt;br&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;begin&lt;/span&gt;&lt;br&gt;    if currval(&lt;span style="color: rgb(255, 160, 122);"&gt;&amp;#39;bitmap_idx_seq&amp;#39;&lt;/span&gt;) &amp;lt; (e/32)+1 &lt;span style="color: rgb(0, 255, 255);"&gt;then&lt;/span&gt;&lt;br&gt;        &lt;span style="color: rgb(0, 255, 255);"&gt;for&lt;/span&gt; i &lt;span style="color: rgb(0, 255, 255);"&gt;in&lt;/span&gt; currval(&lt;span style="color: rgb(255, 160, 122);"&gt;&amp;#39;bitmap_idx_seq&amp;#39;&lt;/span&gt;)..e/32+1 loop&lt;br&gt;             &lt;span style="color: rgb(0, 255, 255);"&gt;insert&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;into&lt;/span&gt; bitmap(segment) &lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; 0::&lt;span style="color: rgb(152, 251, 152);"&gt;bit&lt;/span&gt;(32);&lt;br&gt;         &lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt; loop;&lt;br&gt;    &lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt; if;&lt;br&gt;    sidx := s/32;&lt;br&gt;    eidx := e/32;&lt;br&gt;    if sidx = eidx &lt;span style="color: rgb(0, 255, 255);"&gt;then&lt;/span&gt;&lt;br&gt;         &lt;span style="color: rgb(0, 255, 255);"&gt;update&lt;/span&gt; bitmap &lt;span style="color: rgb(0, 255, 255);"&gt;set&lt;/span&gt; segment = (d_data &amp;lt;&amp;lt; &lt;span style="color: rgb(176, 196, 222);"&gt;cast&lt;/span&gt;((32-e%32) &lt;span style="color: rgb(0, 255, 255);"&gt;as&lt;/span&gt; &lt;span style="color: rgb(152, 251, 152);"&gt;int&lt;/span&gt;))&amp;amp;(d_data &amp;gt;&amp;gt; &lt;span style="color: rgb(176, 196, 222);"&gt;cast&lt;/span&gt;((s%32) &lt;span style="color: rgb(0, 255, 255);"&gt;as&lt;/span&gt; &lt;span style="color: rgb(152, 251, 152);"&gt;int&lt;/span&gt;)) &lt;span style="color: rgb(0, 255, 255);"&gt;where&lt;/span&gt; idx = sidx;&lt;br&gt;     &lt;span style="color: rgb(0, 255, 255);"&gt;else&lt;/span&gt;&lt;br&gt;        &lt;span style="color: rgb(0, 255, 255);"&gt;update&lt;/span&gt; bitmap &lt;span style="color: rgb(0, 255, 255);"&gt;set&lt;/span&gt; segment = segment | (d_data &amp;gt;&amp;gt; &lt;span style="color: rgb(176, 196, 222);"&gt;cast&lt;/span&gt;((s%32) &lt;span style="color: rgb(0, 255, 255);"&gt;as&lt;/span&gt; &lt;span style="color: rgb(152, 251, 152);"&gt;int&lt;/span&gt;)) &lt;span style="color: rgb(0, 255, 255);"&gt;where&lt;/span&gt; idx = sidx;&lt;br&gt;         &lt;span style="color: rgb(0, 255, 255);"&gt;update&lt;/span&gt; bitmap &lt;span style="color: rgb(0, 255, 255);"&gt;set&lt;/span&gt; segment = d_data &lt;span style="color: rgb(0, 255, 255);"&gt;where&lt;/span&gt; sidx &amp;lt; idx &lt;span style="color: rgb(0, 255, 255);"&gt;and&lt;/span&gt; idx &amp;lt; eidx;&lt;br&gt;         &lt;span style="color: rgb(0, 255, 255);"&gt;update&lt;/span&gt; bitmap &lt;span style="color: rgb(0, 255, 255);"&gt;set&lt;/span&gt; segment = segment | (d_data &amp;lt;&amp;lt; &lt;span style="color: rgb(176, 196, 222);"&gt;cast&lt;/span&gt;((e%32) &lt;span style="color: rgb(0, 255, 255);"&gt;AS&lt;/span&gt; &lt;span style="color: rgb(152, 251, 152);"&gt;INT&lt;/span&gt;)) &lt;span style="color: rgb(0, 255, 255);"&gt;where&lt;/span&gt; idx = eidx;&lt;br&gt;     &lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt; if;&lt;br&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt;;&lt;br&gt;$$ &lt;span style="color: rgb(0, 255, 255);"&gt;language&lt;/span&gt; plpgsql;&lt;br&gt;&lt;/pre&gt;  &lt;h5&gt;优化分析&lt;/h5&gt;  &lt;p&gt;此示例中，segment_over 操作并未做任何深度优化，仅仅是将前述的思路直接实现出来而 已。实际上根据业务，我们还可以进一步的提高操作效率。&lt;/p&gt;  &lt;p&gt;例如，由于覆盖度操作是在数据区迭加或操作，单向的填充动作，可以将已覆盖区间直接压 缩掉（这需要将 idx 字段扩展为起点和终点一对数据，或者通过触发器机制同步一个查询 表）。&lt;/p&gt;  &lt;p&gt;再例如，前例中我们的 segment 长度为32，idx 是一个 bigint 类型。这对于海量数据比 较有效，不过如果总样本量较小，完全可以用 integer ，这样可以减少很多转型操作。&lt;/p&gt;  &lt;p&gt;在 segment_over 中，我写了一个防出错的探测代码，如果索引值超出了初始化范围，会扩 展 bitmap 表。但是实际上很多应用，其总数据量是有明确上界的。此时可以去掉这部分代 码，使用一个一次性的初始化代码，例如下面这样：&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;create&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;or&lt;/span&gt; replace &lt;span style="color: rgb(0, 255, 255);"&gt;function&lt;/span&gt; &lt;span style="color: rgb(135, 206, 250);"&gt;init&lt;/span&gt;(len bigint) &lt;span style="color: rgb(0, 255, 255);"&gt;returns&lt;/span&gt; void &lt;span style="color: rgb(0, 255, 255);"&gt;as&lt;/span&gt; $$&lt;br&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;declare&lt;/span&gt;&lt;br&gt;    i bigint;&lt;br&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;begin&lt;/span&gt;&lt;br&gt;    truncate &lt;span style="color: rgb(0, 255, 255);"&gt;table&lt;/span&gt; bitmap restart &lt;span style="color: rgb(0, 255, 255);"&gt;identity&lt;/span&gt;;&lt;br&gt;     &lt;span style="color: rgb(0, 255, 255);"&gt;insert&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;into&lt;/span&gt; bitmap(idx, segment) &lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; 0, 0::&lt;span style="color: rgb(152, 251, 152);"&gt;bit&lt;/span&gt;(32);&lt;br&gt;     if len/32 &amp;lt; 1 &lt;span style="color: rgb(0, 255, 255);"&gt;then&lt;/span&gt;&lt;br&gt;        &lt;span style="color: rgb(0, 255, 255);"&gt;return&lt;/span&gt;;&lt;br&gt;    &lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt; if;&lt;br&gt;    &lt;span style="color: rgb(0, 255, 255);"&gt;for&lt;/span&gt; i &lt;span style="color: rgb(0, 255, 255);"&gt;in&lt;/span&gt; 1..len/32 loop&lt;br&gt;         &lt;span style="color: rgb(0, 255, 255);"&gt;insert&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;into&lt;/span&gt; bitmap(segment) &lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; 0::&lt;span style="color: rgb(152, 251, 152);"&gt;bit&lt;/span&gt;(32);&lt;br&gt;     &lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt; loop;&lt;br&gt;    if len%32 &amp;gt; 0 &lt;span style="color: rgb(0, 255, 255);"&gt;then&lt;/span&gt;&lt;br&gt;        &lt;span style="color: rgb(0, 255, 255);"&gt;insert&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;into&lt;/span&gt; bitmap(segment) &lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; 0::&lt;span style="color: rgb(152, 251, 152);"&gt;bit&lt;/span&gt;(32);&lt;br&gt;     &lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt; if;&lt;br&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt;;&lt;br&gt;$$ &lt;span style="color: rgb(0, 255, 255);"&gt;language&lt;/span&gt; plpgsql;&lt;br&gt;&lt;/pre&gt;  &lt;p&gt;将 segment 的宽度调整为略大于覆盖区间长度期望值的数值，可以减少对中间区间的覆盖 操作，也有优化读写效率的可能。&lt;/p&gt;   &lt;h5&gt;数据库实现的动机&lt;/h5&gt;  &lt;p&gt;使用数据库存储 bitmap ，可以避免海量长度 bitmap 超出内存容量的情况，是一个比较经 济的实现方式，特别是在机器不够强劲，或者没有条件部署云或网格集群的场景。只要熟练 数据库层开发即可。&lt;/p&gt;  &lt;p&gt;由于 Postgres 中已经实现了完备的并发访问、数据备份和查询等功能，可以避免自己开发 文件型 bitmap 所需要考虑以上问题，带来的开发成本。轻松可以满足 OLAP 的需求，在线 随时查询覆盖情况。&lt;/p&gt;  &lt;p&gt;合理的使用数据库功能，可以有效的降低使用和开发成本，这种组件式的开发和集成方式， 对于当下追求灵活、高性价比和可持续性的 IT 产业理念，是契合的。&lt;/p&gt;&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-6090294391283262490?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/6090294391283262490/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=6090294391283262490' title='5 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6090294391283262490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6090294391283262490'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/10/bitmap.html' title='Bitmap 的数据库持久化实现'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-8603893645541012116</id><published>2009-09-24T20:55:00.001-07:00</published><updated>2010-04-17T15:44:20.923-07:00</updated><title type='text'>sqlalchemy、storm和web2py dal的比较报告</title><content type='html'>工作项目报告，所以抹掉项目名先，以"X"代之。&lt;br&gt;&lt;br&gt;分割线内内容仅代表个人意见，与所供职企业及参与社区无关。&lt;br&gt;&lt;br&gt;===================================&lt;br&gt;&lt;br&gt;X 从很早的时候就出现各种数据库访问错误。包括链接数占用过多，死锁，&lt;br&gt;僵尸事务等。本周我集中梳理了一遍代码。我认为，虽然数据库设计方面有诸多&lt;br&gt;不合理之处，但是这些不合理主要影响业务错误，造成 X 性能和使用上的&lt;br&gt; 问题是因为使用的ORM框架 storm 有严重的缺陷。&lt;br&gt;&lt;br&gt;首先，storm 对数据库架构的同步有非常奇怪的设定。它不自动同步表结构，却插&lt;br&gt;手外键关联关系。强制要求外键必须都是级联更新、级联删除、set null。且不说&lt;br&gt;其设定中有自相矛盾之处，本身在MIS系统中做级联删除就是一件很危险的事——除&lt;br&gt;了系统维护、分表，MIS系统不应该删除任何数据。 storm 从一开始设计恐怕就没&lt;br&gt;有考虑企业级应用，但是对于web 开放式应用，storm 对外键的依赖又太笨重了。&lt;br&gt; &lt;br&gt;其次，storm 在联接数据库后应该会有 DDL 操作（即修改数据库结构）或独占锁&lt;br&gt;定事务，此推断的证据在于用 storm 联接到 S 库后， S 无法进行&lt;br&gt;vacuumdb -a -f 处理。而根据 Postgres 手册，PG只有在遇到有链接正在进行&lt;br&gt;DDL 操作时，才会产生库级锁，造成 vacuum 操作无法进行（或手工建立一个隔离&lt;br&gt;级别相当的事务锁定）。作为世界上并发能力最强的数据库产品，正常的数据访问&lt;br&gt; 操作与 vacuum 根本不会冲突，热处理资源回收正是 PG 独到的特性。这样造成了&lt;br&gt;X 应用频繁用光所有的链接数，还在数据库服务器遗留僵尸进程。我观察到&lt;br&gt;有僵死十几二十天没有完成过的 X 链接，这应该是因为同时多个 storm 链&lt;br&gt;接提交错误的锁定关系，造成死锁。&lt;br&gt;&lt;br&gt;第三，storm 如果可以象 DAL 那样，明确设定不同步数据库，以上问题至少可以&lt;br&gt;解决一半，但是它没有链接配置参数。这造成了我们对其出现的问题无法进行友好&lt;br&gt; 的调整。现在同事在 X 的 controll 层添加了强制的 commit 操作，一定&lt;br&gt;程度上减少了死链，但是我仍然观察到有点击 X 页面（在测试环境下）无&lt;br&gt;法响应，甚至造成数据库服务器闪断重启的现象，对于同时运营三十几个数据库的&lt;br&gt;数据库服务器，这是非常大的安全隐患。&lt;br&gt;&lt;br&gt;昨天我尝试将 X 的数据库访问层迁移至 web2py DAL，经过一天尝试，总结&lt;br&gt;出以下的问题：&lt;br&gt;&lt;br&gt;第一，DAL 缺少精确实数计算类型，它不支持 Numeric 或 Decimal，只能用&lt;br&gt; double，这是一个相当大的安全隐患，对于涉及财会计算的应用，使用浮点数是一&lt;br&gt;种很不严肃的作法。&lt;br&gt;&lt;br&gt;第二，DAL 不支持数组和大数据类型，此类字段在 X 中有几处应用。&lt;br&gt;&lt;br&gt;第三，DAL 对原生SQL的支持比较初级，虽然也可以使用，但是有时需要兼顾开发&lt;br&gt;速度，希望可以组合使用的时候，就会受限。&lt;br&gt;&lt;br&gt;以上问题不是不能解决，但是需要修改 DAL 本身。虽然我一直有计划改造 DAL，&lt;br&gt;fork 一个对 PG 有良好支持的分支出来。但是这显然需要更多的开发时间。&lt;br&gt; &lt;br&gt;昨天我详细查阅了一下 sqlalchemy 的文档，进行了一些简单的尝试，感觉这个&lt;br&gt;ORM 框架比较符合我们的需求：&lt;br&gt;&lt;br&gt;第一，sqlachemy 对数据库链接的隔离级别和事务有良好的控制，默认也不会去尝&lt;br&gt;试DDL操作。&lt;br&gt;&lt;br&gt;第二，sqlachemy 有非常丰富的数据类型支持，包括BLOB，Decimal/Numeric，以&lt;br&gt;及为 PG 特别定制的数组类型。&lt;br&gt;&lt;br&gt;第三，sqlachemy 的查询类似 DAL （很可能DAL学习自sqlachemy)，对各种查询操&lt;br&gt; 作有良好的支持，还可以嵌入 SQL 片段，也可以方便的直接传入 SQL。&lt;br&gt;&lt;br&gt;第四，sqlachemy 其实并不难学，它的功能虽然丰富，但是只掌握自己要用到的部&lt;br&gt;分即可，不需要完全学会，上手还是很简单的。&lt;br&gt;&lt;br&gt;第五，文档比 storm 完整的多，而且现在仍在活跃开发。&lt;br&gt;&lt;br&gt;第六，数据存储模型与业务模型分离，虽然看起来有重复劳动，但是对于一个需要&lt;br&gt;长期维护的企业应用项目，这是正确和严肃的作法。&lt;br&gt; &lt;br&gt;第七，对特定数据库的特性有良好的支持，还可以扩展。&lt;br&gt;&lt;br&gt;在使用 sqlachemy 时，我们还可以结合 web2py 原有的一些优秀工具，例如广泛&lt;br&gt;用于 DAL 的storage类型，这是一个类似 JS Object 的智能对象类型，很适合动&lt;br&gt;态结构的数据对象。&lt;br&gt;&lt;br&gt;对于 X 项目的数据访问层重构，我评估工作量至少在一周左右。如果全部&lt;br&gt;换用 sqlachemy ，可以一劳永逸的解决数据库访问的问题，甚至 S 的后续版&lt;br&gt; 本，我也建议尝试使用这个框架，毕竟这比 hack dal 要方便一些。&lt;br&gt;&lt;br&gt;sqlachemy 当前的稳定版本是 0.5.6 ，我昨晚试验了开发中的 0.6 ，发现功能还&lt;br&gt;没有完整实现，现在还不能实用。&lt;br&gt;&lt;br&gt;&lt;br&gt;===================================&lt;br clear="all"&gt;&lt;br&gt;-- &lt;br&gt;光见贼吃肉，没见贼挨打。&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-8603893645541012116?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/8603893645541012116/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=8603893645541012116' title='3 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8603893645541012116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8603893645541012116'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/09/sqlalchemystormweb2py-dal.html' title='sqlalchemy、storm和web2py dal的比较报告'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-6832272521885561808</id><published>2009-09-15T18:49:00.001-07:00</published><updated>2010-04-17T15:44:20.927-07:00</updated><title type='text'>近日 twitter 发布信息整理</title><content type='html'>   &lt;br&gt;   数据库的架构与设计，应该从整体的范围去看问题，要从"数据存储和管理服务"的角度，而不是"数据库软件"&lt;br&gt;&lt;br&gt;   应用层开发阶段，可以不加入外键，在数据库运营阶段，由DBA补完&lt;br&gt;&lt;br&gt;   ID列的主键身份，其实只是为了与ORM妥协，在很多情况下，一个表只有ID主键列，既危险又愚蠢。如果应用层存在BUG，就会引发严重问题。我的建议是，至少在运营前期，就为关键实体数据加上唯一索引。&lt;br&gt;&lt;br&gt;   存储过程有没有用？这个要看情况。做数据库架构就像建筑工程，不是"对""不对"这么简单。早年Oracle宣传一切逻辑都封装在存储过程中，数只从存储过程访问，这叫蛋疼。后来MySQL宣传外键存储过程触发器都不需要，只要能增删改查，这叫犯二。&lt;br&gt; &lt;br&gt;   一个数据库要如何设计，业务对数据可靠性、完整性、性能的需求平衡是一个很重要的因素。对于用户访问日志（包括点击率等等），有一定的误差也是允许的，而商务活动往往是不允许有哪怕毫厘之差。针对不同的业务，应该有不同的设计和架构方案。&lt;br&gt;          &lt;br&gt;   上次李元佳先生介绍的EDB集群方案给我一定启发。典型的集群节点可以每节点由三个机器组成：一个查询节点，一个写入节点，一个备份节点。写入节点和备份节点多出来的内存可以共享给查询节点做为无限缓存（EDB特有功能）。&lt;br&gt;           &lt;br&gt;   有时查询节点还要负担起OLAP的任务，或者本身统计查询的计算量就远大于写入量，此时可以由一个写入节点带多个查询节点，或者直接向一个EDB网格同步。当然网格化以后，写入性能通常会有接近线性提升，这样可能就不需要写入节点了。&lt;br&gt;&lt;br&gt;   前段时间我写过一篇文章，合理利用触发器，可以把一个无限增长的日志表访问，变成限定于20条数据的访问，如果按现在流行的MySQL+ORM，这种东西是做不出来的。&lt;br&gt;           &lt;br&gt;   事实上很多项目，尤其是高负载的互联网应用，最终都没有使用ORM。既然如此，拒绝数据库服务端编程就是一件很奇怪的事。在数据库服务端进行合理的编程，可以大大提高性能。&lt;br&gt;          &lt;br&gt;   既然数据库服务是作为一个整体使用的，那么性能分析也要从整体考虑。有些功能虽然单独比较性能不高，但是却能在合适的时候，提高应用的整体性能。&lt;br&gt;          &lt;br&gt;   数据库设计要符合业务，不要拿来作为自己炫技的场所。&lt;br&gt;           &lt;br&gt;   虽然这年头是个人都知道出来吼一句"数据库太慢！"以显示自己牛B，但实际上有多少是真的牛B到跨过了关系型数据库的极限，有多少只是不懂数据库在那里装B呢？&lt;br&gt;          &lt;br&gt;   观点：DBA的职责，除了维护工作，应该有以下几个——帮助设计人员找出性能和安全方面的问题；帮助开发人员解决数据库方面的问题，例如一些用其它手段难以解决的性能瓶颈和复杂逻辑；协助架构师制定系统持久层架构；为乙方（通常是DBA就职的一方）提供架构方面的专业意见。&lt;br&gt;           &lt;br&gt;    个人认为大多数应用不应该出现大量存储过程，尤其在ORM比较成熟的现今，基本不需要为CRUD操作封装存储过程，而存储过程不能与用户多步交互执行，这就决定了它不会主导事务处理逻辑。但是存储过程可以简化复杂的事务逻辑。&lt;br&gt;          &lt;br&gt;    简单的说，存储过程最好是在开发由粗到精的优化过程中加入，代替需要优化的代码部分。如果是那种希望快速粗放的拼装一个项目然后交钥匙不管的初等级开发（不要拿这个来冒充XP过程）并不适合过多使用存储过程，最好是用熟ORM，积累并复用确实有效的一些存储过程和SQL脚本模板。&lt;br&gt; &lt;br clear="all"&gt;&lt;br&gt;-- &lt;br&gt;话题越大，废话越多；名字越火星，问题越脑残。&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-6832272521885561808?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/6832272521885561808/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=6832272521885561808' title='3 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6832272521885561808'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6832272521885561808'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/09/twitter.html' title='近日 twitter 发布信息整理'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-2867063562898885041</id><published>2009-08-31T20:09:00.001-07:00</published><updated>2010-04-17T15:44:20.930-07:00</updated><title type='text'>[Postgres Story]最近访问用户问题</title><content type='html'>&lt;p&gt;前几天有个朋友提了一个问题:&lt;/p&gt;  &lt;p&gt;应用平台需要统计最近访问的20个用户的信息。&lt;/p&gt;  &lt;p&gt;我第一个想到的是用memcache之类的专用的缓冲，以用户名为键，以最后访问时间为值，如 果用户访问比较均匀，限定一个合适的 超时值，查询的时候遍历过滤就好了，虽然不是很 精确，但是实现起来够简单。&lt;/p&gt;  &lt;p&gt;答：不可以，领导要求只在数据库端解决。&lt;/p&gt;  &lt;h3&gt;环境准备&lt;/h3&gt;  &lt;p class="first"&gt;提问的朋友使用的是 MySQL ，这里我用一个 PostgreSQL 8.4(Enterprise DB Postgres Plus Stand Server 8.4) 建立实验环境。操作系统是 Windows 7 RC。&lt;/p&gt;  &lt;p&gt;客户端是 Emacs sql-postgres，\timing on。&lt;/p&gt;  &lt;p&gt;使用 explain analyze 分析性能消耗。&lt;/p&gt;   &lt;h3&gt;尝试&lt;/h3&gt;  &lt;p class="first"&gt;那么，最简单的应该是在日志表上查询：&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; username, &lt;span style="color: rgb(176, 196, 222);"&gt;max&lt;/span&gt;(logintime)&lt;br&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;from&lt;/span&gt; log&lt;br&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;group&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;by&lt;/span&gt; username&lt;br&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;order&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;by&lt;/span&gt; 2 &lt;span style="color: rgb(0, 255, 255);"&gt;desc&lt;/span&gt;&lt;br&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;limit&lt;/span&gt; 20;&lt;br&gt;&lt;/pre&gt;   &lt;p&gt;这里假定的是会话表(log)中有名为 username 的用户标识列，名为 logintime 的时间戳字 段。&lt;/p&gt;  &lt;p&gt;但是显然这个查询的性能并不好，它要遍历整个日志表。&lt;/p&gt;  &lt;p&gt;首先，我们给日志表加一个聚集索引，使它按插入时间倒排（显然这对插入效率不利）。&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;create&lt;/span&gt; index idx_logintime &lt;span style="color: rgb(0, 255, 255);"&gt;on&lt;/span&gt; login_session(logintime &lt;span style="color: rgb(0, 255, 255);"&gt;desc&lt;/span&gt;);&lt;br&gt;&lt;br&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;alter&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;table&lt;/span&gt; &lt;span style="color: rgb(135, 206, 250);"&gt;login_session&lt;/span&gt; cluster &lt;span style="color: rgb(0, 255, 255);"&gt;on&lt;/span&gt; idx_logintime;&lt;br&gt; &lt;br&gt;&lt;/pre&gt;  &lt;p&gt;然后，再进行查询，可见效率有微弱提升。在我本机的 Postgres 8.4 上，通过 explain analyze 可见有不到百分之二的效率上升。我甚至怀疑这仅仅是源自一些随机事件的影响。&lt;/p&gt;  &lt;p&gt;换一个角度分析，只要 select max ... group by ... 的模式不改变，就很难有根本的性 能提升，最好的办法仍然是缓存。&lt;/p&gt;   &lt;h3&gt;改进&lt;/h3&gt;  &lt;p class="first"&gt;有个简单的办法，可以在PG数据库中制造一个缓存表：&lt;/p&gt;  &lt;ul&gt;&lt;li&gt;首先，直接建立一个 user-&amp;gt;timstamp 键值对表。如果用户量不大，可以直接这样：&lt;/li&gt;&lt;/ul&gt;  &lt;pre class="src"&gt;  &lt;span style="color: rgb(0, 255, 255);"&gt;create&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;table&lt;/span&gt; &lt;span style="color: rgb(135, 206, 250);"&gt;sessions&lt;/span&gt;(username text, seqs serial, logintime &lt;span style="color: rgb(152, 251, 152);"&gt;timestamp&lt;/span&gt;, &lt;span style="color: rgb(0, 255, 255);"&gt;primary&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;key&lt;/span&gt;(username));&lt;br&gt; &lt;/pre&gt;  &lt;ul&gt;&lt;li&gt;然后创建一个触发器函数&lt;/li&gt;&lt;/ul&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;create&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;or&lt;/span&gt; replace &lt;span style="color: rgb(0, 255, 255);"&gt;function&lt;/span&gt; &lt;span style="color: rgb(135, 206, 250);"&gt;on_log&lt;/span&gt;() &lt;span style="color: rgb(0, 255, 255);"&gt;returns&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;trigger&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;as&lt;/span&gt; $$&lt;br&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;begin&lt;/span&gt;&lt;br&gt;  if &lt;span style="color: rgb(0, 255, 255);"&gt;exists&lt;/span&gt;(&lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; * &lt;span style="color: rgb(0, 255, 255);"&gt;from&lt;/span&gt; sessions &lt;span style="color: rgb(0, 255, 255);"&gt;where&lt;/span&gt; username=NEW.username) &lt;span style="color: rgb(0, 255, 255);"&gt;then&lt;/span&gt;&lt;br&gt;     &lt;span style="color: rgb(0, 255, 255);"&gt;update&lt;/span&gt; sessions &lt;span style="color: rgb(0, 255, 255);"&gt;set&lt;/span&gt; seqs = nextval(&lt;span style="color: rgb(255, 160, 122);"&gt;&amp;#39;sessions_seqs_seq&amp;#39;&lt;/span&gt;) &lt;span style="color: rgb(0, 255, 255);"&gt;where&lt;/span&gt; username=NEW.username;&lt;br&gt;   &lt;span style="color: rgb(0, 255, 255);"&gt;else&lt;/span&gt;&lt;br&gt;    &lt;span style="color: rgb(0, 255, 255);"&gt;insert&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;into&lt;/span&gt; sessions(username, logintime) &lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; NEW.username, NEW.logintime;&lt;br&gt;   &lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt; if;&lt;br&gt;  if (&lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; &lt;span style="color: rgb(176, 196, 222);"&gt;count&lt;/span&gt;(*) &lt;span style="color: rgb(0, 255, 255);"&gt;from&lt;/span&gt; sessions)&amp;gt; 20 &lt;span style="color: rgb(0, 255, 255);"&gt;then&lt;/span&gt;&lt;br&gt;     &lt;span style="color: rgb(0, 255, 255);"&gt;delete&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;from&lt;/span&gt; sessions &lt;span style="color: rgb(0, 255, 255);"&gt;where&lt;/span&gt; seqs &amp;lt; (&lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; &lt;span style="color: rgb(176, 196, 222);"&gt;min&lt;/span&gt;(seqs) &lt;span style="color: rgb(0, 255, 255);"&gt;from&lt;/span&gt; (&lt;span style="color: rgb(0, 255, 255);"&gt;select&lt;/span&gt; seqs &lt;span style="color: rgb(0, 255, 255);"&gt;from&lt;/span&gt;&lt;br&gt; sessions &lt;span style="color: rgb(0, 255, 255);"&gt;order&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;by&lt;/span&gt; seqs &lt;span style="color: rgb(0, 255, 255);"&gt;desc&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;limit&lt;/span&gt; 20) &lt;span style="color: rgb(0, 255, 255);"&gt;as&lt;/span&gt; t);&lt;br&gt;   &lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt; if;&lt;br&gt;  &lt;span style="color: rgb(0, 255, 255);"&gt;return&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;NEW&lt;/span&gt;;&lt;br&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;end&lt;/span&gt;;&lt;br&gt; $$ &lt;span style="color: rgb(0, 255, 255);"&gt;language&lt;/span&gt; plpgsql;&lt;br&gt;&lt;br&gt;&lt;span style="color: rgb(0, 255, 255);"&gt;create&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;trigger&lt;/span&gt; onlog &lt;span style="color: rgb(0, 255, 255);"&gt;after&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;insert&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;on&lt;/span&gt; log &lt;span style="color: rgb(0, 255, 255);"&gt;for&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;each&lt;/span&gt; &lt;span style="color: rgb(152, 251, 152);"&gt;row&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;execute&lt;/span&gt; &lt;span style="color: rgb(0, 255, 255);"&gt;procedure&lt;/span&gt; &lt;span style="color: rgb(135, 206, 250);"&gt;on_log&lt;/span&gt;();&lt;br&gt; &lt;/pre&gt;  &lt;p&gt;OK，这样一来，经过反复测试，在我的笔记本上，插入速度基本没有降低（约 2%~ 5%）， 而查询"最新的20个用户"这一操作，速度提升了一百七十余倍。几乎可以视作是一个数据库 端的队列缓存了。这个数值是基于 log 表中有五万条数据，在实际应用中日志表十万以上 （按每日切分导出）比比皆是，性能差距会更为显著。&lt;/p&gt;   &lt;h3&gt;分析&lt;/h3&gt;  &lt;p class="first"&gt;"最新的 20 个用户"，这一问题，与"最近5分钟内的用户"此类问题的区别在于，它不能通 过获取当前时间，简单回溯得到有效数据区间。如何避免遍历整个日志表是一个关键问题———— 现代的在线服务系统每日访问日志量动辄数十万，几百上千万的也不罕见，第一种解法明显 不能满足。相比之下，第二种方案不需要干涉日志表的存储排序，不需要建立多余的索引。 通常我们讲触发器速度比较慢，但具体到这里，只是却是一个比较快速高效的实现方案。&lt;/p&gt;  &lt;p&gt;如果用户量不大，在几千以内，为用户表建立一个最后登录时间字段，绑定 null last 索 引（可以加上FASTUPDATE=ON），然后利用基本的order by limit就可以得到比较慢意的效 果了。但是对于大型SNS等应用，数十上百万用户也是有可能的（甚至大型企业内部应用， 数万乃至上十万用户的系统也不在少数）。此时，第二种解决方案就理想的多。虽然看起来 在session表上反复进行计算和写入，但是由于session表的数据量非常小，永远只有几十 条，所以计算速度很快。如果对 log 表进行恰当的分区，同时将sessions表存储到另一个 区域（甚至直接缓存到内存），那么对于海量数据，也会有一个稳定的，良好的性能表现。&lt;/p&gt;  &lt;p&gt;更进一步的解决方法，则应该是在数据库或应用层服务器环境，建立一个内存中的队列，来 记录这个数据。只要注意并发写入的问题，就可以得到很好的性能。这方面Haskell的STM机 制、我之前用在MSG.Summoner.Trac的旋转锁机制、都可以比较漂亮的解决这一问题。&lt;/p&gt;   &lt;h3&gt;优化&lt;/h3&gt;  &lt;p class="first"&gt;sessions 表的读写都非常频繁，通常有比较多的 update，，但是数据量基本恒定。应该积极的执行 vacuum。&lt;/p&gt;  &lt;p&gt;可以为日志表添加一个 uuid 字段，以便在数据量上升时按 hash 分表。这样可以获得更好 的写入性能。&lt;/p&gt;  &lt;p&gt;可以将 session 的主键索引设为 FASTUPDATE=ON 。&lt;/p&gt;  &lt;p&gt;触发器中设定&lt;/p&gt;  &lt;pre class="src"&gt;SET LOCAL synchronous_commit TO OFF;&lt;br&gt;&lt;/pre&gt;  &lt;p&gt;打开 WAL 异步事务提交，可以进一步提高并发写入速度。&lt;/p&gt;  &lt;p&gt;更进一步的，可以写一个常驻的守护进程，用应用语言建立一个队列，将这一需求建立为独 立的服务。这个服务可以通过 pl 嵌入语言与数据库联接，也可以直接联接到应用层，有一 些应用层架构依赖于并发的多 fastcgi 实例，此时要注意并发访问冲突和数据同步的问题。&lt;/p&gt;   &lt;h3&gt;总结&lt;/h3&gt;  &lt;p class="first"&gt;之所以出现这样一个困扰开发人员的问题，根本在于项目领导不提供，也不认同使用灵活、 开放的思维方式解决此问题。服务项目的架构是一个系统工程，类似这样的问题，完全可以 用更开阔的眼光去寻找出路。建立一个简单的缓存队列实例，用perl或python，只需要数十 行。结合Postgres的plperl或plpython等嵌入脚本，可以非常简单的达到目的。甚至不用担 心使用多实例 fastcgi 时，多个应用服务进程争用缓存队列I/O的问题。&lt;/p&gt;  &lt;p&gt;即使从方案二出发，通过利用服务器环境的资源，也可以做出更多优化。如把"删除第20之 后的旧数据"这部分脚本，移到 crontab 中，与vacuum 结合执行。会得到更好的效率。&lt;/p&gt;  &lt;p&gt;找我请教的这位朋友，使用的不是Postgres，而是MySQL，相对来说很多PG的服务端编程技 巧难以照搬，再加上团队管理的政治问题，我也只有祝他好运了。&lt;/p&gt;&lt;br clear="all"&gt;&lt;br&gt;-- &lt;br&gt;话题越大，废话越多；名字越火星，问题越脑残。&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-2867063562898885041?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/2867063562898885041/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=2867063562898885041' title='3 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2867063562898885041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2867063562898885041'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/08/postgres-story.html' title='[Postgres Story]最近访问用户问题'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-2890949629288676324</id><published>2009-07-24T08:57:00.001-07:00</published><updated>2010-04-17T15:44:20.934-07:00</updated><title type='text'>Emacs 窗体的透明设置</title><content type='html'>&lt;div class="content clear-block"&gt;     &lt;p class="first"&gt;昨天朋友发来一个代码给我，可以让 windows 上的 emacs 窗体像 X 或苹果那样实现半透 明。代码并不复杂：&lt;/p&gt;  &lt;pre class="src"&gt;(set-frame-parameter (selected-frame) &amp;#39;alpha (list 85 50))&lt;br&gt;(add-to-list &amp;#39;default-frame-alist (cons &amp;#39;alpha (list 85 50)))&lt;br&gt; &lt;/pre&gt;  &lt;p&gt;然后经过一番努力，我写了一个切换功能，按f7进入透明，按f8退出透明状态。&lt;/p&gt;  &lt;pre class="src"&gt;(defun transform-window (a ab)&lt;br&gt;  (set-frame-parameter (selected-frame) &amp;#39;alpha (list a ab))&lt;br&gt;  (add-to-list &amp;#39;default-frame-alist (cons &amp;#39;alpha (list a ab)))&lt;br&gt; )&lt;br&gt;&lt;br&gt;(global-set-key [(f7)] (lambda()&lt;br&gt;                         (interactive)&lt;br&gt;                         (transform-window 85 55)))&lt;br&gt;&lt;br&gt;(global-set-key [(f8)] (lambda()&lt;br&gt;                         (interactive)&lt;br&gt;                          (transform-window 100 100)))&lt;br&gt;&lt;br&gt;&lt;/pre&gt;  &lt;p&gt;但是这种东西两键切换显然不够友好，于是我把它改成了一键切换。&lt;/p&gt;  &lt;pre class="src"&gt;(setq is-alpha nil)&lt;br&gt;&lt;br&gt;(defun transform-window (a ab)&lt;br&gt;  (set-frame-parameter (selected-frame) &amp;#39;alpha (list a ab))&lt;br&gt;   (add-to-list &amp;#39;default-frame-alist (cons &amp;#39;alpha (list a ab)))&lt;br&gt;)&lt;br&gt;&lt;br&gt;&lt;br&gt;(global-set-key [(f8)] (lambda()&lt;br&gt;                         (interactive)&lt;br&gt;                         (if is-alpha&lt;br&gt;                             (transform-window 100 100)&lt;br&gt;                            (transform-window 85 50))&lt;br&gt;                         (setq is-alpha (not is-alpha))))&lt;br&gt;&lt;br&gt;&lt;/pre&gt;  &lt;p&gt;后来复读了一下代码，觉得还有改进的余地。首先，只是一个透明度切换，没必要占用两个 快捷键，对于我这种用 Emacs 写 N 种东西的人，快捷键是种相当宝贵的资源。再一点，目 前的设计只能支持两种透明度，还多用了一个全局变量。&lt;/p&gt;   &lt;p&gt;在 Feather 兄弟的指点下，我突击了一下 emacs lisp ，写出了这样的版本：&lt;/p&gt;  &lt;pre class="src"&gt;;; transform window&lt;br&gt;;; Anchor: March Liu (刘鑫) &amp;lt;&lt;a href="mailto:march.liu@gmail.com"&gt;march.liu@gmail.com&lt;/a&gt;&amp;gt;&lt;br&gt;;;&lt;br&gt;;; This is a script to set emacs window&amp;#39;s alpha value.&lt;br&gt; ;; It work well on windows xp and vista with EmacsWin32&lt;br&gt;;; useage: add below line in your .emacs&lt;br&gt;;;&lt;br&gt;;; (load-file &lt;span style="color: rgb(255, 160, 122);"&gt;&amp;quot;path/alpha-window.el&amp;quot;&lt;/span&gt;)&lt;br&gt;;;&lt;br&gt;;; you can define your alpha-list to set the transform combine&lt;br&gt; ;; bind key with below code:&lt;br&gt;;;&lt;br&gt;;; (global-set-key [(f11)] &amp;#39;loop-alpha)&lt;br&gt;&lt;br&gt;(setq alpha-list &amp;#39;((100 100) (95 65) (85 55) (75 45) (65 35)))&lt;br&gt;&lt;br&gt;(defun loop-alpha ()&lt;br&gt;  (interactive)&lt;br&gt;  (let ((h (car alpha-list)))                ;; head value will set to&lt;br&gt;     ((lambda (a ab)&lt;br&gt;       (set-frame-parameter (selected-frame) &amp;#39;alpha (list a ab))&lt;br&gt;       (add-to-list &amp;#39;default-frame-alist (cons &amp;#39;alpha (list a ab)))&lt;br&gt;       ) (car h) (car (cdr h)))&lt;br&gt;    (setq alpha-list (cdr (append alpha-list (list h))))&lt;br&gt;     )&lt;br&gt;)&lt;br&gt;&lt;br&gt;&lt;/pre&gt;  &lt;p&gt;这个脚本的特点如下：&lt;/p&gt;  &lt;ul&gt;&lt;li&gt;单命令轮转任意多个状态&lt;/li&gt;&lt;li&gt;可以用 (global-set-key [(f11)] &amp;#39;loop-alpha) 把 loop-alpha 绑定到快捷键上&lt;/li&gt;&lt;li&gt;我设定了四个透明度组合，你可以在自己的 .emacs 里重定义 alpha-list ，设定自己 的透明度方案&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;目前我只在windows上试验过了，X窗口下如果没有开透明效果应该是不行的。当然，X本身 的半透明就很好用了，我在X上从来没想过需要这么个功能==；&lt;/p&gt;   &lt;p&gt;应该只能用于图形界面：P。&lt;/p&gt;  &lt;/div&gt;&lt;br clear="all"&gt;&lt;br&gt;-- &lt;br&gt;话题越大，废话越多；名字越火星，问题越脑残。&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-2890949629288676324?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/2890949629288676324/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=2890949629288676324' title='3 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2890949629288676324'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2890949629288676324'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/07/emacs.html' title='Emacs 窗体的透明设置'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-4431073772907223702</id><published>2009-07-23T04:23:00.001-07:00</published><updated>2010-04-17T15:44:20.938-07:00</updated><title type='text'>Postgres 8.4 的更新内容概要</title><content type='html'>&lt;div id="art" style="margin: 15px;"&gt; &lt;p class="first"&gt;After many years of development, PostgreSQL has become feature-complete in many areas. This release shows a targeted approach to adding features (e.g., authentication, monitoring, space reuse), and adds capabilities defined in the later SQL standards. The major areas of enhancement are:&lt;/p&gt; &lt;p&gt;经过多年开发后，PostgreSQL 在很多方面都具有了完备的功能。这次发布定位于添加功能（例如授权、监控、空间回收），以及添加最新 SQL  标准中的新功能。主要的增强在于以下几个方面：&lt;/p&gt; &lt;ul&gt;&lt;li&gt;Windowing Functions Windowing 函数&lt;/li&gt;&lt;li&gt;Common Table Expressions and Recursive Queries 通用表表达式和递归查询&lt;/li&gt;&lt;li&gt;Default and variadic parameters for functions 函数的默认和动态参数&lt;/li&gt;&lt;li&gt;Parallel Restore 并行恢复&lt;/li&gt;&lt;li&gt;&lt;br&gt;&lt;/li&gt; &lt;li&gt;Column Permissions 列权限&lt;/li&gt;&lt;li&gt;Per-database locale settings 每数据库区域设置&lt;/li&gt;&lt;li&gt;Improved hash indexes 优化哈希索引&lt;/li&gt;&lt;li&gt;Improved join performance for EXISTS and NOT EXISTS queries 对于 EXISTS 和 NOT  EXISTS 查询优化了查询性能&lt;/li&gt;&lt;li&gt;Easier-to-use Warm Standby 易用的温备&lt;/li&gt; &lt;li&gt;Automatic sizing of the Free Space Map 可用空间映射自动伸缩&lt;/li&gt;&lt;li&gt;Visibility Map (greatly reduces vacuum overhead for slowly-changing tables)  视界映射 （大大缩减了很少变化的表进行优化的负载）&lt;/li&gt;&lt;li&gt;Version-aware psql (backslash commands work against older servers) 依赖版本的  psql （旧的服务器上没有的反斜杠命令）&lt;/li&gt; &lt;li&gt;Support SSL certificates for user authentication 支持 SSL 认证的用户身份验证。&lt;/li&gt;&lt;li&gt;Per-function runtime statistics 每函数运行时统计&lt;/li&gt;&lt;li&gt;Easy editing of functions in psql psql 函数更容易编辑&lt;/li&gt;&lt;li&gt;New contrib modules: pg_stat_statements, auto_explain, citext, btree_gin 新的附加模块：pg_stat_statements， auto_explain， citext， btree_gin&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;说明：&lt;/p&gt; &lt;p&gt;PostgreSQL 8.4 Releas Note 我已经翻译完成，完整版本在 &lt;a href="http://zerolab.co.cc/?q=node/6"&gt;http://zerolab.co.cc/?q=node/6&lt;/a&gt; 但是我估计我那个免费空间抗不住这么大访问压力，所以会逐篇把主要内容放在这里。&lt;/p&gt; &lt;p&gt;该文档使用 Emacs Muse 编写。&lt;/p&gt; &lt;p&gt;这段时间我会自己开发或者搞一个 Muse2Wiki 的插件，把文档放到 PG 中文社区的 WIKI 上。&lt;/p&gt; 		 		 		 &lt;/div&gt;    &lt;p style="margin: 5px; line-height: 150%;"&gt;     &lt;/p&gt;    &lt;font color="#000099"&gt;&lt;b&gt;原文地址&lt;/b&gt;&lt;/font&gt; &lt;a href="http://www.postgresql.org/docs/8.4/static/release-8-4.html" target="_blank"&gt;http://www.postgresql.org/docs/8.4/static/release-8-4.html&lt;/a&gt;&lt;br clear="all"&gt;&lt;br&gt;-- &lt;br&gt;话题越大，废话越多；名字越火星，问题越脑残。&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-4431073772907223702?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/4431073772907223702/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=4431073772907223702' title='3 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4431073772907223702'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4431073772907223702'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/07/postgres-84.html' title='Postgres 8.4 的更新内容概要'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-5312256738866520114</id><published>2009-06-30T22:40:00.001-07:00</published><updated>2010-04-17T15:44:20.940-07:00</updated><title type='text'>jQuery 风格的HTML文本转义</title><content type='html'>&lt;p&gt;astinus开发过程中，我自己就在不断的使用。有次贴了一些JS代码进去，于是页面显示错误。显然，把源代码直接放进html文本了——好吧，从05年转去做网游以后，一直没有正经的做过web了。&lt;/p&gt; &lt;p&gt;那么，我需要一个方法转义。网上搜了一下，大部分是自己编写一个正则替换。不过我的习惯是尊重既有的资源。有人提出可以用dom的功能。先作为innerTEXT传给一个dom对象，再取innerHTML属性，就可以取到转义后的文本了。&lt;/p&gt; &lt;p&gt;方法不错，不过写法上，有没有取巧的办法呢？&lt;/p&gt; &lt;p&gt;JQuery社区有人给出了办法：假设有文本 context，可以对一个jQuery对象 $(x)进行 $(x).text(context).html()，就会返回一个转义后的文本。&lt;/p&gt; &lt;p&gt;其实刚刚想起来，很多时候完全可以用text()函数对jQuery对象赋值就好了……&lt;/p&gt;&lt;br clear="all"&gt;&lt;br&gt;-- &lt;br&gt;柳文扬先生逝世两周年祭&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-5312256738866520114?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/5312256738866520114/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=5312256738866520114' title='3 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5312256738866520114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5312256738866520114'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/06/jquery-html.html' title='jQuery 风格的HTML文本转义'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-5576563675520767517</id><published>2009-06-30T18:14:00.001-07:00</published><updated>2010-04-17T15:44:21.186-07:00</updated><title type='text'>终于装成了Vbox 3.0</title><content type='html'>自从 Virtual Box 2.1.4 之后的版本我就从来没有升级成功过。运行安装程序就会提示不能打开临时目录。直到昨晚下载了Vbox 3 。终于出错提示多了点，这次提示打不开我用户名（刘鑫）下的临时目录打不开。更重要的是，提示我"unicode"路径无法解析。&lt;br&gt;显然，近几个版本的vbox安装文件，使用了一个只认识英文字符的打包工具。于是我建立了一个英文名的用户，切换过去，安装成功。&lt;br&gt;&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-5576563675520767517?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/5576563675520767517/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=5576563675520767517' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5576563675520767517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5576563675520767517'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/06/vbox-30.html' title='终于装成了Vbox 3.0'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-5071626955372198368</id><published>2009-06-26T10:41:00.001-07:00</published><updated>2010-04-17T15:44:21.190-07:00</updated><title type='text'>空调坏了</title><content type='html'>用着用着突然发出爆响。开窗一看压缩机在外面台子上跳舞，伸出之手都按不住。只好关了。&lt;br&gt;刚才把插头插上，一开风扇，不转，把我悲痛坏了，我想这叫祸不单行么。&lt;br&gt;然后仔细一看，我插的电蚊香。&lt;br clear="all"&gt;&lt;br&gt;-- &lt;br&gt;杀人放火金腰带，补路修桥无尸骸。&lt;br&gt;&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-5071626955372198368?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/5071626955372198368/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=5071626955372198368' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5071626955372198368'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5071626955372198368'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/06/blog-post.html' title='空调坏了'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-7273292241937526559</id><published>2009-06-22T19:54:00.001-07:00</published><updated>2010-04-17T15:44:21.196-07:00</updated><title type='text'>Re: web2py 正式支持 order by desc</title><content type='html'>2009/6/23 刘鑫 &amp;lt;&lt;a href="mailto:march.liu@gmail.com"&gt;march.liu@gmail.com&lt;/a&gt;&amp;gt;:&lt;br&gt;&amp;gt; 昨晚做gastinus的消息编写页，发现orderby=&amp;quot;post_on&amp;#160;desc&amp;quot;居然出错，进去gluon里的源码一看，跟以前不一样了（刚更新了服务器代码）。于是搜索了一下文档，发现关于orderby的文档更新了，现&amp;#160;在DAL的orderby不再支持&amp;quot;fieldname&amp;#160;desc&amp;quot;的形式，而是通过=~实现同样的功能。例如，gastinus的发言页面，需要按post_on倒排，应该写成&lt;br&gt;&amp;gt;&lt;br&gt;&amp;gt; db().select(db.quotation.ALL,&amp;#160;orderby=~db.quotation.post_on)&lt;br&gt;&amp;gt;&lt;br&gt;&amp;gt; 这样确实比以前可读性更好，而且因为逻辑实现在代码而不是字符串里，更利于开发工具实现错误检查。&lt;br&gt;&amp;gt;&lt;br&gt;&amp;gt; 另外，GQLQuery似乎没有limit，只有limitby=(lmin,&amp;#160;lmax)这样一种写法。其实GQL本身的limit语法与关系型数据库的普遍实现并无二致，不明白为何不统一。&lt;p&gt;所以,真男人就用男人版 ~ Trunk 的!&lt;p&gt;&lt;br&gt;-- &lt;br&gt;&lt;a href="http://zoomquiet.org"&gt;http://zoomquiet.org&lt;/a&gt;&lt;br&gt;&amp;#39;&amp;#39;&amp;#39;过程改进乃是催生可促生靠谱的人的组织!&amp;#39;&amp;#39;&amp;#39;&lt;br&gt;KM乃是培育可催生自学习型组织的文化氛围!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-7273292241937526559?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/7273292241937526559/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=7273292241937526559' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7273292241937526559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7273292241937526559'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/06/re-web2py-order-by-desc.html' title='Re: web2py 正式支持 order by desc'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-7674736399015375008</id><published>2009-06-22T19:29:00.001-07:00</published><updated>2010-04-17T15:44:21.201-07:00</updated><title type='text'>web2py 正式支持 order by desc</title><content type='html'>&lt;p class="p0" style="margin-bottom: 0pt; margin-top: 0pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: &amp;#39;Times New Roman&amp;#39;;"&gt;昨晚做&lt;font face="Times New Roman"&gt;gastinus&lt;/font&gt;&lt;font face="宋体"&gt;的消息编写页，发现&lt;/font&gt;&lt;font face="Times New Roman"&gt;orderby=&amp;quot;post_on desc&amp;quot;&lt;/font&gt;&lt;font face="宋体"&gt;居然出错，进去&lt;/font&gt;&lt;font face="Times New Roman"&gt;gluon&lt;/font&gt;&lt;font face="宋体"&gt;里的源码一看，跟以前不一样了（刚更新了服务器代码）。于是搜索了一下文档，发现关于&lt;/font&gt;&lt;font face="Times New Roman"&gt;orderby&lt;/font&gt;&lt;font face="宋体"&gt;的文档更新了，现 在&lt;/font&gt;&lt;font face="Times New Roman"&gt;DAL&lt;/font&gt;&lt;font face="宋体"&gt;的&lt;/font&gt;&lt;font face="Times New Roman"&gt;orderby&lt;/font&gt;&lt;font face="宋体"&gt;不再支持&lt;/font&gt;&lt;font face="Times New Roman"&gt;&amp;quot;fieldname desc&amp;quot;&lt;/font&gt;&lt;font face="宋体"&gt;的形式，而是通过&lt;/font&gt;&lt;font face="Times New Roman"&gt;=~&lt;/font&gt;&lt;font face="宋体"&gt;实现同样的功能。例如，&lt;/font&gt;&lt;font face="Times New Roman"&gt;gastinus&lt;/font&gt;&lt;font face="宋体"&gt;的发言页面，需要按&lt;/font&gt;&lt;font face="Times New Roman"&gt;post_on&lt;/font&gt;&lt;font face="宋体"&gt;倒排，应该写成&lt;/font&gt;&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: &amp;#39;Times New Roman&amp;#39;;"&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="p0" style="margin-bottom: 0pt; margin-top: 0pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: &amp;#39;Times New Roman&amp;#39;;"&gt;db().select(db.quotation.ALL, orderby=~db.quotation.post_on)&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: &amp;#39;Times New Roman&amp;#39;;"&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="p0" style="margin-bottom: 0pt; margin-top: 0pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: &amp;#39;Times New Roman&amp;#39;;"&gt;这样确实比以前可读性更好，而且因为逻辑实现在代码而不是字符串里，更利于开发工具实现错误检查。&lt;/span&gt;&lt;span style="font-size: 10.5pt; font-family: &amp;#39;Times New Roman&amp;#39;;"&gt;&lt;/span&gt;&lt;/p&gt; &lt;p class="p0" style="margin-bottom: 0pt; margin-top: 0pt;"&gt;&lt;span style="font-size: 10.5pt; font-family: &amp;#39;Times New Roman&amp;#39;;"&gt;另外，&lt;font face="Times New Roman"&gt;GQLQuery&lt;/font&gt;&lt;font face="宋体"&gt;似乎没有&lt;/font&gt;&lt;font face="Times New Roman"&gt;limit&lt;/font&gt;&lt;font face="宋体"&gt;，只有&lt;/font&gt;&lt;font face="Times New Roman"&gt;limitby=(lmin, lmax)&lt;/font&gt;&lt;font face="宋体"&gt;这样一种写法。其实&lt;/font&gt;&lt;font face="Times New Roman"&gt;GQL&lt;/font&gt;&lt;font face="宋体"&gt;本身的&lt;/font&gt;&lt;font face="Times New Roman"&gt;limit&lt;/font&gt;&lt;font face="宋体"&gt;语法与关系型数据库的普遍实现并无二致，不明白为何不统一。&lt;/font&gt;&lt;/span&gt;&lt;/p&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-7674736399015375008?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/7674736399015375008/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=7674736399015375008' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7674736399015375008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7674736399015375008'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/06/web2py-order-by-desc.html' title='web2py 正式支持 order by desc'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-3829864740664986539</id><published>2009-06-03T10:50:00.001-07:00</published><updated>2010-04-17T15:44:21.207-07:00</updated><title type='text'>[Web2py]Web2py DAL 与 Postgres数据库</title><content type='html'>在诸多Python 数据访问框架中，Web2py的DAL算是比较有趣的一个。因为工作的关系，最近比较关注DAL与Postgres的组合。这里简单总结一下。&lt;br&gt;开头又臭又长，建议跳过，留着娱乐时间再读。直接从下一集开始阅读&lt;br&gt;&lt;br&gt;&lt;div style="text-align: center;"&gt;&lt;b&gt;数据存储与访问——理想与现实的妥协&lt;/b&gt;&lt;br&gt;&lt;/div&gt;在IT界，从来没有一个系统边界可以像数据库与应用层之间这样不断摩擦，冲突激烈，没推倒前的柏林墙庶几近之。不同的是同胞之间的藩篱终于在两德人民之间轰然倒下，而数据库与应用层的斗争只会光华灿烂，鸡飞狗跳。&lt;br&gt; 鹅，写错了，是光华灿烂，旦复旦兮。几年不读圣贤书，已经赶不上语文课代表啦。&lt;br&gt;是的，应用层与数据访问层的关系，只会在冲突和妥协中不断发展和变化，周而复始。&lt;br&gt;为了吸引用户，满足需求，数据库开发者总是在努力丰富服务器端的功能。从Key/Value到层次存储，网状关联，再到平面表，再到关系数据库，再到复杂数据类型和过程化编程，再到事务、存储过程、甚至嵌入式混合编程，以及咸鱼翻生的MapReduce，千奇百怪，不一而足。（当然，类似分布式存储，裸设备访问这些NB的性能优化技术和存储技术也在不断发展，但这不是今天我们要讨论的问题。）&lt;br&gt; 而为了吸引用户，降低学习成本，提高占有率，应用层开发者总是在努力提高代码的通用性。于是，在第一代数据库SDK直连的理念后，顺理成章的出现了ODBC这样的通用驱动桥接层。由于各种各样的原因，ODBC始终也没有实现一统天下的目标，JDBC等平台专用链接库层出不穷，就连老东家微软都忍无可忍推出了ADO和&lt;a href="http://ADO.NET"&gt;ADO.NET&lt;/a&gt;，于是，这一层的访问方式基本稳定在了一种运行时一个主流库的态势上，Java是JDBC、.net是ADO.net，Perl是DBX，Python统一这一层的是DBAPI。&lt;br&gt; 但是，驱动桥接是面向数据行的，对于编程并不方便，需要应用开发程序员大量编写重复代码，手工维护连接，遍历得到的游标记录集等等，这甚至对于数据库服务器也不够友好。于是，我们看到，ADO.net中开始强烈的倾向一种自动生成对象结构的自动化编程趋势，而这个思想在Java中最先成为一个诱人的产品——Hibernate，这就是ORM。&lt;br&gt;其实大家都明白ORM付出了比较高的性能和功能代价，但是ORM对生产力的提升是不可抵挡的诱惑，各个平台纷纷陷落，还出现了不同的框架自己实现ORM的场面，Python尤其明显，这对于忠诚的"统一标准"信徒，简直是一件无法无天大摇其头的荒唐事。&lt;br&gt; 于是，现在对于数据存储访问，就出现了一个相对比较稳定的分层，最下面是RDBMS以及其它数据库，不过这里我们只讨论最主要的RDB应用；上一层是各种桥接层，现在的趋势是每种数据库针对每种运行时提供自己的桥接层，以达到最优的功能和性能接口，而过去由ODBC、BDE、OLE DB这些系统桥接工具提供的通用性和移植性的黑锅，就由Hibernate们背了起来。&lt;br&gt;也许Python的生产力真的是太高了，以至于即使出了几种广受好评的ORM（例如SQLObject、SQLAlchmy、Storm）之后，Django、Web.py、Web2py等web框架还是实现了自己的ORM，这其中有些是在通用ORM上实现的，有些则是框架直接基于数据库访问接口组建的。而Web2py的DAL（Database Abstraction Layer），算是其中相当有趣的一个。&lt;br&gt; &lt;br&gt;&lt;div style="text-align: center;"&gt;&lt;b&gt;DAL的取舍&lt;/b&gt;&lt;br clear="all"&gt;&lt;/div&gt;如同前面所介绍的，为了使得ORM组件吸引更多的用户，获得更大的通用性，通常都会牺牲数据库层的功能，作为一个年轻的ORM工具，DAL只能做到对最常见，也最简陋的MySQL和SQLite为目标进行了支持。事实上，即使MySQL的编程语法，也做不到完全的支持（至少，MySQL的全文检索就没有支持）。调侃一下，我印象比较深刻的数据库，如果编程能力打个分，大概是下图这个样子。&lt;br&gt; &lt;a href="http://picasaweb.google.com/March.Liu/Blogger0=2#5343071442111853506"&gt;http://picasaweb.google.com/March.Liu/Blogger0=2#5343071442111853506&lt;/a&gt;&lt;br&gt;忘了加sqlite了，不过那东西也就是个40分的样子。而DAL能直接支持的，也就在这个40分的程度上。&lt;br&gt;不幸的是，我工作中用到的，恰恰是那个编程能力最强的，在我看来可以给100分（倒不是说就是满分）的PostgreSQL。&lt;br&gt; 有功能不用，倒是不一定就会有什么不好，但是如果可以充分了解ORM层和DB的功能特性，就可以尽可能的发挥这两层工具的能力，提高工作效率和产品质量，精益求精永远都是一件好事。&lt;br&gt;这里简单介绍几个DAL及相关的技术特色，具体的技术细节凡是可以直接在文档中查询到的，这里就不细讲了。&lt;br&gt;&lt;b&gt;&lt;br&gt;数据库连接定义&lt;/b&gt;&lt;br&gt;DAL用来连接数据层的，是DB，对应通常的关系型数据库，是SQLDB对象，对应GAE Bigtable，则是GQLDB，这个对象是DAL整个访问操作的核心。当然，一个web2py实例内可以同时存在几个DB对象。&lt;br&gt; 首先DAL的构造接口非常简单，只需要一个URI格式的字符串。例如，对于astinus项目的企业版，这个uri是postgres://&lt;a href="http://astinus:march@127.0.0.1/astinus"&gt;astinus:march@127.0.0.1/astinus&lt;/a&gt;，包含了用户名、口令、服务器种类、位置、数据库名。虽然比起逐个参数进行指定的方式略为复杂，但是由于格式非常符合我们对于web url的使用经验（其实是现有uri的概念，才有了url这个具体的东西），所以非常好记。DAL支持的完整的DB列表及连接方式，可以在DAL的文档 &lt;a href="http://web2py.com/examples/default/dal"&gt;http://web2py.com/examples/default/dal&lt;/a&gt; 中读到。&lt;br&gt; 另外DAL针对一些数据库，例如Postgres，还提供了一个可选参数：pools，也就是数据连接池。有兴趣的朋友可以在glon/tools.py中找到这部分代码。&lt;br&gt;当然，DAL的连接定义也不是完美的，这里主要问题表现在两个方面：&lt;br&gt;&lt;ul&gt;&lt;li&gt;一定要指定password，有些数据库允许密码之外的身份验证方式，这样可以提供更可靠的安全性，但是DAL目前还只能使用密码验证。&lt;/li&gt;&lt;li&gt;不能指定schema。对于PG和Oracle，不得不说是个遗憾。其实实现schema支持并不是一个很麻烦的事情，Web2py社区早有用户提过一些方法，我自己也想到过几个，例如采取类似trac DB URI的n参数方式就不错。目前我对此采用了一个妥协的方法：设定连接用户在数据库环境中的的search_path变量。这样就允许用户访问postgres数据库中public之外的schema，以及可以指定schema搜索顺序。&lt;/li&gt; &lt;/ul&gt;&lt;b&gt;数据库结构定义与对象映射&lt;/b&gt;&lt;br&gt;&lt;br&gt;DAL通过调用define_table方法，可以定义数据表，表结构中的field字段由SQLField对象构造。这种结构使得DAL可以定义丰富的字段约束信息，DAL提供以下特色功能：&lt;br&gt;&lt;ul&gt;&lt;li&gt;假设有SQLDB（或GQLDB对象）对象db，通过db.table或db[&amp;#39;table&amp;#39;]就可以访问指定的表table，再进一步可以访问表中的字段：db.table.fileld。这样就可以用应用层程序员习惯的对象结构构造查询和访问数据&lt;/li&gt; &lt;li&gt;DAL的SQLField对象可以提供非常丰富的数据约束条件，通过SQLFORM对象，可以快速构造带有页面、应用服务器、数据库三层数据验证约束的数据访问层次。&lt;/li&gt;&lt;li&gt;DAL可以同步数据库结构，也可以通过migate=False禁止这中同步。&lt;/li&gt;&lt;li&gt;Web2py提供内置的Database Administrat 界面。&lt;/li&gt;&lt;/ul&gt;同样，DAL的数据库结构定义功能也有一定的不足：&lt;br&gt;首先，同样是schema问题&lt;br&gt; &lt;ul&gt;&lt;li&gt;postgres提供强大的text类型（不同于MySQL等数据库中常见的Text类型），但是在DAL中string无法充分利用这一功能。&lt;/li&gt;&lt;li&gt;postgres提供一个基本功能的XML类型，支持xpath 1.0和dom访问，但是同样DAL无法利用。&lt;/li&gt;&lt;li&gt;DAL定义的表结构一定要带有一个整数自增字段列作为主键，这一点不如django灵活。&lt;/li&gt;&lt;li&gt;DAL定义的表结构，外键定义非常简单好用，但是也不够灵活，不能指定非主键列（可以放在验证中，这样可以看作是实现了外键约束）。&lt;/li&gt; &lt;li&gt;DAL可以支持导出数据，但是不如Django按对象导出那么强大。&lt;/li&gt;&lt;li&gt;DAL的pg实现使用char(1)作为逻辑类型，而PG自带了真正的boolean。&lt;/li&gt;&lt;li&gt;DAL无法映射存储函数，而这时PG中非常强大的一个功能。&lt;/li&gt;&lt;/ul&gt;基于以上原因，我总是手工在数据库中建立表结构和相关对象，并且在define_table中设定关键字参数migrate=False。&lt;br&gt;&lt;b&gt;DAL 的检索&lt;/b&gt;&lt;br&gt;&lt;br&gt; DAL的数据检索（对应SQL select）做的很有意思。它可以支持：&lt;br&gt;&lt;ul&gt;&lt;li&gt;局部化选取，只读取表中一部分字段。&lt;/li&gt;&lt;li&gt;支持连接查询，甚至支持左外连接，外连接在一些ORM工具中没有提供。&lt;/li&gt;&lt;li&gt;由上两个技术，DAL可以支持相当灵活的查询集组合。&lt;/li&gt;&lt;li&gt;子查询。&lt;/li&gt;&lt;li&gt;统计计算，而且这个统计计算是基于SQL生成和惰性计算的，将计算放在服务器端，这是一个非常好的特性，而且使用起来很友好。&lt;/li&gt; &lt;li&gt;服务器端排序和分页访问。得益于新SQL标准的关键字limit，DAL可以比较方便的提供分页查询功能，不过这个功能在MySQL上性能很糟糕（就海量数据集的场景而言），这个黑锅我想不能由DAL背。&lt;/li&gt;&lt;li&gt;将查询集直接生成为多种输出，如XML、XMLRPC、CVS、JSON等。或者生成为SQLFORM以供web请求使用。&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;DAL的检索基本上是如下格式：&lt;br&gt;&lt;ul&gt;&lt;li&gt;db(查询条件).select(字段列表,**{附加参数})&lt;/li&gt; &lt;/ul&gt;这是一个很经典的MapReduce格式&lt;br&gt;&lt;ul&gt;&lt;li&gt;db(filter).select(map, reduce)&lt;/li&gt;&lt;/ul&gt;当然，在select函数中，也包含了个别filter操作（limit），这可以看作是对SQL语法的一种妥协。&lt;br&gt;同样，DAL查询也存在一些问题：&lt;br&gt;&lt;ul&gt;&lt;li&gt;DAL不支持PG的全文检索运算符@@。&lt;/li&gt;&lt;li&gt;DAL不支持PG的正则表达式运算符~和~*。&lt;/li&gt;&lt;li&gt; DAL不支持FULL Join和Right JOIN，当然，这并不是很大的问题。&lt;/li&gt;&lt;li&gt;DAL不支持跨db对象的联接，即使它们指向同一个数据库。这一点我认为是合理的。&lt;/li&gt;&lt;li&gt;DAL查询集不支持完整的序列运算，不能切割，不能反向索引，这一点我认为是一种遗憾，应当改进。&lt;/li&gt;&lt;/ul&gt;我们这里不详解DAL的用法，有兴趣的朋友可以详细阅读DAL的文档，了解各种查询的使用方式。&lt;br&gt;&lt;br&gt;&lt;b&gt;DAL 的update、insert、delete操作&lt;/b&gt;&lt;br&gt; &lt;b&gt;&lt;br&gt;&lt;/b&gt;DAL也同样支持局部化的update/insert操作，这同样是很多ORM做不到的。在数据写操作时，DAL也有一些有趣的功能实现：&lt;br&gt;&lt;ul&gt;&lt;li&gt;DAL允许 data={...}这样的局部化批量赋值，而db&lt;span style="font-weight: bold;"&gt;.&lt;/span&gt;person&lt;span style="font-weight: bold;"&gt;[&lt;/span&gt;&lt;span style="color: red;"&gt;0&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;]=&lt;/span&gt;dict&lt;span style="font-weight: bold;"&gt;(&lt;/span&gt;name&lt;span style="font-weight: bold;"&gt;=&lt;/span&gt;&lt;span style="color: rgb(255, 153, 102);"&gt;&amp;#39;Max&amp;#39;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;)&lt;/span&gt; 则是一个非常巧妙的插入操作（DAL的id是从1开始计数的）。&lt;/li&gt; &lt;li&gt;DAL允许用Python的 del 关键字删除数据记录。&lt;/li&gt;&lt;li&gt;DAL的db对象支持commit和rollback。&lt;/li&gt;&lt;li&gt;DAL增删改查都可以方便的cache。&lt;/li&gt;&lt;li&gt;可以从CSV批量导入数据。&lt;/li&gt;&lt;li&gt;内置的curd辅助工具。&lt;/li&gt;&lt;li&gt;可以方便的生成多种数据提供或RPC服务。&lt;br&gt;&lt;/li&gt;&lt;/ul&gt;同样，我认为DAL在此方面也有一些地方有待改进：&lt;br&gt;&lt;ul&gt;&lt;li&gt;ADO.net 支持定义增删改查对应的存储过程，DAL不提供此类功能。&lt;/li&gt; &lt;li&gt;DAL不能像Django一样提供基于业务对象的数据导入（XML或JSON格式）。&lt;/li&gt;&lt;li&gt;仿dict接口的快捷访问非常漂亮，但是受限于id列，不能像django的get方法或&lt;a href="http://ado.net"&gt;ado.net&lt;/a&gt;的command.ExecuteScala（方法名记不清了）r一样直接构造单个对象选取操作。&lt;br&gt;&lt;/li&gt;&lt;/ul&gt;进一步的，DAL虽然为db对象提供了executesql方法，但是却没有提供参数传入功能，也是一个比较明显的遗憾。&lt;br&gt; &lt;br&gt;&lt;ul&gt;&lt;li&gt;DAL的发展与讨论&lt;/li&gt;&lt;/ul&gt;我认为，针对DAL的PG应用，期待以下功能的实现：&lt;br&gt;&lt;ul&gt;&lt;li&gt;无id列，用户自定义主键的表结构，至少应该支持uuid。&lt;/li&gt;&lt;li&gt;可以方便的指定schema范围或直接引用。&lt;/li&gt;&lt;li&gt;更方便的外键定义。&lt;/li&gt;&lt;li&gt;支持函数，最好可以自定义返回集结构，可以select query。&lt;/li&gt;&lt;li&gt;至少应该提供参数化SQL执行功能。&lt;/li&gt;&lt;li&gt; 基于业务模型的数据迁移能力。&lt;/li&gt;&lt;li&gt;更友好和强大的数据库管理工具。&lt;/li&gt;&lt;li&gt;更充分和合理的利用PG的数据类型，如text和boolean。&lt;/li&gt;&lt;li&gt;支持复杂数据结构，如数组、几何类型等。&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;-- &lt;br&gt;杀人放火金腰带，补路修桥无尸骸。&lt;br&gt;&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-3829864740664986539?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/3829864740664986539/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=3829864740664986539' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3829864740664986539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3829864740664986539'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/06/web2pyweb2py-dal-postgres.html' title='[Web2py]Web2py DAL 与 Postgres数据库'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-1544455614689184614</id><published>2009-05-31T09:11:00.001-07:00</published><updated>2010-04-17T15:44:21.213-07:00</updated><title type='text'>Astinus 文档格式</title><content type='html'>&lt;div id="wiki"&gt; 		&lt;h1 id="astinus"&gt;Astinus 文档格式&lt;/h1&gt; &lt;p&gt;Astinus 项目需要支持相对复杂的通用文档格式，要能够描述多种格式化或带有元信息的文档。因此，需要约定一种通用文档格式。&lt;/p&gt; &lt;h2 id=""&gt;数据格式&lt;/h2&gt; &lt;h3 id=""&gt;基本格式&lt;/h3&gt; &lt;p&gt;Astinus文档的基本单位为条目，使用XML格式存储。根元素为 &lt;code&gt;&amp;lt;entry id=&amp;#39;uid&amp;#39;/&amp;gt; &lt;/code&gt;。&lt;/p&gt; &lt;p&gt;文档条目的主体为 xpath:&lt;em&gt;entry/content  在正文中允许嵌入元信息节点。&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&amp;lt;entry/&amp;gt; 节点下允许加入其它节点，提供map类型的属性映射。&lt;/p&gt; &lt;p&gt;content 的 map 属性通过 xpath:&lt;em&gt;entry/content[@*] 设置。&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&amp;lt;entry/&amp;gt; 节点下不写入text属性&lt;/p&gt; &lt;p&gt;&amp;lt;cotent/&amp;gt; 节点下的信息是有序的&lt;/p&gt; &lt;p&gt;允许在 &amp;lt;entry/&amp;gt;内部嵌入&amp;lt;entry/&amp;gt;，或在 xpath:&lt;em&gt;entry/content 内嵌入 &amp;lt;entry/&amp;gt;&lt;/em&gt;&lt;/p&gt; &lt;h3 id=""&gt;数据退化&lt;/h3&gt; &lt;p&gt;没有附加属性的情况下，可以没有&amp;lt;entry/&amp;gt;节点，根直接为 &lt;code&gt; &amp;lt;content id=&amp;#39;uid&amp;#39;/&amp;gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;可以以XML字符串格式传递，默认的头是 &lt;code&gt; &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt; &lt;/code&gt;&lt;/p&gt; &lt;p&gt;数据表现层或新建文档输入项可以不提供uid，这样数据结构进一步退化为 &amp;lt;entry&amp;gt;&amp;lt;content&amp;gt;&amp;lt;/content&amp;gt;&amp;lt;/entry&amp;gt;&lt;/p&gt; &lt;h3 id="json"&gt;JSON 转换&lt;/h3&gt; &lt;p&gt;Astinus 文档允许表达为JSON 格式，也接受JSON格式的数据转入&lt;/p&gt; &lt;p&gt;JSON 格式的根是{id:&amp;#39;uid&amp;#39;, cotent:[]}，&lt;/p&gt; &lt;p&gt;数据表现层或新建文档输入项可以不提供uid，这样数据结构最大可退化为 [...]&lt;/p&gt; &lt;p&gt;content序列内，以字符串序列表达正文内容，序列元信息通过文本元素间嵌入的{}元素表达。&lt;/p&gt; &lt;h2 id=""&gt;设计原则&lt;/h2&gt; &lt;p&gt;Astinus 文档格式的设计遵循如下的原则：&lt;a href="https://bitbucket.org/March/astinus/wiki/edit/AstinusDocumentFormat"&gt;https://bitbucket.org/March/astinus/wiki/edit/AstinusDocumentFormat&lt;/a&gt;&lt;/p&gt; &lt;ul&gt;&lt;li&gt;对应用层编程友好，尽可能支持通用编程接口。 &lt;/li&gt;&lt;li&gt;对存储层友好，容易存储和优化性能，允许在数据库端编程管理。 &lt;/li&gt;&lt;li&gt;自描述，在离开服务器支持的环境下可以解读 &lt;/li&gt;&lt;li&gt;非SQL表格，支持条目的非结构化字段表述 &lt;/li&gt;&lt;li&gt;允许文本化，添加全文索引支持 &lt;/li&gt;&lt;li&gt;尽可能少的保留字 &lt;/li&gt;&lt;li&gt;允许DAL信息的使用 &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Astinus 的文档处理环境遵循如下的原则：&lt;/p&gt; &lt;ul&gt;&lt;li&gt;在应用层保持文档数据完整性 &lt;/li&gt;&lt;li&gt;允许提取元信息进行计算 &lt;/li&gt;&lt;li&gt;允许元信息编程 &lt;/li&gt;&lt;li&gt;允许版本化支持（考虑并发处理） &lt;/li&gt;&lt;li&gt;允许解释DAL和嵌入式编程 &lt;/li&gt;&lt;/ul&gt;  		 		 	&lt;/div&gt;&lt;br clear="all"&gt;&lt;br&gt;-- &lt;br&gt;杀人放火金腰带，补路修桥无尸骸。&lt;br&gt;&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt; &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-1544455614689184614?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/1544455614689184614/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=1544455614689184614' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1544455614689184614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1544455614689184614'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/05/astinus.html' title='Astinus 文档格式'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-3521657357123574103</id><published>2009-05-25T21:04:00.001-07:00</published><updated>2009-05-25T21:25:36.706-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web2py'/><title type='text'>[web2py]DAL中隐藏的秘密：limit和orderby desc</title><content type='html'>web2py 有所有新兴开源项目的通病：文档落后于代码。&lt;br /&gt;例如，你在document下的DAL相关文档里，只能读到orderby的用法，但是没有告诉你如何desc。在该文档中也找不到limit的介绍。&lt;br /&gt;其实这两个功能都很重要，分页几乎是现代数据库应用中必备的功能，limit关键字就是为此而诞生的。而对于不能任意split（我查了web2py的源码才发现小于0的索引SQLROWS直接抛异常），又不能直接reverse的DAL数据集，倒排查询就是很重要的功能了。&lt;br /&gt;其实这两个功能DAL都有。&lt;br /&gt;oderby的支持看来开发人员还没有找到很好的形式，例如，我们可以 orderby=&lt;a href="http://db.ta.id/"&gt;db.ta.id&lt;/a&gt;，但是没有一个descorderby=&lt;a href="http://db.ta.id/"&gt;db.ta.id&lt;/a&gt;，不过其实我们可以 orderby="&lt;a href="http://ta.id/"&gt;ta.id&lt;/a&gt; desc"，这样看起来有点怪，但是可用，而且不算很难理解。&lt;br /&gt;limit关键字参数的支持就自然很多，类似标准SQL，DAL的limit支持两种形式：&lt;br /&gt;limit=n，取数据集前n项。&lt;br /&gt;limit=(n,m)从m项起取前n项，等效于PG的limit n offset m&lt;br /&gt;--&lt;br /&gt;杀人放火金腰带，补路修桥无尸骸。&lt;br /&gt;&lt;br /&gt;……&lt;br /&gt;&lt;br /&gt;劉鑫&lt;br /&gt;March.Liu&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-3521657357123574103?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/3521657357123574103/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=3521657357123574103' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3521657357123574103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3521657357123574103'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/05/web2pydallimitorderby-desc.html' title='[web2py]DAL中隐藏的秘密：limit和orderby desc'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-8038181681565310294</id><published>2009-05-25T06:53:00.001-07:00</published><updated>2009-05-25T21:27:19.723-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web2py'/><title type='text'>web2py 的自定义路径</title><content type='html'>Web2py不同于django和web.py的一个特色，它自然的就是restful的形式。当然这也造成了一定的限制，就是我们有时候不能方便的自定义请求的路径。&lt;br /&gt;其实Web2py一样可以自定义请求路径。只是它默认情况下没有给出这个配置。简而言之，在web2py的根目录下有一个 &lt;a href="http://routers.examples.py/"&gt;routers.examples.py&lt;/a&gt;，只要看那个文件的内容，就可以参照着写出自己的routers.py了：）。它采用的是类似django和 web.py的正则表达式重定向形式。&lt;br /&gt;--&lt;br /&gt;杀人放火金腰带，补路修桥无尸骸。&lt;br /&gt;&lt;br /&gt;……&lt;br /&gt;&lt;br /&gt;劉鑫&lt;br /&gt;March.Liu&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-8038181681565310294?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/8038181681565310294/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=8038181681565310294' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8038181681565310294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8038181681565310294'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/05/web2py_25.html' title='web2py 的自定义路径'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-7993150497577479153</id><published>2009-05-25T05:38:00.001-07:00</published><updated>2009-05-25T21:13:40.892-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web2py'/><title type='text'>web2py 的远程开放</title><content type='html'>web2py是一个有趣的框架，它的特色之一就是在页面上集成了一个简单的开发环境。但是这个前提是只能从127.0.0.1访问，否则会提示你"不是安全通道"。&lt;br&gt;那么，是不是就没有办法把代码集中部署到一个远程服务器上开发了？&lt;br&gt;后来，我在web2py的官网上搜索到一个&lt;a href="http://www.web2py.com/AlterEgo/default/show/143"&gt;方法&lt;/a&gt;，，就是通过ssh映射远程端口到本地，可以实现远程开发。例如，我在192.168.0.13主机上run了一个web2py示例：&lt;br&gt; python web2py.py -i 0.0.0.0&lt;br&gt;&lt;br&gt;那么我就从本机：&lt;br&gt;&lt;br&gt;ssh -L 8000:&lt;a href="http://127.0.0.1:8000"&gt;127.0.0.1:8000&lt;/a&gt; 192.168.0.13&lt;br clear="all"&gt;&lt;br&gt;登录之后，如果再从浏览器访问 &lt;a href="http://127.0.0.1:8000"&gt;http://127.0.0.1:8000&lt;/a&gt; ，打开的就是远程主机的回环IP。此时，就可以对远程的站点项目进行开发了。由于Web2Py提供了编辑冲突保护，甚至我们可以用这种方法进行团队集中式开发。&lt;br&gt; &lt;br&gt;当然，我还是建议尽可能走成熟的版本管理系统进行开发。一个比较好的模式是每个团队成员在本机有一个开发环境，在测试服务器上有一个集中的环境，成员通过分布式版本管理系统——例如Python和OpenJDK项目使用的mercurial，或web2py和ubuntu项目使用的bzr——与测试服务器上的中心仓库同步，就可以实现比较理想的写作效果。更严格的管理模式，可以使用一个SVN，然后对可集成的分支挂一个hook，使其可以在有用户commit后自动发布到测试/生产服务器。&lt;br&gt; &lt;br&gt;在这种情况下，通过ssh远程进入web2py的开发环境，也可以帮助我们获取错误信息，仍然是一项很有意义的技巧。&lt;br&gt;-- &lt;br&gt;杀人放火金腰带，补路修桥无尸骸。&lt;br&gt;&lt;br&gt;……&lt;br&gt;&lt;br&gt;劉鑫&lt;br&gt;March.Liu&lt;br&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-7993150497577479153?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/7993150497577479153/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=7993150497577479153' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7993150497577479153'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7993150497577479153'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/05/web2py.html' title='web2py 的远程开放'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-977828403283228591</id><published>2009-05-13T07:39:00.000-07:00</published><updated>2009-05-13T10:32:06.470-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Postgres Story'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><title type='text'>数据库设计建议，范式以及进一步</title><content type='html'>几乎每一个新人在初学关系型数据库设计的时候，都会接触到关系范式。但是，我还是见到了大量很离谱的设计。客观的说，背下关系范式，离一个合格的数据库设计师还差很远。设计工作总是在理想与现实之，规范与工艺之间妥协。建筑如是，造船如是，操作系统设计如是，数据库设计亦如是。&lt;br /&gt;是的，你记得范式，你还记得反范式建议。你知道范式减少冗余，提高一致性；你还知道反范式可以方便编程。不幸的是，最终的结果总是遵守范式的做法使自己的应用层代码混乱，而反范式的企图使得数据库也陷入混乱。&lt;br /&gt;这是谁的错？&lt;br /&gt;不必太自责，设计工作是一个经验的积累过程。没有人天生就会做设计。天才与勤奋，是乘法关系。并不是你笨，只是天才对面的那个系数还不够大而已。&lt;br /&gt;以下的一些经验，或许在你读完关系范式以后，可以抽空看一看 。世上没有魔法，读完这篇文章，并不会立即让你拥有多年设计经验。但是，这些在设计工作中积累的经验教训，应该可以帮助你少走一些弯路。&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;关于范式&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;关系范式并不邪恶，也不要把它想得太神秘，如果书本上的定义不能让你很快理解，不妨试着回答以下的问题：&lt;br /&gt;字段还可以再分吗？分成两个或更多的字段以后，还能不能表达完整的含义？&lt;br /&gt;字段的值是不是有限的几个离散的状态？&lt;br /&gt;两个或若干个字段，能不能提取出来建立为一个数据字典？&lt;br /&gt;如果表中某个字段依赖其他表，被依赖的字段是不是唯一的（最好是主键）？&lt;br /&gt;查询中是否会出现超过两个表的Join？&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;将数据库设计与系统设计结合起来&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;数据库设计并不是一个孤立的过程，整个软件生命期中，各方面的工作应该有机结合。这方面我觉得ACCP过去的教材讲得还不错，至少思路是对的：&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;在做需求分析的时候，做Use Case。&lt;/span&gt;此时可以分析出应用层的功能接口，对于数据库的实体分类可以有一个大概的划定。例如，这个项目会需要一个工作流，这个项目会需要一个订单系统，或者一个文档库，等等。通常，每个子系统可以对应一个&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;在做概要设计的时候，出类关系和ER简图。&lt;/span&gt;通常来说，此时不能确定所有的字段，但是会有哪些表，有哪些主外键依赖，有哪些地方应该需要存储过程和触发器的辅助，等等。&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;详细设计时尽可能将数据库结构完全固定。&lt;/span&gt;尽管现代开发工具不断提升XP能力，重构越来越简单。数据库的重构仍然是一件牵一发而动全身的事情，毕竟数据库是信息存储的根本。大厦楼顶加个小花园容易，把地基下面的承重柱子拔出来换两根试试？&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;重视SQL&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;近年来ORM发展很快，几乎每个框架都要提供这个功能，以至于会有些菜鸟认为“ORM”会淘汰SQL语言。&lt;br /&gt;这是一块试金石，如果你有这样的感觉，应该考虑认真评估一下自己在这个领域是不是太菜了。&lt;br /&gt;SQL不是一种编程语言这么简单，SQL代表的是一种与应用开发语言完全不同的思想。面向集合，过程无关，着眼于规则定义。可以说，SQL是FP High Order计算的最成功应用，也可以说，SQL是一种静态强类型的MapReduce语言。&lt;br /&gt;看，换上时髦的名词，会不会让你觉得它上等起来了？&lt;br /&gt;在应用层语言惨烈竞争的同时，SQL语言压倒了同时代出现的其他关系型数据库操作语言，在这个拥有巨大利润的领域占据了绝对统治地位。即使桀骜不驯的Postgres，也在1995年变身为PostgrSQL。这一过程，并非像VC淘汰BC那么多盘外招，而是长时间争议与选择的结果。&lt;br /&gt;对于信息操作规则定义，SQL几乎是最好的表达方式。接近自然语言，高度可读，并且非常利于优化。&lt;br /&gt;打个比方，一个基于过程语言的上帝，这样说：&lt;br /&gt; * 构造一个光源对象&lt;br /&gt; * 构造一个能源对象&lt;br /&gt; * 调用光源对象方法，设置能源&lt;br /&gt; * 调用光源对象的发光方法，传入照明范围内的对象列表&lt;br /&gt;基于SQL的上帝说，要有光。&lt;br /&gt;当然，在这位老兄背后，要有打杂的小弟去完成插电点灯的事情，但是作为上帝，什么活都自己做了，要天使干什么？&lt;br /&gt;看看那些应用层语言的list comprehensions（列表推导式）。不止一次我想要为Python实现一个基于存储层的列表推导式实现，都可耻的失败了。&lt;br /&gt;当然，我承认这跟跟人能力有关，我不是Gudio。&lt;br /&gt;看看LINQ，不管如何吹嘘，它就是一个抽象出I/O的SQL。我见过一些人激烈的贬低SQL，抬高ORM，同时又对LINQ顶礼膜拜，这可真够分裂的。&lt;br /&gt;ORM对应用层编程效率的提升是客观的，无需回避。但是随着你数据操作越来越精细和复杂，就越来越需要通过规则定义来抽象High Order I/O过程。当你转了一圈儿回来，会发现自己又在写SQL。&lt;br /&gt;想想Hibernate的HQL，想想C#的LINQ。&lt;br /&gt;计算机不会变魔术。想让它做事更聪明，就需要你这个驭者更加聪明才行。&lt;br /&gt;好的工具和方法可以给你带来更高的能力系数，但是记住，一个乘法计算，仅有一头大是不够的。&lt;br /&gt;不懂SQL的人，是不能驾驭好ORM的。&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;与ORM做朋友&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;ORM对于开发工作，无疑是有好处的。我的朋友沈葳说，人脑能组织和分析的事务是有限的，所以代码越短，越有利于提高代码质量。从这个角度讲，ORM是非常重要的开发工具，其意义不亚于C API 函数集到GUI 框架的进步。&lt;br /&gt;要想让ORM充分发挥威力，有时候需要从数据库设计时就做出一定妥协。&lt;br /&gt;例如，你往往会需要加入自增标识列，会放弃一些精巧但是不利于ORM访问的依赖设定，甚至要放弃一些漂亮的命名（它们在应用层语言中是保留字，但是你用的ORM不懂如何规避）。&lt;br /&gt;但是，这往往是必要的。就像建筑师向气候和建筑材料妥协一样。&lt;br /&gt;在ORM默认的自增字段外，也许你还需要基于业务规则的唯一约束，那么额外加索引。&lt;br /&gt;好的ORM会帮助你方便的查询数据字典，生成对象映射，跟踪数据变更，提供数据完整性的应用层检查，构造两阶段提交事务，减少不必要的I/O。&lt;br /&gt;同样，不懂得运用ORM，也可能会破坏数据完整性，降低数据访问速度，甚至造成数据库死锁。作为项目开发人员，应该将ORM视为朋友而不是负担。&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;合理分层&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;过去，流行使用复杂的数据库设计，将业务规则存储于数据库的存储过程。现在，又流行抛弃数据层的一切约束，所有的规则都放在应用层。&lt;br /&gt;这两者都不合理，除了应用需求的影响，前者与Oracle的广告部宣传有关，后者与MySQL阵营的鼓动有关。背后都有一些不合理的力量推动。&lt;br /&gt;每一层应该保证自己的完整性，这才是分层的意义。那么，在数据库层，应该保证数据的完整性。&lt;br /&gt;数据库备份出来，再恢复进去，应该可以得到所有的业务信息。&lt;br /&gt;直接向数据库导入数据，应该可以有完整的数据规则保护。&lt;br /&gt;数据库里保存的，不仅仅是表和记录，应该是完整的持久性信息。&lt;br /&gt;从这个角度讲，配置文件和应用层代码中不应该有任何业务数据定义，这些信息都应该是数据字典表。如果出现了这种配置文件，大多数情况下都是愚蠢的错误。&lt;br /&gt;实际上，包括Web网站常见的附件上传，都应该保存在数据库中。&lt;br /&gt;独立的I/O文件存储、包括将外键约束转移到应用层，往往是因为对性能的妥协。以及，这里面确实存在MySQL阵营在推广过程中的一些不道德的宣传。&lt;br /&gt;有效利用数据库功能，可以提高应用层的开发速度，简化代码结构，使得数据存储更安全。这通常仰赖与设计人员的经验，根据项目的具体需求进行调整。&lt;br /&gt;基于这个原则，合理利用数据库功能，编写存储过程，触发器，调校索引，都是必要的。&lt;br /&gt;我敢打赌，随着MySQL实现越来越多的功能，它的宣传材料上会越来越多的出现以前被MySQL所摒弃的复杂设计理念，并且宣称这是MySQL所独创或一贯倡导的。&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;收集整理常见的模式&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;在设计模式提出这么多年，在关系型数据库问世如此之久后，我很惊讶的一件事就是数据库设计模式仍然是一个相当冷门的领域。实际上，关系数据库的模式也有很多可循之规。例如用户信息（HR或CRM）、工作流，权限管理（如RBAC），订单等等，都有相当成熟的行业经验和时间，往往只要修改一些字段名，或者在关键架构的基础上加以扩展，就可以很好的用于实践。&lt;br /&gt;每一个有志于成为高水平设计人员的开发者，都应该积极的收集自己体会到的数据库设计模式，积极的与同行交流。&lt;br /&gt;这方面，Oracle的示例Schema，Postgres的示例数据库项目（在Soureforge上可以找到），都是很好的例子。相对来说，微软在MSSQL和Access中提供的示例库更为轻量和简单，也是作为入门的不错借鉴。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-977828403283228591?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/977828403283228591/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=977828403283228591' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/977828403283228591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/977828403283228591'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/05/blog-post.html' title='数据库设计建议，范式以及进一步'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-5784235333662190369</id><published>2009-04-26T03:11:00.000-07:00</published><updated>2009-04-26T03:52:40.558-07:00</updated><title type='text'>啤酒鸭</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_WAdxCs9S6dI/SfQ8jp65KLI/AAAAAAAACCE/3mf6HoWXy64/s1600-h/download1.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_WAdxCs9S6dI/SfQ8jp65KLI/AAAAAAAACCE/3mf6HoWXy64/s320/download1.jpg" alt="" id="BLOGGER_PHOTO_ID_5328950842332096690" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;这个其实超级好做，加上超市经常会有鸭子减价（例如珠海家乐福就时不时来个10元半只），实在是懒汉的好菜。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;鸭半只&lt;br /&gt;土豆若干&lt;br /&gt;大葱葱白一段&lt;br /&gt;大蒜瓣三瓣&lt;br /&gt;花椒八角少许&lt;br /&gt;啤酒两瓶&lt;br /&gt;&lt;br /&gt;这锅半只鸭子我们两口子吃了两顿，单身汉过周末一般有这么多也差不多过一天了——如果你不吃饭，没别的菜，就鸭子和啤酒。&lt;br /&gt;&lt;ul&gt;&lt;li&gt;土豆削皮切块备用&lt;/li&gt;&lt;li&gt;鸭半只，切块，洗净下锅，净水焯一下，去掉浮沫捞出。&lt;/li&gt;&lt;li&gt;锅里水倒出，放点油，下葱白，蒜瓣，花椒八角爆香。&lt;/li&gt;&lt;li&gt;加入鸭块，倒啤酒一瓶。&lt;/li&gt;&lt;li&gt;加入土豆，煮至鸭块熟透。&lt;/li&gt;&lt;li&gt;加盐少许，这道菜只需要很少的盐。&lt;/li&gt;&lt;li&gt;继续煮至土豆酥烂即可。&lt;/li&gt;&lt;li&gt;中途观察，如果啤酒不够随时再倒一瓶进去。一般想要土豆煮够火候，一瓶不够。&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;备注：如果你喜欢稍清淡一点，啤酒味儿不那么浓的，可以只加一瓶，后面就续水。&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_WAdxCs9S6dI/SfQ8w9A-RiI/AAAAAAAACCM/1fTgLuInp-U/s1600-h/download.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_WAdxCs9S6dI/SfQ8w9A-RiI/AAAAAAAACCM/1fTgLuInp-U/s320/download.jpg" alt="" id="BLOGGER_PHOTO_ID_5328951070796170786" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-5784235333662190369?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/5784235333662190369/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=5784235333662190369' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5784235333662190369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5784235333662190369'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/04/blog-post_26.html' title='啤酒鸭'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_WAdxCs9S6dI/SfQ8jp65KLI/AAAAAAAACCE/3mf6HoWXy64/s72-c/download1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-523073197382751652</id><published>2009-04-24T06:53:00.001-07:00</published><updated>2009-04-24T07:04:51.743-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='烹饪'/><title type='text'>家常烤鸭</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_WAdxCs9S6dI/SfHEbfCew6I/AAAAAAAAB-Y/uKbS0fxZh0E/s1600-h/IMG_9726.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_WAdxCs9S6dI/SfHEbfCew6I/AAAAAAAAB-Y/uKbS0fxZh0E/s320/IMG_9726.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5328255810623423394" /&gt;&lt;/a&gt;不追求全聚德水平的话，其实烤些鸭肉一家人吃并不难，简单的小烤箱也可以烤半只鸭了。我这里只有不到四分之一，所以看起来很小。&lt;div&gt;烤鸭真正麻烦的是烤前的准备，提前一天准备好鸭子，收拾干净以后用生抽、料酒、盐、桂皮、花椒、大料、桂叶等调料抹匀，反复按揉一段时间，为的是入味，肉质也会比较松软。然后保鲜膜包好放进冰箱，12小时以后拿出来，才差不多入味。然后可以看自己喜好要不要涂一层蜂蜜。烤箱200度预热好，烤一小时。或者像我这次这样，鸭肉比较小块，就自己估计要不要提前出炉。&lt;/div&gt;&lt;div&gt;香料不一定百分百按前述来，但是强烈推荐桂叶和八角，很香。&lt;/div&gt;&lt;div&gt;喜欢北京风味，可以提前涂上蜂蜜，风干。网上介绍北京风味脆皮烤鸭的文章有不少，就不献丑了。&lt;/div&gt;&lt;div&gt;可以配啤酒，也可以配烙饼，都是不错的搭配。&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-523073197382751652?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/523073197382751652/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=523073197382751652' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/523073197382751652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/523073197382751652'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/04/blog-post_24.html' title='家常烤鸭'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_WAdxCs9S6dI/SfHEbfCew6I/AAAAAAAAB-Y/uKbS0fxZh0E/s72-c/IMG_9726.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-1763630568393997600</id><published>2009-04-20T06:41:00.000-07:00</published><updated>2009-04-20T06:49:13.464-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='烹饪'/><title type='text'>砂锅鱼块</title><content type='html'>在太太的努力下，家里终于开始像个家样了。在亲爱的老婆大人布置的厨房里，连我都想做饭了！&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_WAdxCs9S6dI/Sex8kSJe9SI/AAAAAAAAB-Q/RSl9JqyScLo/s1600-h/IMG_9724.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 249px;" src="http://2.bp.blogspot.com/_WAdxCs9S6dI/Sex8kSJe9SI/AAAAAAAAB-Q/RSl9JqyScLo/s320/IMG_9724.jpg" alt="" id="BLOGGER_PHOTO_ID_5326769422061204770" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;其实这东西煮起来很简单，嘿嘿。最下面铺白菜块，要大块一些，然后是鱼块，然后是豆腐，码齐以后加水，煮到九成熟加盐，出锅时调一点胡椒，很方便吧：）。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-1763630568393997600?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/1763630568393997600/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=1763630568393997600' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1763630568393997600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1763630568393997600'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/04/blog-post.html' title='砂锅鱼块'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_WAdxCs9S6dI/Sex8kSJe9SI/AAAAAAAAB-Q/RSl9JqyScLo/s72-c/IMG_9724.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-5355630347051811743</id><published>2009-04-07T06:43:00.001-07:00</published><updated>2009-04-07T09:58:04.628-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PostgreSQL'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><title type='text'>Why Postgres?</title><content type='html'>关于这个话题，我想了很久，反复开了几个头都不太满意。看来还是直接了当比较好。&lt;div&gt;我并不是只使用过Postgres的用户。正相反，从MSSQL到Firebird再到Oracle和MySQL，加上LDAP等，我在工作中用过的至少有八九种数据库产品。但是，Postgres是我至今遇到的最满意的数据库平台。&lt;/div&gt;&lt;div&gt;首先，Postgres足以可靠的支持我所要管理的数据量。历经二十多年发展的Postgres一直是主流数据库服务系统的代表之一。长期大量用户的使用，已经证明了它在TB级应用的可靠性和可用性，这足以满足我的需求。早在上个世纪，Postgres就与DB2和Oracle等昂贵的商业平台一样，支持多层存储管理，从文件系统到表空间再到数据区集群。通过完备严谨的分层定义，为海量数据管理提供了可能。&lt;/div&gt;&lt;div&gt;能够提供这一级别的数据存储能力的数据库不在少数，但是Postgres除了这一基本能力，还拥有一些独到的特长。&lt;/div&gt;&lt;div&gt;Postgres以完备和高度可靠的事务见长。这在一个严肃的商用系统中是非常重要的指标。正因为如此，传统的几大数据库巨头对此都非常重视。然而Postgres作为伯克利分校出身的开源产品，一直在数据库事务技术的发展中处于领导地位，这是一项非常了不起的成就，也是商业应用的有力保障。&lt;/div&gt;&lt;div&gt;Postgres与*nix有良好的集成，几乎每一个linux/unix/bsd发行版，都将Postgres的支持和集成作为重要内容。从Postgres 7.x 版本至今，Postgres对于windows支持也越来越出色。现在的Postgres For Windows 版甚至还附带了一个集成配置安装工具。对于高性能及高可靠性应用场合，能否允许灵活的选择适合的系统组合，还是很重要的。&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Postgres 在少量连接数的情况下，性能测试并不出色，只能说中规中矩。然而高并发高负载的严苛环境，Postgres 的测试结果远超MySQL等竞争对手。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;以下链接可以看到FreeBSD7.x上，Postgres对MySQL表现出的强大性能优势：&lt;/div&gt;&lt;div&gt;&lt;a href="http://postgresql-chinese.blogspot.com/2007/11/postgresql-vs-mysql-freebsd-7.html"&gt;http://postgresql-chinese.blogspot.com/2007/11/postgresql-vs-mysql-freebsd-7.html&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;以下链接可见Postgres在高并发应用中表现出的优势。&lt;/div&gt;&lt;div&gt;&lt;a href="http://tweakers.net/reviews/657/6"&gt;http://tweakers.net/reviews/657/6&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;这种高并发优势，无论web广域应用，还是企业应用，都至关重要。强大的并发支持，可以允许架构设计人员在应用层架构选型时有更多的机动余地。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;除了以上几点，Postgres的编程能力，更是远超群雄。Postgres曾经在近十年间都以SQL语言的竞争者身份出现，然而这不等于它在SQL编程能力方面有所缺失。恰恰相反，Postgres拥有当下语法最丰富好用的SQL/PLSQL脚本引擎。Postgres独到的实现了函数与过程的统一定义和使用，并且做到了标量函数与矢量集函数的灵活组合。无论触发器、主外键关联、视图、唯一索引，各种关系约束一应俱全。&lt;/div&gt;&lt;div&gt;Postgres 实现了相当完备的面向对象支持。一直以来，Postgres都是对象-关系型数据库的技术先锋。灵活的类型设计，加上完备的关系机制，使得 Postgres 成为最方便实现设计思想的数据库之一。&lt;/div&gt;&lt;div&gt;除了传统上的C扩展存储过程，各主流数据库还纷纷支持各种应用开发语言，例如，Oracle支持java（通常都是过时版本），MSSQL支持.net  CLR，但是，早在这股风潮之前，Postgres就已经支持高达十余种不同的编程语言。得益于开源社区的贡献，Postgres支持Java、Python、Perl甚至Scheme、sh等编程语言，而这个列表还在增加。而且这些功能都是单独安装的，使用系统集成的适用版本，项目设计人员和架构师可以方便的选择最适合的组合。&lt;/div&gt;&lt;div&gt;Postgres还有大量类似的“插件”可供技术人员选择，包括数据库集群、服务器同步、全文索引（已有完整的中文支持）等等。大部分扩展功能，都可以通过简单的安装步骤直接使用。&lt;/div&gt;&lt;div&gt;这些强大的功能，足以使得Postgres服务器成为一个强大的数据管理和挖掘平台，或者一个功能丰富的业务开发平台。无论对与开发人员、维护人员、项目设计人员，还是最终用户，Postgres&lt;/div&gt;&lt;div&gt;总能提供更方便、强大和可靠的支持。&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-5355630347051811743?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/5355630347051811743/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=5355630347051811743' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5355630347051811743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5355630347051811743'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/04/why-postgres.html' title='Why Postgres?'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-4784372468105925955</id><published>2009-03-05T20:50:00.000-08:00</published><updated>2009-03-05T20:52:48.002-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='时间帐单 postgres jquery trac web.py mercury opensource'/><title type='text'>[坑]启动一个时间管理项目，广告一下</title><content type='html'>现在我用的时间帐单工具，昨晚正式开放出源码。&lt;br /&gt;&lt;br /&gt;大概google大神被我的若干僵尸项目激怒，昨晚code.google又神奇的不能开项目了。于是我找了一个支持mercurial工具的网站。&lt;br /&gt;&lt;br /&gt;项目地址如下：&lt;br /&gt;http://bitbucket.org/March/astinus/overview/&lt;br /&gt;&lt;br /&gt;有兴趣的同学可以注册一个帐号，发给我，我加你进项目组。该网站支持openid（www.openid.net），我就是用自己的blogger地址（blogger是openid成员之一）注册的。&lt;br /&gt;&lt;br /&gt;背景：&lt;br /&gt;Astinus（阿斯特纽斯），龙枪编年史中的大图书馆馆长，克莱恩编年史的作者。他永远都在不停的书写编年史，记录克莱恩世界所发生的一切。他是永生不死之人，是第一个踏上克莱恩之人，最后一个离开克莱恩之人。传说他是中立之神吉利安的化身，但从来没有得到过证实。&lt;br /&gt;&lt;br /&gt;目标平台：&lt;br /&gt;客户端：web浏览器，或web service客户端&lt;br /&gt;服务器：Linux，我希望可以扩展到FreeBSD，前提是只要nlpbamboo支持。以目前的情况看，无法保证windows服务器环境的可用性，但是我欢迎有进行windows移植的人，理论上讲只需要解决nlpbamboo的移植。&lt;br /&gt;数据库：Postgres&lt;br /&gt;应用层：web.py&lt;br /&gt;&lt;br /&gt;开发环境准备：&lt;br /&gt;操作系统：linux（如前，欢迎Windows尝试，非常渴望FreeBSD支持）&lt;br /&gt;版本管理工具：mercurial&lt;br /&gt;数据库：Posgres 8.3.x +&lt;br /&gt;应用服务器：web.py&lt;br /&gt;浏览器：欢迎任何常见浏览器用户的参与，精力所限，我本人只关注Firefox 3。&lt;br /&gt;第三方开发包：nlpbamboo(http://code.google.com/p/nlpbamboo)，jquery，以及其它可能存在的相关组件。&lt;br /&gt;&lt;br /&gt;本项目欢迎（非必须，欢迎任何有兴趣的朋友参与）：&lt;br /&gt;文档高手，特别是擅长组织文档及编写使用指南的人士。&lt;br /&gt;翻译专家，需要有意帮我中译英的好人。&lt;br /&gt;网页开发人员，本项目使用JQuery，对CSS和Ajax的需求不言而喻。&lt;br /&gt;熟悉web.py的程序员。&lt;br /&gt;我关注该项目与Trac的互联能力，欢迎trac开发高手。&lt;br /&gt;欢迎有意为这个项目开发各种异构客户端的人。&lt;br /&gt;数据统计与分析，特别是自然语言分析方面的人士。&lt;br /&gt;Postgres程序员，非常欢迎能比我更擅长写SQL脚本的人士。&lt;br /&gt;或许，我们还需要FreeBSD上的C++程序员，目前nlpbamboo项目的开发人员不保证该工具在BSD上的可用性。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-4784372468105925955?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/4784372468105925955/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=4784372468105925955' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4784372468105925955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4784372468105925955'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/03/blog-post.html' title='[坑]启动一个时间管理项目，广告一下'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-2756918620059401398</id><published>2009-03-01T04:25:00.000-08:00</published><updated>2009-03-01T04:33:24.234-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PostgreSQL'/><title type='text'>[Postgres] 近期关注</title><content type='html'>授权与授权的管理&lt;br /&gt;数据库版本升级时的数据保护（manual 24.5）&lt;br /&gt;数据库同步与复制（manual 25）&lt;br /&gt;数据库自动优化autovcuum（manual 18.9&amp;amp;23.1.4）&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-2756918620059401398?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/2756918620059401398/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=2756918620059401398' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2756918620059401398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2756918620059401398'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/03/postgres.html' title='[Postgres] 近期关注'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-8699694258280173232</id><published>2009-02-22T02:01:00.001-08:00</published><updated>2009-02-22T02:01:41.039-08:00</updated><title type='text'>中华田园老公</title><content type='html'>冷笑话一则。如题，余下自己想。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-8699694258280173232?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/8699694258280173232/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=8699694258280173232' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8699694258280173232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8699694258280173232'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/blog-post_22.html' title='中华田园老公'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-7088783009827671038</id><published>2009-02-22T00:01:00.000-08:00</published><updated>2009-02-22T00:50:57.016-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='trac'/><title type='text'>Trac 0.12 开发环境（二）设定测试用户权限</title><content type='html'>昨晚头痛，上一篇没写完，其实对于trac，如果你只是把htpasswd文件传给它，还是不能在web端配置，因为没有admin用户。&lt;br /&gt;&lt;br /&gt;这个时候我们需要回到命令行下，用trac-admin来试试：&lt;br /&gt;&lt;br /&gt;详细文档请见&lt;a href="http://trac.edgewall.org/wiki/TracPermissions"&gt;这里&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;简单的说，按我习惯的开发用户march，我要将其设定为当前这个trac应用的管理员用户：&lt;br /&gt;&lt;br /&gt;trac-admin . permission add march TRAC_ADMIN&lt;br /&gt;&lt;br /&gt;现在再启动trac，以march登录，就可以看到页面左上角多了一个"admin"链接。&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://trac.edgewall.org/raw-attachment/wiki/TracPermissions/admin.png"&gt;&lt;img style="cursor: pointer; width: 175px; height: 89px;" src="http://trac.edgewall.org/raw-attachment/wiki/TracPermissions/admin.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;这样，就可以使用march用户在web界面上继续管理工作啦。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-7088783009827671038?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/7088783009827671038/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=7088783009827671038' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7088783009827671038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7088783009827671038'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/trac-012_22.html' title='Trac 0.12 开发环境（二）设定测试用户权限'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-2150246531517299321</id><published>2009-02-21T06:54:00.000-08:00</published><updated>2009-02-21T07:34:46.328-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='trac'/><title type='text'>Trac 0.12 开发环境（一）授权</title><content type='html'>上帝说，要有光，所以就有光。&lt;br /&gt;武总问，trac怎么设用户？所以就有了本文。&lt;br /&gt;＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝&lt;br /&gt;这是针对开发人员的，对于实际生产环境，可以选的用户认证方式有很多。公司这边现在用的LDAP，效果不错。&lt;br /&gt;相关链接（更多内容可google）：http://trac-hacks.org/wiki/LdapPlugin&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;正式开场&lt;/h1&gt;&lt;br /&gt;对于开发人员，通常总是会选用tracd作为开发环境，无它，简单。 tracd支持所谓的standalone模式。基本的运行很简单：&lt;br /&gt;&lt;br /&gt;tracd -p %port %path&lt;br /&gt;&lt;br /&gt;但是这个时候点击login，只会看到这样的一个页面：&lt;br /&gt;&lt;br /&gt;&lt;div id="content" class="error"&gt;           &lt;h1&gt;Trac Error&lt;/h1&gt;             Authentication information not available. Please refer to the &lt;a href="http://localhost:8080/trac/wiki/TracInstall#ConfiguringAuthentication" title="Configuring Authentication"&gt;installation documentation&lt;/a&gt;.       &lt;p&gt;         &lt;a href="http://localhost:8080/trac/wiki/TracGuide"&gt;TracGuide&lt;/a&gt; — The Trac User and Administration Guide       &lt;/p&gt;     &lt;/div&gt;&lt;br /&gt;默认情况下trac需要它的前端服务器为其提供授权，比如apache或nginx，standalone的时候，得要自己准备一个口令文件，文档上给出了用htpasswd工具，或直接使用Python脚本来生成口令文件的&lt;a href="http://http://trac.edgewall.org/wiki/TracStandalone"&gt;方法&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;有了这个文件，我们就可以用basic-auth的方式使得我们的开发环境支持简单的用户登录：&lt;br /&gt;&lt;br /&gt;tracd -p 8080 --basic-auth=trac,.htpasswd,trac .&lt;br /&gt;&lt;br /&gt;这里的--basic-auth指定基本授权方式，它的格式是[项目目录],[密码文件路径],[realm]&lt;br /&gt;虽然文档上要求全都写完整目录，但是实践证明相对路径也是可以的，以上命令是我执行成功的。&lt;br /&gt;测试过程中如果看到浏览器提示可能因为拒绝cookie造成服务器循环引用地址而产生错误，很可能说明你的设置成功了，只要把地址指向login以外的位置，应该就可以正常访问啦。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-2150246531517299321?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/2150246531517299321/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=2150246531517299321' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2150246531517299321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/2150246531517299321'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/trac-012_21.html' title='Trac 0.12 开发环境（一）授权'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-4463139611744133461</id><published>2009-02-21T05:27:00.000-08:00</published><updated>2009-02-21T05:56:46.867-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='trac'/><title type='text'>Trac 0.12 安装</title><content type='html'>注：比起ZOPE系，trac的安装已经算相当的简单啦。&lt;br /&gt;&lt;br /&gt;如果是release版的trac，我的建议就简单粗暴，你的系统提供什么软件包（或者你能在&lt;a href="http://trac.edgewall.org/wiki"&gt;trac的主站&lt;/a&gt;上下到的包）。就直接拿来用好了。当然，release版的trac安装还是都很靠谱的，就算是源码版， sudo python setup.py install足矣。&lt;br /&gt;&lt;br /&gt;developer版稍麻烦些，setup.py install的时候，你会发现它要求的genshi库版本自己找不到。其实，这时候可以先&lt;br /&gt;&lt;pre class="wiki"&gt;easy_install -U "&lt;span class="searchword1"&gt;Genshi&lt;/span&gt;==dev"&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;，然后再回过头来继续安装吧。&lt;br /&gt;接下来就按部就班的建站点，trac-admin %path% initenv。它会以问答的形式指引用户设定项目名、源码库、数据库（SQLite之外的用户移步&lt;a href="http://trac.edgewall.org/wiki/TracEnvironment#DatabaseConnectionStrings"&gt;这里&lt;/a&gt;）。&lt;br /&gt;此时，启动tracd -p %port %path ，已经可以看到站点了。安装至此算是初步成功，祝你顺利：）。&lt;br /&gt;&lt;br /&gt;附：其实这就是Django所宣传的MVT结构（Model View Template），trac可以说是比较典型的MTV应用。理解这一点，开发trac的插件或对它进行定制，思路就会清楚多了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-4463139611744133461?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/4463139611744133461/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=4463139611744133461' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4463139611744133461'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4463139611744133461'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/trac-012.html' title='Trac 0.12 安装'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-1584757678014762735</id><published>2009-02-06T03:58:00.000-08:00</published><updated>2009-02-06T04:00:31.605-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='流水帳'/><title type='text'>今日流水帳</title><content type='html'>在私记薄里记录想法。&lt;br /&gt;在即时贴里记录todo小tips，这些事项可能不会很正规，未经整理，强调的是快捷。&lt;br /&gt;在公司wiki的todo页跟踪事务序列（Todo/Doing/Done）&lt;br /&gt;养成做事前先明确的习惯。谋定而后动，坐言起行，不拖泥带水。&lt;br /&gt;不要想太多，先做，详细记录，用到出问题，再找解决方法，比猜测需求要可靠的多。&lt;br /&gt;事事留心皆学问。&lt;br /&gt;发现GMail的POP接口每天能接收的邮件是有数量限制的－－正常的人类也不会一天收三千封邮件吧。&lt;br /&gt;小时候我爸妈帮我办了残疾人证，可能怕伤害到我的自尊心，没告诉过我，是我无意中看到的。&lt;br /&gt;后来到了快三十才发现有这个可以免费进博物馆什么的，结果现在政府不承认我这样的算残疾了。&lt;br /&gt;党的政策好了，我现在不残疾了！&lt;br /&gt;不该买新鞋哇！&lt;br /&gt;还好没在本子上继续训练分词，pos训练慢到吐血，这会儿我的台式工作机已经卡到不行。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-1584757678014762735?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/1584757678014762735/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=1584757678014762735' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1584757678014762735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1584757678014762735'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/blog-post_06.html' title='今日流水帳'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-5042559039497514389</id><published>2009-02-05T18:50:00.000-08:00</published><updated>2009-02-05T18:52:38.080-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='流水帐'/><title type='text'>昨日流水帐</title><content type='html'>工作计划应该至少精确到小时，一个优秀的工程师应该可以精确到十分钟，虽然不一定可以百分百的达成。&lt;br /&gt;人真的是有惰性的动物，记录时间帐单，催促自己提高工作效率。&lt;br /&gt;记录自己将要做的事而不是刚刚做的事，推动自己从被动完成向主动做事的转变，养成每次只做一件事，做好一件事的习惯。&lt;br /&gt;要锻练自己及时发现讨论跑题，并且要及时拉回来。&lt;br /&gt;工作讨论，寻找共识比起战胜对方更重要。应该从大专辩论会那种“谁有理”转为沟通、理解、互利的角度。&lt;br /&gt;OO表格在不同机器上有变形，导成PDF比较安全。&lt;br /&gt;&lt;br /&gt;其实我要说的是，A正太昨天多了一个仰慕他的男FANS！恭喜A正太！&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-5042559039497514389?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/5042559039497514389/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=5042559039497514389' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5042559039497514389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5042559039497514389'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/blog-post_05.html' title='昨日流水帐'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-6395467759288762202</id><published>2009-02-04T05:39:00.001-08:00</published><updated>2009-02-04T05:39:57.054-08:00</updated><title type='text'>流水帳</title><content type='html'>寒，写了一天的流水帐，save changes的时候opera居然死了，你丫狠，居然还清了我的剪贴板……&lt;br /&gt;我现在等Acrobat Reader装完，查一下PG里yesterday是啥……&lt;br /&gt;PG的Timestamp还分有时区没时区，还不能隐式转换，差儿坑死我。&lt;br /&gt;trunc_date是个好东西。&lt;br /&gt;extract是个好东西。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-6395467759288762202?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/6395467759288762202/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=6395467759288762202' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6395467759288762202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6395467759288762202'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/blog-post_04.html' title='流水帳'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-3108950582243145896</id><published>2009-02-03T04:37:00.000-08:00</published><updated>2009-02-03T04:39:40.525-08:00</updated><title type='text'>流水帳</title><content type='html'>昨天准备把车上写的两篇关于Postgres的文章放到blog上，结果发现找不到了。想来想去，只有可能是因为手贱删了两个私记薄的备份文件。看来它现在学习MoinMoin，把版本记录到了历史文件中。这下我可不敢随便删文件了，还是把它存到一个独立的目录吧。&lt;br /&gt;下午试着DONE了一下，果然不作save changes的话，它不会保存。&lt;br /&gt;PGAdmin III启动，Tips 弹出一个：42是生命、宇宙和万物的终极解答。&lt;br /&gt;结果我很没有面子的在例会时间笑出声了，NND。&lt;br /&gt;&lt;br /&gt;我干了一件很白烂的事，写了一个联接查询，把一百多个IP重复ping了两遍（其中大部分是ping不通的），多等N久，明天把時間賬單的報表查出來就知道了……&lt;br /&gt;这种时候应该用表子查询。  &lt;!--}}}--&gt;&lt;!--{{{--&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-3108950582243145896?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/3108950582243145896/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=3108950582243145896' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3108950582243145896'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3108950582243145896'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/blog-post_03.html' title='流水帳'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-4433577403559927626</id><published>2009-02-02T05:56:00.000-08:00</published><updated>2009-02-22T01:33:21.783-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PostgreSQL'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>ubuntu 的 postgreSQL</title><content type='html'>ubuntu上其实有PostgreSQL的安装包，sudo apt-get install，就可以直接使用，但是刚开始的时候这个包也给我弄出不少麻烦。以至于有很长一段时间我宁可使用源码自己编译。总结一下，比较容易给新手造成麻烦的地方有这样几个：&lt;br /&gt;&lt;br /&gt;ubuntu上PostgreSQL的超级用户是postgres，与官方文档一致。而bsd上是psql。&lt;br /&gt;&lt;br /&gt;ubuntu上的pg命令行工具名为psql，bsd上是pgsql。&lt;br /&gt;&lt;br /&gt;pg_ctl找不到？但是ubuntu上有一个pg_ctlcluster，它可以帮你完成pg_ctl的工作，只是需要一点题外的东西:&lt;br /&gt;ubuntu 将PostgreSQL的服务器按集群管理（我是不太想在一台硬件主机上架一个“集群”那么多个PostgreSQL实例，那跑起来能快吗？做开发和学习测试还差不多）。因此执行pg_lsclusters，你会看到一个集群列表。默认是只有一个。用pg_cltcluster管理服务器的时候，你要比pg_ctl多指定一个版本号，一个集群名，就是从这里来的，不知道该指定谁的时候，可以查看pg_lsclusters。&lt;br /&gt;&lt;br /&gt;在apt安装以后，pg实例就自动运行起来了，如果想要手工控制，同样使用pg_ctlcluster。如果想参考一下apt的pg启动配置，推荐看一下/etc/init.d/postgresql-8.3（或者你自己安装的那个版本）脚本。&lt;br /&gt;&lt;br /&gt;默认的数据区不在postgres文档所推荐的/usr/local/pgsql/data，而在/var/lib/postgresql/8.3/main/。而配置文件在/etc/postgresql/8.3/main，到时候不要找不到pg_hba.conf哦：）。&lt;br /&gt;&lt;br /&gt;contrib目录在/usr/share/postgresql/8.3/contrib/，安装插件什么的一定会用到。&lt;br /&gt;&lt;br /&gt;安装bamboo的时候，我有很久找不到pg_conf跑哪里去了，这也是我早先使用pg源码的一个非常非常重要的原因。其实这个命令在postgresql-server-dev-8.3里。万恶吧！我google了好久才找到……&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-4433577403559927626?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/4433577403559927626/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=4433577403559927626' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4433577403559927626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4433577403559927626'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/ubuntu-postgresql.html' title='ubuntu 的 postgreSQL'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-6339930533488342372</id><published>2009-02-02T04:45:00.000-08:00</published><updated>2009-02-02T05:56:09.282-08:00</updated><title type='text'>书店偶历</title><content type='html'>春节生活回顾。&lt;br /&gt;&lt;br /&gt;和太太出門逛街，本來是要去安泰找相機維修店的，結果相機沒修成，半路倆人又溜進了圖書城。存了包，兩個人各投所好，她去裝潢家居，我去IT區。&lt;br /&gt;實體書店的IT柜，我沒見過一個不囧的。CCNA與HTML比鄰，LINUX管理與Windows入門相伴。總算安泰強過閩臺，竟然C專柜沒有見到C#的教程，而且知道VC專列一柜，大大的讓人刮目相看。VC和VC#混在一起，我想就不必強求了。&lt;br /&gt;大概太久不逛實體書店，來到C++柜，居然不見當年那些《STL源碼剖析》等老面孔。雖然確實過了好幾年，但是現在還是覺得好書永不過時。想到現在的年輕人讀不到這些好書，還是為他們有點小遺憾。&lt;br /&gt;又 見到《C++大學教程》，這本書當年買來一直沒有讀進去，最后送給了同事。想想也許邱仲潘老師的譯本確實有些毛病，但是自己心浮氣躁應該還是主要問題。這 次見到已經是第五版了，希望以后買到這本英文版的朋友不會像我當年一樣半途而廢，C++是一門需要花時間學習的語言，如果做不到勤奮二字，再好的教程也沒 有用。&lt;br /&gt;赫然看到一本比《C++大學教程》更厚的《C大學教程》，仔細揉了揉眼睛，發現英文標題真的是跟《C++大學教程》對應來的，真的不是BT 吖。心說《C Programming Language》不過二百多頁。這本大學教程能翻出什么花樣寫出千把頁的內容？了了翻過不禁莞爾，原來這作者把C/C++/Java來個一勺燴，還夾雜 進去一堆GUI編程之類的東西，難怪能如此肥大。想來如果是某些逢中文書必貶，豆瓣收藏都是英文影印書的IT裝13青年，倒是大大的和此書的意境。確實大 家評選垃圾書的時候，多關注的是國內的剪刀黨，或者機器翻譯風格的家伙，原文書普遍來說，還是比較有保障。但是前有21天精通XXXX系列，后又出了這個 《C大學教程》，充分說明了遠道的和尚一樣不少瞎念經的，以后垃圾IT書排版也應該分出不同的類別，比如原創垃圾書、翻譯變寶為廢、洋垃圾等等才是。&lt;br /&gt;本 來也沒什么目的性，就是瞎轉一番，看到一對中年夫妻擠在VC柜討論了半天，聽不清說什么，走進去看看沒有自己喜歡的書，連VC++編程內幕這種當年的基準 資料書都沒有。不知道是我老了還是這家書店太out了。看到一本關于多核程序設計的書，頁數不多，但是內容很實在，簡單質樸，翻了翻，類似性能評估，并行 算法之類的介紹很有一些，而不是簡單的講講API。對比下自己買的那本，頓時覺得虧到了。小小遺憾下，還是恭敬的放回去，希望能遇到識貨的買家。&lt;br /&gt;偶 然間看到了《編程珠璣》，這本書發售當時是大大的火了一番，但是我這人天生別扭，買書偏不喜歡與流行時，眾人說好的往往避之不及。今天看到，想起當年曾有 個家伙說我發表在《程序員》上的某算法講解是抄襲自此書，心血來潮，便拿起來翻翻。沒想到內容居然是大大的喜歡，知識講的簡單清楚，橋段也八的津津有味。 讀到美國人構想從衛星上發射電纜到地面以建立高可靠的通信網絡，甚是心馳神往，六七十年代的美國佬上九天登月，下五洋捉鱉，端的是激情澎湃。越讀越覺得與 此書甚是有緣，便不管安泰不打折，拿了前后兩冊結帳去也。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-6339930533488342372?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/6339930533488342372/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=6339930533488342372' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6339930533488342372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6339930533488342372'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/02/blog-post.html' title='书店偶历'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-1644917053159201824</id><published>2009-01-18T05:34:00.000-08:00</published><updated>2009-01-18T05:35:45.616-08:00</updated><title type='text'>鸡蛋三明治</title><content type='html'>喜欢半夜做宵夜的日子，玉米沙拉，意粉，鸡蛋三明治。&lt;br /&gt;其实鸡蛋三明治很简单，重要是煎蛋时的手感。&lt;br /&gt;&lt;ul&gt;&lt;li&gt;先准备好面包片，摊好。&lt;/li&gt;&lt;li&gt;菜片，生菜叶或其它喜欢的生食菜品，铺好，我喜欢这个时候就涂沙拉酱上去。用两片面包就夹好它。摆在盘上。&lt;/li&gt;&lt;li&gt;升火，火不能太大，要有平底锅，少少油，轻轻的把蛋打下去，慢慢看它成形。等底部煎定型以后视情况铲起来滑一下，免得焦底。出锅不太早，不然会完全流掉，也不能太老，就不好吃了。以最上面的蛋白定型，可包住蛋黄为宜。&lt;/li&gt;&lt;li&gt;把蛋直接摆到面包片上，油多的话可以先控下油再摆。&lt;/li&gt;&lt;li&gt;然后把夹着菜的面包片摆上来。&lt;/li&gt;&lt;li&gt;经验上来说，最好不要直接在蛋上涂沙拉酱，感觉不好吃。&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;好喜欢太太吃到三明治的时候开心的样子，上次的早餐煎得有点糊，太手忙脚乱啦。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-1644917053159201824?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/1644917053159201824/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=1644917053159201824' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1644917053159201824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/1644917053159201824'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/01/blog-post.html' title='鸡蛋三明治'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-3941338703615512539</id><published>2009-01-04T04:57:00.000-08:00</published><updated>2009-01-04T05:13:58.486-08:00</updated><title type='text'></title><content type='html'>&lt;h1 id="Bamboo.2BW4mIxWMHU1c-"&gt;Bamboo 安装指南&lt;/h1&gt; &lt;span class="anchor" id="line-11"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-12"&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;p class="line862"&gt;文档属主: &lt;a href="http://wiki.rdev.kingsoft.net/moin/MarchLiu"&gt;刘鑫&lt;/a&gt; &lt;span class="anchor" id="line-13"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;目标读者: 软件事业部全体员工    &lt;span class="anchor" id="line-14"&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;版本历史:     &lt;span class="anchor" id="line-15"&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;p class="line862"&gt;v0.1 ~ 20090104 &lt;a href="http://wiki.rdev.kingsoft.net/moin/MarchLiu"&gt;刘鑫&lt;/a&gt;创立 &lt;span class="anchor" id="line-16"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-17"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2Bc69Yg5cAbEI-"&gt;环境需求&lt;/h2&gt; &lt;span class="anchor" id="line-18"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-19"&gt;&lt;/span&gt;&lt;p class="line874"&gt;实现本文过程使用的环境如下： :: &lt;span class="anchor" id="line-20"&gt;&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Ubuntu 8.10 &lt;span class="anchor" id="line-21"&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;gcc 4.2 &lt;span class="anchor" id="line-22"&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;g++ 4.1 &lt;span class="anchor" id="line-23"&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;PostgreSQL 8.3.5 &lt;span class="anchor" id="line-24"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-25"&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2BUXNOjgBamboo"&gt;关于 Bamboo&lt;/h2&gt; &lt;span class="anchor" id="line-26"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-27"&gt;&lt;/span&gt;&lt;p class="line867"&gt;&lt;a class="http" href="http://code.google.com/p/nlpbamboo"&gt;Bamboo&lt;/a&gt;项目是一个中文语言分析引擎，开发者是Postgre领域久负盛誉的&lt;a class="http" href="http://www.eeeeworks.org/"&gt;eeee&lt;/a&gt;团队。目前使用者还不算多，但是从&lt;a class="http" href="http://code.google.com/p/nlpbamboo/wiki/Benchmark%EF%BD%9C%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE"&gt;http://code.google.com/p/nlpbamboo/wiki/Benchmark｜测试数据&lt;/a&gt;来看也有着不错的成绩 。它最大的亮点是集成了PostgreSQL接口。 &lt;span class="anchor" id="line-28"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-29"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;Bamboo项目提供DEB、RPM等几种格式的安装程序，不过源码编译也很方便。 &lt;span class="anchor" id="line-30"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-31"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2BU8KAA41EZZk-"&gt;参考资料&lt;/h2&gt; &lt;span class="anchor" id="line-32"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-33"&gt;&lt;/span&gt;&lt;p class="line867"&gt;&lt;a class="http" href="http://code.google.com/p/nlpbamboo/"&gt;Bamboo 项目站点&lt;/a&gt; &lt;span class="anchor" id="line-34"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-35"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;a class="http" href="http://code.google.com/p/nlpbamboo/wiki/GettingStarted"&gt;Bamboo 安装入门&lt;/a&gt; &lt;span class="anchor" id="line-36"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-37"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;a href="http://wiki.rdev.kingsoft.net/moin/PostgreSQLInstall"&gt;PostgreSQL 数据库系统 *NIX 简易安装指南&lt;/a&gt; &lt;span class="anchor" id="line-38"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-39"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;a class="http" href="http://code.google.com/p/nlpbamboo/wiki/TSearch2"&gt;Bamboo 与 Tsearch2 的集成配置&lt;/a&gt; &lt;span class="anchor" id="line-40"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-41"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2Bc69Yg1HGWQc-"&gt;环境准备&lt;/h2&gt; &lt;span class="anchor" id="line-42"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-43"&gt;&lt;/span&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="CRF.2B-.2B-"&gt;CRF++&lt;/h3&gt; &lt;span class="anchor" id="line-44"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-45"&gt;&lt;/span&gt;&lt;p class="line862"&gt;编译Bamboo需要&lt;a class="http" href="http://crfpp.sourceforge.net/"&gt;CRF++&lt;/a&gt;库支持，可以从&lt;a class="http" href="http://sourceforge.net/project/showfiles.php?group_id=192973"&gt;这里下载&lt;/a&gt;。 &lt;span class="anchor" id="line-46"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-47"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;CRF++的安装并不复杂，标准的configure/make： &lt;span class="anchor" id="line-48"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-49"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-50"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-51"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-52"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-53"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;./configure&lt;br /&gt;make&lt;br /&gt;sudo make install&lt;/pre&gt;&lt;span class="anchor" id="line-54"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-55"&gt;&lt;/span&gt;&lt;p class="line874"&gt;但是此时如果编译Bamboo，会提示找不到libcrfpp.so.0，因为默认情况下crf安装到/usr/local，我们可以修改path，也可以configure的时候指明--prefix参数。这里我建立了一个ln： &lt;span class="anchor" id="line-56"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-57"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-58"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-59"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-60"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo ln -s /usr/local/lib/libcrfpp.so.0.0.0 /usr/lib/libcrfpp.so.0&lt;/pre&gt;&lt;span class="anchor" id="line-61"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-62"&gt;&lt;/span&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="CMake"&gt;CMake&lt;/h3&gt; &lt;span class="anchor" id="line-63"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-64"&gt;&lt;/span&gt;&lt;p class="line874"&gt;Bamboo 安装需要cmake支持，在ubuntu下可以用apt安装： &lt;span class="anchor" id="line-65"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-66"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-67"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-68"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-69"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo apt-get install cmake&lt;/pre&gt;&lt;span class="anchor" id="line-70"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-71"&gt;&lt;/span&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2BW4mIxQ-"&gt;安装&lt;/h2&gt; &lt;span class="anchor" id="line-72"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-73"&gt;&lt;/span&gt;&lt;p class="line874"&gt;假设用户位于~/third-party目录下，Bamboo的源码包在~/downloads下： &lt;span class="anchor" id="line-74"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-75"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-76"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-77"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-78"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-79"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-80"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-81"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-82"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-83"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-84"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;tar vxf ~/downloads/bamboo-1.1.0.tar.bz2&lt;br /&gt;cd bamboo-1.1.0&lt;br /&gt;mkdir build&lt;br /&gt;cd build&lt;br /&gt;cmake ..&lt;br /&gt;make&lt;br /&gt;sudo make install&lt;/pre&gt;&lt;span class="anchor" id="line-85"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-86"&gt;&lt;/span&gt;&lt;p class="line862"&gt;默认情况下bamboo安装在/opt/bamboo目录下，此时的bamboo还不能直接使用，因为需要辞典，训练过程很简单，详见&lt;a class="http" href="http://code.google.com/p/nlpbamboo/wiki/GettingStarted"&gt;Bamboo 安装入门&lt;/a&gt;。简单来说可以这样： &lt;span class="anchor" id="line-87"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-88"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-89"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-90"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-91"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo /opt/bamboo/bin/autobuild -t crf_seg&lt;/pre&gt;&lt;span class="anchor" id="line-92"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-93"&gt;&lt;/span&gt;&lt;p class="line874"&gt;这个过程非常漫长，在AMD 2300+ CPU，1G内存，kubuntu 8.10的机器上用时超过了12小时。我没有尝试，但是也许可以直接把编译生成的data和index目录复制过去使用。 &lt;span class="anchor" id="line-94"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-95"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line862"&gt;Bamboo项目现在还支持词性、实体词和主题词分析，详见&lt;a class="http" href="http://code.google.com/p/nlpbamboo/wiki/GettingStarted"&gt;Bamboo 安装入门&lt;/a&gt;。 &lt;span class="anchor" id="line-96"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-97"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="PostgreSQL.2BlsZiEA-"&gt;PostgreSQL 集成&lt;/h3&gt; &lt;span class="anchor" id="line-98"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-99"&gt;&lt;/span&gt;&lt;p class="line862"&gt;要在PostgreSQL中使用全文索引，首先应该安装Tsearch2引擎，详见&lt;a href="http://wiki.rdev.kingsoft.net/moin/PostgreSQLInstall"&gt;PostgreSQL 数据库系统 *NIX 简易安装指南&lt;/a&gt;。 &lt;span class="anchor" id="line-100"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-101"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;配置好PostgreSQL后，进入Bamboo目录安装PostgreSQL支持，在默认设置下，我们需要编辑一下Make命令，将PG_CONFIG 指定为 /usr/local/pgsql/bin/pg_config。执行命令如下： &lt;span class="anchor" id="line-102"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-103"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-104"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-105"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-106"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-107"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;cd /opt/bamboo/exts/postgresql/chinese_parser&lt;br /&gt;make&lt;br /&gt;sudo make install&lt;/pre&gt;&lt;span class="anchor" id="line-108"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-109"&gt;&lt;/span&gt;&lt;p class="line874"&gt;形式上我们需要一个stop word文件，实际上bamboo并不需要它的支持，所以可以在如下位置（默认安装路径）建立如下的空文件： &lt;span class="anchor" id="line-110"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-111"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;/usr/local/pgsql/share/tsearch_data/chinese_utf8.stop &lt;span class="anchor" id="line-112"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-113"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;接下来以超级用户登录到需要支持中文搜索的数据库，执行： &lt;span class="anchor" id="line-114"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-115"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-116"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-117"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-118"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;# \i /usr/local/pgsql/share/contrib/pg_bamboo.sql&lt;br /&gt;# \i /usr/local/pgsql/share/contrib/chinese_parser.sql&lt;/pre&gt;&lt;span class="anchor" id="line-119"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-120"&gt;&lt;/span&gt;&lt;p class="line874"&gt;如果没有错误，就可以执行SQL测试一下分词效果了： &lt;span class="anchor" id="line-121"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-122"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-123"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-124"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;select bamboo('中文分词');&lt;/pre&gt;&lt;span class="anchor" id="line-125"&gt;&lt;/span&gt;&lt;p class="line874"&gt;或 &lt;span class="anchor" id="line-126"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-127"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-128"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;SELECT to_tsvector('chinesecfg', '我爱北京天安门');&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-3941338703615512539?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/3941338703615512539/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=3941338703615512539' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3941338703615512539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/3941338703615512539'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/01/bamboo-v0.html' title=''/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-6871643338504069629</id><published>2009-01-03T06:06:00.000-08:00</published><updated>2009-01-03T06:07:44.490-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PostgreSQL'/><title type='text'>PostgreSQL 数据库系统 *NIX 简易安装指南</title><content type='html'>&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2Bc69Yg5cAbEI-"&gt;环境需求&lt;/h2&gt; &lt;span class="anchor" id="line-18"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-19"&gt;&lt;/span&gt;&lt;p class="line874"&gt;实现本文过程使用的环境如下： :: &lt;span class="anchor" id="line-20"&gt;&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Ubuntu 8.10 &lt;span class="anchor" id="line-21"&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;gcc 4.2 &lt;span class="anchor" id="line-22"&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;g++ 4.1 &lt;span class="anchor" id="line-23"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-24"&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class="line874"&gt;本文涉及的内容同样适用于KUbuntu，其它Linux可能在启动设置上略有差别，编译与安装过程也同样适用于类似的UNIX操作系统。 &lt;span class="anchor" id="line-25"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-26"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2BU8KAA2WHcy4-"&gt;参考文献&lt;/h2&gt; &lt;span class="anchor" id="line-27"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-28"&gt;&lt;/span&gt;&lt;p class="line867"&gt;&lt;a class="http" href="http://www.postgresql.org/docs/8.3/interactive/install-procedure.html"&gt;PostgreSQL 8.3.5 Document : Install Procedure&lt;/a&gt; &lt;span class="anchor" id="line-29"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-30"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2BbOhhD06LmHk-"&gt;注意事项&lt;/h2&gt; &lt;span class="anchor" id="line-31"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-32"&gt;&lt;/span&gt;&lt;p class="line874"&gt;很多Linux发行版的软件库中都集成了 PostgreSQL。例如ubuntu可以通过 &lt;span class="anchor" id="line-33"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-34"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-35"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-36"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-37"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo apt-get install postgresql&lt;/pre&gt;&lt;span class="anchor" id="line-38"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-39"&gt;&lt;/span&gt;&lt;p class="line874"&gt;命令直接安装。但是ubuntu的postgresql做了一些定制，命令行位置、调用格式等都略有差别，对于只有官方文档的学习者，可能自己编译更容易掌握和学习。 &lt;span class="anchor" id="line-40"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-41"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2BW4mIxY.2FHegs-"&gt;安装过程&lt;/h2&gt; &lt;span class="anchor" id="line-42"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-43"&gt;&lt;/span&gt;&lt;p class="line874"&gt;=== 获取代码 ===  &lt;span class="anchor" id="line-44"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-45"&gt;&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p class="line862"&gt;下载 Postgresql 的源码（当前最新版本是 &lt;a class="http" href="http://wwwmaster.postgresql.org/download/mirrors-ftp/source/v8.3.5/postgresql-8.3.5.tar.gz"&gt;8.3.5&lt;/a&gt;）。 &lt;span class="anchor" id="line-46"&gt;&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;解压（这里假设下载至~/downloads） &lt;span class="anchor" id="line-47"&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-48"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-49"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-50"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;tar vxf ~/downloads/postgresql-8.3.5.tar.gz&lt;/pre&gt;&lt;span class="anchor" id="line-51"&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;进入解压后的源码目录  &lt;span class="anchor" id="line-52"&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-53"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-54"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-55"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;cd postgresql-8.3.5&lt;/pre&gt;&lt;span class="anchor" id="line-56"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-57"&gt;&lt;/span&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="A.2BkU1.2Fbk4OfxaL0VuJiMU-"&gt;配置与编译安装&lt;/h3&gt; &lt;span class="anchor" id="line-58"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-59"&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;执行配置命令 &lt;span class="anchor" id="line-60"&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-61"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-62"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-63"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;./configure&lt;/pre&gt;&lt;span class="anchor" id="line-64"&gt;&lt;/span&gt;&lt;p class="line874"&gt;应注意阅读这一步输出的信息，可能你的系统中缺少一些依赖库，可能你想要添加一些其它的设置，如我在这里编译时调用的是： &lt;span class="anchor" id="line-65"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-66"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-67"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-68"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;./configure --with-python --with-perl&lt;/pre&gt;&lt;span class="anchor" id="line-69"&gt;&lt;/span&gt;&lt;p class="line874"&gt;* 编译与安装 &lt;span class="anchor" id="line-70"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-71"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-72"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-73"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-74"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;make&lt;br /&gt;sudo make install&lt;/pre&gt;&lt;span class="anchor" id="line-75"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-76"&gt;&lt;/span&gt;&lt;p class="line874"&gt;这样编译安装后，数据库位于/usr/local/pgsql目录。 &lt;span class="anchor" id="line-77"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-78"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="A.2BYmlcVQ-"&gt;扩展&lt;/h3&gt; &lt;span class="anchor" id="line-79"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-80"&gt;&lt;/span&gt;&lt;p class="line874"&gt;PostgreSQL 的一些扩展功能，例如tsearch2全文检索，默认不会安装。需要手工安装（使用linux发行版的集成版本时，可能需要单独指定其软件包安装）。 &lt;span class="anchor" id="line-81"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-82"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;这个安装过程也很简单，只要在源码的contrib子目录下执行make：  &lt;span class="anchor" id="line-83"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-84"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-85"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-86"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-87"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-88"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;cd contrib&lt;br /&gt;make&lt;br /&gt;sudo make install&lt;/pre&gt;&lt;span class="anchor" id="line-89"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-90"&gt;&lt;/span&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2BkU1.2Fbk4OVC9SqA-"&gt;配置与启动&lt;/h2&gt; &lt;span class="anchor" id="line-91"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-92"&gt;&lt;/span&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="A.2BkU1.2Fblf6ZyxP4WBv-"&gt;配置基本信息&lt;/h3&gt; &lt;span class="anchor" id="line-93"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-94"&gt;&lt;/span&gt;&lt;p class="line874"&gt;首先，PostgreSQL需要一个名为postgres的系统帐户做为默认的超级用户。$pg_path/bin目录下的命令需要以这个用户的身份执行。 &lt;span class="anchor" id="line-95"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-96"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-97"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-98"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo adduser&lt;/pre&gt;&lt;span class="anchor" id="line-99"&gt;&lt;/span&gt;&lt;p class="line874"&gt;（或许你需要用passwd命令给它设定一个密码） &lt;span class="anchor" id="line-100"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-101"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;建立默认的数据存储位置，我遵循文档设定，建在/usr/local/pgsql/data。 &lt;span class="anchor" id="line-102"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-103"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-104"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-105"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo mkdir /usr/local/pgsql/data&lt;/pre&gt;&lt;span class="anchor" id="line-106"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-107"&gt;&lt;/span&gt;&lt;p class="line874"&gt;接下来，要将这个目录的所有权赋给postgres。 &lt;span class="anchor" id="line-108"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-109"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-110"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-111"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo chown postgres /usr/local/pgsql/data&lt;/pre&gt;&lt;span class="anchor" id="line-112"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-113"&gt;&lt;/span&gt;&lt;p class="line874"&gt;然后，初始化数据库目录。这一步需要切换到postgres帐户 &lt;span class="anchor" id="line-114"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-115"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-116"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-117"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-118"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;su - postgres&lt;br /&gt;/usr/local/pgsql/bin/initdb --locale=zh_CN.utf8 --encoding=utf8 /usr/local/postgres/data&lt;/pre&gt;&lt;span class="anchor" id="line-119"&gt;&lt;/span&gt;&lt;p class="line874"&gt;从系统提示来看，ubuntu linux上“--locale=zh_CN.utf8 --encoding=utf8”已经是默认配置了，不需要干涉。 &lt;span class="anchor" id="line-120"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-121"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="A.2BVC9SqE4Oj9CITA-"&gt;启动与运行&lt;/h3&gt; &lt;span class="anchor" id="line-122"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-123"&gt;&lt;/span&gt;&lt;p class="line874"&gt;开发人员可以手工启动PosggreSQL，这也需要在postgres用户下。 &lt;span class="anchor" id="line-124"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-125"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-126"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-127"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-128"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-129"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;su - postgres&lt;br /&gt;/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data start &lt;/pre&gt;&lt;span class="anchor" id="line-130"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-131"&gt;&lt;/span&gt;&lt;p class="line874"&gt;pg_ctl可以控制服务运行，详见其help或PosgreSQL文档。 &lt;span class="anchor" id="line-132"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-133"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line874"&gt;需要以守护进程模式运行在系统后台的话，可以手工配置。在contrib目录下有一个子目录名为start_scripts，分别有freeebsd、linux和osx上的启动脚本示例。这里只介绍linux脚本的使用：:: &lt;span class="anchor" id="line-134"&gt;&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;将contrib/start_scripts/linux文件放到系统的init.d目录： &lt;span class="anchor" id="line-135"&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-136"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-137"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-138"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo cp linux /etc/init.d/postgresql&lt;/pre&gt;&lt;span class="anchor" id="line-139"&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;然后建立六个符号链接，该脚本自己的示例是： &lt;span class="anchor" id="line-140"&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-141"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-142"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-143"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-144"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-145"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-146"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-147"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-148"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo ln -s /etc/init.d/postgresql /etc/rc.d/rc0.d/K02postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc.d/rc1.d/K02postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc.d/rc2.d/K02postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc.d/rc3.d/S98postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc.d/rc4.d/S98postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc.d/rc5.d/S98postgresql&lt;/pre&gt;&lt;span class="anchor" id="line-149"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-150"&gt;&lt;/span&gt;&lt;p class="line874"&gt;然而我用这样的方法在ubuntu上不能启动，经梁庆喜指点，改为如下六个符号链接： &lt;span class="anchor" id="line-151"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-152"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-153"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-154"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-155"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-156"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-157"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-158"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-159"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-160"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-161"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;sudo ln -s /etc/init.d/postgresql /etc/rc3.d/S98postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc6.d/K90postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc0.d/K02postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc2.d/S90postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc1.d/S90postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc4.d/S98postgresql&lt;br /&gt;sudo ln -s /etc/init.d/postgresql /etc/rc5.d/S98postgresql&lt;/pre&gt;&lt;span class="anchor" id="line-162"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-163"&gt;&lt;/span&gt;&lt;p class="line874"&gt;重启即可。 &lt;span class="anchor" id="line-164"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-165"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="A.2BbUuL1Q-"&gt;测试&lt;/h3&gt; &lt;span class="anchor" id="line-166"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-167"&gt;&lt;/span&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h4 id="A.2BdntfVVIw-postgres"&gt;登录到postgres&lt;/h4&gt; &lt;span class="anchor" id="line-168"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-169"&gt;&lt;/span&gt;&lt;p class="line874"&gt;先确认服务器处于运行状态。然后执行：  &lt;span class="anchor" id="line-170"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-171"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-172"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-173"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-174"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-175"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;su - postgres&lt;br /&gt;/usr/local/pgsql/bin/psql postgres&lt;/pre&gt;&lt;span class="anchor" id="line-176"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-177"&gt;&lt;/span&gt;&lt;p class="line874"&gt;在此控制台环境下可以查看可用的命名（\?），可以查看SQL语法帮助（\help），可以查看环境信息（\SHOW ALL）。详请可以执行\?查看。 &lt;span class="anchor" id="line-178"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-179"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h4 id="A.2BUhte.2BmWwdShiNw-"&gt;创建新用户&lt;/h4&gt; &lt;span class="anchor" id="line-180"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-181"&gt;&lt;/span&gt;&lt;p class="line874"&gt;切换到postgres用户下执行创建命令： &lt;span class="anchor" id="line-182"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-183"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-184"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-185"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-186"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-187"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;su - postgres&lt;br /&gt;/usr/local/pgsql/bin/createuser march&lt;/pre&gt;&lt;span class="anchor" id="line-188"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-189"&gt;&lt;/span&gt;&lt;p class="line874"&gt;这样会将我系统中的 march 用户与之关联起来。 &lt;span class="anchor" id="line-190"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-191"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h4 id="A.2BUhte.2BmVwY25ekw-"&gt;创建数据库&lt;/h4&gt; &lt;span class="anchor" id="line-192"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-193"&gt;&lt;/span&gt;&lt;p class="line874"&gt;切换到postgres用户下执行创建命令： &lt;span class="anchor" id="line-194"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-195"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-196"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-197"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-198"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-199"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;su - postgres&lt;br /&gt;/usr/local/pgsql/bin/createdb -O march UniSearch&lt;/pre&gt;&lt;span class="anchor" id="line-200"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-201"&gt;&lt;/span&gt;&lt;p class="line874"&gt;-O 参数指定这个数据库的所有者为march。 &lt;span class="anchor" id="line-202"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-203"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="A.2BT.2BFO.2B2OIZ0M-"&gt;信任授权&lt;/h3&gt; &lt;span class="anchor" id="line-204"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-205"&gt;&lt;/span&gt;&lt;p class="line874"&gt;如果从网络无法访问数据库，提示连接被拒绝，可能是权限问题，此时打开数据库目录下的pg_hba.conf，找到类似如下格式的文本（通常在最后）: &lt;span class="anchor" id="line-206"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-207"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-208"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-209"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;# IPv4 local connections:&lt;br /&gt;host    all         all         127.0.0.1/32          trust&lt;/pre&gt;&lt;span class="anchor" id="line-210"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-211"&gt;&lt;/span&gt;&lt;p class="line874"&gt;将需要信任的IP加入，设为trust。或按照注释文档中的说明进行配置。 &lt;span class="anchor" id="line-212"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-213"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h2 id="A.2BlkRfVQ-"&gt;附录&lt;/h2&gt; &lt;span class="anchor" id="line-214"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-215"&gt;&lt;/span&gt;&lt;p class="line867"&gt; &lt;/p&gt;&lt;h3 id="INSTALL.2BZYdoY04tdoQShortVersion.2BTgCCgg-"&gt;INSTALL 文档中的 Short Version 一节&lt;/h3&gt; &lt;span class="anchor" id="line-216"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-217"&gt;&lt;/span&gt;&lt;p class="line867"&gt;&lt;span class="anchor" id="line-218"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-219"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-220"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-221"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-222"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-223"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-224"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-225"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-226"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-227"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-228"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-229"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-230"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-231"&gt;&lt;/span&gt;&lt;/p&gt;&lt;pre&gt;./configure&lt;br /&gt;gmake&lt;br /&gt;su&lt;br /&gt;gmake install&lt;br /&gt;adduser postgres&lt;br /&gt;mkdir /usr/local/pgsql/data&lt;br /&gt;chown postgres /usr/local/pgsql/data&lt;br /&gt;su - postgres&lt;br /&gt;/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data&lt;br /&gt;/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data &gt;logfile 2&gt;&amp;amp;1 &amp;amp;&lt;br /&gt;/usr/local/pgsql/bin/createdb test&lt;br /&gt;/usr/local/pgsql/bin/psql test&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-6871643338504069629?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/6871643338504069629/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=6871643338504069629' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6871643338504069629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/6871643338504069629'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2009/01/postgresql-nix.html' title='PostgreSQL 数据库系统 *NIX 简易安装指南'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-29871570984692733</id><published>2008-12-19T08:00:00.000-08:00</published><updated>2008-12-19T08:10:09.460-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scheme'/><category scheme='http://www.blogger.com/atom/ns#' term='Haskell'/><title type='text'>Write Yourself a Scheme in 48 Hours/Parsing（四）</title><content type='html'>&lt;p class="first"&gt;上一次，我们给出了原子（Atom）的解析器，其中已经把逻辑类型（#t/#f）的解析功能一并做进去了。&lt;/p&gt;  &lt;p&gt;接下来是解析数值的组件：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/553/"&gt;Haskell语言: Parsing 代码片段九&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseNumber&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseNumber&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; liftM (&lt;span style="color: rgb(34, 139, 34);"&gt;Number&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;.&lt;/span&gt; read) &lt;span style="color: rgb(184, 134, 11);"&gt;$&lt;/span&gt; many1 digit&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;这一段不难理解，主要是 $ 和 . 两个函数的运用。 many1 解析器匹配一个或多个参数，所以我们可以用来匹配一个或多个数字字符（0-9）。不过我们需要返回的是一个LispVal数值，所以还要把解析到的字符串 再转为数字。于是，我们用 . 组合 Number和read。它先将传入的参数作用于右值，再将得到的结果作用于左值。&lt;/p&gt;  &lt;p&gt;many1返回的值实际上是 Parser String，而我们需要一个 String，还要返回一个 Parser LispVal。所以 Number . read 读不了它。liftM 起到析值的作用。也就是说，我们将many1 digit解析得到的 Parser String，传递给 $ （它等效于一对括号）的左值， liftM (Number . read)。&lt;/p&gt;  &lt;p&gt;为了使用这个 Monad ，我们需要导入 Monad 模块：&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Monad&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;这种风格的编程－－重度依赖函数组合、应用、传递－－在 Haskell 代码中非常普遍。它通常可以使你在一行中写出非常复杂的算法。有时候你可能需要从右向左读Haskell代码（想想我们学过的那些数学公式）。&lt;/p&gt;  &lt;p&gt;Barolo 号称意大利国酒－－要知道正是古罗马的军团将亚平宁半岛的葡萄酒文化带到了法国和整个地中海沿岸，至今意大利仍是世界第一大葡萄酒生产国－－但它的强劲却 令很多初尝葡萄酒的人退避三舍。很多初学者比较难接受Haskell浓烈粹的FP风格。我个人也仍处于努力学习的阶段。但是，也正是这种风格，是它的魅力 所在。&lt;/p&gt;  &lt;p&gt;现在，我们可以编写一个 parserExpr 接受字符串、数值和原子语素了：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/558/"&gt;Haskell语言: Parsing 代码片段十&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseExpr&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseExpr&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; parseAtom&lt;br /&gt;       &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; parseString&lt;br /&gt;       &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; parseNumber&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;然后修改 readExpr 以使其调用新的 parser：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/559/"&gt;Haskell语言: Parsing 代码片段十一&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; input &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;case&lt;/span&gt; parse parseExpr &lt;span style="color: rgb(188, 143, 143);"&gt;"lisp"&lt;/span&gt; input &lt;span style="color: rgb(160, 32, 240);"&gt;of&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: rgb(34, 139, 34);"&gt;Left&lt;/span&gt; err &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"No match: "&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;++&lt;/span&gt; show err&lt;br /&gt;   &lt;span style="color: rgb(34, 139, 34);"&gt;Right&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;_&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"Found value"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;最后，我要说的是，其实在这一章，我将代码文件分成了三份：BuildIn.hs，Parser.hs，Parsers.hs。其中 Parser.hs是主程序，其它两个文件定义为两个模块。但是多文件编译的话，学原文上的方法并不成功，可能是ubuntu默认的shell和 debian不同，不过更大的可能是高版本的ghc编译器参数变了，总之，我编译的时候用的是：&lt;/p&gt;  &lt;src lang="sh"&gt;  &lt;p&gt;为了阅读方便，我完整给出三个文件的代码。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/560/"&gt;BuildIn.hs:&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;module&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;BuildIn&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;where&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;data&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Atom&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;List&lt;/span&gt; [&lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;]&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;DottedList&lt;/span&gt; [&lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;] &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Number&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Integer&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Bool&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Bool&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;   &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/561/"&gt;Parsers.hs：&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;module&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parsers&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;where&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Monad&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Text.ParserCombinators.Parsec&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;hiding&lt;/span&gt; (spaces)&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;BuildIn&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;symbol&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Char&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;symbol&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; oneOf &lt;span style="color: rgb(188, 143, 143);"&gt;"!#$%&amp;amp;|*+-/:&lt;=&gt;?@^_~"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;spaces&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;spaces&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; skipMany1 space&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; input &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;case&lt;/span&gt; parse parseExpr &lt;span style="color: rgb(188, 143, 143);"&gt;"lisp"&lt;/span&gt; input &lt;span style="color: rgb(160, 32, 240);"&gt;of&lt;/span&gt;&lt;br /&gt;                  &lt;span style="color: rgb(34, 139, 34);"&gt;Left&lt;/span&gt; err &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"No match: "&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;++&lt;/span&gt; show err&lt;br /&gt;                  &lt;span style="color: rgb(34, 139, 34);"&gt;Right&lt;/span&gt; val &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"Found value"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseString&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseString&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;do&lt;/span&gt; char &lt;span style="color: rgb(188, 143, 143);"&gt;'"'&lt;/span&gt;&lt;br /&gt;                x &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;-&lt;/span&gt; many (noneOf &lt;span style="color: rgb(188, 143, 143);"&gt;"\""&lt;/span&gt;)&lt;br /&gt;                char &lt;span style="color: rgb(188, 143, 143);"&gt;'"'&lt;/span&gt;&lt;br /&gt;                return &lt;span style="color: rgb(184, 134, 11);"&gt;$&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt; x&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseAtom&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseAtom&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;do&lt;/span&gt; first &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;-&lt;/span&gt; letter &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; symbol&lt;br /&gt;              rest &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;-&lt;/span&gt; many (letter &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; digit &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; symbol)&lt;br /&gt;              &lt;span style="color: rgb(160, 32, 240);"&gt;let&lt;/span&gt; atom &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; first&lt;span style="color: rgb(34, 139, 34);"&gt;:&lt;/span&gt;rest&lt;br /&gt;              return &lt;span style="color: rgb(184, 134, 11);"&gt;$&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;case&lt;/span&gt; atom &lt;span style="color: rgb(160, 32, 240);"&gt;of&lt;/span&gt;&lt;br /&gt;                         &lt;span style="color: rgb(188, 143, 143);"&gt;"#t"&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Bool&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;True&lt;/span&gt;&lt;br /&gt;                         &lt;span style="color: rgb(188, 143, 143);"&gt;"#f"&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Bool&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;False&lt;/span&gt;&lt;br /&gt;                         otherwise &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Atom&lt;/span&gt; atom&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseNumber&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseNumber&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; liftM (&lt;span style="color: rgb(34, 139, 34);"&gt;Number&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;.&lt;/span&gt; read) &lt;span style="color: rgb(184, 134, 11);"&gt;$&lt;/span&gt; many1 digit&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseExpr&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseExpr&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; parseAtom&lt;br /&gt;           &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; parseString&lt;br /&gt;           &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; parseNumber&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/562/"&gt;Parser.hs&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;module&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Main&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;where&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;System.Environment&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parsers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;main&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;IO&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;main&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;do&lt;/span&gt; args &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;-&lt;/span&gt; getArgs&lt;br /&gt;         putStrLn (readExpr (args &lt;span style="color: rgb(184, 134, 11);"&gt;!!&lt;/span&gt; 0))&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-29871570984692733?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/29871570984692733/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=29871570984692733' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/29871570984692733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/29871570984692733'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2008/12/write-yourself-scheme-in-48_19.html' title='Write Yourself a Scheme in 48 Hours/Parsing（四）'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-4247899413433907739</id><published>2008-12-07T04:33:00.000-08:00</published><updated>2008-12-17T20:08:48.425-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scheme'/><category scheme='http://www.blogger.com/atom/ns#' term='Haskell'/><title type='text'>Write Yourself a Scheme in 48 Hours/Parsing（三）</title><content type='html'>&lt;p&gt;上一节的时候我们讨论到了用附加的解释器组件扩展功能的方法。这一次我们再加些料。 首先，我们定义出Scheme语言中的一些类型：&lt;/p&gt;   &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/533/"&gt;Haskell语言: Parsing 代码片段六&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;data&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Atom&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;List&lt;/span&gt; [&lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;]&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;DottedList&lt;/span&gt; [&lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;] &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Number&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Integer&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt;&lt;br /&gt;            &lt;span style="color: rgb(184, 134, 11);"&gt;|&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Bool&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Bool&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;事实上，Haskell里一切都可以看作是函数，data也不例外。千万不要拿这个东西去随便对应你以前见过的其它叫data的东西。事实上 Haskell里的Type、Class、Instance和Data跟OO语言里的概念都完全不是一回事儿。如果你有学过近世代数，可以去看看上节里推 荐的T1的那篇Monad教程。&lt;/p&gt;  &lt;p&gt;这里，使用data，我们定义了 LispVal 可能的几种类型：&lt;/p&gt;  &lt;ul&gt;&lt;li&gt;Atom 是一个文本，它表示一个原子命名&lt;/li&gt;&lt;li&gt;若干 LispVal 的序列成为一个 List （注意 List 也是一种 data Lispval，所以这是一个递归的定义）&lt;/li&gt;&lt;li&gt;. 联接列表和一个 LispVal 值，组成一个 DottedList&lt;/li&gt;&lt;li&gt;Number 存储整数&lt;/li&gt;&lt;li&gt;String 存储字符串&lt;/li&gt;&lt;li&gt;Bool 存储逻辑值&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;因为Haskell的类型和构造器取自不同的命名空间，所以这里我们定义了与系统类型相同的String、Bool之类的类型，也不会靠成什么问题。类型和构造器都是PASCAL命名。&lt;/p&gt;  &lt;p&gt;现在编写几个解释器函数。首先是字符串。字符串是一对双引号标记，包含若干文。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/534/"&gt;Haskell语言: Parsing 代码片段七&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseString&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseString&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;do&lt;/span&gt; char &lt;span style="color: rgb(188, 143, 143);"&gt;'"'&lt;/span&gt;&lt;br /&gt;                x &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;-&lt;/span&gt; many (noneOf &lt;span style="color: rgb(188, 143, 143);"&gt;"\""&lt;/span&gt;)&lt;br /&gt;                char &lt;span style="color: rgb(188, 143, 143);"&gt;'"'&lt;/span&gt;&lt;br /&gt;                return &lt;span style="color: rgb(184, 134, 11);"&gt;$&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt; x&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;这儿又出来一新的妖招：我们没用 &gt;&gt;，而是用了一个do。这是为了可以取到引号之间的值，这里我们用了char和many两个解析工具。按作者的解释，通常不需要取得 action返回值的时候（比如为了组合它们生成新的monad），使用&gt;&gt;，而需要取值并用于下一个action的时候，用 &gt;&gt;= 或 do-notation。&lt;/p&gt;   &lt;p&gt;取值完成以后，我们将其 return 为一个 LispVal 。抽象数据类型中的每个构造器也同样可以看作是一个函数：返回一个该类型的值。函数进行参数的式匹配的时候，也可以根据data来匹配。&lt;/p&gt;   &lt;p&gt;内置函数 return 可以把我们的 LispVal 提升为一个Parser monad。每一行do代码虽然都要求是同一个类型，但是但是我们的字符串解析函数只是返回了一个 LispVal，这时候就靠 return 帮我们搞定这个类型封装啦。这样，整个 parseString action 就成为了一个 Parser LispVal。&lt;/p&gt;   &lt;p&gt;$只是括号的简写 return $ String x 等同于 return (String x)，这个在Haskell的语法教程中都会有介绍。不过这里有特别提出，$是一个操作符，所以你能对一个函数做什么，就能对它做什么，传递、局部化等 等。在这里，它相当于一个 apply。&lt;/p&gt;  &lt;p&gt;Atom 就是一个原子语素，一个字母或符号，跟随若干数值或字母、符号之类的：&lt;/p&gt;   &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/537/"&gt;Haskell语言: Parsing 代码片段八&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseAtom&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;LispVal&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;parseAtom&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;do&lt;/span&gt; first &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;-&lt;/span&gt; letter &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; symbol&lt;br /&gt;              rest &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;-&lt;/span&gt; many (letter &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; digit &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;|&gt;&lt;/span&gt; symbol)&lt;br /&gt;              &lt;span style="color: rgb(160, 32, 240);"&gt;let&lt;/span&gt; atom &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; first&lt;span style="color: rgb(34, 139, 34);"&gt;:&lt;/span&gt;rest&lt;br /&gt;              return &lt;span style="color: rgb(184, 134, 11);"&gt;$&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;case&lt;/span&gt; atom &lt;span style="color: rgb(160, 32, 240);"&gt;of&lt;/span&gt;&lt;br /&gt;                         &lt;span style="color: rgb(188, 143, 143);"&gt;"#t"&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Bool&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;True&lt;/span&gt;&lt;br /&gt;                         &lt;span style="color: rgb(188, 143, 143);"&gt;"#f"&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Bool&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;False&lt;/span&gt;&lt;br /&gt;                         otherwise &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Atom&lt;/span&gt; atom&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;这里出现了一个新的 Pasec combinator，选择运算符 &lt;|&gt;。它尝试第一个parser，失败就尝试第二个。哪个成功就返回哪个parser的值。&lt;/p&gt;  &lt;p&gt;读取语素的第一个字符以及其余部分后，我们需要把它们合在一起。let语法定义了一个atom变量，我们使用联接符:把它们联起来。如果不用:，还可以用 [first] ++ rest。&lt;/p&gt;  &lt;p&gt;case是基本语句，语法书都讲得很明白，这里不多讨论了。&lt;/p&gt;  &lt;p&gt;发芽网挂了……明天再继续吧……&lt;/p&gt;  &lt;p&gt;有一件挺让人无语的事儿就是你费事儿排的版它总不给显示，在Blogger的编辑器上编辑源码真不是人干的事儿，现在我改用Muse了……不过还是比较麻烦的……有些代码我想还是放到发芽上比较方便。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-4247899413433907739?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/4247899413433907739/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=4247899413433907739' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4247899413433907739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/4247899413433907739'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2008/12/write-yourself-scheme-in-48_07.html' title='Write Yourself a Scheme in 48 Hours/Parsing（三）'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-9101059731713069155</id><published>2008-12-06T22:39:00.001-08:00</published><updated>2008-12-17T20:00:15.817-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scheme'/><category scheme='http://www.blogger.com/atom/ns#' term='Haskell'/><title type='text'>Write Yourself a Scheme in 48 Hours/Parsing（二）</title><content type='html'>&lt;p&gt;囧然发现，代码的排版总是会丢失缩近。这个当然不能怪发芽网的服务不好，因为直接可以看到的是blogger的有些模板就有问题。这东西的可视化编辑器又慢，功能又不全。&lt;/p&gt;  &lt;p&gt;我现在用gmail的内置编辑器，不过这东西也不能算好用，它没办法编辑HTML源码。网上有人贴过muse向blogger发布的代码。等这个坑填完我肯定是要再去弄muse的，到时候不妨拿来看看（话说，muse真是个好东西吖）。&lt;/p&gt;  &lt;p&gt;上次讲到了如何利用 oneOf 函数提取代码中的符号。因为我们没有处理空格，如果符号前有多的空格，就会出错了。&lt;/p&gt;  &lt;p&gt;作者的解释是，Parsec的spaces不符合他的要求，虽然另有一个lexeme可用，但是这里我们可以自己定做一个。&lt;/p&gt;  &lt;p&gt;Haskell语言: Parsing 代码片段四&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;spaces&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;spaces&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; skipMany1 space&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;这东西其实比我猜想的简单多了，还是直接拿了一个东西来用，跟oneOf换汤不换药嘛。&lt;/p&gt;  &lt;p&gt;现在我们修改一下readExpr，把这个新组件串进去。&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; input &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;case&lt;/span&gt; parse (spaces &lt;span style="color: rgb(184, 134, 11);"&gt;&gt;&gt;&lt;/span&gt; symbol) &lt;span style="color: rgb(188, 143, 143);"&gt;"lisp"&lt;/span&gt; input &lt;span style="color: rgb(160, 32, 240);"&gt;of&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: rgb(34, 139, 34);"&gt;Left&lt;/span&gt; err &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"No match: "&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;++&lt;/span&gt; show err&lt;br /&gt;    &lt;span style="color: rgb(34, 139, 34);"&gt;Right&lt;/span&gt; val &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"Found value"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;这里我没用发芽网的服务，主要是给大家看到红字标明的修改标记。如果没有心理准备，大概会被这个简单的用法小小震惊一下。&gt;&gt;符号是 Monad 的四个标准运算符之一，"bind"操作符。通过它，我们把skipMany1 space传入symbol中，组成一个新的parser Monad。这里作者特别有介绍，不同的Monad，它的bind行为可能完全不同。具体的情况一定要阅读文档来确定。在Parser Monad中，它就是尝试匹配第一个组件，不行再匹配第二个，直至匹配成功或最终返回错误。&lt;/p&gt;  &lt;p&gt;Monad一个非常强大的地方就在于这种组合能力，这与OO中的继承或接口复用还是有很大不同的。等这个教程跟完，如果我觉得我对Monad有些了 解了，可能会写一些关于Monad的文章。其实关于Monad，国内已经有一篇非常强大的文章，来自Javaeye社区的Trustno1。简单介绍一下 背景，这大佬多年以前在程序员上以"恶魔吹着笛子来"为署名写了一系列Python教程，这几篇文章将我和很多程序员引入了Python领域。至于 Lee……不需要我多介绍了吧：）。&lt;/p&gt;  &lt;p&gt;这一小节的完整代码：&lt;/p&gt;  &lt;p&gt;Haskell语言: Parsing第二部分的完整代码&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;module&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Main&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;where&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;System.Environment&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Text.ParserCombinators.Parsec&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;hiding&lt;/span&gt; (spaces)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;symbol&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Char&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;symbol&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; oneOf &lt;span style="color: rgb(188, 143, 143);"&gt;"!#$%&amp;amp;|*+-/:&lt;=&gt;?@^_~"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;spaces&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;spaces&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; skipMany1 space&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; input &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;case&lt;/span&gt; parse (spaces &lt;span style="color: rgb(184, 134, 11);"&gt;&gt;&gt;&lt;/span&gt; symbol) &lt;span style="color: rgb(188, 143, 143);"&gt;"lisp"&lt;/span&gt; input &lt;span style="color: rgb(160, 32, 240);"&gt;of&lt;/span&gt;&lt;br /&gt;                  &lt;span style="color: rgb(34, 139, 34);"&gt;Left&lt;/span&gt; err &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"No match: "&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;++&lt;/span&gt; show err&lt;br /&gt;                  &lt;span style="color: rgb(34, 139, 34);"&gt;Right&lt;/span&gt; val &lt;span style="color: rgb(184, 134, 11);"&gt;-&gt;&lt;/span&gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"Found value"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;main&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;IO&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;main&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;do&lt;/span&gt; args &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;-&lt;/span&gt; getArgs&lt;br /&gt;         putStrLn (readExpr (args &lt;span style="color: rgb(184, 134, 11);"&gt;!!&lt;/span&gt; 0)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-9101059731713069155?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/9101059731713069155/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=9101059731713069155' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/9101059731713069155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/9101059731713069155'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2008/12/write-yourself-scheme-in-48_06.html' title='Write Yourself a Scheme in 48 Hours/Parsing（二）'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-8415195839513607327</id><published>2008-12-06T05:01:00.001-08:00</published><updated>2008-12-17T19:08:37.001-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scheme'/><category scheme='http://www.blogger.com/atom/ns#' term='Haskell'/><title type='text'>Write Yourself a Scheme in 48 Hours/Parsing (一)</title><content type='html'>&lt;p class="first"&gt;前一章其实标题本来是"编译与运行"。但是这两步反而难度不高，看了教程就很容易能明白，所以我没有讨论它。 上一章里重点介绍了开发工具，因为Haskell的语法决定，我们需要一个顺手的工具。 还重点介绍了Monad，主要是从我等下里巴人的角度解释一下怎么用它。 对于一个完全的新手，其实还应该学习一些关于Haskell基本数据结构和函数定义的知识，不过这方面的东西最好找专门的Haskell语言教程。&lt;/p&gt;  &lt;p&gt;这一章，会继续练习Monad的使用。我们还会见到一些来自GHC标准库的强大武器。&lt;/p&gt;  &lt;p&gt;这次我们 import 进来一个新的库&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Text.ParserCombinators.Parsec&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;hiding&lt;/span&gt; (spaces)&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;Parsec库是Haskell中专门的解释器工具库。hiding关键字指出，我们导入这个库时，把 spaces 函数排除在外－－因为我们随后要自己实现一个不同的逻辑。&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/524/"&gt;Haskell语言: Parsing 代码片段二&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;symbol&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Char&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;symbol&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; oneOf &lt;span style="color: rgb(188, 143, 143);"&gt;"!#$%&amp;amp;|*+-/:&amp;lt;=&amp;gt;?@^_~"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;   &lt;p&gt;上面是类型声明，也就是说给oneOf函数传这一串符号进去，它返回一个解释器类型。其接收代码文本，针对oneOf传入的符号串进行处理。类似oneOf的这些解释器Monad生成工具，在Parsec库里还有一些其它的解析工具，后面我们会用到一些。&lt;/p&gt;  &lt;p&gt;OK，这个解释器（其实是个解释器零件）我们怎么使用它呢？现在我们写一个解析表达式的工具：&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/527/"&gt;Haskell语言: Parsing 代码片段三&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&amp;amp;&lt;/span&gt;gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; input &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;case&lt;/span&gt; parse symbol &lt;span style="color: rgb(188, 143, 143);"&gt;"lisp"&lt;/span&gt; input &lt;span style="color: rgb(160, 32, 240);"&gt;of&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: rgb(34, 139, 34);"&gt;Left&lt;/span&gt; err &lt;span style="color: rgb(184, 134, 11);"&gt;-&amp;amp;&lt;/span&gt;gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"No match: "&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;++&lt;/span&gt; show err&lt;br /&gt;   &lt;span style="color: rgb(34, 139, 34);"&gt;Right&lt;/span&gt; val &lt;span style="color: rgb(184, 134, 11);"&gt;-&amp;amp;&lt;/span&gt;gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"Found value"&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;这个用法很简单，利用 Parsec 库的 parse，我们把定义好的 symbol 传给 parse （"lisp" 作为注册进来的 symbol 的命名），然后接收一个字符串，parse会返回一个 data ，其中 Left 是表示错误信息，err中有具体内容，Right val则是匹配正确后的结果，这里我们先不管它返回了什么，输出一个"Found value"。&lt;/p&gt;  &lt;p&gt;这段程序可以看出，symbol 的作用比较简单，只要字符串里有任一个匹配oneOf的字符，解析器会把它做为一个词素提出来。而parse就处理这个解析结果。&lt;/p&gt;  &lt;p&gt;这样，很简单的接收命令行参数然后传出解析结果。!!是haskell的列表索引操作符。也就是说，它只处理args的第一个元素。 编译以后执行试试吧，你会看到，它校验你输入的文本中，第一个字符是否在symbol注册的符号中。并返回相应的值。 解释器还很简单，但是后面我们会慢慢完善它。&lt;/p&gt;  &lt;p&gt;这一章的重点，在于学会如何组合多个函数或Monad。具体的原理和定义，推荐一份已经被汉化出来的教程：函数式编程另类指南，其中的currying和Continuations知识，与我们在这里使用的组合技术相关。&lt;/p&gt;  &lt;p&gt;事实上，如果那东西把你搞糊涂了，倒不如当那些名词和定理不存在，我们继续写程序吧XD。&lt;/p&gt;  &lt;p&gt;今天的代码：&lt;/p&gt;   &lt;p&gt;&lt;a href="http://www.fayaa.com/code/view/528/"&gt;Haskell语言: Parsing的完整代码&lt;/a&gt;&lt;/p&gt;  &lt;pre class="src"&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;module&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Main&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;where&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;System.Environment&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Text.ParserCombinators.Parsec&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;hiding&lt;/span&gt; (spaces)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;symbol&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Parser&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Char&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;symbol&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; oneOf &lt;span style="color: rgb(188, 143, 143);"&gt;"!#$%&amp;amp;|*+-/:&amp;lt;=&amp;gt;?@^_~"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;-&amp;amp;&lt;/span&gt;gt; &lt;span style="color: rgb(34, 139, 34);"&gt;String&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;readExpr&lt;/span&gt; input &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;case&lt;/span&gt; parse symbol &lt;span style="color: rgb(188, 143, 143);"&gt;"lisp"&lt;/span&gt; input &lt;span style="color: rgb(160, 32, 240);"&gt;of&lt;/span&gt;&lt;br /&gt;                  &lt;span style="color: rgb(34, 139, 34);"&gt;Left&lt;/span&gt; err &lt;span style="color: rgb(184, 134, 11);"&gt;-&amp;amp;&lt;/span&gt;gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"No match: "&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;++&lt;/span&gt; show err&lt;br /&gt;                  &lt;span style="color: rgb(34, 139, 34);"&gt;Right&lt;/span&gt; val &lt;span style="color: rgb(184, 134, 11);"&gt;-&amp;amp;&lt;/span&gt;gt; &lt;span style="color: rgb(188, 143, 143);"&gt;"Found value"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;main&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;IO&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;main&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;do&lt;/span&gt; args &lt;span style="color: rgb(184, 134, 11);"&gt;&amp;amp;&lt;/span&gt;lt;&lt;span style="color: rgb(184, 134, 11);"&gt;-&lt;/span&gt; getArgs&lt;br /&gt;         putStrLn (readExpr (args &lt;span style="color: rgb(184, 134, 11);"&gt;!!&lt;/span&gt; 0))&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-8415195839513607327?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/8415195839513607327/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=8415195839513607327' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8415195839513607327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8415195839513607327'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2008/12/write-yourself-scheme-in-48.html' title='Write Yourself a Scheme in 48 Hours/Parsing (一)'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-379325813964935216</id><published>2008-12-06T03:58:00.000-08:00</published><updated>2008-12-06T04:01:06.061-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='志趣'/><title type='text'>柚子！</title><content type='html'>今天在超市看到一个小朋友，大概也就一岁多两岁。爷爷买了两个大柚子，他坐在车里抱着柚子玩。到收银台爷爷把柚子拿给收银员，小朋友哇的一声就哭了，一边哭还一边往收银台上爬，想把柚子抢回来。收银员赶紧刷完把柚子还给他。柚子一抱到怀里，小家伙立刻就安静了，脸上还挂着泪珠。安安静静的坐在购物车里。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-379325813964935216?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/379325813964935216/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=379325813964935216' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/379325813964935216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/379325813964935216'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2008/12/blog-post.html' title='柚子！'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-5115924755006200217</id><published>2008-12-02T06:48:00.000-08:00</published><updated>2008-12-17T18:49:00.216-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scheme'/><category scheme='http://www.blogger.com/atom/ns#' term='Haskell'/><title type='text'>Write Yourself a Scheme in 48 Hours/First Steps</title><content type='html'>&lt;p class="first"&gt;本章原文地址： &lt;a href="http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/First_Steps"&gt;http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/First_Steps&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;第一章的内容不多，准备知识而已。这一章的名字就叫“First Step”。起点也很朴实：开发环境的准备。&lt;/p&gt;  &lt;p&gt;前面说过，这份例子是以最主流的Haskell编译器ghc为工具的。当然，ghc本身有一个还不错的shell环境，ghci。不过我们是要跟着写代码，做项目的，不能关机就扔，所以得要有个正经的源码编辑器。&lt;/p&gt;  &lt;p&gt;作者推荐Emacs，如果你在Windows上，可以试试&lt;a href="http://ntemacs.sourceforge.net/"&gt;ntemacs&lt;/a&gt;。Emacs有个相当中规中矩的haskell-mode。里面的功能说实话我还没好好挖掘过，不过值得向大家推荐一下，默认的设置就算挺好用了。&lt;/p&gt;  &lt;p&gt;如果你用不惯Emacs，其实VIM也不错，我本人是Emacs党，不过有回也试了试用VIM写Haskell代码，对于我这类初级用户，感觉没太 大差别。本书作者认为，hskell不是一种用记事本能搞定的语言。确实，虽然它只是纯文本，但是数学化的灵魂还在（还记得数学里那些鬼画符一样的表达式 吧），如果记事本，缩进格式足够把人搞疯的。不需要什么很伟大的IDE，有个靠谱的文本编辑器还是有必要的。&lt;/p&gt;  &lt;p&gt;其实作者也有交待，你要是真用不惯这些 “非”主流编辑器，Eclipse有个 &lt;a href="http://eclipsefp.sourceforge.net/haskell/"&gt;Fucntion Programming&lt;/a&gt;，联想到Eclipse连支持个XML都JJYY，这个Haskell插件竟然已经到了0.9.x，haskell这个圈子水真深啊……）。甚至还有个 &lt;a href="file:///var/www/knowledge/Haskell%20plugin%20for%20Visual%20Studio"&gt;http://www.haskell.org/visualhaskell/&lt;/a&gt; ，看截图应该是VS6的，不过这种小众语言能支持这玩意儿已经很雷人了）。&lt;/p&gt;  &lt;p&gt;另外，我相信一个另小众的编辑器：&lt;a href="http://www.haskell.org/haskellwiki/Yi"&gt;yi&lt;/a&gt;肯定是支持Haskell的。因为，它是Haskell写成的！这东西我没用过，不过谣言频道有人刷屏说这是一款向 Emacs 致敬的作品。从截图看真的有点像Emacs。&lt;/p&gt;  &lt;p&gt;本章的示例代码不多，写个简单的入门例程：&lt;/p&gt;    &lt;pre class="src"&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;module&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;Main&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;where&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(160, 32, 240);"&gt;import&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;System.Environment&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;main&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;::&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;IO&lt;/span&gt; &lt;span style="color: rgb(34, 139, 34);"&gt;()&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;main&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(160, 32, 240);"&gt;do&lt;/span&gt;&lt;br /&gt;   args &lt;span style="color: rgb(184, 134, 11);"&gt;&lt;-&lt;/span&gt; getArgs&lt;br /&gt;   putStrLn (&lt;span style="color: rgb(188, 143, 143);"&gt;"Hello, "&lt;/span&gt; &lt;span style="color: rgb(184, 134, 11);"&gt;++&lt;/span&gt; args &lt;span style="color: rgb(184, 134, 11);"&gt;!!&lt;/span&gt; 0)&lt;br /&gt;&lt;/pre&gt;  &lt;p&gt;如果你对Haskell还不熟……嗯……&lt;/p&gt;  &lt;p&gt;module Main这一行用来声明一个模块，这东西比较像Python的模块概念（如果你没学过Python，这句话请无视）。虽然写个小脚本的话你不加这个也能跑，不过养成好习惯总是好的。&lt;/p&gt;  &lt;p&gt;import 显而易见，就是用来导入外部模块的。这东西跟Python里用法差不多（如果你没学过Python，这句话请无视）。&lt;/p&gt;  &lt;p&gt;4-7行很短，但是对于有学过其它编程语言的人会是一种颠覆性的打击，就算你上一门学的是Python也一样（如果你没学过Python，这句话请无视）。&lt;/p&gt;  &lt;p&gt;第 4行是函数类型声明，它指明了main函数的类型是IO单子，这就是它的返回值。这在Haskell语言里是不同寻常的一种类型。我们先把这个事儿放一 边。之前如果你学过其它编程语言，那么我想你还记得，有些语言一定要有类型声明，比如C++或Java；有些语言根本没有类型声明，比如Python（如 果你没学过Python，这句话请无视）。&lt;/p&gt;  &lt;p&gt;然而Haskell这个变态，是可以有类型声明，也可以没有的。如果你不写，它会自己猜，然后选匹配的类型里最常用的来编译（这一点不同于 Javascript的动态类型）。以我的经验，只要你程序写得正确没有bug，几乎不会出现猜不出来的。然而，自己明确指定类型，可运用到更精准和丰富 的数据类型。显然，从可读性上讲也是有好处的。当然，C＃也有类似的匿名类型，Haskell学的C#也说不定。&lt;/p&gt;  &lt;p&gt;如果你在知道Haskell是上世纪八十年代就有的语言以后还看不出上句话是一个冷笑话，还是不要学Haskell了……&lt;/p&gt;  &lt;p&gt;顺便说一下，haskell对程序代码的排版规范要求非常严格，首先它的语法结构依赖缩进，这一点和Python一样（如果你没学过Python， 这句话请无视），子语句要缩进一级，这一点Haskell学的Python也说不定（参见上一段！）。其次，所有模块名都应该是PASCAL命名，所有函 数名都应该是驼峰命名，切记！&lt;/p&gt;  &lt;p&gt;现在我们终于说到这个万恶的单子（Monad）了。作为一门纯函数语言，Haskell是相当数学化的，我个人觉得 Haskell根本就是一门代数语言。它的函数其实就是算法定义。所以要求必须是“确定的”。也就是说，函数内部除了返回值，不能影响到外部。函数本身输 入值固定的话，必须确定的返回固定的值。&lt;/p&gt;  &lt;p&gt;但是这样的话，就没办法解决一些麻烦，比如程序运行过程中需要保存状态，程序本身需要输入输出（不然我们怎么看到结果呢？）。这些功能都是不符合确定性的。&lt;/p&gt;  &lt;p&gt;所以，Monad就应运而生了。关于Monad的定义，Haskell有一套漂亮的表达。不过我现在不打算关注它。我们只要知道这个东西相当于可以传递和保存值的对象就可以了。现在我们的任务是，先学会用现有的Monad，比如本教程中提到的各种Monad。&lt;/p&gt;  &lt;p&gt;Haskell的main函数相当于c的main函数，都是程序执行入口，所以没得说，这个肯定是一个Monad，事实上它必须是IO()。所以我们的程序代码也一定要匹配这个结果。&lt;/p&gt;  &lt;p&gt;如果要返回一个Monad，有两种办法，一种是把返回值用return 函数封装（编译器会根据你的函数定义选择类型），术语称为lift，当然，被lift的值要匹配定义的Monad；另一种是组合多个Monad。&lt;/p&gt;  &lt;p&gt;后者看起来麻烦，其实操作起来很简单。比如do操作，它后面可以带多行语句，每一行要么是一个action，要么是一个a&lt;-action的 取值操作。这里的action，就是指Monand化的一个事物，可以理解为一个对象实例（那么一个Monad定义可看作是一个类定义）。&lt;/p&gt;  &lt;p&gt;因为串化为一组Monad，do的子语句是按顺序执行的，do块相当于我们在普通的命令式语言中编写的代码片段。&lt;/p&gt;  &lt;p&gt;想到这一点，或许你会初步体会到Haskell是一个多么与众不同的语言。&lt;/p&gt;  &lt;p&gt;实际上do对每行代码有更细致的规定，它其实是一个语法糖，本原是一组Monad串行操作。如果原文你不能允分理解，没关系，后面我们还会大量运用Monad，我们会熟悉它的。&lt;/p&gt;  &lt;p&gt;本章教程最后还讲解了运算符和例程的编译方法，这个没有太惊诧的东西，不多讨论了。&lt;/p&gt;  &lt;p&gt;重点学习读取输入，打印字符到输出，基本编译指令。&lt;/p&gt;  &lt;p&gt;以及，Haskell中字符串就是序列，它的联接用++，而不是单个加号。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-5115924755006200217?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/5115924755006200217/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=5115924755006200217' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5115924755006200217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/5115924755006200217'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2008/12/write-yourself-scheme-in-48-hoursfirst.html' title='Write Yourself a Scheme in 48 Hours/First Steps'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-7546420182614445835</id><published>2008-11-30T03:36:00.000-08:00</published><updated>2008-11-30T04:56:26.666-08:00</updated><title type='text'>48 小时编写 Scheme 解释器的学习笔记：写在最前面。</title><content type='html'>这份教程是本很有趣的书，它并不算长－－48学时能有多久呢？Perl有本很有名的24小时教程，可以翻倍类比一下－－但是内容比较丰富。在前言中，作者提出了一些需要注意的地方：&lt;br /&gt;&lt;br /&gt;这本书为两种人准备：一是已经学会Lisp和Scheme，准备学Haskell的；一种是不会这些编程语言，但是有比较强的专业背景和知识的。原文是说 “familiar with computer”，不过相信我，这肯定不是电脑城招装高手的那个“熟悉计算机”。&lt;br /&gt;&lt;br /&gt;所以，这不是一本入门手册，这是为有一定基础的读者准备的。考虑到Haskell目前是一门如此小众的语言，这样的设定倒也合情合理。&lt;br /&gt;&lt;br /&gt;这个邪恶的家伙特别提到，如果你有过程化编程或面向对象的基础，比如学过C、JAVA、Python之类的，这里最好把它们都忘了，因为Haskell中的类型、函数、甚至return，跟你之前习惯的东西完全不是一码事。&lt;br /&gt;&lt;br /&gt;哈哈！这正是我兴奋的地方，如果再学一个语言，还是跟以前的差不多，也就没有什么意思了。&lt;br /&gt;&lt;br /&gt;差异启迪思想，我不是语言收集狂，我学新东西是想让自己变聪明一些。&lt;br /&gt;&lt;br /&gt;这份教材使用的是ghc编译器。我有很久都以为ghc是GNU Haskell Compiler，但它其实是 Glasgow Haskell Compiler。ghc 是目前最受欢迎的Haskell编译器，据作者说 Hugs 可能也能跑这个教程的例子，但是他没有试过……&lt;br /&gt;&lt;br /&gt;我的学习笔记不一定就按 48 学时，只是个坑而已，看情况啦。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-7546420182614445835?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/7546420182614445835/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=7546420182614445835' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7546420182614445835'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/7546420182614445835'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2008/11/48-scheme.html' title='48 小时编写 Scheme 解释器的学习笔记：写在最前面。'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-8077388052007966552</id><published>2008-11-28T22:28:00.000-08:00</published><updated>2008-11-29T00:14:29.200-08:00</updated><title type='text'>钢笔的诱惑</title><content type='html'>周四早上要出门的时候，想起以后要参加早上的例会。得要做笔记了。于是翻出一个本子，再去找笔。结果发现桌上的一大袋笔只有一只圆珠笔，一只红色的细马克笔，两只粗白板笔，其它都是铅笔。心想家里总要留一只的吧（其实后来想起家里某处好像有一排没开封的圆珠笔）。于是关门下楼，直奔小超市。&lt;br /&gt;小地方，店面不大，文具架只有可怜的一窄条。几只笔筒里插着各种各样的笔。&lt;br /&gt;最里面的地方，我看到几支黑色的钢笔，静静的斜在那里。&lt;br /&gt;伸手摸了过去。&lt;br /&gt;不行，太贵了，三天不写几个字，不值得，放下。&lt;br /&gt;又伸手过去拿起来，拔下笔帽，抚摸笔尖的感觉。沉重的黑色金属笔身，很舒适的手感，英雄，喜欢用这个牌子。&lt;br /&gt;不行，快上班了，拿支一元的水笔吧。&lt;br /&gt;走了两步，又转回头，看货架上的墨水。只有黑的，纯蓝的，问过售货员，没有蓝黑墨水。&lt;br /&gt;死心了，以我这种写字量，用碳素墨水肯定会堵笔尖的。&lt;br /&gt;拿了支长得很像钢笔的圆珠笔。也是金属外壳，沉重的手感。&lt;br /&gt;我如此深爱那种粗重的笔杆，流暢的手感。尽管我的字迹如此糟糕，现在一天也只有几次写字的机会。&lt;br /&gt;丢过很多笔，包括最喜欢的一支。&lt;br /&gt;用坏过很多笔，我曾经总是忍不住要毁坏文具，大概那时候心理上有某种强迫症。&lt;br /&gt;曾经被大人细心教导，如何握笔写字，却从未有学会过正确的姿势。&lt;br /&gt;曾经被教育，要端端正正的写字，堂堂正正的作人，但是一直没有学会漂亮的笔迹。&lt;br /&gt;曾经被教育，一定要用钢笔，才能练出漂亮的字。我一直喜欢钢笔，字迹还是那么糟。&lt;br /&gt;曾经，我们期待和被期待着的成长，在时间中慢慢变成不可能的幻想。&lt;br /&gt;只有一些痕迹留下来。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8229657610060087793-8077388052007966552?l=march-liu.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://march-liu.blogspot.com/feeds/8077388052007966552/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8229657610060087793&amp;postID=8077388052007966552' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8077388052007966552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8229657610060087793/posts/default/8077388052007966552'/><link rel='alternate' type='text/html' href='http://march-liu.blogspot.com/2008/11/blog-post.html' title='钢笔的诱惑'/><author><name>挖坑爱好者</name><uri>http://www.blogger.com/profile/00585759206391459954</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/_WAdxCs9S6dI/SPNNjQEv-II/AAAAAAAABi8/NreF6oWvWTE/S220/IMG_8052.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8229657610060087793.post-5098875983707912982</id><published>2008-11-28T20:58:00.000-08:00</published><updated>2008-12-02T09:09:50.046-08:00</updated><title type='text'>读了一遍《Write Yourself a Scheme in 48 Hours》</title><content type='html'>很惭愧，Lee同学几年前就向我推荐&lt;a href="http://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours"&gt;《Write Yourself a Scheme in 48 Hours》&lt;/a&gt;，而我到最近才把它真正过了一遍。&lt;br /&gt;收获很大，谢谢Lee。&lt;br /&gt;这本书以step by step的方式，教给读者如何在48学时编写出一个Scheme解释器。&lt;br /&gt;这本书可以说是经典的“第二本”读物。通过快速原型，逐步迭代的方式，引入haskell语法的方方面面，特别是各种monad的运用。如果你和我一样读入门教材时被卡在monad进退不能，这本书会帮你解脱。&lt;br /&gt;书中没有使用haskell系统库之外的程序资源，仅仅以414行代码完成一个实用的scheme解释器，充分展示了haskell的强大。&lt;br /&gt;在粗读过后，我准备逐章重新精读这篇教程，把内容彻底消化。&lt;br /&gt;最后，要感谢太太的支持。感谢&lt;a href="http://www.fayaa.com/code"&gt;发芽网&lt;/a&gt;提供了一个很好的服务，让我可以轻松的记录学习进度。我的代码记录在&lt;a href="http://www.fayaa.com/code/view/502/"&gt;这里&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;     &lt;div style="background: rgb(253, 253, 253) none repeat scroll 0% 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;&lt;u&gt;Haskell语言&lt;/u&gt;: &lt;a href="http://www.fayaa.com/code/view/502/"&gt;48 小时编写sheme解释器的学习笔记－SimpleParser.hs&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;     &lt;div style="font-family: &amp;quot;[object HTMLOptionElement]&amp;quot;,&amp;quot;Bitstream Vera Sans Mono&amp;quot;,&amp;quot;monospace&amp;quot;;" class="source"&gt;&lt;span class="lineno"&gt;001&lt;/span&gt; &lt;span class="_kr"&gt;module&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_nn"&gt;Main&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kr"&gt;where&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;002&lt;/span&gt; &lt;span class="_kr"&gt;import&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_nn"&gt;Monad&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;003&lt;/span&gt; &lt;span class="_kr"&gt;import&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_nn"&gt;Control.Monad.Error&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;004&lt;/span&gt; &lt;span class="_kr"&gt;import&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_nn"&gt;System.Environment&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;005&lt;/span&gt; &lt;span class="_kr"&gt;import&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_nn"&gt;IO&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_k"&gt;hiding&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;(&lt;/span&gt;&lt;span class="_nf"&gt;try&lt;/span&gt;&lt;span class="_p"&gt;)&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;006&lt;/span&gt; &lt;span class="_kr"&gt;import&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_nn"&gt;Data.IORef&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;007&lt;/span&gt; &lt;span class="_kr"&gt;import&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_nn"&gt;Text.ParserCombinators.Parsec&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_k"&gt;hiding&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;(&lt;/span&gt;&lt;span class="_nf"&gt;spaces&lt;/span&gt;&lt;span class="_p"&gt;)&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;008&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;009&lt;/span&gt; &lt;span class="_nf"&gt;symbol&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Char&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;010&lt;/span&gt; &lt;span class="_nf"&gt;symbol&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;oneOf&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_s"&gt;&amp;quot;!$%&amp;amp;|*+-/:&amp;lt;=?&amp;gt;@^_~#&amp;quot;&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;011&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;012&lt;/span&gt; &lt;span class="_kr"&gt;data&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Atom&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;013&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;List&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;[&lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_p"&gt;]&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;014&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;DottedList&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;[&lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_p"&gt;]&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;015&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Number&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Integer&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;016&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;017&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Bool&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Bool&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;018&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;PrimitiveFunc&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;([&lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_p"&gt;]&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;ThrowsError&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_p"&gt;)&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;019&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Func&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;{&lt;/span&gt;&lt;span class="_n"&gt;params&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;[&lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_p"&gt;],&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;vararg&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;(&lt;/span&gt;&lt;span class="_kt"&gt;Maybe&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_p"&gt;),&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;020&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;body&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;[&lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_p"&gt;],&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;closure&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Env&lt;/span&gt;&lt;span class="_p"&gt;}&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;021&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;IOFunc&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;([&lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_p"&gt;]&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;IOThrowsError&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_p"&gt;)&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;022&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Port&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Handle&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;023&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;024&lt;/span&gt; &lt;span class="_kr"&gt;data&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispError&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;NumArgs&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Integer&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;[&lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_p"&gt;]&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;025&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;TypeMismatch&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;026&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;ParseError&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;027&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;BadSpecialForm&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;028&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;NotFunction&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;029&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;UnboundVar&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;030&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;|&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Default&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;031&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;032&lt;/span&gt; &lt;span class="_nf"&gt;spaces&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_nb"&gt;()&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;033&lt;/span&gt; &lt;span class="_nf"&gt;spaces&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;skipMany1&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;space&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;034&lt;/span&gt; &lt;br&gt;&lt;span class="lineno special"&gt;035&lt;/span&gt; &lt;span class="_nf"&gt;parseString&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;036&lt;/span&gt; &lt;span class="_nf"&gt;parseString&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kr"&gt;do&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;char&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_sc"&gt;&amp;#39;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;037&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;x&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;many&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;(&lt;/span&gt;&lt;span class="_n"&gt;noneOf&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="_se"&gt;\&amp;quot;&lt;/span&gt;&lt;span class="_s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="_p"&gt;)&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;038&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;char&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_sc"&gt;&amp;#39;&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;039&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;return&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;$&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;String&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;x&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;040&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;041&lt;/span&gt; &lt;span class="_nf"&gt;parseAtom&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;042&lt;/span&gt; &lt;span class="_nf"&gt;parseAtom&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kr"&gt;do&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;first&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;letter&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;&amp;lt;|&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;symbol&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;043&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;rest&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;many&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;(&lt;/span&gt;&lt;span class="_n"&gt;letter&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;&amp;lt;|&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;digit&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;&amp;lt;|&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;symbol&lt;/span&gt;&lt;span class="_p"&gt;)&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;044&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_kr"&gt;let&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;atom&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;first&lt;/span&gt;&lt;span class="_kt"&gt;:&lt;/span&gt;&lt;span class="_n"&gt;rest&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;045&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;return&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;$&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kr"&gt;case&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;atom&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kr"&gt;of&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;046&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_s"&gt;&amp;quot;#t&amp;quot;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Bool&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;True&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;047&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_s"&gt;&amp;quot;#f&amp;quot;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Bool&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;False&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;048&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;otherwise&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Atom&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;atom&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;049&lt;/span&gt; &lt;br&gt;&lt;span class="lineno special"&gt;050&lt;/span&gt; &lt;span class="_nf"&gt;parseNumber&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;051&lt;/span&gt; &lt;span class="_nf"&gt;parseNumber&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;liftM&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;(&lt;/span&gt;&lt;span class="_kt"&gt;Number&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;.&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;read&lt;/span&gt;&lt;span class="_p"&gt;)&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;$&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;many1&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;digit&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;052&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;053&lt;/span&gt; &lt;span class="_nf"&gt;parseExpr&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;054&lt;/span&gt; &lt;span class="_nf"&gt;parseExpr&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseAtom&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;055&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;&amp;lt;|&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseString&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;056&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;&amp;lt;|&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseNumber&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;057&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;&amp;lt;|&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseQuoted&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;058&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_o"&gt;&amp;lt;|&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kr"&gt;do&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;char&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_sc"&gt;&amp;#39;(&amp;#39;&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;059&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;x&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;(&lt;/span&gt;&lt;span class="_n"&gt;try&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseList&lt;/span&gt;&lt;span class="_p"&gt;)&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;&amp;lt;|&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseDottedList&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;060&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;char&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_sc"&gt;&amp;#39;)&amp;#39;&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;061&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;return&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;x&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;062&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;063&lt;/span&gt; &lt;span class="_nf"&gt;parseList&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;064&lt;/span&gt; &lt;span class="_nf"&gt;parseList&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;liftM&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;List&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;$&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;sepBy&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseExpr&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;spaces&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;065&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;066&lt;/span&gt; &lt;span class="_nf"&gt;parseDottedList&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;067&lt;/span&gt; &lt;span class="_nf"&gt;parseDottedList&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kr"&gt;do&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;068&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;head&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;endBy&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseExpr&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;spaces&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;069&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;tail&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;char&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_sc"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;spaces&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseExpr&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;070&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;return&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;$&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;DottedList&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;head&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;tail&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;071&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;072&lt;/span&gt; &lt;span class="_nf"&gt;parseQuoted&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Parser&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;073&lt;/span&gt; &lt;span class="_nf"&gt;parseQuoted&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;=&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kr"&gt;do&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;074&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;char&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_sc"&gt;&amp;#39;&lt;/span&gt;&lt;span class="_se"&gt;\&amp;#39;&lt;/span&gt;&lt;span class="_sc"&gt;&amp;#39;&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno special"&gt;075&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;x&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;parseExpr&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;076&lt;/span&gt; &lt;span class="_"&gt;&amp;nbsp; &lt;/span&gt;&lt;span class="_n"&gt;return&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_o"&gt;$&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;List&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_p"&gt;[&lt;/span&gt;&lt;span class="_kt"&gt;Atom&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_s"&gt;&amp;quot;quote&amp;quot;&lt;/span&gt;&lt;span class="_p"&gt;,&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_n"&gt;x&lt;/span&gt;&lt;span class="_p"&gt;]&lt;/span&gt;&lt;span class="_"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="lineno"&gt;077&lt;/span&gt; &lt;br&gt;&lt;span class="lineno"&gt;078&lt;/span&gt; &lt;span class="_nf"&gt;eval&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;::&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;Env&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;LispVal&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_ow"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&gt;&lt;span class="_kt"&gt;IOThrowsError&lt;/span&gt;&lt;span class="_"&gt; &lt;/span&g
