呃,我搬家了

Sun May 22 23:08:59 2011

最近在重写 Tublog。作为一个喜新厌旧的人,我看着这个 Blog 感觉有点厌了,正好 Tublog 重写到勉强能用的程度,于是就重新搭建了一个 blog。

这个 blog 将停止更新。新的 blog 地址是 http://blog.iwinux.info(免翻墙)或 http://iwinux.appspot.com(需翻墙)。

See you in another life~

Hacking Parcellite

Sat Nov 13 14:29:19 2010

Updated: 在 Parcellite 的项目主页看到作者宣布停止开发,于是我在 Bitbucket.org fork 了一个 ParcelliteMod 项目,有兴趣的可以看看。

Parcellite 是一个轻量级的 GTK+ 剪切板管理器,是用C写的。今天花了点时间给它加了两个新功能,一是显示当前剪贴历史条目的数量,二是在菜单里加入“合并所有条目”的选项。这两个功能主要是方便我看书的时候可以一直按 Ctrl-C 复制文本,等快到达数目上限(我设为50条)时,再打开Evernote(在Wine下运行)全部粘贴进去。

剪贴板历史的管理是在 history.c 里实现的,存储在全局变量 history 中。history 是一个单向链表,用的是 GLib 里的 GSList,其支持的操作可以参考这里。首先在 history.c 里增加一个 merge_history() 函数,其作用是合并所有条目。

/* Merges all entries into one big entry */

void merge_history() {
    if ((history != NULL) && (history->data != NULL)) {
        GSList* element;
        GString* item = g_string_new("");
        history = g_slist_reverse(history);
        for (element = history; element != NULL; element = element->next) {
            g_string_append(item, (gchar*)element->data);
            g_string_append(item, "\n");
        }
        history = g_slist_prepend(history, g_strdup(item->str));
        g_string_free(item, TRUE);
        g_slist_free(history->next);
        history->next = NULL;
    }
}

然后再修改 main.c 的 show_history_menu 函数,在 Parcellite 的历史菜单里加入 Entry Count 和 Merge All 这两个菜单项。merge_selected() 是 Merge All 菜单项对应的事件处理函数。

/* Called when Merge All is selected from history menu */
static void merge_selected(GtkMenuItem *menu_item, gpointer user_data)
{
  /* Check for confirm merge option */
  if (prefs.confirm_clear)
  {
    GtkWidget* confirm_dialog = gtk_message_dialog_new(NULL,
                                                       GTK_DIALOG_MODAL,
                                                       GTK_MESSAGE_OTHER,
                                                       GTK_BUTTONS_OK_CANCEL,
                                                       "Merge all entries?");
   
    if (gtk_dialog_run((GtkDialog*)confirm_dialog) == GTK_RESPONSE_OK)
    {
      merge_history();
      save_history();
      if ((history != NULL) && (history->data != NULL)) {
          gtk_clipboard_set_text(clipboard, (gchar*)history->data, -1);
          gtk_clipboard_set_text(primary, (gchar*)history->data, -1);
      }
    }
    gtk_widget_destroy(confirm_dialog);
  }
}

/* add the following lines into show_history_menu */
/* show entry count in the menu */
GString* etstr = g_string_new("");
g_string_printf(etstr, "Entry Count:%d", g_slist_length(history));
menu_item = gtk_menu_item_new_with_label(etstr->str);
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);
g_string_free(etstr, TRUE);

/* add "merge all" */
menu_item = gtk_menu_item_new_with_label("Merge All");
g_signal_connect((GObject*)menu_item, "activate", (GCallback)merge_selected, NULL);
gtk_menu_shell_append((GtkMenuShell*)menu, menu_item);

修改完之后在 Parcellite 源码目录下执行 ./configure --prefix=/usr && make && sudo make install 就搞定了~~~

Code Reading 读书笔记

Fri Oct 1 21:40:21 2010

P.S. 这是寒假时写的笔记,压箱底了,今天整理笔记的时候看到,姑且发出来,希望这些东西有点用。

本书的核心思想

  1. 通过阅读代码学习程序设计
  2. 通过阅读代码更好地完成编程任务(除错,扩展,重构,etc)
  3. 利用一切手段减少代码阅读量

阅读代码的辅助工具

工具的类别

  1. 正则表达式
  2. 代码索引
  3. 代码浏览
  4. 编译器
  5. 美化、着色、对比(diff)
  6. 调试器
  7. 运行时工具(API 调用、网络、GUI、优化器)
  8. 自制

工具可以完成的任务

  1. 查看对象的定义、实现
  2. 检索对象调用和被调用的情况
  3. 了解、查看代码结构
  4. 定位特定代码
  5. 查错
  6. 了解代码与外界(API,网络,GUI)互动的情况
  7. 查看代码实际执行的情况

零散细节

