Y combinator in PHP

$Y = function($f){
    $x = function($s) use($f){
        return function($n) use($s, $f){
            return $f($s($s))->__invoke($n);
        };
    };
    return $x($x);
};

$factorial = $Y(function($recurse){
    return function($x) use($recurse){
        return $x === 0 ? 1 : $x * $recurse($x - 1);  
    };         
});

echo $factorial(6); // 720

花了一些时间用 PHP 推导了一遍 Y combinator,因为语法上的一些原因,推导过程中总觉得怪怪的。推导结果代码如上所示,精简和美观程度与 LISP 相差甚远,也就勉强能看。

手动更新 VirtualBox Guest Additions

在使用 Vagrant 作为开发环境一段时间后,发现 Vagrant 也并不能做到完全的无缝迁移。昨天更新系统软件之后,在运行 Vagrant 时提示了 VirtualBox Guest Additions 版本不匹配的警告。这个问题一般是由 VirtualBox 更新引起的,在我们的 Vagrant 环境中,Guest Additions 版本不同最可能出现的问题就是无法挂载共享文件夹。

上面的问题可以通过升级 Vagrant box 里的 Guest Additions 来解决,升级也可以采用自动和手动两种方式。

Vagrant 的 Vbguest plugin 可以在启动时自动检测 Guest Additions 的版本,如果版本不同则自动更新同步,具体教程可以参考这篇教程:Vagrant Tip: Sync VirtualBox Guest Additions。我测试了一下这个插件,整个更新过程基本上是不需要人为操作,但是由于国内的网络问题,可能在下载新版本 Guest Additions(60MB+) 时需要等待比较长的时间。

不过我最后还是采用了手动更新,因为使用 Vagrant 的目的是尽量不折腾,就没必要在解决由于依赖引起的问题时引入新的依赖隐患。Guest Additions 的介绍和手动更新方法在 VirtualBox 的文档 Chapter 4. Guest Additions 中写得非常详细了。在这里只是把步骤做一个简单的记录,方便以后需要时查看。

# 在 Vagrant box 中安装更新依赖
vagrant@precise32:~$ sudo apt-get install linux-headers-$(uname -r) build-essential dkms
# 下载新版本的 Guest Additions
vagrant@precise32:~$ wget http://download.virtualbox.org/virtualbox/4.3.10/VBoxGuestAdditions_4.3.10.iso
# 挂载镜像
vagrant@precise32:~$ sudo mount VBoxGuestAdditions_4.3.10.iso -o loop,ro /mnt
# 卸载旧版本,可省略
vagrant@precise32:~$ sudo sh /mnt/VBoxLinuxAdditions.run uninstall
# 安装新版本
vagrant@precise32:~$ sudo sh /mnt/VBoxLinuxAdditions.run

手动安装的过程也非常简单,但是不论是手动还是自动安装完成之后,启动时都可能会遇到以下这两个问题:

Failed to mount folders in Linux guest. This is usually because
the "vboxsf" file system is not available. Please verify that
the guest additions are properly installed in the guest and
can work properly. The command attempted was:

mount -t vboxsf -o uid=`id -u vagrant`,gid=`getent group vagrant | cut -d: -f3` /vagrant /vagrant
mount -t vboxsf -o uid=`id -u vagrant`,gid=`id -g vagrant` /vagrant /vagrant

这个问题普遍反映在 Guest Additions 4.3.10 版本中,可以通过这个命令解决:

vagrant@precise32:~$ sudo ln -sv /opt/VBoxGuestAdditions-4.3.10/lib/VBoxGuestAdditions \
/usr/lib/VBoxGuestAdditions

另外一个问题则表现为在新版安装成功之后依然会提示版本不匹配,VirtualBox 检测到的仍然是已经卸载掉的旧版本。首先需要确认新版本已经安装成功,检测是否已经安装了 vboxguest :

vagrant@precise32:~$ lsmod | grep -i vbox
vboxsf                 42523  1 
vboxguest             219244  2 vboxsf
vboxvideo              12550  0 
drm                   197692  1 vboxvideo

查看 vboxguest 版本:

