2012年10月

以前小时候时常看到老一辈人玩这个打山棋,它的棋盘简单,棋子更简单,随便一个地方都能很快速的画出来,而棋子则更简单了,没有任何特别要求,只需要下棋的双方所使用的棋子能被区分开来即可,所以,我吃了个香蕉就把香蕉皮弄成一小片儿的,他吃了个桔子就把桔子皮弄成一小片儿的然后再画个三层方框的棋盘就能下棋了。

打山棋棋盘样式
╋++++++++╋++++++++╋
++       +       ++
+ +      +      + +
+  ╋+++++╋+++++╋  +
+  ++    +    ++  +
+  + +   +   + +  +
+  +  ╋++╋++╋  +  +
+  +  +     +  +  +
╋++╋++╋     ╋++╋++╋
+  +  +     +  +  +
+  +  ╋++╋++╋  +  +
+  + +   +   + +  +
+  ++    +    ++  +
+  ╋+++++╋+++++╋  +
+ +      +      + +
++       +       ++
╋++++++++╋++++++++╋

上面的就是一个打山棋的棋盘(我是使用“+”来画出来的)的样式,粗的加号“╋”表示摆棋位,在这些位置可以摆放棋子,两人对局,其详细的走棋规则为:

  • 下棋双方先摆棋,一人一着只到将整个棋盘所有棋位都摆满;
  • 摆满后后摆棋子者先走棋;
  • 摆棋子时,逢三子(一方的)连成一线,称为打山;
  • 打山后用自己的棋子压住对方棋子,称为压包;
  • 在摆完棋子走棋前,所有压包都需要拿开;
  • 走棋过程中未发生打山,先无走棋位的一方就是36担;
  • 走棋过程中发生打山,则可以拿掉对方一粒棋子(最多也只允许拿掉一个棋子); +走棋过程中,如遇移动一子可能时形成两山(即同时出现两个一方的三个棋子连成一线),称之为过根;
  • 任意摆方两子,如果这两子中间有三个空位,则从任一个棋子数起第三个棋位(即中间位)为关键根,或者根位;
  • 被拿掉所有棋子的一方输棋。
打山棋根位
☆++++++++◇++++++++◇
++       +       ++
+ +      +      + +
+  △+++++╋+++++╋  +
+  ++    +    ++  +
+  + +   +   + +  +
+  +  ╋++╋++╋  +  +
+  +  +     +  +  +
◇++╋++╋     △++╋++╋
+  +  +     +  +  +
+  +  ◇++◇++☆  +  +
+  + +   +   + +  +
+  ++    +    ++  +
+  ╋+++++╋+++++◇  +
+ +      +      + +
++       +       ++
◇++++++++╋++++++++◇

如上棋盘所未:左下与右上角两个棋子之间总共有三个棋位,“☆”所处的位置即为根位,如果“△”处的棋子也是“◇”一方的棋子,那么,它往左上角移动一位即可同时形成两山,即过根。

今天用着电脑,突然一个轻轻的爆炸声音,电脑报销了,还好有一台备用电脑,把事情做完之后,带着电脑去维修,才买两个月时间,还在保修时间内,是电源烧坏了,换个电源就没有问题了,顺便我就想着给电脑加一块硬盘,整了一块2Tb的,后来也没加到电脑上面来,而是被我拿回来放到服务器上面去了。

服务器上面安装着TeamViewer,所以也不需要插屏幕即可操作,并了机,我用的是联想的台式服务器,以前也从来没有开过机箱,不过今天打开一看,还真不错,用了快两年了,里面居然没有什么灰尘,最开始我还以为和普通PC一样,把硬盘在光驱下面插了半天,怎么就是对不上螺丝孔,后来看了看原装的硬盘,有一个专用的硬盘盒,再看了年机箱的最下面,还有一个空着的,不错。

硬盘盒是免螺丝的,抽出来之后,将硬盘的螺丝孔对着硬盘盒上面的四个有弹性的东西就行了,装好之后直接插入到机箱上面去即可,两个Tb的硬盘用来做文件共享应该是足够了的吧我保留下来的最喜欢的电影有1.5个T左右,照片100来Gb,再加上100来Gb的软件,还可以余下来一些空间,今年一年使用应该足够了。

如果对于你来说,编程最大的乐趣来源于通过代码简单、清楚地表达你的意图,那么,选择Common Lisp作为你的编程语言可能比任何其它语言更得让你快乐,使用它,你会发现,它比其它程序语言使用了更少的代码但是却为你做得更多更快。

作者:Paul Graham

译者:阮一峰

为什么 Lisp 会如此先进?

如果我们把流行的编程语言,以这样的顺序排列:Java、Perl、Python、Ruby。你会发现,排在越后面的语言,越像Lisp。Python模仿Lisp,甚至把许多Lisp黑客认为属于设计错误的功能,也一起模仿了。至于Ruby,如果回到1975年,你声称它是一种Lisp方言,没有人会反对。编程语言现在的发展,不过刚刚赶上1958年Lisp语言的水平。

1958年,John McCarthy设计了Lisp语言。我认为,当前最新潮的编程语言,只是实现了他在1958年的设想而已。这怎么可能呢?计算机技术的发展,不是日新月异吗?1958年的技术,怎么可能超过今天的水平呢?让我告诉你原因。这是因为John McCarthy本来没打算把Lisp设计成编程语言,至少不是我们现在意义上的编程语言。他的原意只是想做一种理论演算,用更简洁的方式定义图灵机。

所以,为什么上个世纪50年代的编程语言,到现在还没有过时?简单说,因为这种语言本质上不是一种技术,而是数学。数学是不会过时的。你不应该把Lisp语言与50年代的硬件联系在一起,而是应该把它与快速排序(Quicksort)算法进行类比。这种算法是1960年提出的,至今仍然是最快的通用排序方法。

Fortran语言也是上个世纪50年代出现的,并且一直使用至今。它代表了语言设计的一种完全不同的方向。Lisp是无意中从纯理论发展为编程语言,而Fortran从一开始就是作为编程语言设计出来的。但是,今天我们把Lisp看成高级语言,而把Fortran看成一种相当低层次的语言。

1956年,Fortran刚诞生的时候,叫做Fortran I,与今天的Fortran语言差别极大。Fortran I实际上是汇编语言加上数学,在某些方面,还不如今天的汇编语言强大。比如,它不支持子程序,只有分支跳转结构(branch)。

