emacs配置系统

emacs是个超级复杂的程序,尤其在配置问题上。贝壳的emacs要跨越三个环境。环境一,WindowsXP+Emacs23。环境二,Debian Testing + Xfce4。环境三,CentOS + Ssh。而整个的操作方式,个性设定需要保持一致。因此,引出一个问题。配置如何设置,跨平台,同步。
首先解决配置的同步问题,贝壳建立了一个svn仓库,用于存储该配置系统。然后在各个系统中co出这个仓库,当有需要调整时ci就可以保持同步了。Linux下可以使用ln连接文件,Windows下比较麻烦点,NTFS格式(大多都是NTFS格式了吧)可以去sysinternals下一个叫做junction的工具,以建立目录的工具链接,当然,.emacs文件只能手工拷贝了。
然后是配置的切分问题,如果只有一个文件,即使使用了版本控制,意义也不大。同时,将配置切割成不同的部分,控制载入过程,也可以跨平台和加速。以下是贝壳的.emacs文件。
;; .emacs profile, written by shell.xu

;; load other set
(add-to-list ‘load-path “~/.emacs.d/”)
(add-to-list ‘load-path “~/.emacs.d/auto-complete/”)
(add-to-list ‘load-path “~/.emacs.d/plugins/”)
(load “emacs-setup”)
(load “emacs-redef”)
(load “emacs-plugin”)
(cond
((not (boundp ‘initial-window-system)) (load “emacs-console”))
((memq initial-window-system ‘(x w32))
(cond
((memq system-type ‘(windows-nt cygwin)) (load “emacs-win”))
((memq system-type ‘(gnu/linux)) (load “emacs-linux”))
)
)
)
(load “emacs-keymap”)
从上可以看出,我们先设定了.emacs.d作为默认加载路径——大多数文件都是放在这里。plugins是各种第三方程序的安装路径,这样这些程序就无需在各个平台上各自安装一次。而auto-complete单独拆出来纯粹是因为文件太多了。而后,我们加载了setup,这个文件内定义了emacs的基本配置,redef文件内定义了各种自定义函数和变量,plugin内控制了需要加载的各个插件和配置。
下面就有点复杂,简单来说,设定无Windows系统的时候加载emacs-console文件,有Windows的情况下,在windows下加载emacs-win,在linux下加载emacs-linux。这是实现跨平台设置的核心。
最后是keymap,经过上面复杂的设定,按键设置是统一的。
setup文件就不细说了,大家按照自己的习惯设定就好。下面我说几个redef中定义的函数。
(defun switch-windows-buffer ()
(interactive)
(let ((this-buffer (window-buffer)))
(switch-to-buffer (window-buffer (next-window (selected-window))))
(switch-to-buffer-other-window this-buffer)
(other-window 1)
)
)
这个函数的目标是用热键交换两个窗口的位置。如果你经常用C-x 3分栏,并且在两者间跳来跳去的话,有的时候往往希望两者的位置换一下。通常都是C-x b切换当前的窗口,然后C-x o切到隔壁去再换。这个太繁琐了。
(defun popup-term ()
(interactive)
(apply ‘start-process “terminal” nil popup-terminal-command)
)
这个函数是用于在当前文件所在路径弹出一个term的。也许有人说了,emacs有term啊。问题是,那个term只能开一个,而且有些东西操作不了。例如你如果想在这个term里面跑aptitude…
注意这个函数里面的popup-terminal-command,这个需要跨平台的,因此在windows和linux下设定各自不同。以下是两个典型设定,至于哪个是哪个我想都看得懂。注意console下面没必要搞这个。
(setq popup-terminal-command ‘(“xfce4-terminal”))
(setq popup-terminal-command ‘(“cmd” “/c” “start”))
然后我们说plugin,这个文件其实很简单,加载插件,然后设定就好。下面是一部分范例。
;; load template
(require ‘template)
;;here set the templates directory
(setq template-subdirectories ‘(“./” “Templates/” “~/.emacs.d/templates/”))
(template-initialize)
(setq template-auto-insert t)
这部分范例说明了如何载入template,并且进行设定。
下面是一点细节问题,tool-bar-mode这个设定相信多数人都有做。问题是,如果你在console下用过就知道,如果你设定了(tool-bar-mode -1),立刻会报错——因为console下面根本没这个东西。所以,记得把这个设定放到各个平台上。
最后,这些设定,必须经过编译才能起效。包括所有第三方插件,都必须编译生效。而显然,每个平台编译一遍是个脑残的事情。因此,我们需要写一个Makefile。
DEPENDS=auto-complete plugins
SOURCES=emacs-console.el emacs-keymap.el emacs-linux.el emacs-redef.el emacs-setup.el emacs-win.el

build: $(SOURCES) emacs-plugin.el
for i in $(DEPENDS); do $(MAKE) -C $$i build || exit 1; done
emacs –batch -f batch-byte-compile $(SOURCES)
emacs –batch -l ~/.emacs -f batch-byte-compile emacs-plugin.el

