<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href='http://feed.feedsky.com/styles/temp01.xsl' type='text/xsl' ?><!--这是一个由Feedsy提供技术支持的Feed，为了提高读者阅读的体验，以及满足用户美化自己Feed的需要，我们设计了多种精美的Feed模板，提供给大家选择，所有最终呈现出来的样式，皆由用户自愿选择使用，未经许可，任何团体和个人，请不要擅自修改样式或者盗用，这是对于用户选择权的尊重。--><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:fs="http://www.feedsky.com/namespace/feed" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link href="http://feed.feedsky.com/oldj" type="application/rss+xml" rel="self"></atom:link><fs:self_link href="http://feed.feedsky.com/oldj" type="application/rss+xml"></fs:self_link><lastBuildDate>Sun, 19 Feb 2012 11:04:43 GMT</lastBuildDate><title>oldj's blog</title><description>oldj的博客 - 最新文章</description><image><url>http://www.feedsky.com/feed/oldj/sc/gif</url><title>oldj's blog</title><link>http://oldj.net</link></image><link>http://oldj.net</link><language>zh-cn</language><pubDate>Sun, 19 Feb 2012 11:04:43 GMT</pubDate><item><title>使用Python将文本转为图片</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630213/6320477/1/item.html</link><description>&lt;p&gt;　　有时候，我们需要将文本转换为图片，比如发长微博，或者不想让人轻易复制我们的文本内容等时候。目前类似的工具已经有了不少，不过我觉得用得都不是很趁手，于是便自己尝试实现了一个。你可以先访问一下查看效果：&lt;a href=&quot;http://txt2.im&quot; target=&quot;_blank&quot;&gt;txt2.im&lt;/a&gt; 。&lt;/p&gt;

&lt;p&gt;　　&lt;a href=&quot;http://txt2.im&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;http://txt2.im/images/logo.png&quot; alt=&quot;txt2.im&quot; style=&quot;border: solid 3px #999;border-radius: 16px;-moz-border-radius: 16px;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;　　在 Python 中，&lt;a href=&quot;http://www.pythonware.com/products/pil/&quot; target=&quot;_blank&quot;&gt;PIL (Python Imaging Library)&lt;/a&gt; 是最常用的绘图库，自然地，尝试从 PIL 开始。&lt;/p&gt;

&lt;p&gt;　　1、使用 PIL 将文字转换为图片&lt;/p&gt;

&lt;p&gt;　　说转换其实并不恰当，真实的过程是：先在内存中生成一张图片，将需要的文字绘制到这个图片上，再将图片保存到指定位置。代码如下：&lt;/p&gt;
&lt;!-- oldj-article-sep --&gt;
&lt;pre class=&quot;brush:python&quot;&gt;
# -*- coding: utf-8 -*-

import os
import Image, ImageFont, ImageDraw

text = u&quot;这是一段测试文本，test 123。&quot;

im = Image.new(&quot;RGB&quot;, (300, 50), (255, 255, 255))
dr = ImageDraw.Draw(im)
font = ImageFont.truetype(os.path.join(&quot;fonts&quot;, &quot;msyh.ttf&quot;), 14)

