从现在开始我的笔记都由 WiKi 方式记录。
这里记录一些跟计算机有关的笔记。可以当成一个主页的更新记录或 "What's New"。
这几天收获真多。首先是,我的工作基本上转入了 Emacs,网页和 LaTeX 都 开始使用 Emacs 编辑。发现了很多有用的 elisp 扩展。开始正式学习 Concrete Mathematics.
加入了新的Emacs 网页,更新了linux网页的应用程序介绍,在mutt网页的FAQ部分加入了对 HTML 和 doc 附件 文档的处理说明。
今天发现一个令人兴奋的软件:WordNet.
WordNet 是 Princeton 大学发明的一种基于认知语言学的英语词典。它不是 光把单词以字母顺序排列,而且按照单词的意义组成一个“单词的网络”。
lftp 缺省不会显示 ftp 服务器的欢迎信息和错误信息,这在很多时候不方便,因为你有可能想知道这个服务器到底是因为没开机连不上,还是连接数已满。如果是这样,你可以在 ~/.lftprc 里写入一行
debug 3就可以看到出错信息了。
讲述了 Emacs 设计中的各种经验。
(defun open-line-a-la-vi () (interactive) (forward-line) (open-line (or prefix-arg 1)) (indent-according-to-mode ))放在这里吧。以后说不定有用处。
一个叫 1.c,内容是
#includeint main() { float f=23.45; printf("main: %f\n", f); t1(f); }
另一个是 2.c,内容是:
#include用 GCC 编译这两个程序到一起:void t1(float f) { printf("t1: %f\n", f); }
gcc 1.c 2.c运行得到结果:
main: 23.450001 t1: 2.000000容易看出毛病吧,但是这只是我程序里的问题抽象出来的一个小例子。实际上这个引起的一连串不明原因的程序异常困扰了我整整一晚上,而没有发现毛病竟然出在这里!
gcc 编译如果不加 -Wall 选项就不给任何错误提示!可是它却吧 t1() 的 prototype 缺省当成了接受整型参数!
教训:编译程序一定要用 -Wall 打开所有警告信息,并且把它们全部消灭!
今天解决了我长久以来从 emacs 和 DDD 调用gdb失去响应的问题。连 Richard Stallman 都没有帮我想到,竟然是我在 ~/.inputrc 里设置了 vi 模式的很多键绑定造成 Emacs 和 DDD 不能将命令行交给 gdb 所致!不使用那些键绑定就行了,而且我决定开始熟悉 Emacs,因为它实在太好了!
问题解决真高兴啊!我可以舒舒服服的享受 Emacs 带来的乐趣了!Emacs确实是“世界上最强大的编辑器”!看来这些工具非得好好利用不可,可以使人用计算机充满乐趣,而且非常高效。
非常好的用来学习Emacs的东东。实际上它比 Emacs manual 容易让用户入门,真正体会到 Emacs 的强大 :)
找到一种非常奇怪的程序语言:INTERCAL。自认为是“超级黑客”的人可以试试用这个语言写程序,之后,也许你会变得谦虚,或者更加骄傲 :P
今天编了一天程序,对 Steven Fortune 的 voronoi 代码做了很大改进。修改了数据结构。内存占用仅为原来的 10%,有微小提速。唉……谁叫我要用人家 20 多年前写的玩具代码呢?呵呵。
今天花了一些时间来搞明白 Makefile 的 empty target 和 .PHONY target 的用法。
我们有时需要一个 target, 比如像 clean 这样的,每当 make clean 就会执行一些操作,而不依赖其它文件。我们以前通常采取的办法是像这样的 rule:
clean: -rm *.o core
当看到这个rule时,make 就会去寻找 clean 这个文件,但是它不存在。所以 make 决定执行命令来得到文件 clean。于是它执行 "rm *.o core"。 但是这个命令其实并不生成 clean 这个文件,所以每次执行 make clean 时,"rm *.o core" 都会再次被执行。
简言之,make 对付这种“目的在于执行操作的目标”,采用一个“不可能存在的文件”来表示这个目标。
但是如果真的存在一个名叫 clean 的文件就会出问题。由于 clean 没有 prerequisite,当存在文件 "clean" 时,make 认为“文件clean是不需要更新的”,这样"rm *.o core" 不会被执行。
所以一般说来,Makefile 的目录里一定不能有一个名叫clean的文件。但是为了确保万无一失,新的 make 提供了 .PHONY target。一旦被定义为 .PHONY,这个目标被认为是“一个永远不存在的文件”。即使它真的存在于目录里,也当它不存在。
.PHONY: all clean distclean install
有时我们需要让一个 pattern rule 不论如何都要执行。比如,我们有一个Makefile:
foo: dummy touch foo bar: dummy touch bar baz: dummy touch baz里面有3个目标 foo, bar, baz.
我们还有一个 GNUmakefile,它里面定义了一个目标叫 foo。我们希望:make 首先依据 GNUmakefile 的内容执行,当遇到 GNUmakefile 没有定义的 target 时,才到 Makefile 去寻找。
为了实现这个目的,我们
foo: dummy @echo making foo in GNUmakefile
%: @$(MAKE) -f Makefile $@
但是这样定义是不对的。你可以发现,"@$(MAKE) -f Makefile $@" 应该是一个不论目标 bar 是否存在都应该执行的操作。如果我们以前生成了一个文件 bar,但是由于它的 prerequisite dummy 后来被修改了,bar 应该被更新。
但是 make 看到 GNUmakefile 的这条规则,认为:“bar已经存在,而且是新的,因为没有 prerequiste 比它新。” 所以 make 决定不执行操作 "@$(MAKE) -f Makefile $@"。这是错误的,因为 bar: dummy 的依赖关系是在 Makefile 里定义的,我们必须让 make 无论如何也要检查 Makefile。所以,"@$(MAKE) -f Makefile $@" 是一个无论 bar 文件存在与否,“必须执行的操作”。
GNU make 的文档里的 GNUmakefile 是这样写的:
%: force @echo using Makefile @$(MAKE) -f Makefile $@ force: ;
它的思想跟我们开头看到的是一样的:让 % 依赖于一个“不可能存在的文件”(force)。那么 make bar 的时候,make 发现 bar 依赖于 force,那么它去更新 force,而 force 是一个“不可能存在的文件”,它每次都会被“更新成功”,从而导致 bar 每次都需要被更新,引起 "@$(MAKE) -f Makefile $@" 被执行。这里的 % 也会匹配 force,所以为了避免循环依赖关系,给 force 一个空的命令定义。
但是其实这里有一个同样的问题:当目录里真的存在一个文件叫 "force",而且 force 比 bar “旧”时,make 认为:“bar 的所有 prerequisite 都比它旧,所以 bar 不需要更新。” 那么它不执行 "@$(MAKE) -f Makefile $@"!这是错误的,我们前面已经得出了结论,这个命令必须执行,才能知道 bar 是否因为 dummy 得改变而需要更新!
解决的办法是一样的。让 force 成为一个“永远不存在的文件”:一个 .PHONY target. 所以完全正确的 GNUmakefile 应该这样写:
%: force @echo using Makefile @$(MAKE) -f Makefile $@ .PHONY: force
从今天的实验,我得出两个经验:
虽然csh是个不错的shell,但是用它编复杂的脚本被认为是不好的。这篇 USENET post 列举了使用 csh 编程的种种弊端。我不用 csh(我用bash),所以没有仔细看过。不过我觉得 Bash 确实很好,如果没有用过 csh,那就先试试 Bash 吧。然后你用会了 Bash,你就觉得没有必要再知道 csh,就跟我一样了 :P
一本使人能熟练使用 Bash shell,并和各种 UNIX 工具配合达到高效管理系统的书。看起来挺长的。我懒,放在这里,以后有空慢慢看 :P
关于 sed,知道这么点就足够了吧。甚至 Part3 我都觉得多余。