clean:
rm -rf *.elc
for i in $(DEPENDS); do $(MAKE) -C $$i clean || exit 1; done
注意,在每个目录中也要分别写Makefile,不过内容简单多了,基本就是emacs –batch -f batch-byte-compile *.el而已。而plugin这个文件比较特殊——他必须依赖于所有插件才能工作,所以最后才进行编译。
以上工作,基本完成了一个跨平台的emacs配置系统的组建。当然,如果你高兴向其中加入更多的内容,可以遵循以上规范进行。

24点计算原理和程序

最近开心上狂算24点,于是贝壳搞了一个24点计算程序,并且说明原理。
我们将24点问题一般化,变成一个搜索问题。假定从一个初始表开始,里面有一些原子。我们定义一个操作,结合。每次操作任意从中选择出两个(或者以上)原子,使用算符连接,成为一个新的原子。那么,一般来说,24点就是计算所有可能的路径,从初始表开始,持续进行结合,直到只剩下一个原子,并且对这个原子求值得24。
有人可能在算符优先级上想不开,其实不用考虑这个问题,每次求值的时候,按照求值顺序优先就可以。你想到的另外一种优先级可能,会在穷举的时候被列举出来算掉,不用担心遗漏。
同时,算子必须是两目以上算子,因为单目算子可以持续作用于同一个对象,因此原子表中的原子个数并不严格单调减少,造成无法肯定路径收敛于有限步骤上。并且,如果允许单目算子,那么我只需要求导和阶乘就可以对任何数字求24点。
((a’)!+(b’)!+(c’)!+(d’)!)!=24
因此,单目算符是没有意义的。
另外,注意算符分可交换和非可交换的。例如:a+b=b+a,但是a-b!=b-a。如果不注意这点,倒是不会漏算,但是会造成搜索空间增大,并且有重复结果。
以下是24点计算程序,python版本的。有兴趣的朋友可以用scheme重写,相信会更简洁有效。回头会用django封装一下,做成网页给大家玩玩。

#!/usr/bin/python
import sys

symbol_list = [
("%s+%s", True), ("%s-%s", False),
("%s*%s", True), ("%s/%s", False), ("%s**%s", False),
# ("min (%s,%s)", True), ("max (%s,%s)", True),
];

def diff_seq (length):
for i in range (0, length):
for j in range (i + 1, length):
yield (i, j);

def get_less_state (input_state):
for i, j in diff_seq (len (input_state)):
temp = input_state[:];
del temp[j];
del temp[i];
for s in symbol_list:
rslt = s[0] % (input_state[i], input_state[j]);
rslt = “(%s)” % rslt;
temp.append (rslt);
yield temp;
temp.remove (rslt);
if s[1]:
continue;
rslt = s[0] % (input_state[j], input_state[i]);
rslt = “(%s)” % rslt;
temp.append (rslt);
yield temp;
temp.remove (rslt);

def do_node (input_state, output):
for i in get_less_state (input_state):
if len (i) > 1:
do_node (i, output);
continue;
try:
rslt = eval (i[0]);
except:
continue;
if rslt == 24.0:
output.add (i[0].replace (“.0″, “”));

if __name__ == “__main__”:
rslt = [];
for i in sys.argv[1:]:
rslt.append (float (i));
output = set ([]);
do_node (rslt, output);
for i in list (output):
print “%s=24″ % i;

SCIP,lambda,Church

贝壳最近在看SCIP,感觉受益匪浅。其中有一个2.6,使用函数表达数字,很难理解。贝壳查了查资料,这篇(http://blogs.sun.com /yongsun/entry/lambda%E6%BC%94%E7%AE%97%E4%B8%8Echurch%E8%AE%A1%E6%95 %B0)写的很好,贝壳就不多说了。贝壳把自己写的内容贴上来,作为一个借鉴。
(define zero (lambda (f) (lambda (x) x)))
(define one (lambda (f) (lambda (x) (f x))))
(define two (lambda (f) (lambda (x) (f (f x)))))
(define three (lambda (f) (lambda (x) (f (f (f x))))))
(define (add-1 n)
(lambda (f) (lambda (x) (f ((n f) x)))))
(define (add m n)
(lambda (f)
(lambda (x) ((m f) ((n f) x)))))
(define (mult m n)
(lambda (f) (m (n f))))
(define (show-func-number n)
(define (inc x)
(+ x 1)
)
((n inc) 0)
)
(show-func-number zero)
(show-func-number one)
(show-func-number (add-1 one))
(show-func-number (add one two))
(show-func-number (mult two three))
结果:
0
1
2
3
6
show-func-number这个函数是将高阶抽象函数序列映射到一个具体的数上的。工作方法是,建立一个函数x=x+1,然后使用给定的高阶函数来映射这个函数。n次高阶函数会映射这个函数n次,于是结果函数就是x=x+n。然后将这个函数作用于0,不难得到结果吧?