dr.text((10, 5), text, font=font, fill=&quot;#000000&quot;)

im.show()
im.save(&quot;t.png&quot;)
&lt;/pre&gt;

&lt;p&gt;　　当然，要使上面的代码能正常运行，你还需要在代码目录建一个“fonts”文件夹，并将字体文件“msyh.ttf”（微软雅黑）放在里面。最终生成的图片效果如下：&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201202/19/20120219160328_Rsw42.png&quot; alt=&quot;&quot; style=&quot;border:solid 1px #999&quot; /&gt;&lt;/p&gt;

&lt;p&gt;　　从上面可以看到，使用 PIL 把文本转为图片是非常方便的。当然，上面还只是一个简单的例子，还没有考虑长文本换行的问题。&lt;/p&gt;

&lt;p&gt;　　2、使用点阵字体&lt;/p&gt;

&lt;p&gt;　　对于大多数屏幕阅读来说，较小的字号（&amp;lt;= 14px）使用点阵字体来说更加清晰和便于阅读，例如 Windows 中文版下的默认字体宋体在字号较小时就通常被渲染成点阵字体。那么，我们是否可以在生成的图片中使用点阵字体呢？&lt;/p&gt;

&lt;p&gt;　　修改上面的代码的第 10 行，将字体从微软雅黑（msyh.ttf）换成了宋体（simsun.ttc），其它保持不变。&lt;/p&gt;

&lt;pre class=&quot;brush:python&quot;&gt;
font = ImageFont.truetype(os.path.join(&quot;fonts&quot;, &quot;simsun.ttc&quot;), 14)
&lt;/pre&gt;

&lt;p&gt;　　生成的图片如下：&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201202/19/20120219161205_4tVnz.png&quot; alt=&quot;&quot; style=&quot;border:solid 1px #999&quot; /&gt;&lt;/p&gt;

&lt;p&gt;　　杯具发生了，汉字没有正常显示！&lt;p&gt;

&lt;p&gt;　　网上搜索了一圈，发现这好像是 PIL 的一个 bug，PIL 目前的版本中，不能正确处理非 ASCII 字符的点阵字体的渲染。对于像宋体这样的字体来说，只有 &amp;gt;= 18px 时，才会被当作矢量字体处理，也就是说只有当字体 &amp;gt;= 18px 时，文字才能正常显示：&lt;/p&gt;

&lt;pre class=&quot;brush:python&quot;&gt;
font = ImageFont.truetype(os.path.join(&quot;fonts&quot;, &quot;simsun.ttc&quot;), 18)
&lt;/pre&gt;

&lt;p&gt;　　效果如下：&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201202/19/20120219162222_F8ai5.png&quot; alt=&quot;&quot; style=&quot;border:solid 1px #999&quot; /&gt;&lt;/p&gt;

&lt;p&gt;　　增大字体虽然解决了汉字不能正常显示的问题，但还是没有解决我们一开始的初衷：使用点阵字体进行渲染。但是，这个目标使用现阶段的 PIL 似乎有点难以实现了。&lt;/p&gt;

&lt;p&gt;　　3、使用 pyGame 渲染点阵字体&lt;/p&gt;

&lt;p&gt;　　Python 的第三方模块或组件非常多，可用来绘图的除了 PIL 之外，就还有 &lt;a href=&quot;http://cairographics.org/pycairo/&quot; target=&quot;_blank&quot;&gt;Pycairo&lt;/a&gt;、&lt;a href=&quot;http://matplotlib.sourceforge.net/&quot; target=&quot;_blank&quot;&gt;matplotlib&lt;/a&gt;、&lt;a href=&quot;http://pygame.org/news.html&quot; target=&quot;_blank&quot;&gt;pyGame&lt;/a&gt; 等。在这儿，我使用 pyGame 来完成点阵字体的渲染工作。&lt;/p&gt;

&lt;p&gt;　　代码如下：&lt;/p&gt;

&lt;pre class=&quot;brush:python&quot;&gt;
# -*- coding: utf-8 -*-

import os
import pygame

pygame.init()

text = u&quot;这是一段测试文本，test 123。&quot;
font = pygame.font.Font(os.path.join(&quot;fonts&quot;, &quot;simsun.ttc&quot;), 14)
rtext = font.render(text, True, (0, 0, 0), (255, 255, 255))

pygame.image.save(rtext, &quot;t.jpg&quot;)
&lt;/pre&gt;

&lt;p&gt;　　效果如下：&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201202/19/20120219181536_VG3sC.jpg&quot; alt=&quot;&quot; style=&quot;border:solid 1px #999&quot; /&gt;&lt;/p&gt;

&lt;p&gt;　　可以看到，使用 pyGame ，点阵字体的问题终于搞定了。&lt;/p&gt;

&lt;p&gt;　　4、结合 PIL 和 pyGame&lt;/p&gt;

&lt;p&gt;　　pyGame 虽然可以解决点阵字体的渲染问题，但讲到对图片的处理，还是 PIL 更为强大。那么，我们为什么不把两者结合起来呢？用 pyGame 渲染点阵字体，然后用 PIL 生成整张图片。&lt;/p&gt;

&lt;p&gt;　　代码如下：&lt;/p&gt;

&lt;pre class=&quot;brush: python&quot;&gt;
# -*- coding: utf-8 -*-

import os
import StringIO
import Image, ImageFont, ImageDraw
import pygame

pygame.init()

text = u&quot;这是一段测试文本，test 123。&quot;

im = Image.new(&quot;RGB&quot;, (300, 50), (255, 255, 255))
#dr = ImageDraw.Draw(im)
#font = ImageFont.truetype(os.path.join(&quot;fonts&quot;, &quot;simsun.ttc&quot;), 18)
font = pygame.font.Font(os.path.join(&quot;fonts&quot;, &quot;simsun.ttc&quot;), 14)

#dr.text((10, 5), text, font=font, fill=&quot;#000000&quot;)
rtext = font.render(text, True, (0, 0, 0), (255, 255, 255))

#pygame.image.save(rtext, &quot;t.gif&quot;)
sio = StringIO.StringIO()
pygame.image.save(rtext, sio)
sio.seek(0)

line = Image.open(sio)
im.paste(line, (10, 5))

im.show()
im.save(&quot;t.png&quot;)
&lt;/pre&gt;

&lt;p&gt;　　原理很简单，先将文字用 pyGame 渲染为图片，将渲染结果保存在一个 StringIO 对象中，然后再用 PIL 加载它。使用 StringIO 的好处是，一切操作都是在内存中进行的，不需要先将它保存到硬盘再用 PIL 读取，因为硬盘 IO 的效率相对来说是比较低的。&lt;/p&gt;

&lt;p&gt;　　最终效果如下：&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201202/19/20120219172456_m8VYF.png&quot; alt=&quot;&quot; style=&quot;border:solid 1px #999;width:300px;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;　　&lt;/p&gt;

&lt;p&gt;　　到这儿，使用 Python 将文本转为图片的功能就基本实现了，用到了 PIL 和 pyGame。&lt;/p&gt;

&lt;p&gt;　　当然，上面的代码还只解决了最基本的问题，一个真正可用的文本转图片工具，还应该解决以下问题：长文本换行问题、英文单词断字问题、标点符号换行问题等。关于这些问题的分析篇幅也不短，这一次就先略过了。下面是一个综合考虑了诸多因素之后生成的《荷塘月色》的效果图：&lt;/p&gt;

&lt;p&gt;　　&lt;a href=&quot;http://txt2.im/p/36860c918404db69d1c48eab699dfbe4/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;http://oldj.net/uploads/files/201202/19/20120219184532_iaY8F.png&quot; alt=&quot;&quot; style=&quot;width:440px;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;　　最后，欢迎试用我写的在线文本转图片工具：&lt;a href=&quot;http://txt2.im&quot; target=&quot;_blank&quot;&gt;txt2.im&lt;/a&gt; 。:-)&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630213/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630213/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Sun, 19 Feb 2012 19:04:43 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/text-to-image/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/text-to-image/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630213/6320477</fs:itemid></item><item><title>页面加载时间度量</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630214/6320477/1/item.html</link><description>&lt;p&gt;　　页面加载时间（Site Speed 或 User Latency）是网站可用性的一个重要指标，指的是网站的页面从加载开始到加载完成所花费的时间。这个时间反映了网站的访问速度，有一个说法是，如果一个页面超过 8 秒还没有打开，用户就会失去耐心，从而关闭窗口或转向别的页面。关于这个时间的度量，主要有以下几种方法。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;方法一、前后端协同度量&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　服务器端收到当前页面的请求时，记下当前时间 t0，并输出到页面上。页面加载完成时（onload 事件触发），客户端 JavaScript 脚本将当前时间与 t0 比较，得到页面加载时间。&lt;/p&gt;

&lt;p&gt;　　这种方法的不足是需要服务器端配合，并且由于服务器端时间与客户端时间不一定完全相同，还涉及比较复杂的时间同步问题，看似简单，做起来却并不容易。&lt;/p&gt;

&lt;!-- oldj-article-sep --&gt;
&lt;p&gt;　　&lt;strong&gt;方法二、纯前端方法在当前页面度量&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　这种方法的原理为，在页面的顶部放一段 JavaScript 代码，记下当前时间 t0，然后在页面 onload 时再计算当前时间与 t0 的时间差，这个时间差即为页面加载时间。&lt;/p&gt;

&lt;p&gt;　　示例如下：&lt;/p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;

var t0 = new Date().getTime();
function onLoad() {
  var now = new Date().getTime();
  var latency = now - t0;
  alert(&quot;page loading time: &quot; + latency);
}

&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body onload=&quot;onLoad()&quot;&amp;gt;
&amp;lt;!- Main page body goes from here. --&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;

&lt;p&gt;　　这种方法操作简单，而且使用的都是客户端时间，没有时间同步的问题。但不足是顶部记录 t0 的代码执行之前的时间（建立连接、网络延迟、接收并解析最初数据的时间）被忽略了，因此得到的时间一般比真实的页面加载时间要小。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;方法三、前一个页面和当前页面协同度量&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　这种方法的原理为，用户在前一个页面点击到链接或在前一个页面被卸载之前（onbeforeunload），先记下当前时间 t0，然后下一个页面加载完成时（onload）计算当前时间与 t0 的时间差，这个时间差即为页面加载时间。&lt;/p&gt;

&lt;p&gt;　　那么，用户离开前一个页面的时间 t0 保存在哪里呢？可以看看 IBM 奥斯汀研究实验室在 2001 年写的这篇论文：&lt;a href=&quot;http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.69.7329&amp;rep=rep1&amp;type=pdf&quot; target=&quot;_blank&quot;&gt;Measuring Client-Perceived Response Times on the WWW&lt;/a&gt;。里面详细地整理了各种 HTML5 之前的可用的跨页面传递信息的方法，如 cookie、独立窗口、框架等。最令人惊讶的是还提到使用 Window Name 来保存信息，几年之后，这种&lt;a href=&quot;http://www.planabc.net/2008/09/01/window_name_transport/&quot; target=&quot;_blank&quot;&gt;使用 window.name 传递信息&lt;/a&gt;的方法才开始在前端工程师中流传开来。&lt;/p&gt;

&lt;p&gt;　　这种方法的优点是包含了网络延迟时间，得到的数据更真实。缺点是实现和部署较为复杂，并且访客访问的第一个页面的加载时间无法计算，因为在第一个页面之前没有哪个页面能先记下 t0。&lt;/p&gt;

&lt;p&gt;　　另外，前面三种方法都需要添加额外的记录 t0 时间的代码，这会增加页面的计算量，从而影响到页面的交付时间，并影响最终的结果，虽然这个影响非常微小。这类似于物理学中的&lt;a href=&quot;http://en.wikipedia.org/wiki/Observer_effect_(physics)&quot; target=&quot;_blank&quot;&gt;观察者效应&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;方法四、使用浏览器插件或工具栏&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　比如&lt;a href=&quot;http://ecmanaut.blogspot.com/2010/06/google-bom-feature-ms-since-pageload.html&quot; target=&quot;_blank&quot;&gt;使用 Google Toolbar 获取页面加载时间&lt;/a&gt;，见下面的例子：&lt;/p&gt;

&lt;pre class=&quot;brush:javascript;&quot;&gt;
function msSincePageLoad() {
  try {
    var t = null;
    if (window.chrome &amp;&amp; chrome.csi)
      t = chrome.csi().pageT;
    if (t === null &amp;&amp; window.gtbExternal)
      t = window.gtbExternal.pageT();
    if (t === null &amp;&amp; window.external)
      t = window.external.pageT;
  } catch (e) {};
  return t;
}
&lt;/pre&gt;

&lt;p&gt;　　当然，这个方法需要浏览器插件或工具栏的支持，通用性比较小，一般只能辅助使用。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;方法五、使用 HTML5 中的 Navigation Timing 度量&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　HTML5 中引入了一个 &lt;a href=&quot;https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html&quot; target=&quot;_blank&quot;&gt;Navigation Timing&lt;/a&gt; 接口，专门用于记录页面导航的时间。使用这个接口，能完美地记录页面加载时间。&lt;/p&gt;

&lt;p&gt;　　示例代码如下：&lt;/p&gt;

&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
function onLoad() {
  var now = new Date().getTime();
  var page_load_time = now - performance.timing.navigationStart;
  alert(&quot;User-perceived page loading time: &quot; + page_load_time);
}

&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body onload=&quot;onLoad()&quot;&amp;gt;
&amp;lt;!- Main page body goes from here. --&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;

&lt;p&gt;　　下面的图片展示了 Navigation Timing 接口所记录的各个时间点。&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201201/09/20120109112858_wFaa2.png&quot; alt=&quot;Navigation Timing&quot; style=&quot;width:720px;&quot; /&gt;
&lt;br /&gt;　　&lt;span style=&quot;font-size:12px;color:gray;&quot;&gt;（图片来源：&lt;a href=&quot;https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#processing-model&quot; target=&quot;_blank&quot;&gt;W3C 网站&lt;/a&gt;）&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;　　目前，Google Analytics 的 &lt;a href=&quot;http://support.google.com/analytics/bin/answer.py?hl=en&amp;answer=1205784&quot; target=&quot;_blank&quot;&gt;Site Speed&lt;/a&gt; 功能使用了上面提到的方法四和方法五，因此现阶段还只能在安装了 Google Toolbar 或支持 HTML5 的浏览器上才能统计页面加载时间。&lt;/p&gt;

&lt;p&gt;　　最后，有一些专门用于计算页面上各种计时的 JavaScript 框架，比如 Yahoo 的 &lt;a href=&quot;http://yahoo.github.com/boomerang/doc/&quot; target=&quot;_blank&quot;&gt;boomerang&lt;/a&gt; 和 Google 的 &lt;a href=&quot;http://stevesouders.com/episodes2/&quot; target=&quot;_blank&quot;&gt;Episodes&lt;/a&gt;，都可计算页面加载时间，它们综合使用了上面提到的方法三（使用 cookie 记录上一个页面离开的时间）和方法五，其中 Episodes 在不支持 Navigation Timing 的浏览器上还会尝试方法四，如果用户浏览器安装了 Google Toolbar 的话。另外，Episodes 的目标不仅仅是计算页面加载时间，它还提供了对更多的自定义计时的支持，并试图发展为一个工业标准，但似乎有段时间没有更新了。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630214/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630214/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Mon, 09 Jan 2012 20:34:53 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/measuring-the-user-latency/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/measuring-the-user-latency/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630214/6320477</fs:itemid></item><item><title>我的2011年</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630215/6320477/1/item.html</link><description>&lt;p&gt;　　2011 年做了或经历了很多事，这儿是我觉得值得记录的一个摘要：&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;一、做过的事&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　1、工作上，继续捣鼓用户行为数据。&lt;/p&gt;

&lt;p&gt;　　这个工作很有趣，但有些地方也不容易做，经常有“书到用时方恨少”的感觉，还好有老大和同事的支持，很多难题还是逐一解决或绕过了。不过，仔细想想，今年自己在这个工作上并没有很大的突破，大部分精力还是花在了一些比较基础的工作上了，深入的分析做得还太少。&lt;/p&gt;
&lt;!-- oldj-article-sep --&gt;
&lt;p&gt;　　2、参与了犀牛书（《JavaScript 权威指南》）第六版的翻译及校对。&lt;/p&gt;

&lt;p&gt;　　感谢老大及几位前辈的信任，让我这个默默无闻的三心二意的前端工程师也有机会参与这本书的翻译及校对工作。在这项工作上我累计投入了三个半月的业余时间。翻译及校对工作比原想的更难一些，不过，这个忙碌的过程也让我开始反思自己的时间管理方法，并开始尝试&lt;a href=&quot;http://book.douban.com/subject/5916234/&quot; target=&quot;_blank&quot;&gt;番茄工作法&lt;/a&gt;、&lt;a href=&quot;http://zh.wikipedia.org/zh/GTD&quot; target=&quot;_blank&quot;&gt;GTD&lt;/a&gt; 等有趣的方法。&lt;/p&gt;

&lt;p&gt;　　3、参与了 D2 论坛&lt;/p&gt;

&lt;p&gt;　　在 D2 论坛上分享了我们在用户行为收集以及展现方面的一些经验。事后看视频，觉得自己讲得真烂，太羞愧了……&lt;/p&gt;

&lt;p&gt;　　4、当爹了&lt;/p&gt;

&lt;p&gt;　　12 月的最后几天，随着儿子的出世，我也终于步入父亲的行列了。从今以后，我就要对那个小生命负责，教他学习、教他分享、教他礼貌……，可是，好像有些东西我也还不怎么会呢！好吧，新的长征开始了。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;二、读过的书&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　今年事情挺多，读书的速度比买书的速度还慢，年底时桌上床头还堆了若干本没来得及读的书。&lt;/p&gt;

&lt;p&gt;　　这一年里读过的书中，我觉得对我影响最大的三本分别是：&lt;/p&gt;

&lt;p&gt;　　1、《&lt;a href=&quot;http://book.douban.com/subject/2282946/&quot; target=&quot;_blank&quot;&gt;直面内心的恐惧&lt;/a&gt;》：读这本书简直是个自我剖析的过程，这本书可能会改变你对自己或他人的认识。&lt;/p&gt;

&lt;p&gt;　　2、《&lt;a href=&quot;http://book.douban.com/subject/5375620/&quot; target=&quot;_blank&quot;&gt;失控&lt;/a&gt;》：一部关于复杂系统的著作，如果你之前不了解这个领域，这本书有可能会改变你的世界观。&lt;/p&gt;

&lt;p&gt;　　3、《&lt;a href=&quot;http://book.douban.com/subject/4849382/&quot; target=&quot;_blank&quot;&gt;搞定&lt;/a&gt;》：这本书改变了我的任务管理习惯。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;三、明年想做的事&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　虽然每年的明年计划都完成得不是很好，但还是写一下。明年我想做的事有：&lt;/p&gt;

&lt;p&gt;　　1、用户行为数据的工作更深入一点（深入的具体标准会向我的老大汇报，就不在这儿写了 :-]）。&lt;/p&gt;