Lisp和Fortran代表了编程语言发展的两大方向。前者的基础是数学,后者的基础是硬件架构。从那时起,这两大方向一直在互相靠拢。Lisp刚设计出来的时候,就很强大,接下来的二十年,它提高了自己的运行速度。而那些所谓的主流语言,把更快的运行速度作为设计的出发点,然后再用超过四十年的时间,一步步变得更强大。

直到今天,最高级的主流语言,也只是刚刚接近Lisp的水平。虽然已经很接近了,但还是没有Lisp那样强大。

Lisp语言诞生的时候,就包含了9种新思想。其中一些我们今天已经习以为常,另一些则刚刚在其他高级语言中出现,至今还有2种是Lisp独有的。按照被大众接受的程度,这9种思想依次是:

  1. 条件结构(即"if-then-else"结构)。现在大家都觉得这是理所当然的,但是Fortran I就没有这个结构,它只有基于底层机器指令的goto结构。
  2. 函数也是一种数据类型。在Lisp语言中,函数与整数或字符串一样,也属于数据类型的一种。它有自己的字面表示形式(literal representation),能够储存在变量中,也能当作参数传递。一种数据类型应该有的功能,它都有。
  3. 递归。Lisp是第一种支持递归函数的高级语言。
  4. 变量的动态类型。在Lisp语言中,所有变量实际上都是指针,所指向的值有类型之分,而变量本身没有。复制变量就相当于复制指针,而不是复制它们指向的数据。
  5. 垃圾回收机制。
  6. 程序由表达式(expression)组成。Lisp程序是一些表达式区块的集合,每个表达式都返回一个值。这与Fortran和大多数后来的语言都截然不同,它们的程序由表达式和语句(statement)组成。 区分表达式和语句,在Fortran I中是很自然的,因为它不支持语句嵌套。所以,如果你需要用数学式子计算一个值,那就只有用表达式返回这个值,没有其他语法结构可用,因为否则就无法处理这个值。

    后来,新的编程语言支持区块结构(block),这种限制当然也就不存在了。但是为时已晚,表达式和语句的区分已经根深蒂固。它从Fortran扩散到Algol语言,接着又扩散到它们两者的后继语言。
  7. 符号(symbol)类型。符号实际上是一种指针,指向储存在哈希表中的字符串。所以,比较两个符号是否相等,只要看它们的指针是否一样就行了,不用逐个字符地比较。
  8. 代码使用符号和常量组成的树形表示法(notation)。
  9. 无论什么时候,整个语言都是可用的。Lisp并不真正区分读取期、编译期和运行期。你可以在读取期编译或运行代码;也可以在编译期读取或运行代码;还可以在运行期读取或者编译代码。

在读取期运行代码,使得用户可以重新调整(reprogram)Lisp的语法;在编译期运行代码,则是Lisp宏的工作基础;在运行期编译代码,使得Lisp可以在Emacs这样的程序中,充当扩展语言(extension language);在运行期读取代码,使得程序之间可以用S-表达式(S-expression)通信,近来XML格式的出现使得这个概念被重新"发明"出来了。

Lisp语言刚出现的时候,它的思想与其他编程语言大相径庭。后者的设计思想主要由50年代后期的硬件决定。随着时间流逝,流行的编程语言不断更新换代,语言设计思想逐渐向Lisp靠拢。

思想1到思想5已经被广泛接受,思想6开始在主流编程语言中出现,思想7在Python语言中有所实现,不过似乎没有专用的语法。

思想8可能是最有意思的一点。它与思想9只是由于偶然原因,才成为Lisp语言的一部分,因为它们不属于John McCarthy的原始构想,是由他的学生Steve Russell自行添加的。它们从此使得Lisp看上去很古怪,但也成为了这种语言最独一无二的特点。Lisp古怪的形式,倒不是因为它的语法很古怪,而是因为它根本没有语法,程序直接以解析树(parse tree)的形式表达出来。在其他语言中,这种形式只是经过解析在后台产生,但是Lisp直接采用它作为表达形式。它由列表构成,而列表则是Lisp的基本数据结构。

用一门语言自己的数据结构来表达该语言,这被证明是非常强大的功能。思想8和思想9,意味着你可以写出一种能够自己编程的程序。这可能听起来很怪异,但是对于Lisp语言却是再普通不过。最常用的做法就是使用宏。

术语"宏"在Lisp语言中,与其他语言中的意思不一样。Lisp宏无所不包,它既可能是某样表达式的缩略形式,也可能是一种新语言的编译器。如果你想真正地理解Lisp语言,或者想拓宽你的编程视野,那么你必须学习宏。

就我所知,宏(采用Lisp语言的定义)目前仍然是Lisp独有的。一个原因是为了使用宏,你大概不得不让你的语言看上去像Lisp一样古怪。另一个可能的原因是,如果你想为自己的语言添上这种终极武器,你从此就不能声称自己发明了新语言,只能说发明了一种Lisp的新方言。

我把这件事当作笑话说出来,但是事实就是如此。如果你创造了一种新语言,其中有car、cdr、cons、quote、cond、atom、eq这样的功能,还有一种把函数写成列表的表示方法,那么在它们的基础上,你完全可以推导出Lisp语言的所有其他部分。事实上,Lisp语言就是这样定义的,John McCarthy把语言设计成这个样子,就是为了让这种推导成为可能。

就算Lisp确实代表了目前主流编程语言不断靠近的一个方向,这是否意味着你就应该用它编程呢?

如果使用一种不那么强大的语言,你又会有多少损失呢?有时不采用最尖端的技术,不也是一种明智的选择吗?这么多人使用主流编程语言,这本身不也说明那些语言有可取之处吗?

另一方面,选择哪一种编程语言,许多项目是无所谓的,反正不同的语言都能完成工作。一般来说,条件越苛刻的项目,强大的编程语言就越能发挥作用。但是,无数的项目根本没有苛刻条件的限制。大多数的编程任务,可能只要写一些很小的程序,然后用胶水语言把这些小程序连起来就行了。你可以用自己熟悉的编程语言,或者用对于特定项目来说有着最强大函数库的语言,来写这些小程序。如果你只是需要在Windows应用程序之间传递数据,使用Visual Basic照样能达到目的。

那么,Lisp的编程优势体现在哪里呢?

语言的编程能力越强大,写出来的程序就越短(当然不是指字符数量,而是指独立的语法单位)。

代码的数量很重要,因为开发一个程序耗费的时间,主要取决于程序的长度。如果同一个软件,一种语言写出来的代码比另一种语言长三倍,这意味着你开发它耗费的时间也会多三倍。而且即使你多雇佣人手,也无助于减少开发时间,因为当团队规模超过某个门槛时,再增加人手只会带来净损失。Fred Brooks在他的名著《人月神话》(The Mythical Man-Month)中,描述了这种现象,我的所见所闻印证了他的说法。