vagrant@precise32:~$ modinfo vboxguest
filename:       /lib/modules/3.2.0-23-generic-pae/updates/dkms/vboxguest.ko
version:        4.3.10
license:        GPL
description:    Oracle VM VirtualBox Guest Additions for Linux Module
author:         Oracle Corporation
srcversion:     FDAC0A8218FE4AEFD8732E8
alias:          pci:v000080EEd0000CAFEsv00000000sd00000000bc*sc*i*
depends:        
vermagic:       3.2.0-23-generic-pae SMP mod_unload modversions 686 

查看 guestproperty 中的版本号(id 可以在项目目录的 .vagrant/machines/default/virtualbox/id 中查看):

MiniThinker@localhost:~$ VBoxManage guestproperty get <id> /VirtualBox/GuestAdd/Version
Value: 4.2

前面确认已经成功安装新版本,VBoxManage 仍然检测到的是旧版本,手动设置更新版本号:

MiniThinker@localhost:~$ VBoxManage guestproperty set <id> /VirtualBox/GuestAdd/Version

在问题解决后重新打包整个环境即可,如果不怕麻烦的话,可以卸载和删掉更新过程中安装和下载的文件。

Youdao firefox alpha

Chromium 升级到版本 37 后在 linux 下的变现欠佳。特别是在字体方面,就算在 Chromium 中设置了相关的字体,也不能完全得到改善。不过 Firefox 32 的表现却与之相反,启动速度、浏览器界面与字体渲染都令人非常满意,所以在 Chromium 相关问题未得到解决的情况下,我会将 Firefox 作为默认浏览器。之前在 Chromium 中使用的大部分插件也有对应的 Firefox 版本,没有迁移障碍。不过翻译插件的迁移就没有那么顺利了。

之前在 Chromium 中主要使用有道词典的划词插件以及网友开发的 Halo 词典,这两者都没有对应的 Firefox 版本。Firefox 自身的翻译插件基本上都是基于 Google 翻译,以目前的网络环境来看,没翻墙的情况下差不多都已残废。我在 Firefox 插件列表中找到了一个叫 Youdao-firefox 的翻译插件,界面和快捷键还比较适合,但是试用的时候发现无法会出现无法翻译的问题,把插件解包了大致看了一下源代码,它依然使用的是 Google 翻译的接口,这是几个意思啊?

既然没能找到好用的翻译插件,那就只能自己动手了,Youdao-firefox-alpha 就这样诞生了。我在 Youdao-firefox 的基础上,将 Google 翻译接口调整为有道词典的接口,删掉了仅对 Google 有用的多语言翻译配置,以及除中英以外的其他国际化支持。在界面和快捷键上仍然与原版一致,在选中单词过后按下 alt + z 就会弹出翻译框。暂时我只添加了有道翻译的基本释意,所以在翻译结果中不能看到网络释意,以后是否会支持还未决定。

插件包不准备放到官方网站上去,所以如果你也需要的话可以在 Github 上下载已经打包好的插件。当然,你也可以 fork 之后增加自己需要的功能,然后重新打包。

Vagrant 与 LFS

这几天一直在折腾迁移之前的开发环境。我现在使用的系统只划分了 /、/home、/boot 以及 swap 这几个分区,开发环境则沿用了 Apache 的 /var/www 目录。这样带来的问题是每次系统崩溃需要重装时就必须用 live CD 进行备份数据,重装后还需要还原开发环境,我希望以后能尽量避免类似的情况。另外最近可能还有跨平台的开发需求,这也提高了保证开发环境一致性的难度,所以决定把开发环境迁移到 Vagrant 上来。

Vagrant 是一款构建虚拟开发环境的工具,可以简单的把它理解成一个集成开发环境的虚拟机。我们可以把代码目录放在宿主系统上,而代码的运行环境则由 Vagrant 来提供。Vagrant 的安装与配置非常简单,大致过程可以参照 Segmentfault 的文章:「使用 Vagrant 打造跨平台开发环境」。现在我已经把自己最常用的 LNMP 开发环境通过 Vagrant 打包,然后把开发目录迁移到了 /home 分区下面,整个过程非常顺利。