&lt;p&gt;　　2、写一个自己用得着别人也用得着的开源软件。&lt;/p&gt;

&lt;p&gt;　　3、写一个自己觉得好玩的游戏。&lt;/p&gt;

&lt;p&gt;　　&lt;/p&gt;

&lt;p&gt;　　就这些了，各位 2012 年见！&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630215/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630215/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Sat, 31 Dec 2011 23:13:56 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/my-2011/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/my-2011/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630215/6320477</fs:itemid></item><item><title>生产率法则</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630216/6320477/1/item.html</link><description>&lt;p&gt;　　最近几个月比较关注效率以及时间管理的主题，前段时间看到一个幻灯片“&lt;a href=&quot;http://lunar.lostgarden.com/Rules%20of%20Productivity.pdf&quot; target=&quot;_blank&quot;&gt;Rules of Productivity&lt;/a&gt;”，觉得很不错，于是抽了点时间将它翻译成了中文。&lt;/p&gt;

&lt;p&gt;　　幻灯片中，作者先解释了什么是生产率，然后讲述了 8 个生产率的实验，每个实验之后都总结出一些经验和教训。比如，第一个实验是关于加班的，长时间工作会有什么后果？&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201112/06/20111206194759_nLRPx.png&quot; alt=&quot;生产率与工作时间的曲线&quot; style=&quot;width:600px;border:solid 1px #000;&quot; /&gt;&lt;/p&gt;
&lt;!-- oldj-article-sep --&gt;
&lt;p&gt;　　上图展示了在长时间工作（加班）的情况下，生产率随着时间会如何变化。可以看到，一开始的三个星期里，生产率的确提高了，但提高的幅度在不断下降，到第四周时，已经降到和基线同一水平了，再往后，生产率将降到基线之下。长时间加班持续得越久，损失的产出其实越多。&lt;/p&gt;

&lt;p&gt;　　下面是总的八个实验的列表：&lt;/p&gt;

&lt;p&gt;　　1、长时间工作会有什么后果？
&lt;br /&gt;　　2、如果间歇性地努力工作会如何？
&lt;br /&gt;　　3、在知识工作者而不是工厂工人之间的实验；
&lt;br /&gt;　　4、有没有例外的人呢？
&lt;br /&gt;　　5、多大的开发团队生产率最高？
&lt;br /&gt;　　6、生产率最高的物质环境是什么样的？
&lt;br /&gt;　　7、怎么把不同专业背景的工作者组织起来？
&lt;br /&gt;　　8、应当按团队能力的百分之多少来安排工作日程？
&lt;/p&gt;

&lt;p&gt;　　详细内容，可以点击下面的链接下载我翻译的版本：&lt;/p&gt;