如果使用Lisp语言,能让程序变得多短?以Lisp和C的比较为例,我听到的大多数说法是C代码的长度是Lisp的7倍到10倍。但是最近,New Architect杂志上有一篇介绍ITA软件公司的文章,里面说"一行Lisp代码相当于20行C代码",因为此文都是引用ITA总裁的话,所以我想这个数字来自ITA的编程实践。 如果真是这样,那么我们可以相信这句话。ITA的软件,不仅使用Lisp语言,还同时大量使用C和C++,所以这是他们的经验谈。

根据上面的这个数字,如果你与ITA竞争,而且你使用C语言开发软件,那么ITA的开发速度将比你快20倍。如果你需要一年时间实现某个功能,它只需要不到三星期。反过来说,如果某个新功能,它开发了三个月,那么你需要五年才能做出来。

你知道吗?上面的对比,还只是考虑到最好的情况。当我们只比较代码数量的时候,言下之意就是假设使用功能较弱的语言,也能开发出同样的软件。但是事实上,程序员使用某种语言能做到的事情,是有极限的。如果你想用一种低层次的语言,解决一个很难的问题,那么你将会面临各种情况极其复杂、乃至想不清楚的窘境。

所以,当我说假定你与ITA竞争,你用五年时间做出的东西,ITA在Lisp语言的帮助下只用三个月就完成了,我指的五年还是一切顺利、没有犯错误、也没有遇到太大麻烦的五年。事实上,按照大多数公司的实际情况,计划中五年完成的项目,很可能永远都不会完成。

我承认,上面的例子太极端。ITA似乎有一批非常聪明的黑客,而C语言又是一种很低层次的语言。但是,在一个高度竞争的市场中,即使开发速度只相差两三倍,也足以使得你永远处在落后的位置。

附录:编程能力

为了解释我所说的语言编程能力不一样,请考虑下面的问题。我们需要写一个函数,它能够生成累加器,即这个函数接受一个参数n,然后返回另一个函数,后者接受参数i,然后返回n增加(increment)了i后的值。

Common Lisp的写法如下:

(defun foo (n)
    (lambda (i) (incf n i)))

Ruby的写法几乎完全相同:

def foo (n)
    lambda {|i| n += i } end

Perl 5的写法则是:

sub foo {
    my ($n) = @_;
    sub {$n += shift}
}

这比Lisp和Ruby的版本,有更多的语法元素,因为在Perl语言中,你不得不手工提取参数。

Smalltalk的写法稍微比Lisp和Ruby的长一点:

foo: n
    |s|
    s := n.
    ^[:i| s := s+i. ]

因为在Smalltalk中,局部变量(lexical variable)是有效的,但是你无法给一个参数赋值,因此不得不设置了一个新变量,接受累加后的值。

Javascript的写法也比Lisp和Ruby稍微长一点,因为Javascript依然区分语句和表达式,所以你需要明确指定return语句,来返回一个值:

function foo (n) {
    return function (i) {
        return n += i } }

(实事求是地说,Perl也保留了语句和表达式的区别,但是使用了典型的Perl方式处理,使你可以省略return。)

如果想把Lisp/Ruby/Perl/Smalltalk/Javascript的版本改成Python,你会遇到一些限制。因为Python并不完全支持局部变量,你不得不创造一种数据结构,来接受n的值。而且尽管Python确实支持函数数据类型,但是没有一种字面量的表示方式(literal representation)可以生成函数(除非函数体只有一个表达式),所以你需要创造一个命名函数,把它返回。最后的写法如下:

def foo (n):
    s = [n]
    def bar (i):
        s[0] += i
        return s[0]
    return bar

Python用户完全可以合理地质疑,为什么不能写成下面这样:

def foo (n):
    return lambda i: return n += i

或者:

def foo (n):
    lambda i: n += i

我猜想,Python有一天会支持这样的写法。(如果你不想等到Python慢慢进化到更像Lisp,你总是可以直接……)

在面向对象编程的语言中,你能够在有限程度上模拟一个闭包(即一个函数,通过它可以引用由包含这个函数的代码所定义的变量)。你定义一个类(class),里面有一个方法和一个属性,用于替换封闭作用域(enclosing scope)中的所有变量。这有点类似于让程序员自己做代码分析,本来这应该是由支持局部作用域的编译器完成的。如果有多个函数,同时指向相同的变量,那么这种方法就会失效,但是在这个简单的例子中,它已经足够了。

Python高手看来也同意,这是解决这个问题的比较好的方法,写法如下:

def foo (n):
    class acc:
        def _ _init_ _ (self, s):
            self.s = s
        def inc (self, i):
            self.s += i
            return self.s
    return acc (n).inc

或者

class foo:
    def _ _init_ _ (self, n):
        self.n = n
    def _ _call_ _ (self, i):
        self.n += i
        return self.n

我添加这一段,原因是想避免Python爱好者说我误解这种语言。但是,在我看来,这两种写法好像都比第一个版本更复杂。你实际上就是在做同样的事,只不过划出了一个独立的区域,保存累加器函数,区别只是保存在对象的一个属性中,而不是保存在列表(list)的头(head)中。使用这些特殊的内部属性名(尤其是call),看上去并不像常规的解法,更像是一种破解。

在Perl和Python的较量中,Python黑客的观点似乎是认为Python比Perl更优雅,但是这个例子表明,最终来说,编程能力决定了优雅。Perl的写法更简单(包含更少的语法元素),尽管它的语法有一点丑陋。

其他语言怎么样?前文曾经提到过Fortran、C、C++、Java和Visual Basic,看上去使用它们,根本无法解决这个问题。Ken Anderson说,Java只能写出一个近似的解法:

public interface Inttoint {
    public int call (int i);
}
public static Inttoint foo (final int n) {
    return new Inttoint () {
    int s = n;
    public int call (int i) {
    s = s + i;
    return s;
    }};
}

这种写法不符合题目要求,因为它只对整数有效。

当然,我说使用其他语言无法解决这个问题,这句话并不完全正确。所有这些语言都是图灵等价的,这意味着严格地说,你能使用它们之中的任何一种语言,写出任何一个程序。那么,怎样才能做到这一点呢?就这个小小的例子而言,你可以使用这些不那么强大的语言,写一个Lisp解释器就行了。

