2010年10月26日星期二

Shell脚本

关于Linux Shell以及脚本应用的简单索引与指南用贴


本文是关于shell脚本的一篇索引,很是简略
内容很是含糊,仅作为记录,更多请参阅其他文档
它们用起来都是很实用的东西的说,属于Tools的范畴
是用熟练了反而能体会到它到来的好处的东西
这类话题相关入门的书籍和文章还是算较容易接触到的


目录结构

Linux默认的目录结构如下
/
/bin
/sbin
/etc
/dev
/dev/null
/dev/random
/dev/urandom
/home
/mnt
/lib
/root
/tmp
/usr
/usr/bin
/usr/include
/usr/lib
/usr/local
/var
/var/log
/var/mail
/var/spool
/var/tmp
每个目录有固定的用途,这里不详述


man和info

用来查看帮助文档
还有额外的文档在/usr/share/doc/


sh - shell

shell的作用是执行程序和管理回话
bash是现在最常用的shell实现,在debian中还有一个较简单的dash
shell有部分命令是Builtin的,例如cd、echo、exit,还有job相关的jobs、fg、bg(kill在bash也算了),管道(这个不算命令来着,文件和流也是shell的重要部分)
在一个session中可以执行多个进程且可以嵌套,回话终止时会结束它的子进程除非用nohup(还有at、cron可用,ps或top可看到进程)
除了Emacs的快捷键可用,还有用来发送信号的信号的按键:C-c 终止、C-z 挂起、C-d 退出

shell也可以写成脚本来执行,将命令行写为文本文件保存
开头#!/bin/sh接下来是脚本内容,然后添加可执行权限chmod +x filename
脚本中可以使用变量,列表,环境变量,执行命令,执行表达式,使用test判断,使用管道,添加控制流程,定义函数,处理参数
脚本也可以用其他的解释语言来写,对于调用者来说是一致的

存在若干默认的启动脚本。


ed - text editor

ed通过相互的输入命令来编辑文本行
命令的格式是[address [,address]]command[parameters]
编辑显示存取文本都需要输入相应的命令来执行


sed - stream editor for filtering and transforming text

sed常和管道一起用在shell脚本中
其工作是以行为单位的,下面是一个文本替换的例子,也可打印删除什么的
sed -e 's/old/new/g' inputFileName > outputFileName
用;分号构成语句序列可以跳转,单行语句(包括块语句)前后构成条件关系,有剪切粘贴


awk - a pattern scanning and text processing language

常用的实现是mawk,debian里默认装的这个
命令的格式是pattern { action statements }
也可包含BEGIN块来设置行匹配的选项,或者print一些东西。
语法接近于c和sh,可用于将文本数据生成数据报告
作为单行命令行或者写成脚本都是可以的方式


其他

对于文本操作还用若干命令可以使用:
tee - read from standard input and write to standard output and files
cat - concatenate files and print on the standard output
grep - print lines matching a pattern
iconv - Convert encoding of given files from one encoding to another

ls可以用来在目录中查找文件,此外还有:
find - search for files in a directory hierarchy
locate - find files by name


链接

http://www.debian.org/doc/manuals/debian-reference/ch-program.zh-cn.html
http://en.wikipedia.org/wiki/Shell_script

2010年10月22日星期五

实用的原型继承

现实生活中的原型继承

时间匆忙,此帖就略,来介绍一下原型继承是什么东西。


* Self

Self语言,是一种基于原型的面向对象程序设计语言,以原型简化了类的概念,让类在视觉上直观。
虽然其研究已经停滞,但是在动态语言的实现优化上有应用。
相关信息在一份名为self: the power of simplicity的paper有明了的说明。

此文详述了原型继承的优点(简化实现,增加对象灵活性),以及原型继承和类继承的比较,这里不再转述。
其中关键的两个概念是slot和parent:
在self中所有对对象的使用都是通过消息来进行的,对象的数据成员的读写通过命名slots。
例如对象有属性x,则x消息用于读取,x:消息用于写入,只读消息则只有读槽没有写槽。
槽是一个方法,也是Lisp中的一个Closure。
当对象消息所指向的槽不存在时,则指向其parent对象,对象间的数据共享也通过parent的solts进行。
从简化实现的角度说,对象方法的局部环境也可以用一个子类来实现,用parent指向对象的环境。


* 类对象系统

C++中的Class是对Struct的扩展,数据和虚函数表属于对象,而方法函数属于类。
如果一个方法不是虚函数的话,它将在编译的时候转化为一个函数的调用。
smalltalk的对象机制则有些差别,它对方法的查找是在运行时动态的,详情我不清楚。
c++所选择的机制是从运行时性能的角度来考虑的。


* 设计模式

在《设计模式》一书中,PROTOTYPE是作为一种对象创建模式来讲解的。
可以用于以原型的克隆来减少类的数目,且提供在运行时控制对象原型的能力。
在C++这样的类对象系统中使用原型继承的方法并不唯一,在实现的时候需要考虑的是对象的管理方式(是否需要存放在一个关联数组中),如何去定义对象的clone操作(就是C++的复制构造,有复制深浅和如何初始化的问题),以及指向原型对象的指针可以用来托管操作(this->proto->clone())。
因为实现方式上有很大的自由度,这里不做代码举例,重点在对象的名为proto的指针和名为clone的方法,前者类型为指向自身类型的指针,后者返回和对象自己同样的类型。
当然,这里也不排除我对文本的内容的某些片面理解。

class A{
private:
    A* proto;
public:
    A clone(){
        return A(*this)
    }
}
这里的代码只是用来体现代码结构,并没有完善实现细节,例如完善默认构造和复制构造。
对象成员中可以包含函数指针,且方法调用中有一个隐含的this指针,这为自行对机制的扩种提供的方便,只是缺少一些语法的方便(用操作符作DSL?)。


* JavaScript