&lt;p&gt;　　&lt;a href=&quot;http://www.everbox.com/f/RuGHkT3VS4AjxeWHIuFgMP8ZwE&quot; target=&quot;_blank&quot;&gt;生产率法则.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;　　水平有限，某些地方翻译得可能不是很好，欢迎各位指正。:-)&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630216/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630216/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Tue, 06 Dec 2011 21:13:35 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/rules-of-productivity/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/rules-of-productivity/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630216/6320477</fs:itemid></item><item><title>IE下链接包含@字符时的一个问题</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630217/6320477/1/item.html</link><description>&lt;p&gt;　　最近遇到 IE 下的一个闻所未闻的 bug：如果 &amp;lt;a&amp;gt; 元素的子节点为纯文本节点（即 nodeType 为 3，innerHTML 中没有 HTML 标签），并且值包含“@”字符，则在修改该 &amp;lt;a&amp;gt; 元素的 href 属性时，它的 innerHTML 也会跟着变。&lt;/p&gt;

&lt;p&gt;　　重现 bug 的代码类似这样：&lt;/p&gt;
&lt;!-- oldj-article-sep --&gt;
&lt;pre class=&quot;brush: html;&quot;&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
	&amp;lt;meta charset=&quot;utf-8&quot;&amp;gt;
	&amp;lt;title&amp;gt;请用 IE6/7/8 访问本页面&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;a href=&quot;http://www.taobao.com&quot; id=&quot;a-test&quot;&amp;gt;链接@BABY&amp;lt;/a&amp;gt;

&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
	setTimeout(function () {
		var a = document.getElementById(&quot;a-test&quot;);
		a.href = &quot;http://rate.taobao.com/&quot;;
//		a.setAttribute(&quot;href&quot;, &quot;http://rate.taobao.com/&quot;);
	}, 3000);
&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/pre&gt;

&lt;p&gt;　　示例可以看这儿：&lt;a href=&quot;http://oldj.net/static/ie-href-at-bug/t.html&quot; target=&quot;_blank&quot;&gt;bug 示例&lt;/a&gt;。请用 IE6、IE7 或 IE8 打开，IE9 中已没有这个问题，同时，在 IE9 的兼容模式下也正常。可以看到，页面打开时，链接的内容为“链接@BABY”，一切正常，但当 js 函数执行，修改了 &amp;lt;a&amp;gt; 元素的 href 属性时，它的 innerHTML 值也变成了同样的值。除了直接给 a.href 赋值外，使用 a.setAttribute 方法也不行，甚至加上 a.setAttribute 的&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms536739(v=VS.85).aspx&quot; target=&quot;_blank&quot;&gt;第三个参数&lt;/a&gt;也不行。&lt;/p&gt;

&lt;p&gt;　　这个问题看起来是 IE6/7/8 的 bug。搜索了很久，与几位同事也一起研究了好久，但都没有找到有关这个问题的描述及解决办法。&lt;/p&gt;

&lt;p&gt;　　最后，只好使出了最暴力但是有效的方法：&lt;/p&gt;

&lt;pre class=&quot;brush: javascript;&quot;&gt;
var a = document.getElementById(&quot;a-test&quot;), s;
s = a.innerHTML; // 先记下 innerHTML
a.href = &quot;http://rate.taobao.com/&quot;;
a.innerHTML = s; // 再将 innerHTML 的值写回去

&lt;/pre&gt;

&lt;p&gt;　　不知道有没有更好的解决办法。&lt;/p&gt;

&lt;hr noshade=&quot;noshade&quot; size=&quot;1&quot; /&gt;

&lt;p&gt;　　&lt;strong&gt;补充&lt;/strong&gt;（2011-12-01）&lt;/p&gt;

&lt;p&gt;　　上面那种先记下 innerHTML 的值，修改 href 之后再将它写回去的做法是有隐患的。比如，&amp;lt;a&amp;gt; 元素里面可能不是一段文本，而是一个 &amp;lt;img&amp;gt;，并且这个 &amp;lt;img&amp;gt; 上绑定了事件，那么，再将 innerHTML 写回去时，就会生成新的节点，原来绑定的事件也就失效了。&lt;/p&gt;

&lt;p&gt;　　感谢 &lt;a href=&quot;http://hellohtml5.com/&quot; target=&quot;_blank&quot;&gt;sophiasmth&lt;/a&gt; 提供了一个更好的方法，只需在要写入的 href 的值前面加一个半角空格即可，而现代浏览器会安全地忽略掉 href 值前后的多余的空白字符。即：&lt;/p&gt;

&lt;pre class=&quot;brush: javascript;&quot;&gt;
a.href = &quot; &quot; + &quot;http://rate.taobao.com/&quot;;

// 或者：

a.href = &quot; http://rate.taobao.com/&quot;; // 注意前面的空格
&lt;/pre&gt;

&lt;p&gt;　　暂时还没发现这种方式的副作用。&lt;/p&gt;

&lt;hr noshade=&quot;noshade&quot; size=&quot;1&quot; /&gt;

&lt;p&gt;　　&lt;strong&gt;补充 2&lt;/strong&gt;（2011-12-01）&lt;/p&gt;

&lt;p&gt;　　进一步测试，发现如果 &amp;lt;a&amp;gt; 链接的内容以“www.”开头（如），也会有这个问题（&lt;a href=&quot;http://oldj.net/static/ie-href-at-bug/t3.html&quot; target=&quot;_blank&quot;&gt;测试页面&lt;/a&gt;）。看起来，凡是形如电子邮件（包含“@”字符）或网址（以“www.”开头）的字符串，在 IE6/7/8 中都有特殊行为。&lt;/p&gt;


&lt;hr noshade=&quot;noshade&quot; size=&quot;1&quot; /&gt;

&lt;p&gt;　　&lt;strong&gt;补充 3&lt;/strong&gt;（2011-12-05）&lt;/p&gt;

&lt;p&gt;　　经小马的帮助，发现国外也有类似的报告，如 &lt;a href=&quot;http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/aa6bf9a5-0c0b-4a02-a115-c5b85783ca8c&quot; target=&quot;_blank&quot;&gt;JavaScript modifying href changes link text as well for mailto: protocol&lt;/a&gt; 和 &lt;a href=&quot;http://gabriel.nagmay.com/2008/11/javascript-href-bug-in-ie/&quot; target=&quot;_blank&quot;&gt;JavaScript HREF bug in IE&lt;/a&gt;。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630217/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630217/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Wed, 30 Nov 2011 22:38:57 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/ie-bug-at-href-innerHTML/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/ie-bug-at-href-innerHTML/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630217/6320477</fs:itemid></item><item><title>学习GTD</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630218/6320477/1/item.html</link><description>&lt;p&gt;　　GTD 是 Getting Things Done 的缩写，是一种任务管理的方法。这个概念由 David Allen 提出，并随着他的书《&lt;a href=&quot;http://book.douban.com/subject/3303341/&quot; target=&quot;_blank&quot;&gt;Getting Things Done&lt;/a&gt;》（中文版名为《&lt;a href=&quot;http://book.douban.com/subject/4849382/&quot; target=&quot;_blank&quot;&gt;搞定&lt;/a&gt;》）而广为流传。&lt;/p&gt;

&lt;p&gt;　　我们都羡慕那些有着良好时间管理的效率达人，我们也都曾经或多或少地接触过一些时间管理方法或 todo 工具，或许你已经在使用一些感觉还不错的方法或工具了。那么，GTD 与其它方法相比有什么不同呢？&lt;/p&gt;

&lt;p style=&quot;font-size:16px;font-weight:bold;&quot;&gt;　　一、GTD 方法&lt;/p&gt;

&lt;p&gt;　　GTD 方法分为五个步骤：搜集、处理、组织、回顾、行动。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;搜集&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　GTD 的理念认为，我们的大脑并不擅长于记住太多的待办事项，我们每次往大脑里加入一件待办事项时，大脑的负荷就会增加一些，效率也会降低一些。因此，我们首先需要一个机制，将所有的未尽事宜从大脑中清空出去。&lt;/p&gt;
&lt;!-- oldj-article-sep --&gt;
&lt;p&gt;　　把这些未尽事宜清空的过程叫搜集。你需要将所有你想到的要处理或者可能要处理的事情全部搜集起来，放入一个妥善的收集箱之中。这些事情可能是工作上某个悬而未决的事项如新产品的发布计划，也可能是生活上某个拖了好久没处理的事情如换厨房的灯泡，甚至可以是某件暂时不太可能发生的事如设计自己梦想中的房子。搜集阶段的要点是不去考虑这个事情的重要程度与可行性，只做搜集，其它的都留到后面的步骤处理。&lt;/p&gt;