在使用自己制作的 Vagrant 包的时候可能会出现:「_Vagrantfile:5: warning: already initialized constant VAGRANTFILE_API_VERSION」的问题。这是因为我们把已经配置好的 Vagrantfile 配置也打包到了里面,还原开发环境时(vagrant init)也会自己生成一个 Vagrantfile 配置文件,在这两个配置文件中重复定义了 VAGRANTFILE_API_VERSION 这个常量。从官方文档中得知 Vagrant 使用 ruby 来解释它的配置文件,并且项目目录下的配置会覆盖 package 中的配置,所以只需要用以下方式修改 ~/.vagrant.d 中对应的 _Vagrantfile 即可:

// 只需检测是否之前已经定义过该常量
if !defined? VAGRANTFILE_API_VERSION
    VAGRANTFILE_API_VERSION = "2"
end

这虽然能够修正这个错误,但是每次还原都这样修改依然很麻烦。我推荐使用这种方法修改项目目录中的 Vagrantfile,然后重新打包。

另外,我花了近 30 个小时的时间达成了成功安装 LFS 系统的成就。在迁移完开发环境之后我发现 Vagrant 包竟然有近 600M,后来在寻找更小巧的 Base 镜像时发现了一个超小的 LFS 镜像,这让我对 LFS 的兴趣大增。恰好国内恰好有完整的 LFS 安装手册,为了以后能更好的使用 linux,我决定在虚拟机里把整个安装过程跑一遍,好处多多。

这里有几点关于安装 LFS 的心得可以记录一下。如果是第一次尝试安装的话,强烈推荐下载包含完整源码的 live CD,我安装的时候直接下了一个 mini 镜像,源码都是安装时再下载,导致频繁的在宿主环境与 chroot 环境中切换,并且手册中给出的下载地址很多已失效,还需要搜索相关的 LFS mirror,非常费时。按照手册的步骤完整基本上不会出现太大的问题,我只在第六章安装 file 和 man-db 时遇到了 zlib.h 和 db.h 等头文件无法找到的问题,自己把 /usr/include 加入到 C_INCLUDE_PATH 和 CPLUS_INCLUDE_PATH 环境变量即可。

其实最可能遇到的问题应该是安装完成之后的网络设置部分,很多人都可能遇到 eth0 dosent exist 的问题。这里需要在编译 kernel 时添加相关驱动模块的支持。解决这个问题最简单的办法就是在宿主系统上使用 lspci | grep -i Ethernet 命令查看网卡参数,然后在 kernel 里选择合适的模块。其实究其根本,还是因为平常使用中很少会涉及到内核部分,对其众多的参数没有基本的了解。

新一轮晒桌面开始

openbox

据可靠日期记录,我持续使用了近一年半的 Debian sid 系统终于被我滚挂了。事情的起因要归结到这两天被大家玩坏了的 bash 漏洞,一直采用超惰性升级的我,在为这个漏洞打补丁的时候顺手滚动升级了一下系统软件,然后就没有然后了。这次崩溃非常彻底,以前出问题最多是无法启动 x-window,这次连 /sbin/init 都无法启动了,提示说 segment fault。期间尝试了很多种修复方案,但未能见效。

无奈之下只好重新安装 Debian,采用一贯的硬盘启动网络安装的方式,过程非常顺利。未料到的是在我辛苦调教好 openbox,重启之后崩溃再现,仍然无法启动 /sbin/init 进程。令人疑惑的是我在新系统上没有做任何危险操作,只是安装了 openbox,docky 等几个我常用的软件而已。继续重装,然后这个过程重复了 4 次,我尝试了 Debian testing 和 sid 两个版本以及几个常用的桌面环境,都无一例外的在重新安装 openbox 环境重启之后崩溃。

因为手里没有 Linux 的系统盘,很多检查工具用不了,就直接尝试安装其他的 Linux 发行版了。考虑到使用习惯,选择了自带 openbox 的 lubuntu 发行版,图片中就是最后的调教效果,已经非常接近之前的样式了,只是换了一下壁纸。其实 lubuntu 自带的主题也挺好看的(如下图),但是与 conky 有兼容性问题。

lubuntu

这张图就是 lubuntu 默认主题,只是换了一下图标而已。另外需要说到的就是开源的显卡驱动进步了很多,没有出现之前风扇狂转的问题了,但是在登录之后仍然有花屏的现象。暂时先这样用着,过段时间再考虑需不需要换到闭源驱动。