依照ECMA-262的1.3和1.5文档第4.2.1节的描述,js中提供原型机制实现了对对象继承和数据共享的支持。
在Javascript中函数也是对象的一种,所以对象中不会像类继承中和其他成员不区分对待,函数作为方法时可以通过this访问使用的对象。
构造器constructor是一种特殊的对象Object,它是一个函数(有[[Call]]方法)且拥有"prototype"(原型)属性(默认是每个函数对象都会自动创建该属性,以便我们往其中添加添加属性,当然也可以指向已有对象)。
因为构造器是一个函数,它可以按照一个函数来定义它的功能(例如Date(2009,11)返回一个字符串)。
而如果通过new表达式使用constructor,则会创建新的对象(例如new Date(2009,11)返回一个Date对象),并以该对象为变量this去执行构造函数(可选的是如果构造函数有返回对象则new返回这个对象)。
其创建的对象的属性prototype指向其构造其的prototype,在查询对象成员的时候如果缺少则系统就会转向查询其prototype所指向的对象。
函数对象在创建时在原型属性中默认会有指向自身的constructor属性,可方便于执行clone操作。
操作符instanceof调用对象的[[HasInstance]]的方法,其实现是依据对象的prototype实行来判断。
Type和对象将自动在需要的时候被执行,类似与Java的Box/unBox机制,例如从StringType到StringObject的转换。
对象的prototype属性可以在运行时改变,这是原型机制在动态性的一个体现。

实例代码(没有测试):
Point=function(x,y){
    this.x=x;
    this.y=y
}
Point.prototype.move=function(x,y){
this.x+=x;
this.y+=y;
}
使用:
var p=new Point(4,5)
p.move(3,6)
以上是目前较常见的使用方式,在第三方库往往会再做包装。
值得注意的是,这里的构造器在来的创建中不是必须的,它只是js语言本身提供的一种方便的机制,并和其他一些识别对象的函数保持供用的信息。


* Lua

Lua是一种和Javascript很相似的语言。
它对象是Table,并提供了metatable机制,对象实现的方式交给了使用者指定。

例如,可以实现为(未测试):
Point = {
    new = function(self, x, y)
        local object = { x=x, y=y }
        setmetatable(object, self)
        self.__index = self
        return object
    end,
    move = function(self,x,y)
        self.x, self.y = self.x+x, self.y+y
    end,
}
然后使用
local p=Point:new(4,5)
p:move(5,-2)
这里虽然用了类似于原型的机制,但实现上Point是起的类的作用,用来创建实例和负责类的方法。


* Python

虽然Python是使用的Class机制,不过由于其动态的,在实现上和原型机制有类似的地方。
类和实例都处于对象:类有属性__dict__和__bases__,前者是方法和类属性所在的空间,后者是基类的列表;实例是有调用class对象创建,有属性__dict__和__class__,前者是用于存储类的属性,后者指向创建它的类。
如果没有方法截获属性查询的语法,实例成员的查找会现在实例中查找然后再到它的类中,也就是说只有类可以做原型使用(Python在它的class机制中会保护一点),而类的原型则是其继承机制的实现方式。
这样的限制为在Python中导入C++类带来方便,而如果仅有原型机制则为实现的一致性带来的障碍。

试验代码:
class A:
    pass
class B:
    pass
a=A()
a.__class__=B
print a
虽然这段代码看不出明了的价值,但是它是能够工作的,最后的输出为%lt;__main__.B instance$gt;。
class也是对象,它是type对象的实例,也可以用type(name, bases, dict)的语法来创建。
并同时也实现了元类,即类的类这一递归的概念,派生出type的子类来进行类的创建。

不过虽然Python允许在运行时给对象添加属性包括使用lamdba,但是对象方法仍然需要在类中定义。
如果需要self那样的原型机制,一个可行的方式是使用@classmethod,代码示例如下:
class A:
    v=5
    pass
class B(A):
    @classmethod
    def f(cls):
        print cls.v
即把class直接作为一个对象来使用,用继承来表示原型。
或者,用模块和import *,效果类似。


* 单实例类

原型机制的一个好处是用来实现单实例类,例如用来实现对一个虚拟世界的抽象。
代码请自行补脑。


* Scheme