&lt;p&gt;　　如果你第一次开始 GTD 的搜集工作，这个过程可能会花费较长的时间，比如半天甚至一两天。&lt;/p&gt;

&lt;p&gt;　　收集箱可以是桌子上的一个真实的箱子，也可以是某种电脑或手机软件。收集箱的数目要少，可能的话一个就够了，并且要你随时可以很方便地访问，这样你才能确信，那些你所念念不忘的未尽事宜都在这里可以找到，只有你从心底确信这一点，你才能真正将一个事项从脑子里赶出去。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;处理&lt;/strong&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;
　　　　* 委托别人完成，或者&lt;br /&gt;
　　　　* 把它延期&lt;br /&gt;
　　* 否则&lt;br /&gt;
　　　　* 把它存档，以便将来查询，或者&lt;br /&gt;
　　　　* 把它扔掉，或者&lt;br /&gt;
　　　　* 把它放入“将来也许会”清单&lt;/p&gt;

&lt;p&gt;　　每一个待处理事项都必须要有一个具体的可以执行的下一步行动。比如，如果收集箱中的某项内容是“整合 A 项目和 B 项目”，这就只是一个较为宽泛的目标，而不是一个切实可行的操作。此时应该将其分解，直到找到可以执行的下一步行动，比如这个目标的下一步行动可能是“召集 A 和 B 的相关人员讨论一下整合细节”，更具体的行动可能是“发起一个会议邀请给相关人员”，这时，发起会议邀请就是可以执行的下一步行动。&lt;/p&gt;

&lt;p&gt;　　这儿有一个很重要的原则是两分钟原则，如果收集箱中某项需要处理的事件只需要两分钟就能处理完，则应该马上将它搞定，否则就将它委托或延期。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;组织&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　接下来，我们需要将上一步中剩下的（未被丢弃和马上处理的）事项合理地组织起来。GTD 建议可以用下一步行动、项目、等待、将来也许会几个清单来管理需要采取行动的事项。&lt;/p&gt;

&lt;p&gt;　　每个等处理事项都应该有一个具体的下一步行动，这些行动都可归入下一步行动清单。如果一个事项需要多个步骤才能完成，则应当将它列入项目清单。每个项目都应当至少有一个具体的下一步行动。&lt;/p&gt;

&lt;p&gt;　　那些需要在指定时间或日期执行的下一步行动可以放入日程表中。&lt;/p&gt;

&lt;p&gt;　　注意 GTD 中不赞成滥用日程表，日程表中应当只放置必须在指定时间或日期执行的行动，比如某个时间已定的会议，而不应该放置那些打算在那天做的事。在 GTD 中，日程表是一个神圣的地方，一个行动放入日程表，即表示该行动一定会在指定时间或日期得到执行或处理，而其它事项，则应当放入普通的下一步行动清单。&lt;/p&gt;

&lt;p&gt;　　对大多数人来说，每天的工作总会有若干不确定性，比如原本计划接下来两个小时处理某项工作，但上司突然临时指派了一件紧急任务，或者突然有一个突发事件要处理。因此，那种每天早上规则好当天每一分钟的工作，然后完美地执行的愿望是极难实现的，所以我们不应该过度使用日程表来安排工作计划，日程表应该是属于天然有时间或日期属性的事务的领地，其它工作，应该放入更灵活的下一步行动清单中。&lt;/p&gt;

&lt;p&gt;　　除了项目和下一步行动清单，我们还需要一个等待清单，用于管理那些委托他人处理的事项。你应当定期检查其中的任务是否已经可以采取行动或需要发出一个提醒。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;回顾&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　大多数人都有这样的体验：在经过几天特别忙碌的工作之后，便对周围的工作失去了控制。回顾的作用则在于让你能够在更高的角度俯看全局，对所有的事项有一个整体的把握，同时保证你的系统中所有事项都已更新到符合最新的情况，从而可以让你决定什么是当前最重要的工作。&lt;/p&gt;

&lt;p&gt;　　为了保持系统的完善，GTD 要求至少每周回顾一次所有悬而未决的事项清单：项目、下一步行动、等待、将来也许会。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;行动&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　前面所有的步骤，都是为了帮助你对各类事务采取具体的行动。&lt;/p&gt;

&lt;p&gt;　　在某个时间，应该处理哪个下一步行动呢？GTD 的回答是“靠你的直觉”。同时，GTD 也提供了一些建议，如根据当前的情境（在办公室还是在外面，在电话机前还是在电脑前）、有多少时间、有多少精力以及事务的重要性来决定，或者根据这个任务是属于预先安排好的任务、突发任务还是本身是安排任务的任务来决定，再或者，还可以从不同的层次来审视工作，根据它的重要程度到底有多高来决定。&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p&gt;　　以上就是 GTD 的基本内容。另外，GTD 并不建议将工作和生活分开处理，毕竟我们大部分人的工作与生活无论从时间上还是内容上都并不能完全分开，因此，应当将你需要处理的所有事务都纳入这一个系统中加以统一的管理。当然，这并不意味着工作与生活就会混杂在一起，事实上有很多技巧，例如使用“情境”，来在不同的场合过滤出相关的任务。&lt;/p&gt;


&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p style=&quot;font-size:16px;font-weight:bold;&quot;&gt;　　二、图示&lt;/p&gt;

&lt;p&gt;　　GTD 方法的流程如下图所示：&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201111/23/20111123223336_RhZkt.png&quot; alt=&quot;GTD&quot; /&gt;
&lt;br /&gt;　　&lt;span style=&quot;color:gray;&quot;&gt;（注：图片来自互联网。）&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;/p&gt;

&lt;p style=&quot;font-size:16px;font-weight:bold;&quot;&gt;　　三、相关资源&lt;/p&gt;

&lt;p&gt;　　1、&lt;a href=&quot;https://doit.im/cn/&quot; target=&quot;_blank&quot;&gt;Doit.im&lt;/a&gt; 国人开发的 GTD 思想的在线时间任务管理系统；
&lt;br /&gt;　　2、&lt;a href=&quot;http://gtdlife.cn/&quot; target=&quot;_blank&quot;&gt;GTDLife&lt;/a&gt; 时间管理达人的博客，很多 GTD 相关文章；
&lt;br /&gt;　　3、&lt;a href=&quot;http://www.mifengtd.cn/&quot; target=&quot;_blank&quot;&gt;褪墨&lt;/a&gt; 时间管理的博客，很多 GTD 相关文章；
&lt;br /&gt;　　4、&lt;a href=&quot;http://www.mifengtd.cn/articles/the-gtd-mastery-100.html&quot; target=&quot;_blank&quot;&gt;一百步掌握GTD&lt;/a&gt; 关于 GTD 的若干要领。
&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630218/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630218/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Wed, 23 Nov 2011 22:59:26 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/learning-GTD/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/learning-GTD/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630218/6320477</fs:itemid></item><item><title>发布一个快捷切换hosts的小工具：SwitchHosts!</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630219/6320477/1/item.html</link><description>&lt;p&gt;　　日常开发工作中，我们可能经常需要切换各种 hosts 绑定，比如在本地开发时可能需要一个开发环境的 hosts 绑定方案，发布到测试环境后又有一个测试环境的 hosts 绑定方案，然后可能还有一个预发布环境，最后可能才是真实的线上环境。本地开发过程中，还可能有多组不同的 hosts 需要绑定，比如前端开发环境，数据库对应的开发环境……。其结果就是，我们经常需要在各个 hosts 绑定之间切换，这会花掉不少时间，而且这个过程毫无乐趣可言。&lt;/p&gt;

&lt;p&gt;　　同事介绍过几个切换 hosts 方案的小工具，但看起来都不是很好，于是抽空自己动手做了一个小工具： &lt;a href=&quot;https://github.com/oldj/SwitchHosts&quot; target=&quot;_blank&quot;&gt;SwitchHosts!&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;　　这个工具是使用 Python + wxPython 开发的，因此在 Windows / Linux / Mac 上都可以运行。不过由于不熟悉 Linux 和 Mac 下 Python 的打包及编译，目前只提供了 Windows 下的二进制可执行文件&lt;a href=&quot;http://www.everbox.com/f/QTrmjdqBCMXlmG4lipbtmam6Dk&quot; target=&quot;_blank&quot;&gt;下载&lt;/a&gt;，Linux 及 Mac 用户在配置好环境（Python / wxPython / chardet）后可以直接运行源码。当然，也非常欢迎熟悉 Linux 或 Mac 平台下的 Python 的大侠帮忙制作一个 Linux 或 Mac 版的可执行文件。:-)&lt;/p&gt;