这样做听上去好像开玩笑,但是在大型编程项目中,却不同程度地广泛存在。因此,有人把它总结出来,起名为"格林斯潘第十定律"(Greenspun's Tenth Rule):

“任何C或Fortran程序复杂到一定程度之后,都会包含一个临时开发的、只有一半功能的、不完全符合规格的、到处都是bug的、运行速度很慢的Common Lisp实现。”

如果你想解决一个困难的问题,关键不是你使用的语言是否强大,而是好几个因素同时发挥作用:

a. 使用一种强大的语言 b. 为这个难题写一个事实上的解释器 c. 你自己变成这个难题的人肉编译器

在Python的例子中,这样的处理方法已经开始出现了,我们实际上就是自己写代码,模拟出编译器实现局部变量的功能。

这种实践不仅很普遍,而且已经制度化了。举例来说,在面向对象编程的世界中,我们大量听到"模式"(pattern)这个词,我觉得那些"模式"就是现实中的因素(c),也就是人肉编译器。 当我在自己的程序中,发现用到了模式,我觉得这就表明某个地方出错了。程序的形式,应该仅仅反映它所要解决的问题。代码中其他任何外加的形式,都是一个信号,(至少对我来说)表明我对问题的抽象还不够深,也经常提醒我,自己正在手工完成的事情,本应该写代码,通过宏的扩展自动实现。

使用telnet或SSH远程登录linux时,如果连接非正常中断,重新连接时,系统将开一个新的session,无法恢复原来的session。screen命令可以解决这个问题。

Screen工具是一个终端多路转接器,在本质上,这意味着你能够使用一个单一的终端窗口运行多终端的应用。

基本命令:

开一个会话,并命名为temp:

screen -S temp

临时退出这个会话:

ctrl-a d

再次进入名为temp的会话:

screen -r temp

检查正在运行的会话:

screen -ls

对 Lisp 早就有所耳闻了,不过一直也没有去看过相关的资料(除了那些说Lisp如何如何的文章或者某些文章里面顺便带过的文字,我对Lisp可以说是一无所知),不过理论上来讲应该不是很难吧。

现在我已经可以完全回到大学时代对编程的热爱了,因为我现在不是以它为职业,我从来都不希望自己的兴趣变成职业,当前在北京,虽然靠着自学的那点东西能混口饭吃,但是毕竟我还是选择了离开那个职业。

现在我每天忙完自己了工作,可以随便自己的兴趣写写代码,玩玩摄影,是没有任何负担的去玩,而不是带着压力的去写,我写好写坏,没有任何人来说我,只是唯一的遗憾是我现在只能自己一个人摸索,无任何旁人的指点,进步总是很慢的,但是这样得到的结果是我能更深刻地学习。

从05年接触互联网开始,我已经陆陆续续接触了Asp,PHP,Java,Python,JavaScript,Ruby这些语言,Asp是很果断的抛弃的,之后就是PHP,一直都是需要的时候就用用,不过自己的项目除了当年在民工网上班时上头指定使用PHP,我也不会在自己的项目中去使用,Java是在北京学习时系统学习过的,不过后来还是因为个人原因果断抛弃(这东西,唉),Python是07年就开始接触的,不过真正地去学习它也是后面的事情了,现在它还是我使用最多的语言(没有之一),JavaScript我也没怎么去学过,都是需要用到什么的时候就去学习什么(毕竟只是作为兴趣,所以一切都以服务我的兴趣为目的),前几个月开始接触了Go,感觉很不错,加上现在的Lisp,准备把自己的主要语言再增加两个,一个是Go,一个是Lisp。

在Windows上配置Common Lisp的开发环境步骤简单来说,就是下面四步:

  1. 下载并安装 CLisp:(http://clisp.cons.org/
  2. 下载并安装 EMacs:(http://www.gnu.org/software/emacs/
  3. 下载并安装 Quicklisp:(http://www.quicklisp.org/
  4. 进行一些必要的配置以使得所有工具能正确的工作。

第一步:下载并安装 CLISP

你可以在下面网页中下载 CLISP:

http://sourceforge.net/projects/clisp/files/clisp/2.48/

*注意*: 2.48并不是最新的CLISP版本,原因在于新版本的CLISP对于Windows的路径的处理还有一些问题,所以现在我还是推荐你使用2.48版。

请下载 clisp-2.48-win32-mingw-big.exe ,默认情况下,CLISP将安装到你的 C:Program Filesclisp-2.48 目录下,同时安装程序还会添加该路径到你的 PATH 环境变量中。

注意*:如果你和我一样,使用的是Windows 7 64位系统,则默认的安装路径为 *C:Program Files (x86)clisp-2.48

在 clisp-2.48 目录里,你会发现有一个名为 libsvm 的子目录,复制该目录下的 svm.dll 文件,并将其粘贴到 clisp-2.48/full 目录下。

完成前面的工作之后,你会发现在开始菜单中将有一个GNU CLISP 2.48 的链接,你现在已经可以运行它。

要退出GNU CLISP,最简单的办法就是按 Ctrl + D 或者在命令行中输入 (quit) 即可。

下载并安装 Emacs

最新版的 Emacs 二进制发行包可以在 http://ntemacs.sourceforge.net/ 找到,你可以选择下载最新的CVS版本或者最新的稳定发行版,你只能下载到 .7z 格式的发布包,所以,如果你的电脑上面没有可解压 .7z 格式文件的软件,你还需要安装一个来解压它http://www.7zip.org/ 。

解压 ntemacs 文件到任何一个你想它被安装的目录中(C:Program Files 同样是可以的)。在ntemacs/bin 目录中,你会看到有一个名为 runemacs.exe 的文件,在一个你感觉最方便的位置创建一个指向该文件的快捷方式(桌面,开始菜单等),之后点击该快捷方式以运行 Emacs。

下载并安装 Quicklisp

http://www.quicklisp.org/ 下载 Quicklisp (在本文发布时,它还只有Beta版本,但是它已经提供了非常好的功能,并且可以很好的工作)。下载下面这个文件:

http://beta.quicklisp.org/quicklisp.lisp

保存该文件至任何一个位置,之后打开 GNU CLISP,并运行下面这行代码:

(load "C:/path/to/quicklisp.lisp")
*注意*:你可以使用正斜线以替代Windows路径格式中的反斜线,或者使用两个反斜线,比如:(load “C:pathtoquicklisp.lisp”)

当上面的代码运行完成之后,接着运行下面这一行代码:

(quicklisp-quickstart:install :path "C:quicklisp")
注意*: 默认Quicklisp 将尝试安装到 *$HOME/quicklisp 目录中,由于在Windows中,$HOME 变量是根据你在哪个位置运行程序来定义的,所以,我们需要手工的指定安装路径。

现在,Quicklisp 安装脚本会为你下载所需要的文件并为你安装它们,现在你可以返回到http://www.quicklisp.org/ 查看Quicklisp的帮助文档以了解如何使用它。它能非常方便快速的为你搜索并安装Common Lisp库,比如:

(ql:quickload "ieee-floats")

上面代码将会首先检查所需要的库是否已经添加到你的CLISP环境中,如果没有,它会自动的下载该库并为你添加至CLISP环境中。

必要的配置以使得所有工具都能正常的工作

首先,CLISP需要一些配置,以使得它能找到正确的临时文件目录,找到 $HOME.clisprc.lisp (比如C:UsersusernameAppDataRoaming.clisprc.lisp )

添加如下代码:

;;; Load Quicklisp when CLISP launches
#-quicklisp
(let ((quicklisp-init "C:quicklispsetup.lisp"))
  (when (probe-file quicklisp-init)
    (load quicklisp-init)))

;;; Fix for CLISP on Windows.
(setf temporary-file-directory "C:<home-directory>AppDataLocalTemp")
(setf (ext:getenv "temp") temporary-file-directory)
(setf (ext:getenv "tmp") temporary-file-directory)

修改代码中的 为你的个人HOME目录,比如我的是 *C:/Users/pantao/*,上面的代码在 Windows Vista 以及 Windows 7 中能正常工作,对于 Windows XP,使用下面的路径:

C:<home-directory>AppDataTemp

下一步,重新打开 GNU CLISP,并运行下面代码:

(ql:quicklisp "quicklisp-slime-helper")

它将为你安装 SLIME (http://common-lisp.net/project/slime/),一个十分好的 Common Lisp 开发环境。

最后,你需要打开你的 Emacs 配置文件,最简单的办法是打开 Emacs然后Ctrl + X, Ctrl + F ,之后再输入 *~/.emacs*。

在该文件中,添加下面两行:

(setq inferior-lisp-program "clisp.exe")
(load "C:quicklispslime-helper.el")

现在,你应该已经可以使用 SLIME了,重新打开 Emacs,然后按 Alt + X之后键入 *slime*。

实在是有点儿累啊,腰有些酸痛,不过即使到了现在,也还是有很多事情没有忙完,老婆的车晚点了三个多小时,最后搞到三点多才到家,肯定是很累的啊,我也是从昨天上午九点开始干活,一直到现在才算基本上搞完,这个和效率好像没有多大关系,只是事实确实是太多了。

这个星期还需要去井坡子的交通大厅交身份证,1号路考,搞完这个就OK的可以拿证了吧,所以,还是希望能一次性过啊,不想有个第二次,尤其是在前面几个科目都是一次性满分通过的情况下更不想最后一个科目来个补考,那就真的是不爽了。

现在的收入啊,有点低呢……得想法子

现在的事业啊,还没成功……得努力啊

我发现我不是一个细心的人,有的时候有点儿想得太多,有的时候又完全是什么都不想,还不像一个成功人士(我的意思不是说像一个已经成功的人士,而是像一个能成功的人士),时间浪费很严重……需要努力改正……

又在学习一门新语言 Go 了,不过我也不知道我现在还需要去学语言干嘛,除了计算机编程语言,我还想学好法语,再学西班牙语,都是很喜欢的语言啊。

朋友 胡潇 今天让我做了一个类似于百度地图、谷歌地图的UI界面的简单实现,抽了点时间给他整了一个,没有进行过什么美化,只是把基本他需要的功能做了出来,写了一点点JS实现的,JS文件map.js 的代码:

//计算文档的可见高度

function windowHeight() {
    var de = document.documentElement;
    return self.innerHeight||(de && de.clientHeight)||document.body.clientHeight;
}

function hasClass(el, name) {
   return new RegExp('(s|^)'+name+'(s|$)').test(el.className);
}

function removeClass(el, name)
{
   if (hasClass(el, name)) {
      el.className=el.className.replace(new RegExp('(s|^)'+name+'(s|$)'),' ').replace(/^s+|s+$/g, '');
   }
}

function toggleTool() {
    var tools = document.getElementById("tools-wrapper");
    var map = document.getElementsByClassName("inner")[0];
    var tw = tools.offsetWidth;
    if(hasClass(tools, "toggled")) {
        removeClass(tools, "toggled");
        map.style.marginLeft = "20em"
    } else {
        tools.className += " toggled";
        map.style.marginLeft = "0em";
    }
}

//window.onresize是窗口改变大小的时候,因为窗口改变大小,文档的可见高度或宽度会变化。
window.onload=window.onresize=function(){
 var wh=windowHeight();
 document.getElementById("tools").style.height = document.getElementById("map").style.height =
  (wh-document.getElementById("header").offsetHeight-document.getElementById("navigator").offsetHeight)+"px";
}

HTML结构很简单

body
    #header
    #navigator
    #dashboard
        .columns
            #map-wrapper.main
            #tools-wrapper .sub

然后就使用JS去控制上面这些个东西即可,我使用的是负外边距的方式来实现左右分栏,其实后来想想,JS应该可以更简单一些,比如不需要使用JS直接对 #map 的样式进行操作,只需要 #toggle-button 点击之后,给 .columns 或者 #dashboard 加一个类 .toggled 即可,然后其它的工作就全部都是CSS的事情了。

大小自适应则完全使用JS实现了,也就是网页可视区域的高度减去 #header 与 #navigator 的高度即可,如果再在加上一个 #footer或者其它的占整个网页高度的东西的话,再减去这个新加的东西的高度即可,比如我如果加了一个 #footer 在最下面,那么改变高度的函数就成了:

window.onload=window.onresize=function(){
 var wh=windowHeight();
 document.getElementById("tools").style.height = document.getElementById("map").style.height =
  (wh - document.getElementById("header").offsetHeight - document.getElementById("navigator").offsetHeight) - document.getElementById("footer").offsetHeight +"px";
}

HTML 文件 index.html :

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8" />
        <title>Map</title>
        <link charset="UTF-8" href="style.css" rel="stylesheet" />
        <script src="map.js" charset="UTF-8" type="text/javascript"></script>
    </head>
    <body id="map-page">
        <div id="header"></div>
        <div id="navigator">
            <p>
                <a href="#" title="#">One Key Service</a>
                <span><label>Tel.</label><span>18374888188</span></span>
                <span><label>User Info.</label><span>China Mobile</span></span>
            </p>
            <ul id="global">
                <li>
                    <a href="#" title="#">Directions</a>
                </li>
                <li>
                    <a href="#" title="#">Travel</a>
                </li>
                <li>
                    <a href="#" title="#">Location</a>
                </li>
                <li>
                    <a href="#" title="#">Marketing</a>
                </li>
                <li>
                    <a href="#" title="#">Locate</a>
                </li>
                <li>
                    <a href="#" title="#">Behavior Statistics</a>
                </li>
                <li>
                    <a href="#" title="#">Real-time Traffic</a>
                </li>
            </ul>
        </div>
        <div id="dashboard">
            <div>
                <div id="map-wrapper">
                    <div>
                        <!-- <iframe src="http://cox.antusoft.com" id="map"></iframe> -->
                        <div id="map">
                            <div id="map-demo">
                                Your Map Content Here!
                            </div>
                        </div>
                    </div>
                </div>
                <div id="tools-wrapper">
                    <div>
                        <div id="tools">
                            <a href="#" onclick="toggleTool()" title="Toggle" id="toggle-button">Toggle</a>
                            <div>
                                <form action="#" method="get">
                                    <h2>Search</h2>
                                    <div>
                                        <ul>
                                            <li>
                                                Bus
                                            </li>
                                            <li>
                                                By Car
                                            </li>
                                        </ul>
                                        <div>
                                            Your content here!
                                        </div>
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
                <div></div>
            </div>
        </div>
    </body>
</html>

CSS 文件 style.css :

/* http://meyerweb.com/eric/tools/css/reset/
   v2.0 | 20110126
   License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
    display: block;
}
body {
    line-height: 1;
}
ol, ul {
    list-style: none;
}
blockquote, q {
    quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
    content: '';
    content: none;
}
table {
    border-collapse: collapse;
    border-spacing: 0;
}

/** common **/
body {
    font-size: 80%;
    line-height: 1.5em;
}

a {
    text-decoration: none;
}

.clear {
    clear: both;
}

.section {
    width: 100%;
}

/** .columns **/
.columns {
    width: 100%;
}

.columns .main {
    float: left;
    width: 100%;
}

.columns .sub {
    float: left;
    width: 20em;
    margin-left: -100%;
}

.columns .main .inner {
    margin-left: 20em;
}

/** .nav **/
ul.nav,
ol.nav {
    letter-spacing: -0.31em; /* webkit: collapse white-space between units */
    *letter-spacing: normal; /* reset IE < 8 */
    word-spacing: -0.43em; /* IE < 8 && gecko: collapse white-space between units */
}

ul.nav li,
ol.nav li {
    display: inline-block;
    zoom: 1; *display: inline; /* IE < 8: fake inline-block */
    letter-spacing: normal;
    word-spacing: normal;
    vertical-align: top;
}

ul.nav a,
ol.nav a {
    display: block;
    padding: .5em;
}

/** #navigator **/
#navigator p.nav {
    line-height: 2em;
    border-bottom: .1em solid #666;
}

#navigator ul.nav {
    border-bottom: solid .3em #55aed7;
    color: #bbb;
    background-color: #666;
    background-image: -khtml-gradient(linear, left top, left bottom, from(#777), to(#666));
    background-image: -webkit-gradient(linear, left top, left bottom, from(#777), to(#666));
    background-image: -webkit-linear-gradient(#777, #666);
    background-image: -moz-linear-gradient(#777, #666);
    background-image: -o-linear-gradient(#777, #666);
    background-image: linear-gradient(#777, #666);
}

#navigator ul.nav a {
    color: #bbb;
    padding: .5em 1em;
}

#navigator ul.nav li.active {
    color: #f0f0f0;
    background-color: #55aed7;
    background-image: -khtml-gradient(linear, left top, left bottom, from(#6ebce0), to(#55aed7));
    background-image: -webkit-gradient(linear, left top, left bottom, from(#6ebce0), to(#55aed7));
    background-image: -webkit-linear-gradient(#6ebce0, #55aed7);
    background-image: -moz-linear-gradient(#6ebce0, #55aed7);
    background-image: -o-linear-gradient(#6ebce0, #55aed7);
    background-image: linear-gradient(#6ebce0, #55aed7);
}
#navigator ul.nav li.active a,
#navigator ul.nav a:hover {
    color: #f0f0f0;
}

/** #dashboard, #main **/
#tools {
    position: relative;
    border-right: .5em solid #ccc;
    box-shadow: 1px 1px 3px #333;
}

.toggled {
    width: 0px !important;
}

.toggled .tool {
    display: none;
}

#toggle-button {
    position: absolute;
    height: 3em;
    width: 1em;
    top: 50%;
    left: 100%;
    margin-top: -1.5em;
    background-color: #ccc;
    border-radius: 0 .5em .5em 0;
    box-shadow: 1px 1px 3px #333;
    text-indent: -9999em;
}

.toggled #tools {
    border-right-color: #999;
}

.toggled #toggle-button {
    background-color: #999;
    box-shadow: 1px 1px 3px #333;
}

#map {
    width: 100%;
    border: none;
    overflow: auto;
}

div#map {
    background-color: #ddd;
    background-image: -khtml-gradient(linear, left top, left bottom, from(#eee), to(#ccc));
    background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#ccc));
    background-image: -webkit-linear-gradient(#eee, #ccc);
    background-image: -moz-linear-gradient(#eee, #ccc);
    background-image: -o-linear-gradient(#eee, #ccc);
    background-image: linear-gradient(#eee, #ccc);
}

/** Demo Content **/
div#map {
    position: relative;
}

#map-demo {
    position: absolute;
    width: 20em;
    height: 3em;
    margin-top: -1.5em;
    margin-left: -10em;
    top: 50%;
    left: 50%;
    text-align: center;
    font-size: 3em;
    font-family: Arial, Helvetica, sans-serif;
}

这个问题似乎对于我一个本身是英语专业的原计算机从业人员来讲,是有点复杂,从我10年10月回湖南开始,做服装这一行也有整整两年的时间了,我自我感觉有了很大的进步,回顾一下最开始的我状态是一问三不知,那个时候我不知道服装零售行业的半点儿知识,唯一知道的也都是任何一个正常人都能想到的,那就是去批发商那里进货,以某种方式计算一个零售价,然后零售给最终客户,但是现在却越来越发现,作为一个好的服装生意人,这里面的道道还是很多的,比如我感觉对我来说最难的就是跟踪的有零售店里面的货品的销售及调配了。

做服装零售,以这两年的所见所得,感觉要赚更多的钱,有下同这些问题需要解决:

  • 尽可能的减低固定成本开支(房租,水电等等),这笔开支是不管店了营业营业都会有的。
  • 尽可能的降低货品成本(这个就得在进货的时候讲价,越来越发现,讲价也是一门技术,更可以说是一门艺术)
  • 减少在职工身上的开支(这个我们的做法其实相反,我们是愿意给职工发高工资的,但高工资的前提是她能带来更高的销售业绩,我们可以说是全市提成点最高的零售店了)
  • 要有好的商品
  • 尽可能的销售更多的货品同时又要尽可能的减少货品的积压
  • ……

可能还有一些其它的问题需要注意,但是我感觉上面这一些能足够构成是否能赚钱的前提了,我做为一上三非人员(或者说什么都是吧,因为我确实是什么事情都要做),做得最多的还是上面说的最后面这一项,就是如何尽可能地减少货品的积压,这使得我需要有能力在尽可能短的时间里掌握下面这些信息:

  • 任何一个款式在任何一个门店里面的库存量;
  • 任何一个款式在任何一个门店里面的销售情况(最尽三天、七天、十天……)
  • 哪些是门店中可返回批发商处退换货品、它们的退换期限、距离退换期限时间、退至批发商所需要的物流时间等等
  • 哪些货品是已经补个过的不允许退换的(如果某一个款式有多个颜色,那么未补过货的颜色是可以退换的)
  • 任何一款货品还能销售多长的时间(季节性商品只有在它所属的季节销售,过季要么打折,要么下帐下一次再销售)
  • 任何一款衣服所有门店+仓库的库存总量、日平均销售量等
  • 任何一款衣服每一个颜色每一个尺码的销售量、库存量等信息
  • 任何一款衣服的上架时间
  • ………………

还有很多我也不知道如何去说的信息,只是所有这些信息只是需要我采集得到,然后再需要将这些信息转换成下一步的工作,该拿去退换的拿去退换,该在门店之间调配的就要调配,比如某一款衣服的黑色 L 码在 一店已经卖断 ,但是在三店却只卖了一件XL,五店也只卖了一件 2XL,那么我可以把三五店的L码先设调到一店,然后再看一两天的销售情况,如果一店再卖断,那么就需要适当的补货,补货需要考虑颜色,尺码,可销售时间等等,在这个观察期内,我们还需要关注其它门店的销售情况是否有所好转等等。

上面只是最简单的对一个款式的跟踪,我真正的工作,需要把上面那样简单的事情扩大几百倍吧,因为我需要同时跟踪数百个款式,每一个款式又有多个颜色、多个尺码,它们来自多个供货商,每一个供货商的可退换货时间是不同的,地点也是不一样的,退货物流时间也是不一样的……我现在使用的还是最笨的方法来完成这样的任务:

每天在门店日结之后,将其所销售的货品号抄下来,然后在进销存查询中去专门查询这些款式的销售情况,再决定调配,然后周日、周二、周四的晚上需要把所有在售的款式(不管是以前积压的还是新近的款式)都查一次进销存,尤其是周五(我需要在这一天把货品调一次,让每一个货品都能在周末展示在卖得好的那个门店里面)查询到了的结果我也是一个一个的抄下来,比如下面这样的:

|款号|颜色|数量|调配| |–|–|–|–| |102022|黑|XL/2; 2XL/1|三 -> 五| |102022|红|L/1|一 -> 六| |102023|杏|S/1,M/2,L/1|仓 -> 二|

这样一个一个抄下来,然后第二天在门店上班的第一时间打电话给店子一个一个的通知,中午再由一个人把所有货品去取、发到各个门店(这个成本有点高,但是现在也还没有想到别的降低这个成本的办法),只是有一个,我可以把商品的移仓单(门店至门店的调货单据)全部开好,然后店员只需要对照单据发货即可,但是因为以前遗留下来的问题太多,比如几乎所有店员都习惯于以前的手工单据,她们会把销售人顾客的服装款号及实际售价抄在本子上,然后再在某个时间输入到电脑上面,这样一来,客人没有详细的票据,而且因为颜色尺码都是靠得记忆,很有可能出错,所以,店里面的商品库存又有可有是不正确的(事实证明有很多都是不正确的,比如卖的是黑色M码,但是开的单据却是黑色L码)。

这些遗留下来的问题让我的工作难上加难,所以,我还需要定期的对商品进行盘点,而盘点的最主要作用就是恢复每一款商品每一个颜色尺码的正常库存数以方便货品的跟踪,但是我会发现,不出一个星期就会有很多商品的库存又出错了。而这还并不是唯一的问题,还涉及到金钱上的,因为遗留下来的问题之一是允许顾客议价,所以,1000元的衣服,在A手上卖出去可能是990元,在B手上卖出去可能是900元,因为我们有一个最低允许折扣,在这个折扣之上,对于店员来说是自由的,那么,如果卖出去的时候顾客支付了990元,等到店员开单的时候可能只开900元,这个,如果店里面没有收银员的话是可以的,而且如果店员与收银员“合作”的话,几乎任何时候都可以这么做。

问题多多,最难的还是遗留问题,但是这些应该是传统转向现代方法过程中都会遇到的问题吧,只是我还无法去解决它……或者说还没有找到最好的解决办法。

不知不觉中,宝贝女儿潘艾已经一周岁了,一岁生日有学校所有二十多个小朋友的陪伴,奶奶给买了一个大蛋糕,我也和老婆一起回来了,让人最开心的事情是五十多天没见我们,一见着我们就扑了过来。看那个大黑袋子是什么?那是给潘艾同志滴玩具哈,因为新鲜事物对她来说,过时得太快了,只好一次性多买些花样,然后时不时的拿出一个给她玩,玩腻了再换一个没玩过的。

女儿一岁照
女儿一岁照-点击照片查看一岁生日相册

折腾无极限啊,从06年开写玩博客,最开始使用的是Z-Blog,之后换成Bo-Blog,然后就用了几年的WordPress,再又是Drupal,再又是TextPattern,之后上一次不知道是什么原因又转成了WordPress,然后这一次也不知道是什么原因让我冲动的又转到了TextPattern,可能是看到TextPattern破天荒的进行一次小版本升级?

好吧,其实我想可能原因是因为这一次TextPattern的默认主题不再那么“简单”了,至少看上去像个样子了,对于我这种懒人来说,那就太OK了,再加上,WordPress越来越多的功能是我不需要的,所以,我还是回到最适合我的TextPattern上来吧。

把TextPattern的文本格式化改成了 MarkDown(Textile还是不太适合我啊),修改方法见 在 TextPattern 4 中使用Markdown ,另外,还做了一个简单的后台主题,如下图:

UPDATE: 下图个屁啊,图片都被删除了,一不小心把整个网站都删除了,还好数据库还在

颜色和样式模仿的是 OutLook.com 的,我太喜欢它这种简单的风格了,准备哪天有时间最主要是哪天有心情的时候把前台也换成那个样子。

如果你也想从WordPress转到TextPattern的话,TextPattern提供了一个导入工具,只需要提供你现在的WordPress的博客的数据库信息即可,但是有几个要注意的地方:

  • 在WordPress 里面的所有内容分类和链接分类都会导入进来
  • 在WordPress 中的标签在TextPattern全部会导入为分类(如果你的标签很多的话,你会发现导入之后TextPattern里面就有那么多个分类)
  • TextPattern里面好像没有Tag功能(我一直没去在意这个东西)
  • WordPress的文章图片会导入成为一个完整的URL地址
  • WordPress 媒体库里面的内容是无法导入过来的,要么保留原来的目录结构,要么就是手再上传或者你可以自己再写一个导入工具)

我在导入之前把以前的博客的数据库备份了一下,然后把所有的标签都删除了,把所有的文章分类也都删除了,越简单越好,毕竟,我感觉有一个搜索功能就成了。

TextPattern 是一个十分不错的博客工具,也是我一直使用的工具,与另一个很好的博客工具WordPress比起来,TextPattern清新许多,它所有的,都是我所要的,没有任何多余的在里面,唯一的一个缺点就是它没有提供Markdown插件(或者说它只提供了Textile支持),而我一直使用的都是Markdown,所以,还得自己整一下。

首先需要下载两个必须的文件:markdown.php 以及 smartypants.php ,它们的下载地址是:

之后修改 ./textpattern/lib/classTextile.php 为 ./textpattern/lib/classTextile.php.bak 对原 classTextile.php 作一个备份,之后将下载并解压后得到的 smartypants.php 与 markdown.php 两个文件移动到 ./textpattern/lib/ 目录中,并修改 markdown.php 为 classTextile.php。

安装之后,需要让 markdown 真正以 textile 的方法运行,我们还需要修改当前的 classTextile.php 文件:

打开 classTextile.php 后搜索 “### Textile Compatibility Mode ###” ,可以看到如下代码:

if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
    # Try to include PHP SmartyPants. Should be in the same directory.
    @include_once 'smartypants.php';
    # Fake Textile class. It calls Markdown instead.
    class Textile {
        function TextileThis($text, $lite='', $encode='') {
            if ($lite == '' && $encode == '')    $text = Markdown($text);
            if (function_exists('SmartyPants'))  $text = SmartyPants($text);
            return $text;
        }
        # Fake restricted version: restrictions are not supported for now.
        function TextileRestricted($text, $lite='', $noimage='') {
            return $this->TextileThis($text, $lite);
        }
        # Workaround to ensure compatibility with TextPattern 4.0.3.
        function blockLite($text) { return $text; }
    }
}

修改为:

if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
    # Try to include PHP SmartyPants. Should be in the same directory.
    @include_once 'smartypants.php';
    # Fake Textile class. It calls Markdown instead.
    class Textile {
        function TextileThis($text, $lite='', $encode='') {
            if ($lite == '' && $encode == '')    $text = Markdown($text);
            if (function_exists('SmartyPants'))  $text = SmartyPants($text);
            return $text;
        }
        # Fake restricted version: restrictions are not supported for now.
        #function TextileRestricted($text, $lite='', $noimage='') {
        #   return $this->TextileThis($text, $lite);
        #}
        # Workaround to ensure compatibility with TextPattern 4.0.3.
        function blockLite($text) { return $text; }
        function TextileRestricted( $text, $lite='', $encode='')  {
            return $this->TextileThis($text, $lite, $encode);
        }
    }
}

现在已经可以直接在编辑内容时使用 Markdown 语法了,当然,这样一来,不能再使用 Textile 语法,不过有一种想法,修改函数 TextileThis() ,让 $text 通过真正的 Textile 函数处理一次再返回应该是没问题的,不过如果 Textile 与 Markdown里面有相同的标记语法但有不同的解释的话,就不成了。

FastCGI是一种最常见的在如Nginx,Lighttpd和Cherokee等服务器上部属Flask应用的方法,当然,首先得先有一个FastCGI服务器,使用最多的就是Flup。

创建一个 .fcgi 文件

最开始需要创建一个FastCGI服务器文件,比如我的一个应用为costony,那么我可以把这个文件叫作: costony.fcgi :

#!/usr/bin/python
from flup.server.fcgi import WSGIServer
from costony import app
if __name__ == '__main__':
WSGIServer(app).run()

如果使用的Apache服务器的话,那么上面这些就已经足够了,但是Nginx或者老一些的Lighttpd服务器的话,还需要明显地把 socket 传送给 WSGIServer。

WSGIServer(app, bindAddress='/tmp/costony-fcgi.sock').run()

上一行的Socket路径应该与服务器配置文件里面的一样,保存 costony.fcgi 文件到任何一个你还可以找得到的地方,比如 : /home/wwwroot/costony.com/ 或者其它的类似的目录,然后需要让这个文件可执行:

#chmod +x /home/wwwroot/costony.com/costony.fcgi

配置 Lighttpd

基本的Lighttpd FastCGI配置如下:

fastcgi.server = ("/costony.fcgi" =>
((
"socket" => "/tmp/costony-fcgi.sock",
"bin-path" => "/home/wwwroot/costony.com/costony.fcgi",
"check-local" => "disable",
"max-procs" => 1
))
)
alias.url = (
"/static/" => "/home/wwwroot/costony.com/static"
)
url.rewrite-once = (
"^(/static.*)$" => "$1",
"^(/.*)$" => "/costony.fcgi$1"
)

别忘记必须开启FastCGI,Alias以及Rewrite模块。

配置 Nginx

Nginx有一些不同,它默认情况是没有FastCGI参数被发送的,所以一个基本的Nginx FastCGI配置如下:

location = /costony { rewrite ^ /costony/ last; }
location /costony { try_files $uri @costony; }
location @costony {
include fastcgi_params;
fastcgi_split_path_info ^(/costony)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_pass unix:/tmp/costony-fcgi.sock;
}

上面这个配置是让应用在 /costony 这一级目录被访问,如果像我一样,把应用直接放在根目录被访问的话,我们可以像下面这样:

location / { try_files $uri @costony; }
location @costony {
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param SCRIPT_NAME "";
fastcgi_pass unix:/tmp/costony-fcgi.sock;
}

运行 FastCGI 进程

如果上面这样还不行的话,我们还需要主动运行FastCGI,运行下面这个命令:

$screen
$python /home/wwwroot/costony.com/costony.fcgi

之后我们还需要对这个Socket相应的权限:

$chmod 0777 /tmp/costony-fcgi.sock