分类 文章 下的文章

今天用着电脑,突然一个轻轻的爆炸声音,电脑报销了,还好有一台备用电脑,把事情做完之后,带着电脑去维修,才买两个月时间,还在保修时间内,是电源烧坏了,换个电源就没有问题了,顺便我就想着给电脑加一块硬盘,整了一块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

最近这段时间新店开业,然后把仓库也从东塘搬到了火车站这边,仓库面积大了,可利用的空间也就大了,这边的房子是两层的,一楼门店二楼做仓库,在电信开了一个新户,然后老仓库的网络也搬了过来,两个四Mb的本来是想用一个多WAN路由器同时连接,但是后来却没有这么多,利用现有的一个十六口路由器,一个Model,一个TP-Link无线路由器以及一个TP-Link无线路由+Model一体机,将两个网分开来,基于路由+Model一体机的网就简单了,但是另外的一个却有点儿麻烦。

以前一直都是使用一个路由+Model一体机的网或者一个Model+一个无线路由器的网,这种是最简单的了,但是我现在的需求是:多LAN有线路由,同时还在带有一个无线WIFI热点,我的计划是下面这样的:Model连接ADSL线,有线路由器连接Model,无线路由器连接有线路由器,经过几天的实验,找到了下面两种组网方法(其实后来Google了一下,这两种方法网上早就有介绍,只是自己却一直没有去搜索,原因很简单,按着自己的想法去做确实实现了组网,就“很理性”的认为只有这一种方法。

方法一:无线路由器WAN口连接有线路由器LAN口

这种方法是最容易理解的,两个路由器的作用是一样的,连入网,并将网络分配给多台终端,配置方法如下:

  1. 恢复路由器配置至出厂设置;
  2. 先连接无线路由器(这个看你是把哪个路由器接入Model,那个不接入Model的先进行设置);
  3. 将无线路由器的设置修改为下面这样的:
    • WAN口静态IP:192.168.1.2 (只要是有线路由器的网络中未使用的IP均可)
    • WAN口子网掩码:255.255.255.0
    • WAN口网关:192.168.1.1
    • LAN口IP:192.168.0.1 (只要与有线路由器的IP不在同一网段即可)
  4. 打开DHCP服务
  5. 设置其它参数即可完成配置

这种方式,连接到有线路由器的终端与无线路由器的终端是不在同一网段的,慢慢的发现了一些问题,比如我的MACBOOK PRO不能访问Windows共享文件夹或者打印机等,后来就有了下面这种方式。

方法二:无线路由器LAN口连接有线路由器LAN口

这种方法的配置更简单:

  1. 先连接无线路由器
  2. 设置其LAN口IP地址为:192.168.1.2(与有线路由器的IP在同一网段)
  3. 关闭 DHCP服务
  4. 设置其它参数即可完成配置

这个时候,无线路由器的作用就是一个交换机,不管是连接到哪个路由器上面,IP地址都将由有线路由器分配,并且所有的终端都在同一个网段中。

以前从来没有玩过路由器,都是在虚拟机里面用得多,最主要的原因还是我从来就没有想过TP-Link的路由器除了路由还可以做交换机,或者说本来从路由器产生的那天起就是这样的?

引导语:我在美国生了两个孩子,生育前后都有培训班,家庭医生每次洗脑让我受益匪浅,我的两个宝宝在婴儿时期乖巧得好像家里没有小婴儿,我甚至疑心她们会不会哭?如今外婆常拿这句傻话笑我。回国后看到朋友或邻居们被小祖宗折磨得精疲力竭,想着将妈妈经写下来,这几年在她的指导下学到很多一辈子都受益的育儿宝典。

绝招一:如何让小宝宝不哭

爱哭的BABY都是爸妈教出来的。才出生的小BABY听不懂话,他们靠条件反射来做判断。中国的家长一听到孩子哭就抱起来。JANEGU医生反复告诫我:孩子一哭就放下来,不哭才抱起来,正好反过来。这样一来,再小的孩子都能明白,不哭的时候才有得抱,这招极灵。放下爱哭的孩子开始训练,看着时钟,不要抱让她哭,准备等10分钟再抱。第一次可以设定5分钟,以后一次比一次时间拉长,我的妞妞第一次就没有熬到5分钟,大约3分半的时候就停了,我已经心疼得肝肠寸断自己都快哭了,一定要忍住了。让孩子知道哭声是叫不来妈妈的。

训练“不哭的孩子”要排除4个哭的其他原因:尿了、病了、饿了,困了。上帝保佑,我家两个孩子都不会无理由哭,偶尔哭一下当作运动也是极少数,妞妞和妙妙100%是快乐的宝宝,躺在床上都咯咯笑出声的孩子。

绝招二:如何让刚出生的小宝宝一觉睡到天亮

美国医生再三批评我:你不是好妈妈,隔壁的孩子一睡到天亮,隔壁的妈妈是好妈妈,你不是!每次去看家庭医生我都挨批评,原因是我晚上要喂奶1-2次,抱怨养孩子很累很辛苦。医生告诉我一个观念:人类晚上不需要进食!我傻乎乎问,为什么呢?医生没办法,只好大声说:因为我们是人类!我印象极深,妞爸早就笑弯腰了。

很显然,我担心BABY晚上会饿根本是多余的,10-12小时的连续睡眠,对孩子来说比吃奶重要100倍!我们被老大妞妞折磨很惨,晚上吃一顿奶的坏习惯一直延续到快2岁。养老二的时候按照医生说的,彻底让我和老公解脱出来,从刚出生到一个礼拜我家妙妙被训练距离5个小时才吃奶,2个礼拜之后,孩子就能睡天亮了。晚上不需要吃奶!!!(三个感叹号是请妈妈们切记的意思)

绝招三:让宝宝自己静静入睡,不要摇晃

医生说,一个快乐的BABY会睡在床上唱歌的(不是真唱,是婴儿咿咿呀呀)!婴儿的大部分时光都需要在床上度过,这里请爸爸妈妈们记住,床是不会动的!所以,抱起婴儿的时候不要摇晃,不要来回走动,更不要这样哄孩子睡觉,轻轻放在床上让他自己睡就好。再说一次,床是不会动的。爸爸妈妈你们没有权利剥夺孩子躺在床上享受快乐的权利。 还有一点一定要注意的就是,兴奋的家长不要用力和孩子“疯闹”,孩子的大脑在婴儿时期还是胶状的果冻状态,很容易受伤。

绝招四:趴着睡养美女

个人经验,孩子趴着睡觉比仰着睡得安稳,才出生的孩子心脏朝下,几乎不会一惊一惊的状态,模仿子宫被包裹的状态。另一个最大的好处是改变亚洲人的大饼脸,5天之内的婴儿需要1个小时帮忙翻脸,5天之后,我家妞就会自己左右转向自如了。妞是标准的小脸,和生出来的时候大方脸好像变了一个孩子,呵呵,得意。

老二妙妙的脸没有刻意这样做,那时候妞妞需要人照顾,没有精力1个小时翻头的精力了,很可惜。这个机会只有在出生到3个月之内有效,大了就没用了。

绝招五:宝宝的抵抗力比你强十倍

爸爸妈妈穿2件,宝宝绝不能穿2件半,只能比你穿的少,不能比你穿的多。我家两个孩子都十分强壮,冷热其实很好控制,偶尔看到儿童医院发烧的孩子被花被子棉袄裹着一层一层的,可怜啊,孩子不会说话,都是家长害的。

绝招六:拒绝抗生素!

感冒发烧是孩子成长的过程,是增强抵抗力的过程。我家宝宝发烧了最常用的就是用冰敷,用有拉链的保鲜袋装冰块,用干净毛巾裹了冰敷在全身,30分钟一定退烧。烧厉害了配点美林或泰诺。如果是着凉了感冒没有发烧,就让孩子吊着鼻涕没有事,几天就好。不要相信任何药能治疗感冒。到目前为止,除了去年的手足口被医院骗进去那一次之外,我家2个孩子从来没有挂过盐水和注射过抗生素。

在美国,是那种要死的病才注射抗生素的,在中国注射室的景观,吓坏了妞妞的爸爸,那个景象让人发毛他说。一排一排的吊针,打手打头打脚的小孩子们在妈妈爸爸爷爷奶奶的陪护下挂盐水。集中营莫过如此,很恐怖。花钱不说,更可怕的是体内的细菌大战存留的各种毒越来越多,周期性的需要挂盐水来压下去,常常听到妈妈们说,有一点症状马上挂水压下去,十分得意。我为她们的无知十分惋惜。我的孩子越来越健壮,从不生病,病了也几天自己好,基本不治疗。邻居的孩子半夜跑医院,累的人仰马翻乐此不疲,从这点看我是好妈妈。

绝招七:天天洗澡,用清水,少用沐浴露

我的2个宝宝健康的另外一个原因是天天洗澡,天天洗澡是必要的。记得我生完孩子出来,准备听我妈妈的教诲躺着不动,打算熬到第二天才洗澡,护士惊讶地拉我起来洗澡“不管你什么传统,把汗水和细菌都洗掉才会健康”。我被误会成个脏妈妈被拖去洗澡,当然要说明的是我生两个都是顺产。产妇如此,何况小孩子?

孩子每天运动量大,臭汗奶腥味便便尿尿什么味道都有,一定要洗澡,不能隔天才洗。这也是健康不生病的重要原因,他们不会着凉的,抵抗力比成人强十倍。医生交代:宝宝洗澡尽量用清水,再名牌的婴儿产品都不如清水好,不要用任何沐浴露、洗发水,清水最最好!医生说尽量不用,一定要用的话一个星期用一次沐浴露就好,我家是三天用一次(我觉得一周太长了,心理作用)。

绝招八:没有奶不够吃的妈妈,只有不想喂奶的妈妈

能吃到2岁半是天下最幸福的宝宝。JANEGU医生说,没有奶不够吃的妈妈,只有不想喂奶的妈妈,孩子的食量多大,妈妈的奶就有多少,生双胞胎的妈妈的奶同样够两个孩子吃。这一点我很失败,尽管GU医生用笔画了一个圆圈,她告诉我,你的孩子吃多少,你就能生产多少,你觉得奶不够,加奶粉进去,奶就会真的不够,最后就没了,事实证明我后来真的越来越少直到没了。孩子不会说话,她吃饱没有很难知道,妈妈觉得奶少了就放弃母奶,就补奶粉,其实是告诉自己的身体,我只能生产这么多了。

要相信自己能当头称职的奶牛。如果再生一个,一定要证明一下。生完小孩第三天就去医院参加“喂奶班”,几个妈妈带婴儿围成一圈,有黑妈妈白妈妈和亚洲妈妈,喂奶课用的是那种很舒服的沙发,脚下有垫脚的专用凳子。医生们将孩子脱光,包括尿布,然后称BABY的重量精确到小数点后面2位数。然后让我们抱孩子喂奶,不准穿衣服只穿尿布。医生一对一教妈妈们如何让孩子一口就咬准奶头,如何保护奶头,一次一次训练,期间有别的妈妈来用免费提供的吸奶机下奶,用塑料袋装回家。我刚开始的害羞和尴尬慢慢没有了,呵呵,我在家完全不知道妞妞吃了多少,这下好了,一堂课下来,马上称孩子份量,我妞妞吃了不少,好像是那天的第二名,前后数据证明,我有奶。

绝招九:宝宝吃奶睡着怎么办?

BABY被妈妈抱着舒服又安全,没吃饱就睡着了,醒了又吃两口,折磨妈妈,宝宝也吃不饱睡不好,害人害己。训练班里学到最有用的一招,如何弄醒孩子让他们专心吃奶,就是脱衣服!脱得就剩尿布,医生告诉我,放心吧,你抱着她有妈妈的体温,这体温能保证婴儿不会着凉,温度刚刚好,BABY有一点凉意,他们会用心进餐(她说的真是进餐,呵呵)。

放心吧,这些看起来弱不禁风的小东西,从妈妈母体带出来的抵抗力比成人强10倍,喂奶的产妇也不容易病,这就是繁殖的力量。(当然室内的温度是有一定温暖的,国内无空调寒冷地区的人不要对照模仿误会)

绝招十:母奶是最好的“抗生素”

妞妞大概10来天的时候,一只眼睛发炎化脓了,我和妞爸吓得抱到医院,还是急诊,儿科的男医生问,你喂奶还是奶粉,我说母奶。他说挤点出来放进孩子眼睛,BABY就会好。What?我排队等急诊老半天就这么打发我回家了。于是如法炮制,母奶当药,几天后妞妞果然见好,是否母奶的原因还是不清不楚,美国医生忽悠得或许有点道理吧。总之母奶是好东西。

绝招十一:你会换尿布吗?

废话,养孩子的谁不会换尿布呢?不一定哦。你是否有被孩子喷到尿或臭便便的时候呢?一天洗5次床单是我的最高记录。换尿布的时候,每次来的新阿姨都要重新教一遍,窍门就是把干净尿布放在臭尿布下面,再打开尿布,擦干净抽出来,这样即使孩子正好那时候又便又尿,你的床单保住了。习惯这么做以后,永远没有意外发生。

绝招十二:你会拍嗝吗?

 

  • 姿势1:趴在肩膀上轻拍孩子后背,这种是传统姿势,很多时候不太容易拍出来嗝。
  • 姿势2:让孩子侧坐在腿上,左手托着孩子的脖子,撑起下巴,右手拍后背,这样效率极高,通常十几下就拍出嗝了。

临睡的最后一顿奶,一定要喂饱足了,拍完嗝继续喂,我家妙妙2礼拜就睡整晚就是用这招,拍完嗝还可以再喂2-3次,直到她真的不吃了,小嘴巴往外推奶瓶的状态,妙妙就能睡到天亮。

 

绝招十三:早点吃五谷杂粮

4个月吃米粉再大点跟大人一起吃东西,孩子长大不太会偏食,早点吃和成人一样的食物对孩子有好处。6颗牙的时候,JANEGU医生说,她已经可以用这几颗牙齿吃很多东西了,什么都可以喂,没有不能吃的。

绝招十四:别破坏宝宝的专注力

再小的宝宝玩玩具或看电视或看图画书的时候,请家里的阿姨或者任何人都别打断她们。即使是吃饭的时间到了,晚一点也没有关系。培养孩子的专注力就等于培养她们长大了能注意力集中做任何事情。我家妞妞专注力很好,她喜欢的事,比如拼图,一个下午几个钟头不抬头,没有人叫她,除了尿尿之外。最怕那种大呼小叫逗孩子的长辈们,自以为聪明,边看电视边问问题。闭嘴,请安静。给孩子一个专注的环境吧。

绝招十五:慢慢走路,小声说话

身体力行很重要,大人怎样的孩子都会学。难得遇到上海一位陆老师,教孩子慢慢走路,轻声说话,人家听得到就好了,别那么大声。回了家乡,小区的幼儿园老师教孩子们扯起嗓门唱歌,我在一旁看,心想完了,培养了半天的小淑女被汉派文化同化了,哈哈,如今的妞妞热干面一样的个性十足,整个儿一辣妹。大环境很厉害,几乎没有不被同化的可能。

绝招十六:只要没有生命危险,鼓励宝宝试试看,摸摸看

中国父母礼节有余,胆子太小。“别动,危险,站起来地上脏,别摸,会刺到。。。。不许这样,不许那样”美国父母最常说的却是“Tryit!”试试看,摸摸看。鼓励宝宝,只要没有生命危险,不要阻止孩子用他们的小手尝试和认识世界。大不了衣服弄脏,地上滚来滚去的宝宝时光很短,饭粒菜汤翻了一地,大不了扫一下。他们会早学会吃饭甚至用筷子,玩水大不了感冒,玩沙子大不了进鞋子难受难清洁。总之有很多阻碍孩子快乐的条条框框,促使我们的小天使们越来越快地变成成人,变成听话和懂事的孩子。

快乐第一重要,衣服脏了不重要,只要没有生命危险,不要阻止你的孩子。

绝招十七:也是最重要的一招,一天只能批评两次!

父母也不可以随便批评宝宝,一天只能批评两次,两次用完了就等明天的配额吧。美国老师这么慎重交代的。于是,宝宝外婆说:“你的孩子一天10次都不够,2次的话我10分钟就用完了,这太难了。我大笑。在婴儿的时期,批评有用吗?当拉个便便也被阿姨批评“宝宝又干坏事了”时,我会反问阿姨,你有哪天不拉屎吗?

编后语:除了照顾好孩子的生活,更重要的是培养好孩子的习惯和性格,要与孩子讲道理,多沟通,孩子 的性格先天也有部分,所以不同的孩子也要有不同的教育方法,因人而异,不要和别的孩子比,特别不要老挂在嘴边。或许可以帮到被小祖宗弄得手忙脚乱、身心疲惫的年轻爸爸妈妈。

好吧,我知道我很傻,最主要的原因还是因为自己的各种不爽,先是WordPress越来越不简单了,如果要复杂,我有Drupal了,我只想有一个程序可以写写博客,但是,我可能有各种控的原因吧,总是感觉不爽,比如,我从TextPattern导入进来的内容一团乱,因为TextPattern使用的是Textile格式的,和WP不同,另外,我还是一个ID控,不习惯于WP没几下子就搞出几百号ID出来,还有,我喜欢编写界面足够的简单,等等的各种各样的问题让我最终决定,还是自己完全按照自己的想法来开发吧。

程序的预览版本可以在 dev.crabison.com 看到,今天才是开发的第二天,我所想要的最基本的功能也已经有了,无非就是增加、删除、修改和查看文章贝,这些都差不多了,只不过太久没写代码了,代码可能还一团乱,也没有什么特别的计划之类的,想到什么写什么,同时,昨天还花了近一个小时写了一个脚本,可以从WP或者 TextPattern上面导入数据,我只需要文章和标签数据,另外,因为系统的文件管理功能还没有写,所以,现在还无法导入文件(图片等)。

我对于这个系统的想法是这样的:

  • 能管理自己的日记、文章、相册、视频、代码、项目以及静态页面
  • 文章、日记、相册、项目都是基于某一个节点,也就是上面所说的这些所有的内容类型其本质上都是一个节点,这样方便我的管理
  • 文章和日志差不多不需要任何其它的修改,直接就按节点的数据结构即可
  • 图片使用图片集的方式组织,即上传任何一张图片都必须至少属于某一个图片集,也就是相册,同时图片还是一个文件,所以对于图片、视频或者项目文件等都是一个文件,有一个文件系统专门来管理,我希望的是所以文件我上传上去之后可以以最简单的方式访问,所以,我计划是以库的形式来组织,这样,我不再需要知道文件的具体存放位置,所有的都由程序去管理,比如我要访问一张图片,只需要使用 “/image/view/1“即可,这就是ID为1的图片的发布网址,如果想直接获取图片的URL地址,则使用 “/image/1”
  • 我不准备使用编辑器,Markdown就很不错,直接把任何文本都以Markdown的格式编辑,如果以后这个系统发布的话,还提供让使用者自己添加格式化标记库的方法,只是现在我还只是自己用。
  • 一张图片可以属于多个图集,这是肯定的,比如我在北京拍了一张花的照片,那么这张照片当然可以属于花,也当然可以属于北京。
  • ……

大体上的功能就是这些吧,先把上面这些想法实现了再说,差不多需要一个星期的开发时间吧,然后就直接开始使用,在使用的过程中再去完善它……同时这个程序也可以为我之后的平台做基础……

今天突然就这么灵感闪现,想到好像还从来没有人开发一款3D俄罗斯广场游戏,我的意思不是说显示出来看着像3D,但是玩法和以前的一样的那种,而是真正的在三维空间里面进行的俄罗斯方块游戏,任何一个方块可以向左、右、前、后四个方向移动,同时还可以向左、右、上、下四个方向转动,当然了,不再是一行就消除,而是必须要把一个平面填满。

这个想法看上去是可行的,但是首先一个能想到的问题是,在以前的俄罗斯方块中,我们可以看到底层未填满的空格,但是在3D的环境下,这个不太好实现,我们只能看到最上面一层的状况,但是还是想开发这么一款游戏,操作其实很简单:

  • W : 在Z轴上向前移动(看上去就是越来越远离屏幕)
  • S : 在Z轴上向后移动 (与W键的效果相反)
  • A : 在X轴上向后移动 (即向屏幕左方移动)
  • D : 在X轴上向前移动(即向屏幕右方移动)
  • I : 向上旋转
  • K : 向下旋转
  • J : 向左旋转
  • L : 向右旋转
  • Space : 确定
然后还需要有一个办法让玩者可以把整个场景左右旋转,我选择下面这两个键:
  • R :场景向左旋转
  • Y : 场景向右旋转

这样正好两只手都在键盘上面时就可以完全控制整个游戏

我现在使用的系统版本是 OS X 10.7.4,以前一直都更新正常,但是最近的一次更新总是会提示无法安装任何更新,一直也没有找到好解决办法,从下面这个图可以看到,系统是有更新的,并且好像已经下载成功了,但是当我点击安装 4 项之后,就提示“未能安装选定的任何更新,出现未知错误”。

点击安装 4 项之后就出现下面这个提示:

哪位出现过同样问题的朋友给个建议啊,这个问题好像是出现在我安装了Final Cut Pro X 之后,有可能是因为我安装的 FCPX 不是正版的原因吧?我删除这个软件再试试。

— 更新 —

刚才把Final Cut Pro X删除了,然后连接了 VPN ,之后就可以更新了,但是我想原因应该还是出在 VPN 上面,可能是苹果的更新服务器的域名解析出问题了吧,最后有一个 JavaForOSX无法更新,我是自己去官方网站上下载的1.0手工安装更新的。

不知道是从什么时候开始喜欢喝饮料的,而且有很多时候不喝一点水之外的液体总是感觉嘴巴里不舒服,前几天无意间在一家店里看到这种进口饮料,不过我喝的不是这个味的,是芒果的,味道十分不错,但是第二次去买的时候就已经没有了,难到他们进货都只进一两瓶?应该不会吧。

CHABAA JUICE DRINK

CHABAA JUICE DRINK

整个过程十分的简单,部属方式也确实十分简单,它不像 wsgi 那么麻烦,整个体验十分的不错,这里记下来我的整个配置过程。

安装 UWSGI

默认系统并没有安装UWSGI,在使用前,必须得先安装它,我使用的是 Ubuntu 服务器,可以使用下面的方法安装它:

sudo -s
add-apt-repository ppa:nginx/stable
add-apt-repository ppa:uwsgi/release
apt-get update
apt-get install uwsgi-python python-virtualenv

你可以在运行上面的命令时, 会提示没有 “add-apt-repository” 的错误,这是因为这个工具并没有随Ubuntu一同安装,你需要自己安装它,安装方法是下面这行命令:

sudo apt-get install python-software-properties

从 Ubuntu 9.10 开始我们可以使用 add-apt-repository 命令(实质是个python脚本)在source.list里添加 ppa 源了(同时完成导入key)

创建Wiki站点的配置文件

我的维基架设在 http://wiki.costony.com 上,准备使用Nginx将其转发到 Unix Socket 上,我所使用的 Nginx 虚拟机的配置文件如下:

server {
    listen 80;
    access_log /home/wwwlogs/wiki.costony.com.access.log;
    error_log /home/wwwlogs/wiki.costony.com.error.log;

    server_name wiki.costony.com;
    location / {
        include uwsgi_params;
        uwsgi_pass unix:///tmp/wiki.costony.com.socket;
        uwsgi_param UWSGI_PYTHOME /home/websites/costony.com/wiki/moinenv/;
        uwsgi_param UWSGI_CHDIR /home/websites/costony.com/wiki/;
        uwsgi_param UWSGI_SCRIPT moin_wsgi;
        uwsgi_param UWSGI_SCHEME $scheme;
        uwsgi_param REMOTE_USER $remote_user;
    }

    location /moin_static194/ {
        alias /home/websites/costony.com/wiki/public/static/;
    }
}

从上面的配置中我们可以看到:

  • 我将Moin安装在它专用的 Virtuan Env中,同时,将其Static文件存放中一个特别的目录下,所以,这些都需要在安装 Moin的时候进行设定。

安装Python环境及 Moin

virtualenv /home/websites/costony.com/wiki/moinenv
cd /tmp
wget http://static.moinmo.in/files/moin-1.9.4.tar.gz
tar xvfz moin-1.9.4.tar.gz
source /home/websites/costony.com/wiki/moinenv/bin/activate
cd /tmp/moin-1.9.4
python setup.py install
deactivate
cp -r /tmp/moin-1.9.4/wiki /home/websites/costony.com/wiki
cd /home/websites/costony.com/wiki/wiki
mv wiki moin
cd moin
cp config/wikiconfig.py wikiconfig.py
cp server/moin.wsgi moin.wsgi
cp moin.wsgi moin_wsgi.py
cp /tmp/moin-1.9.4/MoinMoin/web/static/htdocs/* /home/websites/costony.com/wiki/public/static/
uwsgi-python -s /tmp/wiki.costony.com.socket --wsgi-file moin_wsgi.py -M -p 4 -d /home/wwwlogs/uwsgi.log

安装完成

在计算机图形学与电影摄影术中,高动态范围成像(英语:High Dynamic Range Imaging,简称HDRI或HDR)是用来实现比普通数字图像技术更大曝光动态范围(即更大的明暗差别)的一组技术。高动态范围成像的目的就是要正确地表示真实世界中从太阳光直射到最暗的阴影这样大的范围亮度。

我使用的HDR工具是Photoshop CS4(不用想,我用的是注册机算出来的注册号注册的版本)的自动HDR工具,使用起来十分的简单,而且效果也还不错,只是前期照片的拍摄是一大问题,还没有掌握如何来拍摄最适合的照片用以合成HDR照片,这是拍摄技术的问题了。

我所拍摄的地点是长沙市侯家塘,从二十二楼窗户上面拍的,拍这个和制作全景图时前期的拍摄一样,一个稳定的相机平台必不可少,我也一直没买三脚架,所以,就把相机放在窗台上面了,只是取景的角度不太好调整了,下面这张是合成之后的照片:

湖南省长沙市侯家塘HDR建筑 湖南省长沙市侯家塘HDR建筑

上面这张图片是由下面三张源始照片利用Photoshop的高动态范围成像技术合成的:

湖南省长沙市侯家塘HDR建筑源始图片湖南省长沙市侯家塘HDR建筑 湖南省长沙市侯家塘HDR建筑源始图片湖南省长沙市侯家塘HDR建筑

使用了正常曝光至过曝的三张照片合成的 使用了正常曝光至过曝的三张照片合成的

从上面三张照片可以看到,任何一张照片都只有部分区域曝光正常,最左侧的照片天空部分过曝了,而越往后侧,照片下部建筑区域的暗部就越来越暗以至于已经没有了任何细节,通过合成之后,我们让高光与暗部都尽可能的保留细节。

— 2012.07.15 更新 —

今天在Apple App Store购买了一个名为 HDR Darkroom Pro 的HDR合成软件,下面我使用了六张照片再一次合成了上面这张照片,效果如下图所示:

使用了正常曝光至过曝的三张照片合成的

使用了所有六张照片(从曝光完全不足至过曝的六张) 使用了所有六张照片(从曝光完全不足至过曝的六张)

默认情况下还是Photoshop的效果我最喜欢,但是应该照片在合成的过程中是可以调整的,所以,就看个人的调整技术了,我才刚刚开始接触HDR这个东西(虽然几年前玩过但是都没有去研究过),再加上本人对色彩的控制能力太差,唉,有待提高啊……

不过HDR Darkroom PRO使用起来比Photoshop方便快捷,而且资源占用少得多,调整也方便,所以,这个可以是个首选啊……另外,有一点点感觉上的收获,如果要合成HDR照片,所使用的照片数量不需要太多,也不是很少就可以的,最简单的办法来确定哪几张照片适合合成HDR图,看看它们的直方图就行了,看看他们的高峰是不是从左至右都有,如果有的话合成的一般都还不错……

突然想起来上面两张图片的问题其实很大,我使用的ProPhoto RGB色彩空间,这样照片经过PHP的GD库处理后,就成上面这个严重偏色的了,可以点击图片查看正常的效果……其实还是不错的。

HDR 窗台上的富贵竹 HDR 窗台上的富贵竹

学习HDR最好的素材就是窗台,因为窗内与窗外的光强返差总是很大,我们可以通过不同的曝光让窗外的景物正常曝光或者让窝内的曝光,但是我们总是无法让窗内外曝光都正常,这个时候使用HDR就很容易做到了,如上图所示就是我拍了三张照片合成的HDR,只是自己技术不到位,总是感觉这真太不真实了……

在 Smashing Magazine 上面有一篇《35 Fantastic HDR Pictures》的文章,里面分享了35张十分不错的HDR作品。

选择公司的形式

普通的有限责任公司,最低注册资金3万元,需要2个(或以上)股东,从06年1月起新的公司法规定,允许1个股东注册有限责任公司,这种特殊的有限责任公司又称“一人有限公司”(但公司名称中不会有“一人”字样,执照上会注明“自然人独资”),最低注册资金10万元。

 

如果你和朋友、家人合伙投资创业,可选择普通的有限公司,最低注册资金3万元;如果只有你一个人作为股东,则选择一人有限公司,最低注册资金10万元。

注册公司的步骤

  1. 核名:到工商局去领取一张“企业(字号)名称预先核准申请表”,填写你准备取的公司名称,由工商局上网(工商局内部网)检索是否有重名,如果没有重名,就可以使用这个名称,就会核发一张“企业(字号)名称预先核准通知书”。这一步的手续费是30元。 (30元可以帮你检索5个名字,很多名字重复,所以一般常见的名字就不用试了,免得花冤枉钱)
  2. 租房:去专门的写字楼租一间办公室,如果你自己有厂房或者办公室也可以,有的地方不允许在居民楼里办公。租房后要签订租房合同,并让房东提供房产证的复印件。
  3. 买印花税:签订好租房合同后,还要到税务局去买印花税,按年租金的千分之一的税率购买,例如你的每年房租是1万元,那就要买10元钱的印花税,贴在房租合同的首页,后面凡是需要用到房租合同的地方,都需要是贴了印花税的合同复印件。
  4. 编写“公司章程”:可以在工商局网站下载“公司章程”的样本,修改一下就可以了。章程的最后由所有股东签名。
  5. 刻私章:去街上刻章的地方刻一个私章,给他们讲刻法人私章(方形的)。费用大概20元左右。
  6. 到会计师事务所领取“银行询征函”:联系一家会计师事务所,领取一张“银行询征函”(必须是原件,会计师事务所盖鲜章)。如果你不清楚,可以看报纸上的分类广告,有很多会计师事务所的广告。
  7. 去银行开立公司验资户:所有股东带上自己入股的那一部分钱到银行,带上公司章程、工商局发的核名通知、法人代表的私章、身份证、用于验资的钱、空白询征函表格,到银行去开立公司帐户,你要告诉银行是开验资户。开立好公司帐户后,各个股东按自己出资额向公司帐户中存入相应的钱。银行会发给每个股东缴款单、并在询征函上盖银行的章。 注 意:公司法规定,注册公司时,投资人(股东)必须缴纳足额的资本,可以以贷币形式(也就是人民币)出资,也可以以实物(如汽车)、房产、知识产权等出资。 到银行办的只是货币出资这一部分,如果你有实物、房产等作为出资的,需要到会计师事务所鉴定其价值后再以其实际价值出资,比较麻烦,因此建议你直接拿钱来 出资,公司法不管你用什么手段拿的钱,自己的也好、借的也好,只要如数缴足出资款即可。
  8. 办理验资报告:拿着银行出具的股东缴款单、银行盖章后的询征函,以及公司章程、核名通知、房租合同、房产证复印件,到会计师事务所办理验资报告。一般费用500元左右(50万以下注册资金)。
  9. 注册公司:到工商局领取公司设立登记的各种表格,包括设立登记申请表、股东(发起人)名单、董事经理监理情况、法人代表登记表、指定代表或委托代理人登记表。填好后,连同核名通知、公司章程、房租合同、房产证复印件、验资报告一起交给工商局。大概3个工作日后可领取执照。此项费用约300元左右。
  10. 凭营业执照,到公安局指定的刻章社,去刻公章、财务章。后面步骤中,均需要用到公章或财务章。
  11. 办理企业组织机构代码证:凭营业执照到技术监督局办理组织机构代码证,费用是80元。办这个证需要半个月,技术监督局会首先发一个预先受理代码证明文件,凭这个文件就可以办理后面的税务登记证、银行基本户开户手续了。
  12. 去银行开基本户:凭营业执照、组织机构代码证,去银行开立基本帐号。最好是在原来办理验资时的那个银行的同一网点去办理,否则,会多收100元的验资帐户费用。开基本户需要填很多表,你最好把能带齐的东西全部带上,要不然要跑很多趟,包括营业执照正本原件、身份证、组织机构代码证、公财章、法人章。开基本户时,还需要购买一个密码器(从2005年下半年起,大多银行都有这个规定),密码器需要280元。今后你的公司开支票、划款时,都需要使用密码器来生成密码。
  13. 办理税务登记:领取执照后,30日内到当地税务局申请领取税务登记证。一般的公司都需要办理2种税务登记证,即国税和地税。费用是各40元,共80元。办理税务登记证时,必须有一个会计,因为税务局要求提交的资料其中有一项是会计资格证和身份证。你可先请一个兼职会计,小公司刚开始请的兼职会计一般200元工资就可以了。
  14. 申请领购发票:如果你的公司是销售商品的,应该到国税去申请发票,如果是服务性质的公司,则到地税申领发票。

最后就开始营业了。 注意每个月按时向税务申报税哦,即使没有开展业务不需要缴税,也要进行零申报,否则会被罚款的。

有二点你可能比较关心:

  1. 公司必须建立健全的会计制度:你可能担心自己不会,怎么办?刚开始成立的公司,业务少,对会计的工作量也非常小,你可以请一个兼职会计,每个月到你的公司帮你建帐,二、三天时间就够了,给他200-500左右的工资即可。
  2. 公司的税额:营业税:销售商品的公司,按所开发票额的4%征收增殖税;提供服务的公司,按所开发票额的5%征收营业税。所得税:对企业的纯利润征收18-33%的企业所得税。 小公司的利润不多,一般是18%。对企业所得税,做帐很关键,如果帐面上你的利润很多,那税率就高。所以,平常的购买设备都要开发票,你吃饭、坐车的票都留起来,可以做为你的企业运作成本。

二种税的区别:营业税是对营业额征税,不管你赚没有赚钱,只有发生了交易,开了发票,就要征税;所得税,是对利润征税,利润就是营业额扣减各种成本后剩余的钱,只有赚了钱,才会征所得税。

还有其它各种各样很多种的税,但没有多少钱,主要是上面二种,特别是所得税非常高 .

#!/usr/bin/env python
# encoding: utf-8
"""
File: prime_list.py
Created by Pan Tao on 2011-11-16.
Copyright (c) 2011 CosTony.Com. All rights reserved.
"""
prime_list = lambda n: set([i for i in range(2,n)]) ^ set([i for i in range(2,n) for x in range(2,int(i**0.5)+1) if i%x == 0])
def main():
    print(prime_list(int(raw_input('Enter the max number:'))))
if __name__ == '__main__':
    main()

不要看上面有那么多行字符哈,其实就 prime_list = lambda: .... 这一行就行了,如果你对质数感兴趣,也可以去看看我以前写的一个专门用来求质数的程序,见这里: Python 验证与获得素数(via Miller Rabin & AKS)

在了解验证与获得素数前,先来了解一下什么是素数:

质数,又称素数,指在一个大于1的自然数中,除了1和此整数自身外,无法被其他自然数整除的数(也可定义为只有1和本身两个因数的数)。比1大但不是素数的数称为合数。1和0既非素数也非合数。素数在数论中有着很重要的地位。最小的素数是2,也是素数中唯一的偶数(双数);其他素数都是奇数(单数)。质数有无限多个,所以不存在最大的质数。围绕著素数存在很多问题、猜想和定理。著名的有孪生素数猜想和哥德巴赫猜想。素数序列的开头是这样的:2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113

根据素数的定义想到的一些解决办法

从上面我们可以知道,任何一个大于 1 且除1和自身之外不能被其它整数整除的整数为素数,那么我们首先可能就会想到一个办法来验证一个整数是不是素数:给定一个整数 n ,让 n 依次除以 1 -> n 之间的所有整数,如果都没有能整除的,那么这个数肯定是素数:

def factor(n):
'''Get number's factors'''
end = n+1
for i in xrange(1,end):
if n % i == 0 : yield i
def force_checker(n):
'''Checker weither a number is a prime number'''
if len([i for i in factor(n)]) == 2: return True
return False

上面的代码我们作了什么?其实很简单,根据前面我们的分析,对于任何一个需要验证的数字,我们首先需要得到它的因子,这个工作是由 factor() 函数来完成的,遍历 1 -> n 之间的所有整数,一个一个测试,如果是这个数的因子,则将其返回( yield i )这一句,所以 factor() 是一个“生成器”:/code/python-functions-decorators ,有了这个之后,我再写了一个 force_checker() 函数,来暴力验证,办法还是最原始的,先找到整数 n 的所有因数子,然后检查其个数,如果为两个(也就是 1 与其自身),则这个数是素数。

上面的这两个函数确实是已经可以实现素数的验证了,但是对于一整数,还没有任何关系,但是如果整数过大,那就麻烦了,你的电脑将有很长的时间,CPU 处于 100%的状态,我们可以来作一个测试,来查看一下验证需要多少时间:

>>> from time import time
>>> def test(n):
... print(time())
... print force_checker(n)
... print(time())
...
>>> test(104729)
1317222986.09
True
1317222986.1
>>> test(1047290)
1317222997.53
False
1317222997.66
>>> test(10472900)
1317223000.42
False
1317223001.55

从上面我们可以看到,对于6位的这个数字,整个验证花了不到0.01 秒,我在 104729 后面追加了一个 0 之后,花了 0.13 秒,但是当我再追加一个 0 之后,就花了 1.13 秒了,经过我自己电脑上面的验证,对于大于 10 位的数字,这种办法根本就行不通,所以,还得想其它办法。

我再想到是,先对数字进行过滤,我想到的是下面这些数字其实根本就不需要去测的:

  1. 未位为(0,2,4,5,6,8)的数字都能被 2 整除,所以肯定不是素数
  2. 每一位相加得到的数字如果能被 3 整除,那么这个数也能被 3 整除
  3. 任何一个大于一个整数 ½ 的整数,绝对不是该整数的因子

跟着上面的思路,我想到了先得修改一下下 force_checker() 函数:

def force_checker(n):
'''Checker weither a number is a prime number'''
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
if len([i for i in factor(n)]) == 2: return True
return False

来看看现在的效果:

>>> test(10472900)1317223764.19
False
1317223764.19
>>> test(104729001)
1317223822.58
False
1317223822.58

几乎是不花时间就能检测出我所提供的这两个数字是否为素数,但是你可能也看到了,我其实是取了一个特别的数字,一个是 0 结尾的,一个是各位数字之和为 3 的整数倍的,但是我们还是可以通过另一个办法来看看加了上面的一行判断之后的实际效果,我们来生成一个素数列,看看需要花多少时间:

我们先来写一个用来生成素数数列的函数: prime_generator() ,它是一个生成器,同时,为了以后的测试方法,我还写了一个检测运行时间的函数 test() ,现在我的所有函数是下面这样的:

import sys
def factor(n):
'''Get number's factors'''
end = n+1
for i in xrange(1,end):
if n % i == 0 : yield i
def force_checker_old(n):
if len([i for i in factor(n)]) == 2: return True
return False
def force_checker(n):
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
if len([i for i in factor(n)]) == 2: return True
return False
def prime_generator(limit = None, start = 1, end = None, checker = force_checker):
seek, counter = start, 0
while True:
if limit is not None and counter >= limit: break
if end is not None and seek >= end: break
if checker(seek):
yield seek
seek += 1
counter += 1
else:
seek += 1
def test(limit = None, start = 1, end = None, checker = force_checker):
import sys
from time import time
print('Generate prime numbers with {} checker.'.format(checker.__name__))
time_start = time()
column, counter = 1, 1
for prime in prime_generator(limit,start,end,checker):
sys.stdout.write('{:>5} : {:<10}'.format(counter, prime))
counter += 1
if column % 5 == 0:
sys.stdout.write('n')
column += 1
time_end = time()
print('nnI got {} results in {} seconds(form {} to {}).'.format(
counter-1,time_end - time_start, time_start, time_end))
def main():
test(1000)
test(1000,checker=force_checker_old)
if __name__ == '__main__':
main()

上面代码中 prime_generator() 就是一个素数生成器,我们可以在 for 循环中由 limit 限制的数量个素数,如果 limit 设置为 None,则将不限制素数个数(在 for 循环中,不建议这么干), start 表示我们从哪一个数开始生成,默认为 1 ,表示需要生成 大于 1 的素数, end 默认为 None ,表示不限制最大素数(不是最大素数个数),由于我们需要测试不通的素数测试方法的性能,所以我这里使用了一个 checker 参数,默认使用 force_checker() ,但是我们可以指定不同的验证函数,只要该函数是同样的使用方法(传入一个数,返回 True 或者 False)。

test() 函数是我写的一个测试函数,我们将由它来调用 prime_generator() 函数,并且记录整个程序的运行时间与素数个数,我还将上面的所有代码都保存在一个名为 prime.py 的文件中,这样我可以直接使用我的 TextMate 编辑器运行它,或者你也可以使用 :

$python /path/to/prime.py

命令来运行,我们来测试一下吧,在我的机器上面,同时用两个验证器来验证素数,生成 1000 条记录的时间如下:

Generate prime numbers with force_checker checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
996 : 7879 997 : 7883 998 : 7901 999 : 7907 1000 : 7919
I got 1000 results in 1.05513501167 seconds(form 1317225770.58 to 1317225771.64).
Generate prime numbers with force_checker_old checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
996 : 7879 997 : 7883 998 : 7901 999 : 7907 1000 : 7919
I got 1000 results in 3.41558718681 seconds(form 1317225771.64 to 1317225775.05).

上面的结果一目了然了吧,整整快了2.4秒(机器不同,运行时间与环境不同,可能时间有些许不一样,你的结果也可能和我的不一样,但是绝对是快,而且快很多),在我这里测试获取 2000 个素数的结果是:优化之后的函数使用 4.46425414085 秒,未优化的使用了 15.689232111 秒,获取个数不断增加,它们之间的时间差将成倍的增加。当然,上面优化过后的函数也不会是最好的,为什么我给这个函数取名叫作 force_checker() ?那是因为它是最笨的办法,暴力验证,就是不管你是什么数,我一个一个试,只到试完为止,这是一种没有任何技巧可言的方法。

Miller Rabin 验证法

上面的方法对于小整数而言,速度已经很快了,但是在我们现在的现实应用中,基本上不可能使用上在的这个办法,比如目前素数应用最广的应用领域公共密钥体系中,一般选择的素数都相当的大,至少都在100位以上,而上面的这个办法,对于10位的数字就已经力不从心了,而对于100位的数字,可能需要耗尽你一生的时间才能验证成功,所以,还得选择其它的方法,比如最有名的一个验证素数的办法:Rabin Miller,按我自己的想法,这个属性概率验证法吧,如果证明这个方法的有效性不是我所研究的对象,我也没这个能力,但是至少它是被所有人都认同的快速的验证法。

Miller Rabin 验证法的细节是这样的:

  1. 选择一个代测的随机数 p ,计算 b , b 是 2 整除 p - 1 的次数,然后计算 m ,使用 n = 1 + (2**m) 。
  2. 选择一个小于 p 的随机数 a 。
  3. 设 j = 0 且 z = (a**m) mod p 。 # 如果 z = 1@ 或者 z = p - 1 ,那么 p 通过测试,可能是素数
  4. 如果 j > 0 且 z = 1 ,那么 p 不是素数
  5. 设 j = j + 1 如果 j < b 且 z != p - 1 , 设 z = (z**m) mod p ,然后咽到上面步骤,如果 z = p - 1 ,那么 p 通过测试,可能为素数
  6. 如果 j = b 且 z != p - 1 ,不是素数

数 a 被当成证据的概率为 75% ,这意味着我们在上面的测试过程中,迭代次数为 t 时,产生一个假素数所花费的时间不会超过 1/4**t ,当我们迭代 5 次时,产生素数的概率已经高达 99.999% 了, 这几乎可以说是一个素数了,而国际上通用的作用是,必须迭代50次,下面我们来在我们的prime.py 中实现它:

def miller_rabin_checker(n):
'''Checker wiether a number is a prime number via rabin miller method'''
if n <= 1: return False
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
import sys, random
def to_binary(n):
r = []
while n > 0:
r.append(n % 2)
n = n / 2
return r
def test(a, n):
'''test(a, n) -> bool Test whether n is complex
Returns:
- True : if n is complex
- False: if n is probably prime
'''
b = to_binary(n-1)
d = 1
for i in xrange(len(b) - 1, -1, -1):
x = d
d = (d * d) % n
if d == 1 and x != 1 and x != n - 1:
return True
if b[i] == 1:
d = (d * a) % n
if d != 1:
return True
return False
def checker(n, s = 50):
'''checker(n, s = 50) -> bool checks whether n is prime or not
Returns:
- True : if n is probably prime.
- False: if n is complex.
'''
for j in xrange(1, s + 1):
a = random.randint(1, n - 1)
if test(a,n):
return False
return True
return checker(n)

我们同样可以使用 test() 函数来验证上面的这个函数的效率:

Generate prime numbers with miller_rabin_checker checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
96 : 503 97 : 509 98 : 521 99 : 523 100 : 541
I got 100 results in 0.0682229995728 seconds(form 1317228097.28 to 1317228097.35).
Generate prime numbers with force_checker checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
96 : 503 97 : 509 98 : 521 99 : 523 100 : 541
I got 100 results in 0.0126042366028 seconds(form 1317228097.35 to 1317228097.38).
Generate prime numbers with force_checker_old checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
96 : 503 97 : 509 98 : 521 99 : 523 100 : 541
I got 100 results in 0.0203368663788 seconds(form 1317228097.38 to 1317228097.39).

上面的数据显示运行时间由短到长的验证方法分别为:

force_checker < force_checker_old < miller_rabin_checker

这是怎么回事儿?其实很容易解释,对于小整数,force_checker_old 所作的计算量更少而已,但是如果对于大整数,由于 force_checker_old 或者 force_checker 所用的方法都是试除法,就是所有小于 n 的数字都拿去除一次,所以,待测整数越大,其时间就越多,而且时间是成倍的增加,来看下面的验证(下面的测试我将不再验证 force_checker_old() 函数):

生成1000个素数

I got 1000 results in 0.895807027817 seconds(form 1317228892.57 to 1317228893.47).
I got 1000 results in 3.35119318962 seconds(form 1317228898.47 to 1317228901.82).

生成10000个素数

Generate prime numbers with miller_rabin_checker checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
9996 : 104707 9997 : 104711 9998 : 104717 9999 : 104723 10000 : 104729
I got 10000 results in 11.3178238869 seconds(form 1317228989.72 to 1317229001.03).
Generate prime numbers with force_checker_old checker.
1 : 2 2 : 3 3 : 5 4 : 7 5 : 11
...... 中间部分省略 ......
9996 : 104707 9997 : 104711 9998 : 104717 9999 : 104723 10000 : 104729
I got 10000 results in 571.780455112 seconds(form 1317229006.04 to 1317229577.82).

这个差距就有点儿太大了哈,不过,这就是一个事实。

完整的程序文件: prime.py

#!/usr/bin/env python
# encoding: utf-8
"""
File: prime.py
Created by Pan Tao on 2011-09-28.
Copyright (c) 2011 CosTony.Com. All rights reserved.
"""
import sys
def factor(n):
'''Get number's factors'''
end = n+1
for i in xrange(1,end):
if n % i == 0 : yield i
def force_checker_old(n):
'''Checker weither a number is a prime number'''
if len([i for i in factor(n)]) == 2: return True
return False
def force_checker(n):
'''Checker weither a number is a prime number'''
if n <= 1: return False
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
if len([i for i in factor(n)]) == 2: return True
return False
def miller_rabin_checker(n):
'''Checker wiether a number is a prime number via rabin miller method'''
if n <= 1: return False
if n in (2,3,5): return True
if str(n)[-1:] in ('0','2','4','5', '6','8') or eval('+'.join(str(n))) % 3 == 0: return False
import sys, random
def to_binary(n):
r = []
while n > 0:
r.append(n % 2)
n = n / 2
return r
def test(a, n):
'''test(a, n) -> bool Test whether n is complex
Returns:
- True : if n is complex
- False: if n is probably prime
'''
b = to_binary(n-1)
d = 1
for i in xrange(len(b) - 1, -1, -1):
x = d
d = (d * d) % n
if d == 1 and x != 1 and x != n - 1:
return True
if b[i] == 1:
d = (d * a) % n
if d != 1:
return True
return False
def checker(n, s = 50):
'''checker(n, s = 50) -> bool checks whether n is prime or not
Returns:
- True : if n is probably prime.
- False: if n is complex.
'''
for j in xrange(1, s + 1):
a = random.randint(1, n - 1)
if test(a,n):
return False
return True
return checker(n)
def prime_generator(limit = None, start = 2, end = None, checker = force_checker):
'''Generator of prime numbers
varibles:
- limit : how many prime numbers
- start : where to start
- end : when to stop
- checker : the prime number checker, default to force_checker
'''
seek, counter = start, 0
while True:
if limit is not None and counter >= limit: break
if end is not None and seek >= end: break
if checker(seek):
yield seek
seek += 1
counter += 1
else:
seek += 1
def test(limit = None, start = 2, end = None, checker = force_checker):
import sys
from time import time
print('Generate prime numbers with {} checker.n'.format(checker.__name__))
time_start = time()
column, counter = 1, 1
for prime in prime_generator(limit,start,end,checker):
sys.stdout.write('{:>5} : {:<10}'.format(counter, prime))
counter += 1
if column % 5 == 0:
sys.stdout.write('n')
column += 1
time_end = time()
print('nnI got {} results in {} seconds(form {} to {}).'.format(
counter-1,time_end - time_start, time_start, time_end))
def main():
import sys
checkers = {'fo': force_checker_old, 'f': force_checker, 'mr': miller_rabin_checker, 'e': ''}
while True:
checker_help = '''
Which checker you want to use?
type 'fo' to choose force_checker_old checker
type 'f' to choose force_checker checker
type 'mr' to chose miller_rabin_checker
type 'e' to exit
'''
checker = raw_input(checker_help)
if checker not in checkers:
print('You can only type "fo", "f" or "rm" to choose checker, type again :n')
continue
if checker == 'e':
exit()
action_help = '''
What are you want to do?
type 'g' to generate prime numbers
type 'c' to check whether a number is a primen
'''
action = raw_input(action_help)
if action == 'g':
limit = raw_input('How many numbers do you want to generate?n')
start = raw_input('The min-number of the prime list is:n')
end = raw_input('The max number of the prime list is :n')
if int(end) <= int(start):
print('The end must bigger than start')
continue
if int(limit) < 1:
print('limit must bigger or equal to 1')
continue
test(int(limit), int(start), int(end), checkers[checker])
elif action == 'c':
if checkers[checker](int(raw_input('Enter the number you want to check:n'))):
print('This number is prime number')
else:
print('This number is not a prime number')
if __name__ == '__main__':
main()

setuid、 setgid 和 sticky 权限这几个权限以前从来没有了解过,甚至都不知道有这么三种权限在这里,安装 FreeBSD 的时候,看到了,找了些资料,了解了一下下,这个东西不了解不知道,一了解,解了我很多迷题。

先回忆一下下Linux的权限,在Linux里面或者其它的类Unix系统中,权限是管理得很严格的,任何一个用户做任何事情都必须的相关的权限才行,别人的东西不让你看,你根本就会不知道有这么个东西,那么好了,现在就有了下面这几个问题:

  1. 普通用户修改自己的权限,要修改系统的密码数据库,那密码数据库是不是对所有用户开放?如果是的话,那密码数据还安全?如果不是的话,那为什么所有用户又都可以去修改?
  2. 系统的临时目录 /tmp 里面的所有东西,是规谁的?因为任何一个用户都对这个文件目录有写的权限,那也就是说任何用户都可以修改这个目录里面的内容了,那怎么办?我的临时文件别人也可以删除?

先想想我以前所了解的知识,这种问题还真没法儿解决,但是操作系统却已经解决得很好了,为什么?原因就是今天发现的这三种权限类型。这些权限对于一些 UNIX 操作而言很重要, 因为它们能提供一些一般情况下不会授予普通用户的功能。 为了便于理解, 我们首先介绍真实用户 ID (real user ID) 和生效用户 ID (effective user ID)。

真实用户 ID 是拥有或启动进程的用户 UID。 生效 UID 是进程以其身份运行的用户 ID。 举例来说, passwd(1) 工具通常是以发起修改密码的用户身份启动, 也就是说其进程的真实用户 ID 是那个用户的 ID; 但是, 由于需要修改密码数据库, 它会以 root 用户作为生效用户 ID 的身份运行。 这样, 普通的非特权用户就可以修改口令, 而不是看到 “Permission Denied” 错误了。

setuid 权限可以通过在普通权限前面加上一个数字四 (4) 来设置, 如下面的例子所示:

# chmod 4755 suidexample.sh

这样一来, suidexample.sh 的权限应该如下面这样:

-rwsr-xr-x   1 trhodes  trhodes    63 Aug 29 06:36 suidexample.sh

您会注意到, 在原先的属主执行权限的位置变成了 s。 这样, 需要提升特权的可执行文件, 例如 passwd 就可以正常运行了。

可以打开两个终端来观察这一情形。 在其中一个终端里面, 以普通用户身份启动 passwd 进程。 在它等待输入新口令时, 在另一个终端中查看进程表中关于 passwd 命令的信息。

在终端 A 中:

Changing local password for trhodes
Old Password:

在终端 B 中:

# ps aux | grep passwd
trhodes  5232  0.0  0.2  3420  1608   0  R+    2:10AM   0:00.00 grep passwd
root     5211  0.0  0.2  3620  1724   2  I+    2:09AM   0:00.01 passwd

正如前面所说的那样, passwd 是以普通用户的身份启动的, 但其生效 UID 是 root。

与此对应, setgid 权限的作用, 与 setuid 权限类似, 只是当应用程序配合这一设定运行时, 它会被授予拥有文件的那个组的权限。

如果需要在文件上配置 setgid 权限, 可以在权限数值前面增加数字二 (2) 来运行 chmod 命令, 如下面的例子所示:

# chmod 2755 sgidexample.sh

可以用与前面类似的方法来检视新设定的生效情况, 在组权限的地方的 s 表示这一配置已经生效:

-rwxr-sr-x   1 trhodes  trhodes    44 Aug 31 01:49 sgidexample.sh
注意: 在这些例子中, 尽管 shell 脚本也属于可执行文件的一种, 但它们不会以您配置的 EUID 或生效用户 ID 的身份运行。 这是因为 shell 脚本可能无法直接呼叫 setuid(2) 调用。

我们已经讨论了两个特殊权限位 (setuid 和 setgid 权限位), 它们让用户在使用程序时能够用到更高的权限, 有时这会削弱系统的安全性。 除了这两个之外, 还有第三个特殊权限位: sticky bit, 它能够增强安全性。

当在目录上设置了 sticky bit 之后, 其下的文件就只能由文件的所有者删除了。 这个权限设置能够防止用户删除类似 /tmp 这样的公共目录中不属于他们的文件。 要应用这种权限, 可以在权限设置前面加上数字一 (1)。 例如:

# chmod 1777 /tmp

现在, 可以用 ls 命令来查看效果:

# ls -al / | grep tmp
drwxrwxrwt  10 root  wheel         512 Aug 31 01:49 tmp

这里的结尾的 t 表示了 sticky bit 权限。