&lt;p&gt;　　程序在 Windows 下的运行截图如下：&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201110/20/20111020170516_Vc5NL.png&quot; alt=&quot;SwitchHosts!&quot; /&gt;&lt;/p&gt;
&lt;!-- oldj-article-sep --&gt;
&lt;p&gt;　　程序还可以最小化到系统托盘，在托盘图标的右键菜单上也可以快速切换各 hosts 方案：&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201110/20/20111020164554_GWwTE.png&quot; alt=&quot;系统托盘&quot; /&gt;&lt;/p&gt;

&lt;p&gt;　　感谢 &lt;a href=&quot;http://www.besteric.com/&quot; target=&quot;_blank&quot;&gt;黑三&lt;/a&gt; 和 &lt;a href=&quot;http://weibo.com/zhaozexin&quot; target=&quot;_blank&quot;&gt;小马&lt;/a&gt; 勇敢地做了这个程序的小白鼠并提出了不少意见。&lt;/p&gt;

&lt;p&gt;　　你可以在 GitHub 上查看或下载 &lt;a href=&quot;https://github.com/oldj/SwitchHosts&quot; target=&quot;_blank&quot;&gt;SwitchHosts! 的源码&lt;/a&gt;。Windows 用户也可以直接下载下面的绿色可执行版本：&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;Windows 绿色版本下载：&lt;/strong&gt;&lt;a href=&quot;http://www.everbox.com/f/QTrmjdqBCMXlmG4lipbtmam6Dk&quot; target=&quot;_blank&quot;&gt;http://www.everbox.com/f/QTrmjdqBCMXlmG4lipbtmam6Dk&lt;/a&gt;。
&lt;br /&gt;　　如上面的下载链接失效了，也可以在 &lt;a href=&quot;https://github.com/oldj/SwitchHosts/downloads&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; 或 &lt;a href=&quot;http://download.csdn.net/detail/oldjwu/3923607&quot; target=&quot;_blank&quot;&gt;CSDN&lt;/a&gt; 下载。&lt;/p&gt;

&lt;p&gt;　　欢迎各位下载使用这个小工具，如果有什么问题或发现了 bug，欢迎在这儿或 &lt;a href=&quot;https://github.com/oldj/SwitchHosts/issues&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; 上给我留言。:-)&lt;/p&gt;

&lt;p style=&quot;color:red&quot;&gt;　　&lt;strong&gt;注意：&lt;/strong&gt;请尽量不要将本程序放到中文目录下，不然可能不能运行... （感谢网友 &lt;a href=&quot;http://www.xspio.com/&quot; target=&quot;_blank&quot;&gt;xixi&lt;/a&gt; 的提醒）&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630219/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630219/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Thu, 20 Oct 2011 21:17:51 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/switchhosts/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/switchhosts/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630219/6320477</fs:itemid></item><item><title>关于让浏览器自动刷新</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630220/6320477/1/item.html</link><description>&lt;p&gt;　　最近有人开发了一个叫 &lt;a href=&quot;http://getf5.com/&quot; target=&quot;_blank&quot;&gt;F5&lt;/a&gt; 的软件，用来监视某个目录下的文件，当文件改变时刷新浏览器。它非常适合前端开发使用，尤其是多个显示屏时。&lt;/p&gt;

&lt;p&gt;　　我试用了一下这个软件，也大致研究了一下它的实现。它的原理很简单，在本地启动一个 HTTP 服务，将指定的目录作为根目录，开发者可以在浏览器中通过 http://127.0.0.1 访问这个目录下的文件。同时，这个程序会在访问的 html 文件中插入两个 js，如下：&lt;/p&gt;
&lt;!-- oldj-article-sep --&gt;
&lt;pre class=&quot;brush:html;&quot;&gt;
&amp;lt;script src=&quot;/con/assets/js/jquery-1.6.3.min.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;/con/assets/js/br.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;

&lt;p&gt;　　服务器会监视本地目录上文件的变化，如果文件发生了变化，会通过上面的 br.js 通知页面刷新。于是，实时的刷新功能就完成了。&lt;/p&gt;

&lt;p&gt;　　应该说，这个小软件很有创意，如果使用得当，有时的确能节省很多时间。&lt;/p&gt;

&lt;p&gt;　　不过，由于它需要在本地自己架设一个 HTTP 服务，所有的请求都得通过这个服务，这就也给它带来一些局限，使得它（至少在目前的版本中）只适合用在&lt;strong&gt;纯静态页面&lt;/strong&gt;的开发中。比如我最近经常需要调试 Django 程序的开发，在这类开发中，前端要修改的可能只是一些模板，如果没有后台的数据以及渲染，前端可能很难进行调试，这时，使用它的那个 HTTP 服务就力不从心了。所以，要是能有什么方法，能使用现有的 HTTP 服务，同时在指定目录下的文件发生改变后也能刷新浏览器就好了。&lt;/p&gt;

&lt;p&gt;　　于是我想到了 &lt;a href=&quot;http://www.autohotkey.com/&quot; target=&quot;_blank&quot;&gt;AutoHotKey&lt;/a&gt; （以下简称 ahk）。其实，我们要做的无非两件事：&lt;/p&gt;

&lt;p&gt;　　1、监视文件变化；&lt;br /&gt;
　　2、让浏览器刷新。&lt;/p&gt;

&lt;p&gt;　　监视文件变化比较简单，复杂的是如何让浏览器刷新。上面介绍的软件的实现方式是自己架设 HTTP 服务，然后在页面中插入脚本与服务器通信。但如果使用 ahk 的话，我们好像可以在需要时直接向浏览器发送 F5 消息，这样一来就不需要架设 HTTP 服务了。&lt;/p&gt;

&lt;p&gt;　　例如，下面的 ahk 脚本，即可实现让 IE 当前页面刷新的效果：&lt;/p&gt;

&lt;pre class=&quot;brush:plain;&quot;&gt;
; send f5 to IE

IfWinExist, ahk_class IEFrame
{
	ControlSend, , {F5}, ahk_class IEFrame
}
&lt;/pre&gt;

&lt;p&gt;　　非常简单，如果存在IE窗口（无论这个窗口当前是否获得焦点），向这个窗口发送一个 F5 消息。如果你的电脑上有 ahk 环境，并且打开了一个 IE 窗口，那么运行一下这段脚本，就能发现 IE 窗口被刷新了一次。&lt;/p&gt;

&lt;p&gt;　　ahk 脚本也能被编译为 exe 文件，这样，在没有 ahk 环境的电脑上也能执行。上面的脚本以及它所编译出来的 exe 我在&lt;a href=&quot;http://www.everbox.com/f/itPGwFHH5qNCguK5SLAp71mitG&quot; target=&quot;_blank&quot;&gt;这儿（http://www.everbox.com/f/itPGwFHH5qNCguK5SLAp71mitG）&lt;/a&gt;放了一份，有兴趣的同学可以下载到本地试试。&lt;/p&gt;

&lt;p&gt;　　这样，我们就有了一个新的实现文件修改后浏览器实时刷新的方案：以某种方式监视指定目录或文件，满足一定条件时使用 ahk 脚本（或者 ahk 脚本编译出的 exe）向浏览器发送刷新消息。&lt;/p&gt;

&lt;p&gt;　　不过，这种方案也有一些问题，比如 ahk 目前只支持 Windows 平台，所以这个方案也只能在 Windows 下有效。&lt;/p&gt;

&lt;p&gt;　　先写到这里，如果有人想到其它方案，欢迎给我留言。:-)&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630220/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630220/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Sun, 16 Oct 2011 16:43:18 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/auto-f5/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/auto-f5/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630220/6320477</fs:itemid></item><item><title>绿色版“python -m SimpleHTTPServer”</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630221/6320477/1/item.html</link><description>&lt;p&gt;　　&lt;a href=&quot;http://www.everbox.com/f/MQH4VdlA822odpKOuvkPkUUEuV&quot; target=&quot;_blank&quot;&gt;pySimpleHTTPServer&lt;/a&gt; 是一个简单的 HTTP 服务程序，功能与在安装了 Python 环境的机器上执行“python -m SimpleHTTPServer”一样。&lt;/p&gt;