Scheme给使用者相当大的灵活性,这让我们可以实现自己的对象与消息机制,一个成熟的参考是CLOS。
不过简单的一个对象可以写成:
(define (num value)
(lambda (method)
    (cond
     ((eq? method 'print) (display value) (newline))
     ((eq? method 'next) (num (+ value 1)))
     (else (error "method missing"))
     )
    )
)
对原型的查找实在else语句的那一步执行。


* 原型继承的使用

用来读取富含逻辑的数据


* 多分派

这是题外话,关于C++的Oberride机制。
在C++中,方法可以是virtual的,也就是说调用对象方法所执行的代码是运行时动态判断的。
而目前的overload机制却完全是在编译时静态决定的,也就是说当参数是派生类时,C++依然只按照传入指针变量的类型而不是指向对象的类型来判断所调用的函数。
用if语句判断类型是一种办法,目前一个较通常的解决双分派办法是visitor模式。
再一次virtual,以arg->m(*this)来使用。


* 链接

Self - the power of simplicity:
http://selflanguage.org/
Standard ECMA-262
http://www.ecma-international.org/publications/standards/Ecma-262.htm
一个javascript的仿self环境
http://adamspitz.com/Lively-Outliners/example.xhtml
A Minimal Javascript Object Environment
http://minijoe.com/


* 附以前一段试验代码

虽然这段代码没用用到原型继承,不过有值得这方面的尝试。
此外,在lua用可以用自定函数来构造table,而不一定只用默认的{}语法。
--[[
演示
ee.zsy
2010.1
]]

--BASE
function table.pos(t,sth)
    for i,v in ipairs(t) do
        if v==sth then
            --say("i="..i)
            return i
        end
    end
    return 0
end
function table.has(t,sth)
    return (table.pos(t,sth)>0)
end
function table.del(t,sth)

end
function table.add(t,sth)
    table.insert(t,sth)
end
--UI
function put(sth)
    print(sth)
end
function say(sth)
    print(sth)
    io.read()
end
function key()
    return tonumber(io.read())
end
function cls()
    os.execute("cls")
end
function select(sth)
    if sth.quest then
        print(sth.quest) end
    for i,v in ipairs(sth) do
        print(i.."."..v)
    end
    local c=key()
    return c
end

--SAVE
save={

place="",
bag={}

}
--DATA
map={

home={
    name="家",
    goto={"tree"},
    has={"she"},
    run=function(self)

    end
    },
tree={
    name="森林",
    goto={"home"},
    has={},
    run=function(self)
        local tree={}
        self.goto={"home","shop"}
        self.has={}
        if not table.has(save.bag,"card") then
            self.has={"card"}
        end
        --table.insert(self.goto,"shop")
    end
    },
shop={
    name="商店",
    goto={"home","tree"},
    has={},
    run=function(self)

    --return k==1 and "home" or k==2 and "tree" or nil
    end
    }
    
}
obj={

she={
    name="女友",
    type="human",
    run=function(self)
        say("你好啊")
        --select{"df","df","df"}
    end,
    catch=function(self,o)
        say("什么东西?")
        if o==obj.card then
            say("好啦,我早就认识你了")
            return true
        end
        return false
    end
    },
card={
    name="身份证",
    type="thing",
    run=function(self)    
        return false
    end,
    use=function(self,obj)
        say("你出示了"..self.name)
        return true
    end,
    take=function(self)
        table.add(save.bag,"card")
        return true
    end
    
    }
    
}



--VIEW
function move(plc)
    put("这里通往:")
    for i,v in ipairs(plc.goto) do
        put(i.."."..map[v].name)
    end
    local c=key()
    local o=map[plc.goto[c]]
    if o~=nil then
        save.place=o
        return true
    end
    return false
end
function search(plc)
    put("这里有:")
    for i,v in ipairs(plc.has) do
        put(i.."."..obj[v].name)
    end
    local c=key()
    local o=obj[plc.has[c]]
    if o~=nil then
        o:run()
        
        if o.take and o:take() then
            say("你得到了"..o.name)
        end
        return true
    end
    return false
end
function bag(plc)
    put("你要使用:")
    for i,v in ipairs(save.bag) do
        put(i.."."..obj[v].name)
    end
    local c=key()
    local o=obj[save.bag[c]]
    if o~=nil then
        if o:run() then
            return true
        end
        if o.use then
            put("对什么使用呢?")
            for i,v in ipairs(plc.has) do
                put(i.."."..obj[v].name)
            end
            local c=key()
            local oo=obj[plc.has[c]]
            if oo then
                o:use(oo)
                if oo.catch then oo:catch(o) end
            return true
            end
            return false
        end
    end
    return false
end
function show(plc)
    cls()
    plc:run()
    while 1 do
        cls()
        put("==你在==\n"..plc.name)
        local s=select{quest="你的操作","移动","调查","背包"}
        if s==1 then
            if move(plc) then
                return
            end
        elseif s==2 then
            if search(plc) then
                return
            end
        elseif s==3 then
            if bag(plc) then
                return
            end
        end
        say("你未操作")
    end
end

function main()
    save.place=map.home
    while 1 do
        show(save.place)
    end
end

main()
另一种写法是用switch语句。

这里,包括整篇文字的目的,是表达一下抽象(v.-%gt;n.)。


继承类型的选择

最后,两种继承机制有其侧重,与应用的场合。
在系统设计上,类继承有比较成熟的方法,且比原型机制更容易抽象。
原型继承则较直观地用来变现数据,用代码体现出数据中可执行的方面。

2010年10月15日星期五

日记[101010]

出游·远足


这个周日的时候,班里进行了一趟秋游。一早的时候的,吃完早点,在校门口乘上了公交车,向着目的地的方向驶去。
首先的行程是登山。登山就和爬楼梯一样,在日常看来是件很普普通通的事情。不过想起来自己也曾把登山视作过一种有特别意味的象征,将日常生活的感受与之类比,并寄希望于在其中能让感受变得明了和清晰。
出于节约开支的打算,行程是从山后上山的,下山也是从山后下的。水泥的山路与台阶起伏在山间,从一个山头延伸向另一个山头,连接其若干标志性的建筑。途中也走到了较粗燥的石块路上,上山显得陡峭,下山则显得地滑。不过在同行的协助之下,并无太大危险。
带着略有饥饿的身子,接下在的行程是在山下的另一头吃烧烤。几人围着在一个石桌上,隔着锡纸,用火红的木炭加热着食物。清澈粘稠的食用油涂在暗红色的冷肉上,伴着沾着的孜然发出的香味,在食物的上下翻弄之中,发出滋滋的声响。
手边一瓶度数不高的啤酒,倾斜向面前的塑料杯,看着又一次被灌注的液体和升腾起的泡沫。就这烧烤着,吃着,喝着啤酒,吹着山底的凉风,感受着一阵阵升腾起的烟雾和热度。渐渐地,自己手边的这个啤酒瓶里已由满,变得半空,变得空荡荡的。离开了面前食物的废墟,然后再在一旁吹了会儿风,便踏上和来的方式一个样的,回程的道路。

这就算一篇简单的日记吧。记录了一次出游,虽然只是如流水一样的简单记录了一下行程,其中并无蕴含着特别的想法。
说到想法,现在的自己已经不喜欢在留下过多的想法了,就的凭借自己胡乱的思考仅仅是在积累着往日的浅薄。于是,变得倾向于在文字之中仅留下最简单的记录,记录着自己的视角上看到的或许真实的一切,记录着曾经的经历,以及一些不必对其有任何怀疑的存在。
这便是成长吧,否定着过去的自己,又希望过去有某种永恒可以永远的传递下去。

下面来写个小故事,是在出游归来后的时候想到的:
故事发生在战场之上,两个邻国因为争执正发生着激烈的战争。有一行人,大约也就十来人。出于RP问题,被困在了一个被包围的山林里。突围就有被歼灭的危险,什么都不做就是在这深山之中等死,所以绝望在他们的心中日渐萌芽生长。
行军的生活资源已近面临缺乏,而周围的局势未见任何好转的倾向,他们决定,必须开始最后的尝试了。故事的主角便是在他们大家的推选之下,担当这次突围行动的指挥。大家推选他是因为我们的主角有勇气能够担当其这份责任。
他们推选指挥的目的在于,如果鲁莽的行动或者零散的行动,大家都会损失惨重,甚至无一幸存。我们的主角也相信,在自己和大家共同的努力之下,大家都能够为大家实现一份守护。
故事便在这种团结之中开始了,后来在混乱之中结束了。大家的行动还是变得单独起来,利用着他们,增加着自己活下去的可能。不过这不算一个坏的结局,虽然大多数人不幸死去,还是有一两个人幸运的战士活力下来。
我们故事的主角并没有那么幸运,在进行着一个大家讨论出的危险但或许可行行动的时候,发觉队友并没有更上,然后独自受敌而遇难了。
临死的时候,他仿佛听到了队友的嘲笑——
在绝望的时候,在面临着生存与否这个问题的时候,已经没有人会挂念别人是否活下了。在惊慌了内心里,只会去在意自己的存在,在意只要有幸运者的存在,便去抢夺这种可能。

好了故事说完了,虽然这是出于自己的价值判断来讲述的一个故事,但是在保持着一个旁观者视角的时候,也是自身的视角混杂在这个故事之中了。故事讲完了,也仅仅是讲述完了而已。当自己再想进一步地对这个故事中的人物评价,并得到一个完整而一致的判断的时候,我发现自己已经不知再如何说下去了。觉得再说下去,就是让自己变得单纯,变得天真,变得⑨一样。而以为只有矛盾,只有混沌,只有犹豫,才能把自己眼前的世界还原为一个他应该成为的方式。
作为一篇日记,我是认为这里的文字应该一次写完的。不过出于内容的篇幅的考虑,一次来写显得有些量大。其实分次来写也未尝不可,不利的地方是会多花些日子,有利的地方时可以把这篇日记写完。而且在如何在这里的文字继续往下写这个问题上,需要的是判断,而不是犹豫。

于是,现在在过了久久的时间之后,再把原先想到的一些内容,继续往下写。有些想法,它的存在是因为在曾经的时刻存在过,在日后并不会去怀疑其存在,而是其中是曾希望发现的东西在过后变成想遗忘的东西,去抛弃其中的缺憾。那么,过去的时刻,给将来的时刻留下的,会是如何的经历呢?下面将把若干内容,以简略的方式来说一说。
先说学校的新图书馆,是这学期新开放的。当我初次进入了它的大门的时候,第一反应是去找守则规范一类的东西——也就是使用说明一样的东西,因为新馆的借书方式和之前的其他各馆略有不同。结果是没有找到,于是内心感到了微微的茫然。
守则一类的东西是个有趣的东西,在大一入学的时候学校还让图书馆的工作人员来专门开课指导我们图书馆的使用。这种课的作用也仅仅是了解了图书馆的使用规则,而具体的借书操作仍是要去图书馆实际操作时候,才会对整个流程有所熟悉。往往在经历一些不适时候,就会对书的查找借阅感到得心应手。
守则就是这样一种约束人的东西。但是同时,守则也是对借阅者的守护,因为守则的存在可以让借阅者有了可依照的流程。借阅者行动的重点是去找到需要的书籍,而整个流程的方式有了一个可依靠的方式,反而是以约束来减少了整个借阅行为中的负担的事情。而现在的情况的是,面对了一个新的图书馆,原本的守则之类的东西已经不再完全使用,原本的守则赋予的守护已经变成了陈破的存在,借阅行为已经是一件需要重新去面对的事情。
以上来看,自己是将周围环境简单的变化给复杂化了。因为改变的事情无事不可不在发生,不论是自身的周围的环境,以及自身对待外界的态度。
一次自己在图书馆里坐着,看着来来往往的人群。每当听着门口的报警装备想起来的时候,内心就会觉得这是一件可笑的事情——去笑话新生作出了各种各样的可笑的失误。然后,又在这独自地压抑在内心的嘲笑中,去笑自己的胆怯——觉得已经应该变得成熟,不会去犯幼稚的错误,却没做出任何有价值,或许并不完满的尝试。

出游前一天的一堂课上,老师忽然放下课本,想聊天一样地随意的说了起来,是说那天的课堂有点显得沉闷。当时自己听了有些在意,因为自己也是对此有所想法,所以想听听老师对此是如何的看法。不过另一个更让自己在意的原因是,在一个让人失落的课堂上,老师是需要去设法的回避自己的想法的。
课堂在一个稳固的模式中进行着——投影打出的幻灯一页一页切换着,一个平稳的不带感情的声音重复着上面的文字,就这样仿佛了一年又一年;课本被随意翻弄着,偶尔有起落的笔记。下课铃快想了,老师和学生合上书离开教室;一学期过去了,老师和学生变得像陌生一样。一切显得平静,一切显得波澜不惊。
当然,这是一种悲观的想法,一种悲观地去不发现其中任何的改变的可能。去明白别人耀眼的成绩不属于自己,是自己即使努力也不一定会得到的东西。去认识在自己所处的环境有着极为有限的教育资源,去知道适应远比改变要简单的多。去放低自己的满足的限度,去明白大学是用来虚度时光的,只要有一个毕业有一份工作,然后能够活下去就行了。去觉得别人的生活看起来值得羡慕,却不一定有自己生活的方式那样惬意地幸福。
以前的带我们的班的老师说过这样一句话,当然也不排除随着时间的而自己对其片面地加深了曲解:“我们学校要培养学生的自卑感,这样一来可以便于管理,一来学生容易随意找份工作。”这句话有可以赞同的地方,因为人要学会谦逊,因为我是这个学校的一员,但是同时也后值得不满的地方,因为这是出于一个管理者的立场而不是我所处在的一个学生的视角。后来这位老师升职了,这一定意义上证实了他的信条存在的价值。并且个人的不满不会对别人有任何的好处,而对自己也不会发掘到有价值的东西。这是一种值得欣赏的直白,也是一种去轻烟弥散于空气不可探寻的痕迹。

学习是人处于世界的一种权利,就像认为人人生而平等,造物主赋予他们若干不可让与的权利,其中包括生存权、自由权和追求幸福的权利是不言而喻的真理一样。每个人有接受教育的权利,有通过他所学到的知识去创造价值获得收益的权利,并可以理想地去认为一个成绩的取得是限于他的能力和愿望。
学习的过程是一个知识与直接积累的过程,是为了让一个人可以胜任更复杂的工作。在学习中一个人获得了他面对问题可以凭借的素材,获得了面对问题的自信。这种自信是他的经验和已获得的成绩,而也是这种自己让他可以在面对问题的时候去积极地寻找解决的方法。
当然以上的想法又将事情变得有些理想化,因为当生存都是一个问题的时候,大学的存在悲观地意义上讲就是一个逃避就业的工具,去吸取着父母辛劳的工资,去虚度四年的时光。
这里又将话题转向了悲观的方式,这是因为现实就是一个悲观的存在吗?人总被接受教育去面对眼前的路,哪怕是让人成为一个有追求的人,或者变成一个只要能能够得过且过生存下去的人。可以不论是悲观,还是空想,都同样是一种人对自己的保护机制吧。
用自己能够做到的事情去否定自己能够做到的事情,然后借此去放弃能够获得的东西,然后小心的自己的已有的东西在消失。就是这样的一种半借口半现实地将一切怪罪在自身和环境上面。
学习是一个判断力被积累的过程,学习不是让老师成为了一种用来依靠的东西。让别人的讲述和判断去代替自己的判断,而让自己觉得自己的任何想法都是无知和缺乏自信的。而当别人只是无表情的灌输着文字,在自己复述不能时候,感到深深地无望。学习的是出于知识自身,而非依附于人的存在。
学习学到的东西有很多:一可称为事实,它是对知识判断和运用的基础;一可称为推断,它是事实间的关联,由已知向未知的推进;一可成为经验,它是曾经对事实和推断的应用,让新场合下的应用可以达到一个从无到有的过程。这便是一个学习的过程,一个因为需要存在而存在的东西,而不是悲观视为单纯人生的磨练。
由次也可以推论出一些关于练习和考试的的想法。考试是一个向自己向别人证实自己的方式。考试是对学习过程的一个检验,以让学习的过程可以获得期望的结果。考试为学习建立的一个目标,而明确的方向可以让学习的过程变得果断而作出坚定的付出。考试是为学习阶段的肯定,是对学习的付出的一种回应,这也可以让可有面对实际的问题的时候,可以果断地去运用自己学到的知识。同样的,练习也可视为是种短期的考试,它作用于学习过程中的一个阶段。值得区别的是,练习也可作为一种学习的方式,用以模拟将会遇到的问题来提升思维的活跃度,来作为一个思维积累与之后可以利用的过程,是一个对学习的补充与调剂。总之,练习与考试都是以它们不同的方式,成为整个学习过程中拥有着积极作用的事物。此外,在练习与考试的竞争与协作中,也有利于整体(包括个人,以及自己)的水平的提高。
写着写着,自己的设想是尽可能直白的文字去展示能够被认同的要素。虽然在一些视角上一些显得片面,显得固执的想法,但是能够自圆其说是已经能够作为这些文字存在的的一个限度了。

不过继续往下写,也有让人犹豫的地方,至少如果单纯的以文字堆砌的方式让内容继续下去的话,就不会去思考一个尺度让内容指引向某一个特定的方向。例如正确的方向,当然过于追求的字面上的正确似乎并不存在,那么就是一个对自己有利的方向?
本篇当初自己定其为日记,字面意思,就是记录一下当天的事情和想法。本篇的开始也是,又那天的登山远足开始,不过因为想写的内容有些多,如果一天来写完的话,内容有些多,写起来也会吃力一些。
可以分开写,是因为对要写的内容有完整的打算。虽然是若干显得散乱的话题,可以只要一件件按照次序记录下来,便也可算作是一片日记的内容吧。不过,对记录事情而言是可以,对记录想法却有了难度。自己可以去回忆曾经历过的事情,却不可以再回到那时的感受上。从现在的角度看,就是那个登山之后的那份心情,那份在山间的空气过滤之后周围一些仿佛变得简单的心情。有时不知以那份信心和清晰的感觉去经历事情是对是错,因为它们只是那么短暂的一刻,然后就又回到的一片以河为围栏的校园中,去怀疑自己以及身边一切可以怀疑的事情。
让文字就这么写下来,随意地在键盘上敲打着,不去思考自己预计要写的内容,不去思考自己是否在记录着自己真实的想法,不去思考自己或别人在之后看到这些内容的时候会如何看待这些东西。文字的内容就这么被敲击下来,以其中看似可以连贯的逻辑,以其中看似可以说得通的解释,以其中字数堆积让那个txt文件的体积变得越来越大,以越来越多的文字去这篇里原先打算写下的所谓收尾的文字。以至于难以辨别,这是让本日记的内容得以继续,还是只是以另一种方式去覆盖掉曾经留下的可笑的想法与经历。

月初的时候,说到大一的弟弟的专业并不理想时候,家里的长辈是鼓励他好好学习,去达成转专业的条件。这可以算是他目前学习的努力的意义和一个方向把。这其中和我自己类似的地方是,我也曾认为自己的专业不够称心,而学校在转专业上仅是非常有限的选择。再者现在已是大三,已经两年的时间过去了,也就是说我是不会考虑到他所面对的一个机遇和选择。有一次和班里的老师说到班里的一个同学,他是到了一个有些类似的专业去了,老师当时就表现出很有抵制的样子,大意是选择什么的是一件会让人份心的事情,认为这对于我或许并非是一个好的东西。于是对于专业的被认识就往两个方面展开了。一是说人要学会适应,人要学会生存,能把大学结束然后有个工作,就以前完满了,其他个人的想法就是多余的了。另一种说法是,环境工程其实可以一个不错的专业,在这一行里以后还是有很有前途的地方的,从事一个专业要学会喜欢这个行业。这两种想法没有什么好否定的地方,认为它们是在曲解或修饰什么。就像去简单地认为自己如果去进去了另一个专业,是否就能处理的比现在这个专业好一样这个问题。
以自己经历的失败,去幻想未经历的事情中的成功,是一种对当前现实的逃避。因为一个人能够经历的事情是有限的,而他永远会有未经历的事情,那么以此为借口就可能落得一个一事无成的下场。但是从另一方面说,在当前经历的失败中,去幻想可能出现的最好的未来,也未必就是一件合理的想法。这就让一切变成了一场关于付出的赌博,认为只要付出,越陷入其中的付出,就会有像彩票一样奇迹发生。
这里把选择划分为了两种,虽然自己也没有写明白这两种分别是什么东西。只是重点落在二这个数字上,然后各自想法中可以否定的东西拿出来否定一下。同样的,下面的内容中,也可以这两种想法中挑选一些可以用来肯定一下的内容。
首先是关于对自己的了解,关于自己的长处。他们在面对问题的时候不是多余的东西,是在解决问题中值得凭借的东西。学习什么的,不是以别人的方式去应对,然后重复着别人会遇到的困难,然后再加上自己的不足。那样只会让一个还不错的结果,变成很难不错的结果。
其次是关于选择,对于作出的选择就需要有承担这份选择的责任。而且即使是放弃选择,也并不是意味着就可以放弃其中的责任。
而且,学习不好大学学环工,大学学习不好以后一辈子干环工。这句话虽然写出来有些过分,但是在某种意义上是自己的现实,来所谓对自己的激励也未尝不错。况且自己也并不是讨厌这个专业,只是不擅长,外加一份放弃选择地心态去面对它而已。
由已经经历的挫折去不顾一切地怀疑未来的经历,把未来的经历中的不完满视为一个永远的诅咒,觉得不幸什么的就是注定的,所以认为应为安份与现状,把已经接纳下的事情作为是心目中的最好的选择。这种方式的保护,去以放弃的方式来避免遇到的困难。去认为风险与成功都只是属于别人的东西。这段的想法,也很难说就是正确的,去吧一切的非现实去归纳为不现实,并由此保留仅存的现实。
那么对于人生的选择,是消极或积极,是逃避或责任,这都只是字面的上差异而已。探究其中的相似点,都是在向某个方向度过时日而已。正确与否也不是并借现在面对屏幕的空想就可以得出一个用来安慰自己的结论的。因为在自己小小的空间里,也往往会把想法引向一个片面错误的方向。然后放弃那些显得多余的意义。
那么,这最后的几段文字可以概括为什么呢,是安慰自己的方式给自己鼓劲?
结论是——
把握机遇不等于逃避现实。

至于,这个登山远足后的结论有什么作用,我不知道。
日记就这样,错别字什么就暂不再改了。
况其自己有事情正反着说都说成合理的的习惯,以上内容不必当真。
至于看见废话或是逻辑混乱的地方,知道算我的日记习惯就可以了。
Fri Oct 2010

====
日记这东西是用来记忆,还是用来遗忘的呢?

2010年10月13日星期三

同步用贴

当前使用的Chrome扩展
不定期与本地Chrome状态同步一下,仅表示当前有安装,并不表示推荐或其他。
Linux下依然使用Firefox,midori候补。K-meleon则作为Windows下的候补。
#20101013创建页面

AdSweep
An online ad-removal tool for your favorite web browser.

After the Deadline
Check spelling, style, and grammar in your browser

Chromium Wheel Smooth Scroller
Fully customizable smooth scroller for chrome. A port of Yet Another Smooth Scrolling on Firefox.

Classic Games!
All the classic games that you love to play all assembled in one place on your browser!

Development and Coding Search
Quickly searches programming language and API reference documentation.

Edit with Emacs
Allow user to edit web-page textareas with Emacs (and other editors).

Firebug Lite for Google Chrome
Firebug Lite for Google Chrome, supported by the Firebug Working Group.

Google Dictionary (by Google)
View definitions easily as you browse the web.

Imgur the world
Easy way to put online images on imgur. Open an image click on the extension button and get the imgur link directly in clipboard.

iReader
View news stories and other articles in a very easy to read, clutter-free, scrollable display.

jQuery Shell
Allows jQuery command extension in current page context.

Lazarus: Form Recovery
Autosaves everything you type into textareas so you can easily recover from form-killing timeouts, crashes and network errors.

Note Anywhere
with this ext, you can make notes on any web page, any position. when you open that page again, the notes get loaded automaticly.

Notepad
Save your notes / ideas...forever. Uses HTML5's localStorage.

OnlineDict
Click the word in any page, and the translation will show up.

Proxy Switchy!
Manage and switch between multiple proxies quickly and easily.

ProxyPy Web Proxy
You can load the web page through a fast/stable web proxy. Power by ProxyPy Engine (http://proxypy.org)

Python Shell
A python shell in your browser.

RSS Subscription Extension(由 Google 提供)
在您的工具栏上添加一键订阅。

SingleFile
SingleFile helps you to archive a complete page into a single HTML file

Speed Dial
Speed Dial for Chrome - replace Chrome new tab with your predefined visual bookmarks.

Textarea Code Formatter
Gives textareas basic code formatting abilities found in modern IDE.

TinEye Reverse Image Search
This is the official TinEye Chrome extension. Find out where an image came from, how it's used, or find higher resolution versions.

translate
Zoned word translation

Web Developer
Adds a toolbar button with various web developer tools. The official port of the Web Developer extension for Firefox.

Window Resizer
Resize browser window to emulate various screen resolutions

Youdao Dictionary (by Gecko) (已停用)
用有道的api查词,有音标以及读音(zh-en and en-zh dictionary by using youdao dict api

维基百科伴侣 - 迷你维基浏览器
维基百科用户必备扩展。功能全面:保存您最近的维基查询记录,多语种,后退/前进等等!

迅雷、快车、旋风专用链自动破解
自动把页面里的迅雷,快车Flashget,旋风链接替换为真实地址,使用你自己喜欢的下载方式来下载.

2010年10月1日星期五

Python的Web服务器

Python的Web服务器

* Socket

基本的概念见 http://en.wikipedia.org/wiki/Berkeley_sockets

先拿Netcat做点试验:
输入“nc g.cn 80”命令,然后输入“GET / HTTP/1.0”,然后敲几个回车。
这样会通过使用TCP协议得到HTTP协议下网页返回的内容。
然后输入“nc -l 8080 | nc g.cn 80”,实现了一个简单的转发,-l是listen的意思。

常用到的API是:
socket() 创建Socket对象
bind() 服务器端与地址绑定
listen() 服务器端开始监听TCP连接
connect() 客户端建立TCP连接
accept() 服务器端接受并建立TCP连接
send() recv() 用于数据IO
close() 关闭Socket对象
select() 用于实现非阻塞Socket
poll()
这里Socket的协议通常是PF_INET表示IPv4,类型有:
SOCK_STREAM如TCP
SOCK_DGRAM如UDP
以及基本的SOCK_RAW

用Python代码表示就是:
#+BEGIN_SRC python
#服务端
import socket            
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 8080))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
    data = conn.recv(1024)
    if not data: break
    conn.send(data)
conn.close()
#+END_SRC
#+BEGIN_SRC python
#客户端
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 8080))
s.send('Hello, world')
data = s.recv(1024)
s.close()
print 'Received', repr(data)
#+END_SRC
UDP客户端则没有connect而使用sendto来发送数据。

使用“infds,outfds,errfds = select.select([s,],[],[],5)”会阻塞,直到s有数据传入,以实现非阻塞的网络服务器。


* 内置

使用cgi模块和CGIHTTPServer模块便实现了简单的网络应用及服务器了,不过这不是这里的话题。
感觉Java有界面这个东西会使得一些概念变得清晰,在实现接口知道了要提供的东西,Python的模块提供使用方式似乎有很大的多样化。
这里要说的是SocketServer模块里内容,涉及TCPServer和UDPServer通过实现自己的Handler类来处理数据。
在实现异步服务器上有ForkingMixIn和ThreadingMixIn用于实现多进程。
或者用asyncore和基于它的asynchat模块来使用Socket,通过实现handle_*对Socket的不同阶段进行处理,不过没有实现更抽象的东西。
SocketServer.TCPServer有子类叫HTTPServer,实现有handle()方法将HTTP请求的消息发送发到do_*()方法上。
通过Python模块自身就是用Python编写的,仅在最底层上或出于性能考虑才会和Python的实现直接接触。
#+BEGIN_SRC python
import SocketServer, BaseHTTPServer
class server(SocketServer.ThreadingMixIn,BaseHTTPServer.HTTPServer):
    pass
class handler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        print self.path
        self.wfile.write("Hello World")
httpd = server(('',8000), handler)
while 1:
    httpd.handle_request()
#+END_SRC

不过对于Web框架来说通常的做法是实现WSGI这个接口。

叫底层的SocketServer是,直接贴Py文档中的一段代码了:
#+BEGIN_SRC python
import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    """
    The RequestHandler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print "%s wrote:" % self.client_address[0]
        print self.data
        # just send back the same data, but upper-cased
        self.request.send(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    # Create the server, binding to localhost on port 9999
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)

    # Activate the server; this will keep running until you
    # interrupt the program with Ctrl-C
    server.serve_forever()
#+END_SRC


* CherryPy

CherryPy是一个Python的面向对象的Http框架(这里使用了CherryPy3)。
先来看一段代码,最常见的HelloWorld程序:
#+BEGIN_SRC python
import cherrypy
class Main(object):
    @cherrypy.expose
    def index(self):
        return "Hello World!"
cherrypy.quickstart(Main())
#+END_SRC
看起来和Web.py的HelloWorld很像,而且它们都是用于Web站点的开发。
不过之间的区别还是明显的:Web.py涵盖了模板引擎和数据库绑定(虽然都功能比较基本)需要部署到服务器上;而Cherrypy虽然可以按照TurboGears样子整合其他组件成为一个完整的Web开发框架,但是其自身是一个多线程的Web服务器。

对于Cherrypy有两个值得关注的地方,一是URI与Callable对象的映射规则,一是Configuration文件的使用。

这里的Callable对象是一个"exposed = True"的函数或者有__call__方法的实例,以对象属性的形式构成树形结构。Get和Post的内容以命名参数的形式传入,特殊名称index匹配"/"而default则将URL的剩余部分作为调用的参数。
以下是一个简单的示例:
#+BEGIN_SRC python
import cherrypy
class Main(object):
    @cherrypy.expose
    def index(self):
        return "Hello World!"
    @cherrypy.expose
    def about(self):
        return "Page About<hr />"
class Page(object):
    @cherrypy.expose
    def index(self,q=None):
        return "Page <index>"+("<br />%s"%q if q else "")
    @cherrypy.expose
    def default(self,page_id):
        return "Page %s"%page_id
main = Main()
main.page=Page()
cherrypy.quickstart(main)
#+END_SRC

Cherrypy的配置文件使用了ini的格式(以dict形式写在Py代码中也是可以的),包括针对全局"global"的属性以及针对路径如"/"|"/static"等等的。配置可以作为调用cherrypy.quickstart时的参数,或者用cherrypy.config.update更新全局配置cherrypy.tree.mount为路径添加配置。
例如存在config.ini:
#+BEGIN_SRC ini
[global]
server.socket_port = 8000
server.thread_pool = 10
tools.sessions.on = True
tools.staticdir.dir = "/path/to/app"
[/static]
tools.staticdir.on = True
tools.staticdir.dir = "static"
#+END_SRC
然后是修改很小的HelloWorld程序:
#+BEGIN_SRC python
import cherrypy
class Main(object):
    @cherrypy.expose
    def index(self):
        return "Hello World!"
cherrypy.quickstart(Main(),"/","config.ini")
#+END_SRC

关于CherryPy基本的信息就是这样了。其他信息如自定义URL调度,Sessions机制,Request/Response对象,Tools模块,Plugins机制以及其他更多的Web开发中会用到内容在这里将不再详述。
现在的TurboGears1.1分支依然以CherryPy作为其Controller的部件,模板用的是Genshi,Model部分是SQLAlchemy,还有一个Py味道的MochiKit作为Javascript库。


* Tornado

Tornado是FriendFeed所使用的非阻塞Web服务器,具有优异的特性,可用于构建实时服务。
在接口提供上有对web.py和Django的参照,所以使用起来会有些许似曾相识的感觉。
其自带一份说明文档和若干示例代码,API的信息在其GIT仓库中查询。
首先依旧是HelloWorld,代码有Tornado首页提供:
#+BEGIN_SRC python
import tornado.httpserver
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")
application = tornado.web.Application([
    (r"/", MainHandler),
])
if __name__ == "__main__":
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
#+END_SRC
从正则匹配后调用对应提供get或post方法的类,这正是Web.py的方式。
而模板引擎和Web.py一样是转化render为Py代码(而没有以解释的方式),不过标签是使用Django中{{}}和{%%}来嵌入。
Django的自定义标签这里叫UIModule,在通过RequestHandler或其子类派生的控制器(可有initialize方法用于初始化)中Tornado提供方法通过self调用,返回text/html/json或其他数据流,Post的内容由get_argument和self.request访问,用户状态设置cookie_secret后由Secure cookies存放,应用的配置通过Application构造器传入,有类似的CSRF的XSRF模块,可以提供静态文件,有和Django的Authentication功能相关的用户验证模块(有authenticated修饰器和login_url设置),翻译的文本由csv格式存放,部署的话多实例结合nginx服务器使用。
以上内容先暂且大略说这么些,因为以上内容并不是使用Tornado的有所特别的地方。

下面来说Tornado中特色的功能了——Asynchronous机制。
添加了"@tornado.web.asynchronous"的RequestHandler子类的get方法在结束时请求会依然保持打开,直到调用了self.finish而不是return的时候才会像客户端返回相应。
这样的作用是例如在读取远程文件(包括运程调用等待返回结果)这种需要一定时间的操作时,可以传入一个回调函数来进行之后需要的操作,而不必在自身过程中等待可以去继续执行之后的语句。
这和浏览器中JQuery包装的Ajax函数在原理和接口上有类似的地方。
在调用异步执行的方法(例如由AsyncHTTPClient提供的)时,回调函数在RequestHandler中可以以callback=self.async_callback(self.on_response)形式的参数(这里的async_callback不是必须的,是一个wrapper以添加调用参数)传入,这里的on_response是自行编写的用于处理异步方法返回结果的方法。

一样明显的使用的例子的聊天室程序:
其工作方式是存在一个以async方式的poll方法,创建async_callback对象添加到waiterList中,并在客户端push时去处理这个List中残留的项目。
客户端可以在发送消息时执行push操作,并让异步执行的回调函数之间构成一个循环。
可以的优化是客户端保存并发送一个cursor让服务端只返回需要的数据,还有在poll的时候如果有可以返回的数据就直接返回而不再往waiterList添加新的callback对象。
因为XSRF的使用,在客户端get或post时要传入用作验证的值。
Tornado自带的示例还考虑的不使用Ajax时伪静态的页面也能够正确的通过form和render来正确显示,示例中Ajax使用JQuery库。
这个试验代码写起来有点头晕,效果会比用setInterTime实现的感觉要好,而且最好还是去放到Linux下运行,先保证it just work。
测试用代码:
#+BEGIN_SRC python
见Tornado自带示例
#+END_SRC


* Twisted

一个事件驱动的Python网络框架,可以用来构建高效的Game和ZOPE服务器。
因为用法(去实现事件)是抽象后暴露出来的,和之前的Socket的时候有不同的关注层面。
可以把写Qt代码的感觉找出来一点的,就像去实现自己的Widget一样,虽然说没用抽象的信号机制而是只用到了override的方式事件循环。同时,和Tornado一样,是构建了一个异步非阻塞网络服务器。

最简单的使用是:
通过派生Protocol来实现事件代码,有self.transport用于传输数据,类似于Python库中SocketServer.StreamRequestHandler的作用。
Factory是用来制造的Protocol的子类,只需要一个。
reactor使用Factory的,进行事件循环,负责整个程序的执行,类似于Application对象的作用。
首先启用作为Web服务器。
然后来实现自己的一段简单的代码,不过先乱如一段WebServer代码,这和Cherrypy是同样的作用:
#+BEGIN_SRC python
from twisted.web import server, resource
from twisted.internet import reactor

class Root(resource.Resource):
    isLeaf = True
    def render_GET(self, request):
        return "Hello World"
resource = Root()
site = server.Site(resource)
reactor.listenTCP(8080, site)
reactor.run()
#+END_SRC
接下来是和SocketServer同样原理的一段代码:
#+BEGIN_SRC python
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor
class QOTD(Protocol):
    def connectionMade(self):
        self.transport.write("Hello World")
        self.transport.loseConnection()
factory = Factory()
factory.protocol = QOTD
reactor.listenTCP(8000, factory)
reactor.run()
#+END_SRC
此外还有reactor.callLater添加回调,也可以把回调添加到Deferred对象中,实现Tornado中的那种异步调用。

由于Twisted具有较大的规模和复杂度,所以这里简短的介绍就暂且到这里不再往下了。


* 其他

org-mode中贴源码是用
#+BEGIN_SRC python
#+END_SRC
其实还需要`C-c C-e t'
#+OPTIONS:   H:3 num:t toc:t \n:t @:t ::t |:t ^:nil -:t f:t *:nil <:t
来让导出的显示符合预期

在Web开发中方便调试会对Py模块使用reload机制

貌似在Web开发中涉及模型后直接生成CURD的URL和View会是一个很好的原型。

State Machine是状态机的意思。