emacs + cygwin again

两年前我写了一篇博emacs + cygwin fail,说的是在cygwin中使用emacs的种种不便。这些问题已经得到解决。但如今我的需求又有了转变,想在native emacs on windows上使用cygwin作为shell,于是又产生了新的问题。今天看到ownwaterloo在我博客里的留言,想到应该花点时间把以前遗留下来的问题解决掉,于是有了这篇文章。

在emacs中使用cygwin作为shell,可以使用这个cygwin-shell.el
http://lists.gnu.org/archive/html/help-gnu-emacs/2010-02/msg00668.html

然后在.emacs中如此启用之


(load "D:\\programs\\emacs-24.3\\site-lisp\\cygwin-shell.el")

为了方便可以绑定一个快捷键


(global-set-key (kbd "M-s") 'cygwin-shell)

cygwin_break_emacs

重启emacs试用,发现不工作,原来是bash的路径需要填对:


(let* ((shell-file-name "D:\\cygwin\\bin\\bash")

再进去看,可以用了,但是提示符乱码。根据网上的一些讨论,打开bash.bashrc查看


# Set a default prompt of: user@host and current_directory
PS1='\[\e]0;\w\a\]\n\[\e[32m\]\u@\h \[\e[33m\]\w\[\e[0m\]\n\$ '

由于我用的emacs版本支持分色显示,我删除了前面的\w\a部分,保留了分色显示的\u@\h部分。


# Set a default prompt of: user@host and current_directory
if [[ $TERM = "emacs" ]]; then
    PS1='\[\e[32m\]\u@\h \[\e[33m\]\w\[\e[0m\]\n\$ '
else    
    PS1='\[\e]0;\w\a\]\n\[\e[32m\]\u@\h \[\e[33m\]\w\[\e[0m\]\n\$ '
fi

还有一个问题,就是登录进去后显示两行错误:

bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell

这是说emacs没有提供TTY给bash,因此bash拒绝提供job control功能,也就是说C-c强关进程、&后台执行、C-z挂起等命令行功能都不能使用,非常不便。其实我一般不太用到这些功能,偶尔用到就用emacs的多窗口开多个窗口来执行不同的命令,很长时间就没有去管他。今天由于跟ownwaterloo邮件提到,就想试试解决一下。根据网上的说法,这是个没有解的BUG。。

不过这个问题是从cygwin 1.7.10之后才出现的,可以找到许多帖子在升级到1.7.10~1.7.11过程中抱怨这个事情

既然如此,就不去费心琢磨怎样能给cygwin一个TTY环境了。只好根据人家提供的两条解决方案想想看怎样去找到旧版本的cygwin。

Workarounds:

* Use Cygwin Emacs (package emacs-w32 uses the windows GUI, there are also X11 and console packages)
* Don’t upgrade Cygwin above, I think Cygwin1.dll, version 1.7.9.

由于cygwin官网并不提供旧版本的cygwin安装,只好去找旧版本的镜像。我尝试了许多关键词,搜了一下午,踏遍无数国外的垃圾下载站,被各种坑骗之后,终于在一个必须注册才能下载的网站找到了可以安装的1.7.9版本,号称最后一个兼容emacs的cygwin版本的下载
http://search.4shared.com/postDownload/lMzinjJj/cygwin_179-1.html
emacs-cygwin

直接将1.7.9的cygwin1.dll拷贝到bin目录是不可行的,我下载并尝试过,bash可以执行,但是一些命令,如ls和svn执行没有输出。奇怪的是其他的程序如grep和git又可以执行,可能只是几个函数的挂接点有变化了吧。因此必须重新安装一遍cygwin。方法是启动旧版本的setup.exe后,选择“Install from Local Directory”,并在后续步骤中的“Local Package Directory”选择下载文件附带的那个路径“Cygwin_Arquivos”(我查了一下这似乎是葡萄牙语的Archive的意思,感谢这位葡语程序员!)
cygwin_arquivos

完成安装后,一种方案是可以将cygwin-shell.el中的bash路径指向新装好的cygwin路径,我没有采用。由于我已经在原有的cygwin 1.7.20版本中安装了不少应用,不想切换目录了,因此我尝试将1.7.9的bin目录覆盖到1.7.20中去。居然成功了![其实失败了…]

这次在emacs中测试,正确启动,一切命令正常!有趣的是,用cygcheck -c cygwin检测,查出来的版本还是1.7.20版本。而且新版的mintty.exe也可以正确的使用。非常开心~这暂时可以算是完美解决了~

假如有其他人恰好有跟我一样的蛋疼需求,想在windows native emacs中启用cygwin,又不堪各种坑查到我的博客,为了方便这样的小众同好,我这里给一个百度网盘的link吧。说实话百度开放网盘内容搜索还算挺有用的(虽然总感觉挺可怕的。。),我曾幻想在google中 site:pan.baidu.com 搜索cygwin 1.7.9就能搜到程序包,可惜失败了。这次我提供公开下载,未来再这样搜索的人就能得到方便了吧!

[cygwin 1.7.9 本地安装包] http://pan.baidu.com/share/link?shareid=661677505&uk=3423312229

后注

  • 事实上直接将1.7.9 bin目录中的内容覆盖到1.7.20中是不行的,部分程序无法执行。还是需要老老实实将脚本中的bash路径指向1.7.9版本。
  • 现在在Google中搜索cygwin 1.7.9 inurl:pan.baidu.com会直接找到这个资源了,只有一个搜索结果,就是我的链接,哈哈

google cygwin 1.7.9

本文引用的图片和部分讨论来自网上,以下是主要参考来源的整理

本文参考

自动编程 – 语言的级别

先说一个悖论吧。
程序员的任务是让机器帮助人类做工作。
程序员的唯一工作就是开发代码。
程序员喜欢手动hack开发代码。

这就是我今天想要讨论的。

今天读到了阮一峰翻译的为什么Lisp语言如此先进?感触很深。原文Revenge of the Nerds

正如我开头说明的一样,之所以软件工程这个领域这么奇怪,有什么人月神话;有各种其他工程都完全不可能遇见的奇怪问题;即使开发得非常熟悉的类型的项目,再去开发仍然无法准确估计工期和成本;最讨厌的代码就是别人的代码,要知道别的工程领域流水线都是最重要的提高效率的方案,也就是在其他人做了某个步骤的产品基础之上在做一个步骤;即使软件工程理论已经存在和发展了几十年,软件项目的可测性和可控性都完全没有提高;被业界高呼了二十年的面向对象被骂为骗局;等等等等。。。
之所以这一切问题存在,是因为软件工程根本就是一个悖论。当别的工程领域早就摆脱了手工作坊生产,早就工业化自动化了的时候,软件工程还在手动hack代码。就像摆着高大的吊车和推土机不用,软件工程师们想要徒手建造长城,一次又一次的非要从地基开始重新建起。

这里再附上之前在代码交叉拷贝悖论中同ownwaterloo的讨论吧,在读到上述那篇文章之前,其实我们已经走到某一个角落了,其实已经离这个真理只差一步之遥了。(好吧。。虽然我说的也不见得就是啥“真理”啦。。)

OwnWaterloo at 10/11/2010 18:12
不知道是不是想要这样的效果? 伪代码大致是这样:
for image in [ thumb, caption, buttom /* 这里还可以更多 */ ] :
image.offset(height)
image.rotation(270)
hawk at 10/11/2010 23:58
呜!这段代码很漂亮!
可惜c++效仿起来有难度。。
OwnWaterloo at 10/12/2010 10:36
其实那代码是一个暗示……
做GUI这种效率不吃紧的东西, 能不用C++, 就别用C++……
如果我早点学python, 并且发现OpenCV其实有python的绑定……
不知道可以节省多少时间…… 陪gf玩玩什么的……
hawk at 10/12/2010 16:25
那时候因为是在winCE上做widget,没有.NET,所以只能用C++了。。
现在做界面都尽可能用网页或者.NET了。。。
OwnWaterloo at 10/13/2010 12:37
.net也行, 它至少有元数据, 可以”按名字调用”。
C++在运行前完成”名字到地址的转换”, 运行时就没函数名了。
其实按今天的内存和磁盘容量来说, 元数据并不大。
但需要它的时候, 真离不开它……
貌似C++0x也不打算加入……
hawk at 10/14/2010 23:27
是的,javascript那种字符串直接当代码调用的功能强大得一塌糊涂!!

是的,.net的按名字调用,甚至javascript的自解释。为什么他们就那么强大呢?到lisp这里就找到祖宗了。可能就像文章中说的“格林斯潘第十定律”一样,在你追求代码自动化的过程中,你最终就会找到祖宗那里去了。

当然了,我相信lisp肯定也不是最终的完美。但是它是一个指引,指导我们什么是我们想要的。那就是自动化的编程。以前见过貌似是《程序员修炼之道》里面提到过的unix哲学理念,其中有一条是被人大为忽视的,就是让程序自动生成代码。刚在网上翻了一下,维基上的说法是“六:尽可能地榨取软件的全部价值”[引用]。这点是关键吧。软件工程应该像其他的工程一样,尽可能工业化、自动化,早日脱离3、5人小团队的作坊式生产(当然这样说很可能导致一批人失业,不过那是十年或者二十年后的事情了)。当然手工生产会留下来,做那些最精细,最高难,计算机无法自动完成的事情。另外就是确定需求分析设计这部分,这部分是机器无法自动化的。
当然,软件行业变成当今的现状是很自然的。程序员都是聪明而高傲的。他们喜欢证明自己更优秀。他们喜欢hack。他们瞧不起催进度的老板和搞需求的公关。并且程序究竟该怎么开发这点烂事是企业里的管理人员无法操心的。他们看不见那些无形的重型机械被程序员抛开,也看不见程序员其实是在手工修建长城。但是程序员应该自己反省。是的,一次两次骗过肥头胖脑的老板可能蛮有趣的,但是总是手动hack程序员就偏离了自己职业的宗旨-让机器帮助人。

我一直以来都是c\c++的忠实拥趸,藐视高级语言,喜欢重度hack,喜欢重头发明轮子。但是最近的反思,加上这次看到的文章,让我大大醒悟了。高级语言的真正高级之处,就是他强大的自动生成代码的能力啊!
作为一个高傲的程序员,低头使用别人的代码生成器总有“食嗟来之食”的感觉(当然这是不对的。。当真正要解决问题的时候我还是会用合适的工具的!lisp当然也在学,python和perl也在学,也知道用网页和.net来做一些事情了,也体会到.net和js的强大了)。那就自己去发掘,自己去创造。这话说得貌似又变成重新发明轮子了。当然工程实践中不会这么去做的。但是作为理论学习的方向,这方面应该是重点。我研究生专业学的是嵌入式方向,学的是最底层最hack的东西。学的是怎么把c当成汇编来用,怎么把编程看成是操作实际硬件。假如沉迷于此的话,我的软件观就彻底歪掉了。这些东西都算不上是软件。只不过是硬件接口罢了。软件的宗旨是正好相反的。软件的任务就是形而上学,就是纯粹的逻辑和理学的完美。只有结合了天堂的圣洁和地域的邪恶,才能创造出人类世界。

另外一点就是借着AIIDE的契机,学习了机器学习和人工智能的东西。这是软件工程未来的一个新的方向。就是用人工智能来开发代码。是的,不仅仅是用人工智能来解决问题,还要让人工智能来开发程序,让人工智能来开发人工智能。那其实是人工智能提出的时候最早的目标。可惜大多数学习这个领域的人都专注于用它做其他的事情了。遗传编程,一开始就是为了让程序自动创造程序而研究出来的,可惜现在都在做其他的解决方案。
可能说到人工智能产生人工智能,有些人就要想到黑客帝国了。一方面作为理论研究不应该考虑太多伦理问题,伦理是随着理论进步的。另一方面人工智能不等于生命,再高级也不等于生命。关于他的纯哲学讨论我放到下一篇博客去讲吧。估计这里的人多数不会有兴趣看的。

总之今天就像醍醐灌顶了一样一下子有了很多新想法。可惜我的问题在于想法太多动手太少。。。要努力实践!

lisp学习笔记

2013年重读这篇笔记,错误很多,对定义的理解有很多混淆;作为自己学习和成长的记录不再将其中的错误一一纠正了

刚刚开始学lisp,还只是开始熟悉他的语法。但是学lisp确实是开阔眼界的一个好办法,能从更高的角度来看程序语言。

至今为止任何类型的程序语言其实都是图灵机的一个实现。比如说经典的x86 CPU吧,他就是一个图灵机。这个问题我其实还是想了好久才想出来的。可能是我太愚钝了吧。。图灵机的一种描述是这样的,存在一个两边都无穷长的纸带,一个含有一系列状态和命令的指针。这个指针一次执行一个命令,或者向前或向后移动一格,或者在纸带上读取或写入一个符号,命令有可能改变指针的状态。而读取到的符号会根据指针的当前状态产生一个新的指令。循环往复。直至到达某个特定的状态(终态)停止下来。

一开始我觉得CPU的寄存器表示状态。可这样的话,寄存器的值是不确定的,于是似乎不能模拟确定有限自动机。静下心来想想,其实应该认为所有寄存器的一种取值组合就是一个状态,所以假如有m个寄存器,每个寄存器的取值数量是ni,则总的状态数量就是i从1取到m, 将所有的ni相乘。(不能打公式郁闷。。)然后应该认为汇编代码就是那个带子上的符号。这样比如

ADD a, a, b ;a=a+b

这样的一条指令,应该看作是一个符号(所有可能的汇编指令应该是有限的,可以认为每一个都给编了唯一确定的符号)。这个符号对应CPU的不同状态,(例如这里的a和b的所有不同状态组合),可以看作每个状态都有接受了这个符号之后指向一个新的状态的箭头。使得寄存器状态改变。比如对于a=1,b=3这组状态(1, 3),这个箭头就应该指向(4, 3),当然这里没有考虑溢出位啦。同时又存在一些其他的操作,例如处理内存或中断的操作,可以看作是向纸带上的特定位置写入字符(由于内存和中断的数量也是有限的,所以可以给他们的每一个地址对应纸带上的一个位置)。

那么lisp呢,lisp是怎样实现图灵机的呢?

根据lisp1的自解释逻辑,可以认为lisp是一个不断扫描输入纸带的一个指针。他的状态则由输入中给定的“环境”决定。可以认为lisp仅有函数定义和函数调用两种语法。对于函数定义,其实是增加了新的状态组,把它的所有形参的所有取值组合看作是一组状态加入到自动机中。而函数调用,就是将实参值带入形参,计算函数结果的过程,对应于状态接受一个输入指向另一个状态的一个箭头,即在此实参情况下,如何应该进入什么样的下一个状态(次态)。

不过很有趣的是,鉴于lisp的递归定义,可以认为他的状态机也是递归的。在运行的最初,该状态机仅有函数定义及函数调用的最基本语法。指针扫描纸带(程序源代码)一遍,生成了最外层的函数定义,这是状态机为这些新函数增加新的状态,之后指针移动到该函数调用的起始位置,第二遍扫描源码,生成新一层次的函数定义,向状态机添加新的状态,直至最终所有的函数定义全部生成结束,递归到最底层,所有的实参都被计算成为原子,再依次递回,直至最上层。

从这个角度看lisp的有趣之处在于他用自己的规则定义自己,不断充实自己。不过也因为这种灵活性,使得用c实现一个高效的lisp编译器变得有些困难。一开始我在考虑的是lisp只有递归没有循环,确实不符合c语言的常规思路。不过转念一想,尾递归优化成循环其实倒反而是一件轻松的事情,只需要将形参改成自变量,把递归调用的那一句改成goto到函数开头就可以了。然后再改成while循环即可。麻烦就麻烦在这个函数的动态声明。有没有可能一次扫描完lisp文本之后就确定全部的函数定义呢?给定两个附加条件,一是所有的函数定义必须在他的调用的同一级或外层(注意这里指的是定义这个函数的外层,而不是调用这个函数的外层),二是函数定义必须在函数调用的前面。这样就可以逐级求值而避免递归了(相当于首递归)。当然这里避免的仅仅是(相当于)编译期的递归,在(相当于)运行期的递归仍然是避免不了的(也不可能避免)。当然对于lisp而言,编译和运行是交叉进行的,也没法分得这么仔细啦。(所以才说是解释型语言嘛。。汗)当然话说回来,将lisp先编译成机器码再运行也是可能的。只是编译时间会比较长而已,而且对于变量类型可能要做一些处理。(最简单的想法是用链表,不管是树也好,字符串也好,数字也好,都可以用链式结构存,以适应lisp的list结构。)

好吧,先做这些记录,下次有了感想再写了。

呼,因为结束实习,搬家,赶毕设,一下子好久没有写博了呢。。今天终于补了一篇。。证明我这段时间也没有荒废时间啊。。以后还是要经常写博客自勉啊。。回头看了一下大半夜的逻辑有点混乱估计有写错的地方。。不过天色晚了没心情挑错了,改天再回来改吧。。