&lt;p&gt;　　由于经常需要临时搭建一个 HTTP 服务用于测试，在安装了 Python 环境的机器上这很简单，但有时需要在一些只有最基本的系统的测试机上做测试（比如测试页面在 IE6/7 下的表现），因此，我将 Python 的这个方便的功能做成了一个绿色 .exe 文件。&lt;/p&gt;
&lt;!-- oldj-article-sep --&gt;
&lt;p&gt;　　源码很简单：&lt;/p&gt;

&lt;pre class=&quot;brush:python;&quot;&gt;
# -*- coding: utf-8 -*-

u&quot;&quot;&quot;******************************************************
pySimpleHTTPServer

@about: http://oldj.net/article/py-simple-http-server/
******************************************************
&quot;&quot;&quot;

import sys
import BaseHTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
HandlerClass = SimpleHTTPRequestHandler
ServerClass  = BaseHTTPServer.HTTPServer
Protocol     = &quot;HTTP/1.0&quot;

port = int(sys.argv[1]) if sys.argv[1:] else 8000
server_address = ('127.0.0.1', port)

HandlerClass.protocol_version = Protocol
httpd = ServerClass(server_address, HandlerClass)

sa = httpd.socket.getsockname()
print(__doc__)
print(&quot;Serving HTTP on %s, port %d...&quot; % (sa[0], sa[1]))
httpd.serve_forever()

&lt;/pre&gt;

&lt;p&gt;　　然后，使用 &lt;a href=&quot;http://www.pyinstaller.org&quot; target=&quot;_blank&quot;&gt;pyInstaller&lt;/a&gt; 制作成 Windows 平台可执行的绿色软件，就大功告成了。&lt;/p&gt;

&lt;p&gt;　　pySimpleHTTPServer 的用法很简单，直接双击，然后在浏览器访问 http://127.0.0.1:8000 就可以了，网站的根目录就是 pySimpleHTTPServer 程序所在的目录。也可以使用命令行运行，如：&lt;/p&gt;

&lt;pre class=&quot;brush:plain;&quot;&gt;
cmd&amp;gt; pySimpleHTTPServer 8080
&lt;/pre&gt;

&lt;p&gt;　　其中第二个参数是 HTTP 服务的端口，如果省略则为 8000，可以指定其它值。&lt;/p&gt;

&lt;p&gt;　　最后，再贴一下下载地址：&lt;a href=&quot;http://www.everbox.com/f/MQH4VdlA822odpKOuvkPkUUEuV&quot; target=&quot;_blank&quot;&gt;http://www.everbox.com/f/MQH4VdlA822odpKOuvkPkUUEuV&lt;/a&gt;。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630221/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630221/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Fri, 23 Sep 2011 21:59:45 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/py-simple-http-server/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/py-simple-http-server/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630221/6320477</fs:itemid></item><item><title>关于黑暗森林</title><link>http://item.feedsky.com/~feedsky/oldj/~8217290/607630222/6320477/1/item.html</link><description>&lt;p&gt;　　关注国内科幻小说的朋友应该对“黑暗森林”这个词不陌生，是的，这儿说的就是大名鼎鼎的刘慈欣（以下简称“大刘”，“大刘”即“大名鼎鼎的刘慈欣”）在他的《三体》三部曲第二部中提出的那个黑暗森林。虽然在《三体》第三部中作者对黑暗森林的概念做了一些延伸和补充，但总的来说，这个词的含义基本上就是第二部结尾处对它的描述：&lt;/p&gt;

&lt;p&gt;　　“宇宙就是一座黑暗森林，每个文明都是带枪的猎人，像幽灵般潜行于林间，轻轻拨开挡路的树枝，竭力不让脚步发出一点儿声音，连呼吸都必须小心翼翼：他必须小心，因为林中到处都有与他一样潜行的猎人，如果他发现了别的生命，能做的只有一件事：开枪消灭之。在这片森林中，他人就是地狱，就是永恒的威胁，任何暴露自己存在的生命都将很快被消灭，这就是宇宙文明的图景，这就是对&lt;a href=&quot;http://zh.wikipedia.org/wiki/%E8%B4%B9%E7%B1%B3%E6%82%96%E8%AE%BA&quot; target=&quot;_blank&quot;&gt;费米悖论&lt;/a&gt;的解释。”&lt;/p&gt;

&lt;p&gt;　　&lt;img src=&quot;http://oldj.net/uploads/files/201108/21/20110821140020_5LJNu.jpg&quot; alt=&quot;《三体II·黑暗森林》封面&quot; /&gt;&lt;/p&gt;
&lt;!-- oldj-article-sep --&gt;
&lt;p&gt;　　这个理论的有趣之处在于，它向我们描绘了一个黑暗的宇宙图景。和我们所看的大多数科幻不同，也与我们所接受了多年的教育及幻想不同，黑暗森林告诉我们，宇宙是一个极为险恶的地方，如果存在外星文明并且他们发现了我们，他们几乎不可能对我们表现出善意，更可能的是毫不犹豫地消灭我们，同样地，如果是我们先发现了外星文明，我们所能做的最明智的选择也是立即消灭他们。在宇宙中，跨越种族的星际大同几乎不可能存在，被发现，即被毁灭！&lt;/p&gt;

&lt;p&gt;　　对大多数人来说，这个理论可谓惊世骇俗，在看惯了各种友好的外星种族的描述之后，大刘在《三体》中告诉大家：科幻小说不是成人童话，而是充满了血与火的冒险。自从《三体》第二部出版以来，网络上关于黑暗森林的讨论数不胜数，有人赞同有人否定。对于一部小说来说，能引起这么多的思考和讨论，真是超乎寻常的成功，无论大家是否赞同大刘在著作中的观点，他至少启迪了我们的思维，给我们展示了另外一种可能。不过我看了很多讨论，发现大部分赞同的人似乎没有提到我比较认同的理由，否定的人也没有给出让我信服的论证，因此也在这儿随便写一点自己的一些理解。&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;一、宇宙中的黑暗森林&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　在《三体》中，黑暗森林其实是对“宇宙社会学”的一种简单比喻。“宇宙社会学”有两大基本公理：&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;1、生存是文明的第一需要；&lt;/strong&gt;&lt;br /&gt;
　　&lt;strong&gt;2、文明不断增长和扩张，但宇宙中的物质总量保持不变。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　乍一看，多像经济学中的理性人以及有限资源假设啊！&lt;/p&gt;

&lt;p&gt;　　除了这两大公理，书中还提到两个重要概念：&lt;strong&gt;猜疑链&lt;/strong&gt;、&lt;strong&gt;技术爆炸&lt;/strong&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;a href=&quot;http://zh.wikipedia.org/wiki/%E4%BA%9A%E4%BC%AF%E6%8B%89%E7%BD%95%C2%B7%E9%A9%AC%E6%96%AF%E6%B4%9B#.E9.9C.80.E6.B1.82.E5.B1.82.E6.AC.A1&quot; target=&quot;_blank&quot;&gt;马斯洛需求理论&lt;/a&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;　　很多读者，甚至包括作者，都补充了很多其它信息或条件，比如宇宙空间超远的距离，以及由此而来的通讯的低效（根据相对论，信息的传递不能超过真空中的光速，比如我们向最近的恒星半人马座α发出一条消息，要 8 年多后才能收到回复，虽然可能存在根据量子纠缠态制作的可以超距通讯的智子，但第一次将智子发射到对方星系的过程还是不能超过光速），这些都将加长猜疑链。但其实，这两个重要概念中，真正重要的是技术爆炸，猜疑链只是技术爆炸的结果。而技术爆炸的背后，其实是双方无法判断对方是不是拥有能置己方于死地的能力。更确切一点来说，是双方无法判断对方现在或者在不久的将来是否具备：&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;以较低的成本彻底消灭对手的能力&lt;/strong&gt;！&lt;/p&gt;

&lt;p&gt;　　我们来设想一下，宇宙中两个文明相遇了，假设他们相互合作共同发展，双方都能获得最大的收益（比如收益为1），如果相互攻击，则双方都遭受损失（比如收益为-8），如果一方攻击另一方不攻击，则攻击的一方收益不变，被攻击的一方收益最小（比如为-10）。这时，两个文明就陷入了典型的囚徒博弈。&lt;/p&gt;