正则表达式 \< \> 词的开始、结束。正则表达式查找:Emacs C-M-s 和 C-M-r;Vim / ?

Emacs C-M-u C-M-d C-M-m C-M-p 在代码块中移动光标

代码索引工具:idutils, Exuberant ctags, webglimpse.org

概览模式: Emacs C-x $ (set-selective-display), M-x outline-mode; Vim set-foldenable Zm Zr Zo Zc

grep: -e 显式指定表达式 -l 只列出文件名(当结果只有一个文件时 grep 会输出空行,因此最好加上 -H 即 --with-filename)

代码浏览:cscope, http://cbrowser.sourceforge.net, http://source.redhat.com/sourcenav/, http://lxr.linux.no

代码美化:cb, indent, vgrind, cdecl

运行时监控:strace, API spy; Xev, Spy4Win; tcpdump, WinDump

优化器:gprof 配合 gcc -a -pg -g

调试器前端:xxgdb

http://www.dmst.aued.gr/dds/sw/outwit

把关键代码打印出来读,理解复杂算法时要全神贯注,避免 cache miss, 最好连纸笔都不要用

编译器的创意用法

利用警告信息找到可能存在 bug 的地方

在对象的定义处改掉其名字,编译,看错误信息,即可知道对象在何处被调用

gcc -E 展开代码中的预处理指令

通过符号表了解代码结构 (nm, dumpbin)

gcc -S 通过汇编结果看代码的实际编译情况(比如某些机器、平台特性的表现)

Tublog 1.0.2.3 Bugfix

Sun Sep 26 16:36:07 2010

UPDATED: Tublog 1.0.2.3 Bugfix 源码包已经发布,可以在这里下载。

说来惭愧,之前我挑选的那个“用起来顺手”的 YUI Editor,第一次正式使用就原形毕露。写完 Tublog 1.0.2 Released,发布之后一看,页面显示全乱了,查看HTML发现文章的 <ol></ol> 标记外面出现了一个莫名其妙的 <li>,而在下一个段落前面则出现了未闭合的 <div>……于是昨晚就换掉了那个破编辑器,现在用的是 CKEditor。这是 bug fix 的第一个内容。

同时还修复了一个由于使用 memcache 不当引起 bug,这个 bug 会导致最新发布的文章的标签无法正常显示。

本来这样就结束了,更新程序到 GAE 服务器,刷新首页却出现 internal server error,在后台看到是因为缺少必要的索引而引起的错误。Bug fix 的第三个内容就是清理原先残留的无用的索引,再添加进新的。

最后还在 Updater 里添加了更新数据表结构的代码,由旧版升级的,在升级新版之后访问 /admin/update 即可升级数据。

此次 bug fix 的代码已经提交到 google code 的 hg 仓库,但要观察几天才发布可供下载的源码包(其实昨晚很草率地在未完全修复前发布了两次,所幸及时删除了)。在这里我要为我的粗心大意给大家道歉。

注意:上传 index.yaml 到 GAE 服务器之后还需要等待一段时间索引才能构建完成(有可能要等几个小时),建议在上传之前将 app.yaml 里的 version 修改一个不同的数值,这样上传时就会创建一个不同的版本,不会影响当前正常运行的版本,等索引构建完成后(在GAE的管理后台可以查看索引的状态)再切换到新版本。

Tublog 1.0.2 Released

Sun Sep 26 16:36:07 2010