&lt;p&gt;　　如果这个博弈能进行很多次（比如无限次，或虽然次数有限，但谁也不知道最后一次是什么时候），那么最终能取得优势的一定是类似一报还一报（&lt;a href=&quot;http://zh.wikipedia.org/wiki/%E4%BB%A5%E7%89%99%E9%82%84%E7%89%99&quot; target=&quot;_blank&quot;&gt;Tit for Tat&lt;/a&gt;）这样的合作策略，简单来说是合作策略，文明不会主动彼此攻击，宇宙是光明的，黑暗森林不存在。但这需要一个前提，就是这个博弈能进行很多次。如果像上面说的，文明拥有低成本消灭对方的能力，那么这个博弈就将只能进行一次，囚徒博弈就沦为&lt;a href=&quot;http://zh.wikipedia.org/wiki/%E5%9B%9A%E5%BE%92%E5%9B%B0%E5%A2%83&quot; target=&quot;_blank&quot;&gt;囚徒困境&lt;/a&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;

&lt;p&gt;　　如果存在这样的攻击武器或手段，那么，即使宇宙中上千亿个文明中 99.99% 的文明都是保守的或善意的，但只要有 0.01% 甚至 0.00001% 甚至更少的文明是恶意的，黑暗森林就会出现。就像木桶能装多少水取决于它最短的那块板，宇宙的森林有多黑则取决于它最黑的角落有多黑，这大概是黑暗森林版的水桶原理吧！&lt;/p&gt;

&lt;p&gt;　　&lt;/p&gt;

&lt;p&gt;　　&lt;strong&gt;二、IT 世界的黑暗森林&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;　　一定有人还是不愿意相信黑暗森林的存在，他们从心底希望这个世界是光明的，即便有黑暗也只是暂时的和局部的。但我想，在国内从事 IT 行业的人不应该抱有这样的想法，因为我们所处的行业就是一个黑暗森林。&lt;/p&gt;

&lt;p&gt;　　看看这个表格吧：&lt;/p&gt;

&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;common-table&quot;&gt;
&lt;thead&gt;
	&lt;tr&gt;&lt;td width=&quot;50%&quot;&gt;宇宙&lt;/td&gt;&lt;td&gt;IT行业&lt;/td&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
	&lt;tr&gt;&lt;td&gt;生存是文明的第一需要
	&lt;/td&gt;&lt;td&gt;生存是企业的第一需要
	&lt;/td&gt;&lt;/tr&gt;

	&lt;tr&gt;&lt;td&gt;文明不断增长和扩张，但宇宙中的物质总量保持不变
	&lt;/td&gt;&lt;td&gt;企业不断发展和扩张，但市场总大小在一段时间内相对保持不变
	&lt;/td&gt;&lt;/tr&gt;

	&lt;tr&gt;&lt;td&gt;猜疑链
	&lt;/td&gt;&lt;td&gt;猜疑链
	&lt;/td&gt;&lt;/tr&gt;

	&lt;tr&gt;&lt;td&gt;技术爆炸，一个落后的文明可能在短时间内赶上甚至超过先进文明
	&lt;/td&gt;&lt;td&gt;新应用爆炸，一个不知名的小应用可能在短时间内占领市场并成为主流
	&lt;/td&gt;&lt;/tr&gt;

	&lt;tr&gt;&lt;td&gt;先进文明具备低成本彻底消灭落后文明的能力
	&lt;/td&gt;&lt;td&gt;大公司具备低成本扼杀小公司的能力
	&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;　　想一想，微软是怎么发家的？Google 是怎么崛起的？Facebook 又是怎么流行的？在这个行业，任何一个人都不知道三年甚至一年后会发生什么变化，任何一个不起眼的小网站都可能会引领下一场革命，几个在车库里敲代码的默默无闻年轻人可能在几个月后就成为了举世瞩目的明星，那个现在不可一世的大佬可能半年后就被一家不知名的新成立的公司拉下了神坛。看看这个快速变化着的世界，像不像那个遥远冷寂但随时可能发生技术爆炸的宇宙？&lt;/p&gt;

&lt;p&gt;　　再看看 IT 世界的大佬们对那些后起之秀们做了些什么吧！每当有人想出了什么好的点子，做出了一个不错的新东西，总会至少有一位大佬将它原封不动地抄过来，然后利用自己已有的庞大的用户群及资源的优势，把原来的创新者消灭在摇篮之中（参见《&lt;a href=&quot;http://tech.sina.com.cn/i/2010-07-24/20434467150.shtml&quot; target=&quot;_blank&quot;&gt;狗日的腾讯&lt;/a&gt;》）。这像不像《三体》中神级文明对低级文明的黑暗森林打击？&lt;/p&gt;

&lt;p&gt;　　大佬们为什么不愿意和新秀们和平共处，共同建立一个 IT 大同世界？因为新的应用爆炸随时可能发生，那些现在看起来毫无威胁的小公司，很有可能在极短的时间内崛起并取代现在大佬的位置。大佬不能允许这样的事情发生（或者说，允许这样的事情发生的大佬都已经被消灭了，自然选择的结果是只有具有“清理”基因的大佬才能存活），但只要发现及时，扼杀这些小公司对它们而言是很简单成本也很低的事，它们最有力的武器，就是&lt;strong&gt;抄袭&lt;/strong&gt;，在 IT 世界，大佬们手中的这个武器就相当于《三体》中的质量点或二向箔（其实二向箔更像另一种打击，想一想饭否与新浪微博之间的前世今生），拥有完全消灭小公司的能力。&lt;/p&gt;

&lt;p&gt;　　当然，正如同《三体》中，黑暗森林并不是宇宙的全部，其实它只是更大的宇宙战争之间的插曲，真正的宇宙战争超乎《三体》中的人类的想象，抄袭也并不是 IT 世界的所有手段，更大规模的正面战争发生在现有的诸位大佬之间，比如腾讯、百度以及阿里几大巨头之间的竞争，这一些竞争可能与黑暗森林没有直接的联系，但由于这些巨头的存在，这个行业对新创立的小公司而言，就是一片黑暗森林。&lt;/p&gt;

&lt;p&gt;　　另外，正如很多人所期望的那样，几大巨头似乎并不是人人都热衷于打压新的小公司。但正如上面的水桶原理所说的，这个环境中，只要有一个大佬采取黑暗森林打击的做法，整个环境就会变成黑暗森林。据说很多小公司找 VC 时，VC 都会问他们一个问题：“你们做的东西，如果腾讯也做怎么办？”就算百度、阿里、盛大、网易、新浪、搜狐这些大佬都对新出现的小公司采取宽容的态度，只要有一个巨头（比如腾讯）对它们进行打击，这个环境对这些小公司来说就是一片险恶的黑暗森林。&lt;/p&gt;

&lt;p&gt;　　不过，从公司之间的竞争上，我们似乎也看到了黑暗森林的另一个光明一点的解：收购。对于小公司而言，如果没有机会独自发展壮大，那么被收购总是比被消灭要好。当然，前提是，要有大佬认为你值得收购，并且收购你比消灭你更划算。&lt;/p&gt;

&lt;p&gt;　　对应地，在宇宙中，不同的星际种族之间，是否可能发生类似的“收购”呢？或许新发现了一个文明后，大佬除了“清理”，还有一个“招安”的选择？也或许，不同种族之间巨大的鸿沟导致文明不可能完全融合，进而导致这种“收购”不可能发生，大佬仍然只能进行“清理”？这一切现在的我们都无从知晓，毕竟，在辽阔的宇宙中，我们还只是小小的孱弱的虫子。&lt;/p&gt;&lt;img src=&quot;http://www1.feedsky.com/t1/607630222/oldj/feedsky/s.gif?r=http://item.feedsky.com/~feedsky/oldj/~8217290/607630222/6320477/1/item.html&quot; border=&quot;0&quot; height=&quot;0&quot; width=&quot;0&quot; style=&quot;position:absolute&quot; /&gt;</description><pubDate>Sun, 21 Aug 2011 16:04:20 +0800</pubDate><author>oldj</author><guid isPermaLink="false">http://oldj.net/article/dark-forest/</guid><dc:creator>oldj</dc:creator><fs:srclink>http://oldj.net/article/dark-forest/</fs:srclink><fs:srcfeed>http://oldj.net/article/rss/latest/</fs:srcfeed><fs:itemid>feedsky/oldj/~8217290/607630222/6320477</fs:itemid></item></channel></rss>