暑假在家,除了看书、做读书笔记和学Clojure之外,晚上有空就写一点 Tublog 的代码,一个月竟然就这样过去了。这次的更新主要是前端的,后端则是修修补补:

  1. 换掉了原先的那个富文本编辑器(jQuery + 插件实现的),因为一直觉得它不好用。新的编辑器是 YUI Editor,大概是直接通过雅虎服务器加载的缘故,载入速度比原先的编辑器快,用起来也顺手。比较吐槽的是,默认的编辑器竟然不提供编辑HTML的功能,还要另外写代码加进去(所幸在 YUI Examples 里找到了 现成的代码
  2. 设计了新的后台管理界面(有图有真相http://img.twitrpix.com/bnm9)。把宽度加大了,现在应该没有人用 800x600 的分辨率了吧...小声说一句,我终于弄懂了怎么用渐变背景,也终于学会了怎样用CSS控制表单布局(原先的都是表格 ToT)
  3. 现在 Page 只会出现在 PageList 这个 widget 里,主内容区看不到,按标签检索也不会看到,这样才像 Page 嘛~当然要在页面顶部显示是可以的,但要修改主题文件,迟一点我会写关于主题的文档,到时大家就可以照着文档编写自己的主题了(这是后话,不知何日兑现 = =)

详细内容还是看 Tublog Release Notes吧。

BTW:经过试验,如果想给自己的 blog 添加 Google Analytics 代码的话,可以直接把那段代码塞进 CustomHtml 这个 widget 里。

BBTW:之后这段时间我又要去学一些新的东西了,等学完了用新技术来继续折腾 Tublog >__<

Tublog 1.0.1 Released

Sun Sep 26 16:36:07 2010

如果我没记错,Tublog上一次大动作是在去年暑假,当时几乎把代码重写了一遍。今年3月份的时候写了一个新的主题,功能方面却一直没任何更新。这几天终于抽时间出来更新 Tublog 的代码,今天发布了 Tublog 1.0.1。这个版本相对于 1.0.0 有以下改动:
  1. 页面支持(两个主题都尚未更新相关UI支持,目前可通过 PageList 这个 widget 在 sidebar 里列出所有页面;目前page跟post一样都会出现在主页面以及RSS里)
  2. Links widget(实现方式相当地偷懒,所有的链接都写在一个大文本框里,一行一个链接,标题和URL之间用英文逗号分隔……)
  3. Anti-spam (spam 这个问题困扰了我很久,之前一直懒得理它,这次终于下定决心,加入了简易的 anti-spam 机制。如果这机制不奏效的话我就要考虑用复杂的验证码了)
详情请看 Tublog Release Notes

正确认识心理学 Part III - 心理学简史

Sun Sep 26 16:36:07 2010

由于一直没找到合适的切入点,本系列文停滞了近一个月没有更新。今天我想简要地谈一谈心理学(除非特别指出,以后我都用“心理学”指代“科学心理学”)的发展史。 

似乎是一个固定的套路,在介绍一门学科时总要先说说它的历史。为什么呢?我认为有两个原因。 

首先,这是在向开荒的先人致敬。知识的创新并不是一件容易的事情,不仅要跳出已有的框框,还有可能要挑战世人的接受能力,许多人甚至为此付出生命(我不想举例了,你懂的)。所以我们在享用前人创造的知识的时候,不能忘记他们所做的贡献(在电脑前放一幅冯·诺依曼的画像,保证你的电脑不死机不中木马——开个玩笑)。 另一方面,了解历史是理清学科理论来龙去脉的必经步骤。任何既有理论的成型都不可能在一夜之间发生,从猜想到雏形再到最后成熟的理论,当中的波折,往往就是理解该理论的关键。比如在学习狭义相对论之前,如果对物理学家们因牛顿的经典时空观而走的弯路有所知晓的话,就比较容易理解为什么爱因斯坦要建立新的时空观。 

心理学也是一样的。今天的心理学有二十多个分支1,近四十种流派2, 研究的课题十分繁杂,乍一看恐怕会晕头转向。要想找到一个清晰的脉络,还是先回到19世纪末的欧洲比较好。 

在1879年之前——与其它学科类似——心理学一直被当作是哲学的一个分支(哲学偶尔会是一个贬义词,在某种程度上与科学对立)。1879年,Wilhelm Wundt 在德国的莱比锡大学创建了世界上第一个心理学实验室,标志着心理学从哲学独立出来,成为一门实验科学。Wundt 因此被称作“心理学之父”3。 

很快的,第一个心理学流派也诞生了。E.B.Titchener(Wundt 的一个学生)是结构主义的创始人,主张将人的意识解构为三种基本元素(感觉、意象和情感),让实验对象自身运用内省的方法检视意识的内容。由于研究方法太过主观,缺乏可靠性,结构主义受到许多批评,其影响也比较有限4。与结构主义对立的,是以 William James 为代表的功能主义。这一派的学者视意识为不断变化的持续过程,用直接观察的方式研究实验对象的意识。结构主义和功能主义今天都不再以独立派别出现,但两派学者的思想都影响了心理学后续的发展。 

而现存的派别里,最为悠久的是精神分析,以及行为主义。两者都于19世纪末、20世纪初出现,并盛行了近50年。直到20世纪50年代之后,才逐渐有其它流派(比如人本主义心理学和认知心理学)来挑战它们的地位。这些挑战并没有完全成功——在我看来,如今心理学仍然是各派割据,在许多问题上仍无定论。但这不仅没有成为心理学发展的障碍,反而拓宽了心理学研究的视角(详情日后再谈)。 

需要说明的是,心理学的发展并不是闭门造车。最明显的例子就是现在认知心理学的研究者们会运用核磁共振等生物技术来检测大脑活动,而可能比较少人知道的是,上世纪70年代计算机科学家们对人工智能的研究为认知心理学的发展提供了许多助益。跨学科也许是心理学研究的一个趋势。

而随着心理学理论一步步走向成熟,也许有一天我们真的能如苏格拉底所说,认识我们自己。 


Notes:

1 http://en.wikipedia.org/wiki/Subfields_of_psychology

2 http://en.wikipedia.org/wiki/List_of_psychological_schools

3 http://en.wikipedia.org/wiki/Psychology#History

4 http://psychology.about.com/od/historyofpsychology/a/psychistory.htm