2010年5月25日星期二

吹风~吹风~

来写个无聊的评论

新闻调查《疯狂的裸聊(2010.05.22)》
http://news.cntv.cn/china/20100522/101734.shtml
先不要妄加估测我为了吸引眼球的险恶用心,我只是借题写感想来着。

这是一个科普报道,介绍了网络经营的特点,
其中主要是说了网络广告的原理,介绍了流量和广告联盟间的运作关系。
对于现在网络化的免费资源,广告和服务以成为代替产品之外更主要的收入来源。
此外也介绍了第三方支付平台,在网络支付中安全便捷的作用。
这些都是对新发展的尝试,同时也会和传统的模式和认识有一些冲突。
对于发展的方向和可能性,也需要规则的约束以保障未来的可延续性。
这部分的内容也推荐去看一些更专业的文章,有时候叫视野扩充有时候也就算常识了。

不过这个新闻也有一些外围的争议的话题:
一个是对分级制度的态度,因为一旦提倡了分级制度,就等于默认了限制级内容的公开传播了。在社会上似乎对这方面并没有达成共识,或者说可能分级制度的支持者自身也没达到足够的影响力。
另一个关于如何根除可能出现的危害的问题,这篇报道的看法是从打击利益链入手。但是压制发展并不一定是避免问题最佳的方式。经济发展对社会发展确实存在着有效的推动作用,而监管是作为一种社会发展中手段而不是社会发展的目的了。
最后来说关于关于多元化和严谨性。媒体是表达观点的渠道,即使在它在一个媒体观点有公正的倾向的时候,他也是以其兼顾对社会的影响下,表达自身的看法。
关于严谨的话,举个例子。被参访者说提及成人站点的国外服务托管,记者小声猜测一句说是因为国外ZF的不做为(我也不严谨,因该是去复制原话来着)。这容易引起观众的误解,但实际是国外至少美国是对成人内容有一定限制的。

以上也引起一些自己看法上矛盾的地方:
之前写过一个话题,有选择就必然意味着有所放弃。去接受了科技带来的改变,就必然有了曾经方式带来的好处。而有时又是在不同的选择支难以评价,或者必然面对某种遗憾。
法律法规的完善是对社会规则的完善,但本报道结尾的立场是是让管理行为有了更充足的理由。那么这篇报道的关注点是在新闻事件的本身,还是在于一个执行者对自身行为的公示。
这里成为矛盾,是因为对这则新闻报道有不同的理解倾向。

当然,讨论之中发现对方不足也只是种手段,目的是去寻找最可能正确的方式。
如今网络上已经有了见CCTV必喷的倾向了,导致的结果是对新闻事件本身的无视。
并且每一个社会个体获得的信息是有限的,是提倡交流还是否定个人的冲动,也是不同处理方式。

就像写电影评论一样,本篇也是,目的是能有更好的电影可看,并可以在电影中看出更好的东西。

----
感觉在电脑上敲字缺乏整体感来着,虽然删补什么的是方便了。
最后表达一下个人立场,我对这种社会性新闻是鼓励来着。
一些事拿出来探讨除了用于解闷,还是对展望未来有推动价值的。

富士康这几天还有人在跳,我最近的那篇感想是跟不上更新了,汗。
不然就继续卖口水吧。。。

2010年5月24日星期一

平面 & 色彩 - 简单的

本贴来关注下平面形态与色彩上的基本知识,也就是普及用意。
我感觉自己是写不出什么这方面的知识介绍来着,所以以下内容就纯传送了。
当然以自己的口来说自己的所知是意义不同,虽然其实也不局限个人的。

《二维设计基础教程》
张越 著
目录

概述
教学进程安排
第一部分 形式法则及其作用
 一、多样与统一
 二、对比与调和
 三、对称与均衡
 四、比例与秩序
 五、节奏与韵律
 六、联想与意境
第二部分 形态要素的构成
 一、点的形态及构成
 二、线的形态及构成
 三、面的形态及构成
 四、点、线、面的形态及构成
第三部分 图形的设计与表现
 一、从形态要素到图形
 二、从物象到图形
 三、从图形到图形
  1、群化组合
  2、打散重构
第四部分 形与空间的构成关系
 一、图地关系
 二、构图与空间
第五部分 形态构成的方法、
 一、重复
 二、渐变
 三、特异
 四、对比
 五、辐射
 六、空间
第六部分 色彩的认知和表达
 一、对色彩的感性认识
 二、对色彩的理性认识
 三、色彩的属性和主要对比关系
  1、色相及色相对比
  2、明度及明度对比
  3、艳度及艳度对比
  4、同时对比与视错觉
  5、色面积对比与色调
 四、色彩与心理感应
第七部分 形、色、质的综合运用
 一、版面设计初步
 二、二维设计体验
后记
致谢
参考书目

谈和实用结合的例子的话之前见到有这么一本书来着
《网页设计与配色经典案例解析》
http://www.amazon.cn/mn/detailmore?showtype=3700&ref=DT&prodid=bkbk964273
也就是在谈——
构成:点,线,面,混合
色彩:色相,明度,纯度,色调对比调和
以及结合实际案例进行的创意和选择

然后一些可看的链接:
http://baike.baidu.com/view/471297.htm
http://baike.baidu.com/view/297987.htm?func=retitle
http://zh.wikipedia.org/zh-cn/Category:%E9%A2%9C%E8%89%B2
不过最好还是找点图片实际感受下好。

----
今日荐歌 Rurutia 《Chorion》 风格初听起来是柔弱加坚强型
就个人而已,因为她的一首歌涉及到一些回忆什么的,就别样感受了。

恩,比较简单的一帖,略意义不明。

2010年5月20日星期四

C_to_CPP - OO系列之一

现在去查我的草稿页面,在#091222的时候,为此话题开过一个帖子。当时只是列了写可以开始的话题,不过当初其实没写就后来被闲置了。由于去写另开了一个“OO系列”的其他一组帖子,于是那贴的内容就留着做系列开篇吧。
貌似是以上说的这么回事。只是因为我觉得“就是这么的吧”,就把以上的文字先放在上面了。

先来跑会儿题,这都快要成这里写贴的传统了,有时甚至可以怀疑话题什么的是不是就是为跑题做的一个引子。不过关于写贴这个过程,有些矛盾的东西。
一种想法是去多凭借大脑的思考,相信懂得的越多,那么可写的东西也越丰富。文字本身的功能就是记录。它不是种负担,知道的,和话题有关的,可以写下来。不知道的,和话题有关的,可以知道了再写。不知道的,和话题无关的,那就不考虑了。总之呢,写文就像填空一样。先把话题分解了,列个子话题的提纲来补完,然后再附加点临时的想法就行了。
按照以上的写法来做个日志里普通的一帖也没什么问题来着,除了口水可能会多一点而已。那么下面来说另一种想法,大概是想中国传统水墨画强调的精神境界吧。就像王羲之写兰亭集序那种游山微醉的特别的状态,作品是一种仅存于场合的。尽管可以肯定其作者的卓越水准与才华,也可去想其作者也理当有相当优秀的卓越作品,但是人们还是会更偏向于从他代表作中窥探其作者的全貌。
这里举这个例子(有时就是简单的大家都知道的例子啊,也不多我写一遍),是想表达“珍惜”。也就是说我自己的矛盾在于,是相信有足够数量的文字才留下了足够珍藏的东西呢,还是相信自己能有从全局的角度已经从其中挑选了最重要的东西来写。

跑完题话题还是要收回来的,下面的内容将以《TC++PL》一书为主要参照,然后就以其中的话题来跑题。恩,既然标题是“OO系列”了,况且本文是做规划的开篇,与实际编写时间上的收尾文字。
所以内容的侧重本身就在于某个具体的地方(接口啊,不然下面的子标题是什么东西)了。
然后嘛,不要相信下面的文字是在同一个时间内按从上到下的顺序写的。下面的东西就是罗列性质的,想看心情贴的把滚动条往上回拉。

从C到C++
本文的标题就是这个来着,则中点是语言发展和对象机制的使用。
C++并不是早期的对象机制的先驱,但它这种编程方法的推动者:
1.高性能的实现。在C++之前的语言,对象是放在heap上的,使用时需要动态分配,在使用上也带来的性能代价。而C++通过他的对象机制,将绑定尽可能转化到编译时,并让对象可以在heap上有固定的分配方式。C++中没有带垃圾收集也是性能和实现上的角度考虑的。
2.对C的兼容和扩充。与C兼容是C++设计的一个出发点,这可以让已有的代码和经验继续工作,同时C++中严格的类型的检查(C在标准化时也引入了,只是严格度差别)也提高了C代码的质量(K&R第二版里代码当初就是用C++编译器试验的)。C++在语言上的扩充却能为语言的使用带来方便,例如class就是有struct转化而来,添加了成员方法和访问空是。此外还有命名空间和函数默认值以及overload什么的。
3.用语言表达设计。C++对C在语法上的扩充,用C可能使用OO的方法来编写代码。而C++为了推动OO,为此实现了方便使用的语法。也可让使用者通过对class相关语法的使用来规范地学习并使用OO,实现了OO设计与OO实现之间的一个纽带。

Qt与Glib
以C语言的方式来处理字符串,可以在实现方式上有很多中优化技巧。不过通常就是指要一个能工作正确的字符串类型,而且对C的使用者而言,也会用对某种实现方法的复用,而不是用到字符串都去考虑实现细节的。
不去考虑宽字符的问题,C中的字符串就是一个char[],并可用char*来表达对字符串的引用。除了以数组和指针方式的使用,还可用标准库中string.h里预先实现好的一些提供便利功能。而字符串的储存分配可以用静态的常量指针或定长数组,也可用动态的malloc来分配再free。
C++在std中提供了string类型,它是对C中字符串处理的OO化包装好处。通过string类的成员函数的使用,可以实现对字符串的访问和修改以及迭代和转化,而内存管理是自动进行的,甚至可以有简单的引用计数(参见TC++PL中的那个例子)。这在字符串操作的实现和接口上都相比C提供了进一步功能。
下面不考虑cpp在接口上带来差异,去看Glib中是如何对C语言中字符串操作进行扩充的:
typedef struct {
gchar *str;
gsize len;
gsize allocated_len;
} GString;
GString结构用来表示表示一个标准的C字符串,不过实现了储存空间的自动管理。用"GString* g_string_new(const gchar *init);"来分配,对应有g_string_free()来释放。然后提供了一组"g_string_"开头并以GString*为第一参数的函数,用于执行对GString的操作。
这种风格是不是很熟悉呢,在C标准库stdio.h中对文件的操作就是使用了FILE结构,然后提供了一组f开头的函数来提供对文件的操作的。
那么到了C++中我们获得了那些便利呢:
1.自动析构。对象会在它作用域(块block或者行line)结束的时候自动调用析构函数。免去手写析构不仅是少些一行的事情,能避免错误和麻烦才是更有用的。有时还临时包装一个需要在{}自动执行的语句包装成一个对象(算cpp的固定技巧了)。关于构造,可以直接用cpp的构造函数,有时也有显示init方法来着。
2.隐式的this。拿string("abcdefg").size()举个例子,它等价于string("abcdefg").string::size)()。这里调用对象的方法就是调用它所属类的成员函数,编译器会自动识别的。同时在以方法的形式调用类的非静态函数时,会将对象本身以隐式参数this的形式传入,让函数知道它在操作的对象。
Glib是由GTK的核心组件中分离出来的,用于对c++标准库在使用c语言时功能的替代。同时,GLib有个基于它对象系统叫GObject,因为处于GTK这个GUI库,所以和Qt的QObject比较会更有价值来着,同用于GUI而且都有信号机制的。不过用C手写GObject据说很痛苦的,会重复代码很多。不过GTK+有个c++绑定叫gtkmm,这里拿来对比也很有趣。
这里说OO,模拟和GUI也两个领域可以算起源了吧。
这里又把Qt拿出来说事了,因为它确实很好用来着。首先是充分利用了C的对象结构,还通过预编译器增加了语法上扩充的便利(C++本身就是对C的语法扩充,只不过后来标准化后进化转移到库上。好用就行,我没有语言洁癖。况且Qt文档好查)。然后是有一个很丰富的库,比如QtCore就是对std的扩充,关键还是要用QtGui来着。再然后嘛,Qt也是一个框架,可以供对类的设计上的复用和参考。

Javascript框架实现的对象模型
Javascript的对象模型参照self语言,是基于原型继承的,而且还提供了函数以构造器的形式调用。不过在由于类继承使用得更加广泛,众多js框架就在这一方提供的充满技巧而有趣的库来实现对语言的扩充。
先来看prototype和mootools,前者提供"Class.create()"方法,后者使用"var MyClass = new Class(properties);"的形式。它们得到的返回值例如创建一个Person类,可以用"var guy = new Person('Miro');"的方式来使用。
而且prototype用"create([superclass][, methods...])",在创建类时指定父类,并可立刻或随后添加方法。mootools的类有Extends和Implements这两个属性,用来实现对象间功能的扩展。
下面来看jQuery的对象,其实我也只用过这个框架来着,当然也是很喜欢的(而且不说它的选择器和ajax功能来着)。
1.$/jQuery对象。$对象有两种用法,一种是$.method,直接使用jQuery提供的函数。另一种是用$()去修饰一个以存在的对象,然后可以其实现的方法如show,each...来实现一个代理(这个词用的不大严格,暂求可以表意吧)对象供快捷的使用。
2.链式方法调用。这个cpp里也有的,就是"return *this"然后得到一个自身类型的引用,不过没jQ里用得普遍。这样貌似可以把SQL语句写成一串方法调用,LinQ这样干过,不过后来还是偏语法(非库)扩充了。
3.setter/getter。在jQuery里面这两个操作用同一个方法实现,被操作的属性名称即方法名称,如果有参数,就执行set。其他的API风格可以是两个函数,在方法的参数里指定属性名,或者提供对"="符号的左右两种情况的指定。
说继承的话,jQuery就是用Javascript基本的new+prototype了,不过貌似和代码中见到的$对象无直接使用上的视觉关联。

多态
写成标准的名词叫polymorphism来着,不过我现在对这个名词已经有陌生感了。因为下面要说的其实就是类继承中的override和运用模板的泛型编程,可以去那样使用名词什么的是概括用的符号。虽然cpp主张用keyword表示功能,不过在引入新关键字上还是很节约的(风格啦)。
首先来说虚函数,用关键字表示就是virtual。它和一般成员方法的区别在于,方法调用时所实际执行的函数是在运行时才决定的。通常的方法是用虚函数表,在类的vtbl中储存一组函数的指针。
编译器对多态方法会知道它是表中哪一个,而不会在编译时去和特性的函数的绑定。这样的作用是在以指针的方式传递对象时,派生类可以传给指向基类的指针。由于子类需要覆盖父类的方法,那么在通过指针调用方法时不能在确定只固定调用某具体的函数。
说完了cpp,来看c语言中怎样做。其实就相当于是一个显示的vtbl,一个明显的做法是用一个结构来储存一组函数指针。相比于一般成员函数是作为类的函数,虚函数就就可算是对象的方法了。
这里被我说乱了,貌似也说错什么了。总之呢RTTI也是通过vtbl来对对象的类型进行的运行时类型判定的。也就是说非多态对象在运行的时候是不知道自己所属的类的,方法的绑定全部由编译的时候完成了。
下面来说另一种多态,也就是模板来着。模板的原理就是根据类型对代码进行展开,也就相当于代替手写代码因为类型不同而重复一样。不过由于展开规则的存在,也被用来玩一些技巧性的编译时运算(这里仅仅说常用的啦)。
以模板的方式来使用对象的话,不需要一个共同的基类。它只需要一组对象可以按照特定的方法使用(例如相同名称的成员函数),这样的话就不需要每个类都去继承一个"fat interface"了。一个明显的应用是STL中的序列和算法。
具体的代码实例的话,不想举了。总之呢,对类型的编译时检查还是工作的,可基于继承的类型检查一样。
(关于switch语句与类层次见的转化也是一个常被提及的话题,也算是用类的层次结构减少或分配代码了。这里这点略过。)

错误处理
一个函数返回错误信息可以有很多种方法,cpp中的"Exception Handling"也仅仅是一种可选的机制而已。传统的一种方式(c和cpp里都用的)是用返回值或一个标志变量,由调用者在使用完一个函数后进行判断。
不过就像C中setjmp.h提供的setjmp/longjmp一样,cpp的异常机制也实现了一种特殊的控制流程。它可以在一个嵌套式的函数调用stack上将控制权一层层往外传递,直到有一层捕获了异常并不在向外throw。
不过貌似出于兼容原因,异常机制就在一个限定的代码范围类,没看过在库之间使用的。

类型
C++有个想法是用户定义类型能像原生类型一样使用(C的兼容问题不说,int就是从C继承来的),所以有了操作符override。可以在类中定义操作符的方法,也可以overload一个操作符表示的函数。
由于operator()的存在,也可让一个对象以函数调用的形式来,C++中称这个叫Function Objects。然后也在<functional>中,提供了代表一元和二元操作符的对象,例如"equal_to Binary arg1==arg2",外加Binder和Adapters,用处是在Algorithm函数中做表示操作的参数(通常传入一个函数的)。
TC++PL中还有一个例子,我已分不清是语言的组成还是技巧了:矩阵运算U=M*V+W可以优化为mul_add_and_assign(&U,&M,&V,&W),方法是为每一步运算结构都创建一个新对象,然后最后一步根据得到的对象再运算。
像numeric_limits这样的东西是以类型为参数用模板做了一个映射表了。
在C++中其他机制的,本篇就不可能都说到了,况且本文要有重点的!
或者,以后再补充啊。

参考
《The C++ Programming Language Special 3rd Edition》
http://www2.research.att.com/~bs/
http://www.cplusplus.com
http://www.boost.org
----
果然写成浅薄的炫耀贴了,这种贴最大的特点就是罗列而不深入。
刚才看了别人的日志,来反省一下自己写的东西。
不过这里写下的帖子,也算是以生活很痕迹给人生以勇气吧。

2010年5月16日星期日

类的重构 - OO系列之四

这篇的内容是从书《Refactoring: Improving the Design of Existing Code》来说的,有关的说明可以从它的站点 http://www.refactoring.com/ 上找到一些。
其中文译名叫《重构:改善既有代码的设计》,为Martin Fowler所著,总结工程中重构使用的原则与实例。

话说自己最初对重构的直接认识是IDE中Refactoring菜单下的rename功能,用它可以解析一个项目中的所有代码,找到一个名称的全部使用之处,然后进行更名操作。
这样的好处是,可以较安全地去修改变量使用的名称。使代码变得易读明了,也是方便了之后对代码的扩充。

《R》中给重构两个定义:
(noun)a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.
(verb)to restructure software by applying a series of refactorings without changing its observable behavior.
这样来看,重构对于软件的开发,既是一个方法,也是一个过程。

设计并非一尘不变的,过于设计或设计不足都会给项目带来负担,所以幸好可以在功能添加到间隙里去调整代码的设计。
这是一个重新审视代码的过程,去发现并修改其中的“Bad Smells”,使代码的设计更适合人的阅读和理解,减少不必要的debug过程。
同时在重构过程中增加的所需方面的灵活性,以便进一步对新的功能的实现。让重构在工程进行中和功能的代码的编写,成为一个相结合的过程。

关于重构过程本身,它是和自动化的单元测试共同使用的。开发是个渐进的过程,重构也分布进行。每一步的修改够可以回退,旧的方法可以用托管到新的实现上。让修改之后的代码也可满足足够的测试代码的检验。

接下来就是《R》中最占篇幅的部分了:各种重构的方法。其中主要涉及各种重构的东西做法和结果。
也就是说在特定场合中有了重构的需要,并有了此时要避开的和希望达成的设计,然后如何一步步去做到。
这是希望对既有代码的改进,来减少代码重复,划分类的职责,提高复用,减少类的耦合,方便功能扩充的灵活性,清晰模块间的接口,增强代码的稳定性。

下面复制来正好是我看到的那个版本的目录,书的内容很详尽。我想再说什么,却发现暂时也找不到什么要展开的内容。总之呢,关注点还是放在类的设计上。而且在这里,重构是一个开发中的过程。书的文字在传递着一个信念:要重构,如果利用重构推进项目的开发,以及在重构中发现如何才是更好的设计。
目录

第1章 重构,第一个案例 1
1.1 起点 1
1.2 重构的第一步 7
1.3 分解并重组statement() 8
1.4 运用多态取代与价格相关的条件逻辑 34
1.5 结语 52
第2章 重构原则 53
2.1 何谓重构 53
2.2 为何重构 55
2.3 何时重构 57
2.4 怎么对经理说 60
2.5 重构的难题 62
2.6 重构与设计 66
2.7 重构与性能 69
2.8 重构起源何处 71
第3章 代码的坏味道 75
3.1 Duplicated Code(重复代码) 76
3.2 Long Method(过长函数) 76
3.3 Large Class(过大的类) 78
3.4 Long Parameter List(过长参数列) 78
3.5 Divergent Change(发散式变化) 79
3.6 Shotgun Surgery(霰弹式修改) 80
3.7 Feature Envy(依恋情结) 80
3.8 Data Clumps(数据泥团) 81
3.9 Primitive Obsession(基本类型偏执) 81
3.10 Switch Statements(switch惊悚现身) 82
3.11 Parallel InheritanceHierarchies(平行继承体系) 83
3.12 Lazy Class(冗赘类) 83
3.13 Speculative Generality(夸夸其谈未来性) 83
3.14 Temporary Field(令人迷惑的暂时字段) 84
3.15 Message Chains(过度耦合的消息链) 84
3.16 Middle Man(中间人) 85
3.17 Inappropriate Intimacy(狎昵关系) 85
3.18 Alternative Classes with Different Interfaces(异曲同工的类) 85
3.19 Incomplete Library Class(不完美的库类) 86
3.20 Data Class(纯稚的数据类) 86
3.21 Refused Bequest(被拒绝的遗赠) 87
3.22 Comments(过多的注释) 87
第4章 构筑测试体系 89
4.1 自测试代码的价值 89
4.2 JUnit测试框架 91
4.3 添加更多测试 97
第5章 重构列表 103
5.1 重构的记录格式 103
5.2 寻找引用点 105
5.3 这些重构手法有多成熟 106
第6章 重新组织函数 109
6.1 Extract Method(提炼函数) 110
6.2 Inline Method(内联函数) 117
6.3 Inline Temp(内联临时变量) 119
6.4 Replace Temp with Query(以查询取代临时变量) 120
6.5 Introduce Explaining Variable(引入解释性变量) 124
6.6 Split Temporary Variable(分解临时变量) 128
6.7 Remove Assignments to Parameters(移除对参数的赋值) 131
6.8 Replace Method with Method Object(以函数对象取代函数) 135
6.9 Substitute Algorithm(替换算法) 139
第7章 在对象之间搬移特性 141
7.1 Move Method(搬移函数) 142
7.2 Move Field(搬移字段) 146
7.3 Extract Class(提炼类) 149
7.4 Inline Class(将类内联化) 154
7.5 Hide Delegate(隐藏"委托关系") 157
7.6 Remove Middle Man(移除中间人) 160
7.7 Introduce Foreign Method(引入外加函数) 162
7.8 Introduce Local Extension(引入本地扩展) 164
第8章 重新组织数据 169
8.1 Self Encapsulate Field(自封装字段) 171
8.2 Replace Data Value with Object(以对象取代数据值) 175
8.3 Change Value to Reference(将值对象改为引用对象) 179
8.4 Change Reference to Value(将引用对象改为值对象) 183
8.5 Replace Array with Object(以对象取代数组) 186
8.6 Duplicate Observed Data(复制"被监视数据") 189
8.7 Change Unidirectional Association to Bidirectional(将单向关联改为双向关联) 197
8.8 Change Bidirectional Association to Unidirectional(将双向关联改为单向关联) 200
8.9 Replace Magic Number with Symbolic Constant(以字面常量取代魔法数) 204
8.10 Encapsulate Field(封装字段) 206
8.11 Encapsulate Collection(封装集合) 208
8.12 Replace Record with Data Class(以数据类取代记录) 217
8.13 Replace Type Code with Class(以类取代类型码) 218
8.14 Replace Type Code with Subclasses(以子类取代类型码) 223
8.15 Replace Type Code with State/Strategy(以State/Strategy取代类型码) 227
8.16 Replace Subclass with Fields(以字段取代子类) 232
第9章 简化条件表达式 237
9.1 Decompose Conditional(分解条件表达式) 238
9.2 Consolidate Conditional Expression(合并条件表达式) 240
9.3 Consolidate Duplicate Conditional Fragments(合并重复的条件片段) 243
9.4 Remove Control Flag(移除控制标记) 245
9.5 Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件表达式) 250
9.6 Replace Conditional with Polymorphism(以多态取代条件表达式) 255
9.7 Introduce Null Object(引入Null对象) 260
9.8 Introduce Assertion(引入断言) 267
第10章 简化函数调用 271
10.1 Rename Method(函数改名) 273
10.2 Add Parameter(添加参数) 275
10.3 Remove Parameter(移除参数) 277
10.4 Separate Query from Modifier(将查询函数和修改函数分离) 279
10.5 Parameterize Method(令函数携带参数) 283
10.6 Replace Parameter with Explicit Methods(以明确函数取代参数) 285
10.7 Preserve Whole Object(保持对象完整) 288
10.8 Replace Parameter with Methods(以函数取代参数) 292
10.9 Introduce Parameter Object(引入参数对象) 295
10.10 Remove Setting Method(移除设值函数) 300
10.11 Hide Method(隐藏函数) 303
10.12 Replace Constructor with Factory Method(以工厂函数取代构造函数) 304
10.13 Encapsulate Downcast(封装向下转型) 308
10.14 Replace Error Code with Exception(以异常取代错误码) 310
10.15 Replace Exception with Test(以测试取代异常) 315
第11章 处理概括关系 319
11.1 Pull Up Field(字段上移) 320
11.2 Pull Up Method(函数上移) 322
11.3 Pull Up Constructor Body(构造函数本体上移) 325
11.4 Push Down Method(函数下移) 328
11.5 Push Down Field(字段下移) 329
11.6 Extract Subclass(提炼子类) 330
11.7 Extract Superclass(提炼超类) 336
11.8 Extract Interface(提炼接口) 341
11.9 Collapse Hierarchy(折叠继承体系) 344
11.10 Form Tem Plate Method(塑造模板函数) 345
11.11 Replace Inheritance with Delegation(以委托取代继承) 352
11.12 Replace Delegation with Inheritance(以继承取代委托) 355
第12章 大型重构 359
12.1 Tease Apart Inheritance(梳理并分解继承体系) 362
12.2 Convert Procedural Design to Objects(将过程化设计转化为对象设计) 368
12.3 Separate Domain from Presentation(将领域和表述/显示分离) 370
12.4 Extract Hierarchy(提炼继承体系) 375
第13章 重构,复用与现实 379
13.1 现实的检验 380
13.2 为什么开发者不愿意重构他们的程序 381
13.3 再论现实的检验 394
13.4 重构的资源和参考资料 394
13.5 从重构联想到软件复用和技术传播 395
13.6 小结 397
13.7 参考文献 397
第14章 重构工具 401
14.1 使用工具进行重构 401
14.2 重构工具的技术标准 403
14.3 重构工具的实用标准 405
14.4 小结 407
第15章 总结 409
参考书目 413
要点列表 417
索引 419


我的OO系列篇目之间相隔时间太久了,
似乎缺少《人月神话》里所说的那种知道银弹不存在却依然以激动人心的态度去寻找新方法来对开发的效率产生翻天覆地的影响。
不过文本的出发点还算是一致(这算什么啊)。
《R》中说当初推进重构这个方法时,还涉及实现重构的代价和好处之间的探讨。
不过到现在来说,在功能扩充的立场上,重构的重要性已经是不需要怀疑的事情了吧。
而那个一长串重构方法列表,是属于总结。而它也是在存在,有时在给人豁然开朗的启示。

http://en.wikipedia.org/wiki/Code_refactoring
http://sourcemaking.com/

[人物]Leona Lewis

想起罗曼.罗兰写的 《贝多芬传》,他以他形象而富有感染力的文字,去写伟大人物的传奇人生所构建的宏伟音乐世界。
新闻和传记确实是种很有趣的题材,它总是在生活中发现富有感染力事情。然后不论是以悲伤的还是乐观的情绪,都给一个工业支撑起的社会以未来的光芒。
(其实我还没看完。。。)

先说会儿林语堂的《吾国吾民》吧,这是本介绍中国文化的书。话说当初读的时候确实感到很深的震撼的。
此书成于1933-1934,1939,其内容直接受到1931年九一八事变后中日战争的影响。
全书开篇将中国比做一个老者,一个衰老的疲惫不堪的老者,一个有着无比值得尊敬的年龄却只能喝着茶看不到远方的老者。
然后以详尽的笔墨从名族,品德,心灵,理想,女性,社会,文学,艺术,生活这九个方面一点一点细致剖析当时人们合于“common sense”的角度,以一种欣赏的角度(就像在发掘其中的伟大之处一样地)去展示当时社会的种种落后保守和愚昧(这是后人给予的评价了)的精神状态。
39年增加一章《中日战争之我见》可以是全书情感的一个高潮。当时是正值日军占领广州武汉,抗日战争进入相持阶段(?),如果是绝望的话,因该是最彻底的时刻了吧。
“现在面临的问题,不是我们能否拯救旧文化,而是旧文化能否拯救我们。我们在遭受外界侵略时只有保存自身。才谈得上保存自己的旧文化。”这是我印象比较深刻的一句话。此外,当时的民族的忍耐已经足够深厚了,已经到了无所顾忌只好奋起抵抗的时候了。虽然之前也一再忍受着战争的失败,但是这并不意味着已经放弃了。只要有一丝胜利的曙光,就依然可以为着那一天准备着。在磨难下爆发着一个民族的诞生。这是一篇充满着鼓舞和信念的文字。
不过林语堂貌似在国内影响有限(至少在课本上),可能是因为他是蒋中正的粉丝。不过从我自己的角度说,林语堂是个人很喜欢的一个作家来着。

Ok,话题回来,来说Leona Lewis,中文译为利昂娜·刘易斯。
首先来听《My Hands》,这是FFXIII的英文主题曲。
FFXIII讲述的是一个“由水晶所引导,在魔法和高科技所融合的世界里生存着的人们的故事”是由被水晶守护着漂浮在天空的乌托邦——茧,以及下界构成的世界上的故事。
我对FFXIII的情节还不了解,可能也是出于一种尊敬吧,依然再一次地以激动的心态来期待这个系列在新的PS3技术下故事又展开了的新篇章。
尽然对FFXIII批评文字可以轻易找到,指责它一本道,连个城镇都没有。其实我似乎也是不喜欢这样的FF,可是接下来又能如何呢。去绝望的等待FFXIV这个OlGame过去,然后满足地在生命中看到Fantasy终结?
可是我想法该如何呢,面对所遇事物传递来的情感,延续下去的是失落还是骄傲呢。
正是一份遥远,让我只能感受,而不是评价。去感受生命的喜悦。

下面来听《I See You》,这是James Cameron的电影《Avatar》的主题曲。
话说当初听来这个歌时,我还不知道《A》这个电影,不过确实很喜欢这种的风格。后来看那个电影晚了点,而且我承认我是阿凡达黑(我看的2D版)。实在对这种略呆板的情节发展受不了(就是说亮点还不够嘛,我还更有期待呢),就是一个奇幻加科幻的混合嘛。而且《A》的Ubi那个Game版也太传统了吧(这个无关来着)。
不过当男女主互相看到对方的眼睛时,周围的音箱以浓厚的声音响起"I See You"的旋律的时候,总是不免心头感到一热。
也同时感受一种超越潘多拉星上土生土长纳威人与有生之物间那种淳朴的神经网络的深厚情谊。那是一种表达了爱慕,信赖,肯定,期盼的眼神。
短短的一节"I See You"的旋律,可以在想象着那份情景中,在脑海里回荡好久。来,跟着哼一下,1,2,3。。。

这两个我都是借着有情节剪辑的视频看的,所以以上都在说视频本身的内容了。不过图像和声音都是视频的一部分嘛。是在它们共同的作用下,才有我体会到格外分明的感受的。
从日期上说FFXIII美版是10年首,Avatar是09年时候的。

那么下面来听什么呢,回想一下08年北京奥运会闭幕式的那个“伦敦8分钟”,上面有个来自06年选秀节目《The X Factor》的年轻的新星。还能回想到那份耀眼的场景吗?
哈哈,她就是利昂娜·刘易斯。
如今在这个2010的5月里,不得不感慨时间过得真快啊。而她又取得了更耀眼的成就了呢。

查阅她的简介,她在12岁时开始展现她的音乐才能。而在她歌手事业也曾停滞不前打算放弃,直到她的男友说服她参加了《The X Factor》并一路过关斩将夺得冠军。
话说之前看过一个欧美选秀类节目的视频,其中说到了真诚的歌声:“唱歌的时候,除非能做到以歌传情,否则干脆就别开口唱。今晚每个人都被感动了,因为他用歌演绎真情。而且看得出他是用心在唱,所以感动了每个人。而这就是娱乐的真正目的。”而且这不是祈求人欣赏,而是在触动他人的心灵。所以这样来看Leona Lewis的这段经历,也是体现着年轻人的努力和追求呢。
当时看奥运转播,提及她和传奇摇滚乐手吉米·佩齐的表演,被认为是正好表示着奥林匹克精神中梦想的传承与未来。

以上的,我还是大多在四处跑题啊。当然作为我表示对Leona Lewis有所关注的缘由,还算是足够的。音乐作为一个纽带,可以拉近人的灵魂。也会让人在音乐中,一种无法躲闪的音乐的力量中,而有所体会。
于是呢,我果然还是想把话题往空泛的人生方面的去扯呢。
哈哈,那么这次,就免了吧。

不过感觉现在自己写文字是越来越空洞了,于是想借这篇的机会弥补一下。

传送区:
http://v.youku.com/v_show/id_XMTQ1NTkyNDAw.html
http://v.youku.com/v_show/id_XMTUwMDkzNzIw.html
http://v.youku.com/v_show/id_XNjcxMzcxMDg=.html
http://en.wikipedia.org/wiki/Leona_Lewis
http://www.leonalewismusic.co.uk/cn/bio/
Albums:《Spirit》,《Echo》。
http://www.tudou.com/programs/view/ANo2ryyjjW4/

----
100622
上面是当时写着激动了,不过貌似之后听了她的音乐后,并不觉得非常合拍。

2010年5月15日星期六

啦啦啦~~又是一个好天气啊~~~

前些天有个《南方周末》报道富士康今年“八连跳”的文章,结尾道“这实非一个工厂的内幕,这是一代工人的命运。”
大意是说富士康是个管理规范的工厂,而且富士康的自杀率仍低于全国自杀率的,而值得悲伤的是“流水线”状态下生活的工人。

而且其中想表达的意味是:
工人们互相冷落,互相鄙视,同时也感受着自己的低贱。
生活的内容是站在流水线上,为了发工资的日子,并且“积极地”去加班。
以年轻人为多,有汗水和青春,远离了家族的责任,生活在一个没有终点的“流水线”上。

或许这里的转述中融入了个人的理解,而且这本身就是新闻作者取舍出了一个关注(或掩饰)点。
不过让这个新闻发展往戏剧性的是,今早就有了富士康今年第九起坠楼事件的报道。

关于自杀人数,一直觉得日本是一个比较特别的国家。
根据前天(100513)日本警察厅公布的消息:2009年日本共有32854人自杀,连续12年自杀人数突破3万人。其中年龄涉及年轻人到老年人等各个阶段,而原因最多的是“失业”和“生活苦闷”。

那自杀拿说事,因为这正好是现在发生的社会新闻。而且引起广泛关注和探讨的新闻,似乎大多是蕴含了悲观与愤怒的情绪。这或许只是人体的脆弱吧,不过有时社会和个体间就真的那么容易区分开吗?

不过,我来提这个新闻,并不是想传递其中的某某情绪。只是觉得在能够有心灵上的体会的社会性新闻变得日渐稀少的今天,依然有一些事情在暗示社会中暗藏的群体中体现出的社会发展的意义。
而不是以一种破没的虚假的理想,去修饰出一片灿烂的意境。有些事正因为没有做到,才要去追求人所能获得的幸福感吧。

前天在一个常去的论坛上,有人提议“我出个题目,大家各写个短篇吧”。于是我提交了一篇,满满的一屏幕(小号的),确实满满地文字堆砌的无意义。望着天空,就仅仅是一个天空而已。
那么,文先至此。毕竟希望文本意义的存在,是大于文本本身的存在的。

Links
http://video.sina.com.cn/finance/20100513/101427524.shtml
http://video.sina.com.cn/news/c/v/2010-05-15/195160397.shtml
http://www.infzm.com/content/44881
http://www.kenengba.com/post/2845.html
http://www.ruanyifeng.com/blog/2010/03/some_printing_workshops_in_shenzhen.html
http://blogs.cn.reuters.com/

2010年5月9日星期日

Python的杂乱无关物

关于Python的

A
先说编辑器
任何带语法高亮的文本编辑器都能拿来用
像什么notepad++,geany,jedit,...
Emacs上写Python用的话,一个不复杂但可用的方案是:
python-mode + ropemacs + yasnippet
这是几个比较集成的包了
要不就在Eclipse上PyDev,更加集成来着。

B
维基百科上PyQt4的示例:
#!/usr/bin/env python
#coding=utf-8
import sys
from PyQt4.QtGui import *
app = QApplication(sys.argv)
label = QLabel(u"你好世界",None)
label.show()
sys.exit(app.exec_())
然后,就是涉及信号槽的使用了。
见PyQt文档的new-style-signal-and-slot-support一节。

C
一个PyGame示例:
import sys
import pygame
pygame.init()
size = width, height = 640, 480
black = 0, 0, 0
screen = pygame.display.set_mode(size)
while 1:
....for event in pygame.event.get():
........if event.type == pygame.QUIT:
............sys.exit()
....screen.fill(black)
....pygame.display.flip()
这里没用Sprite和OO的结构。

D
用lua直接重写,以示比较:
其中用到通过LunaticPython调用pygame
http://lua-users.org/wiki/LunaticPython
古董级VC6只改了"luaL_openlibs(L);"居然编译过了
--# Head Start #--
require("python")
local py=python.eval
local pe=python.execute
local pg=python.globals
local u=function(text)
return python.builtins().str.decode(text,"utf-8")
end
local t=function(a)
l=python.builtins().list()
for _,v in ipairs(a) do
python.builtins().list.append(l,v)
end
return python.builtins().tuple(l)
end
local index=function(t)
return python.asindx(t)
end
local slice=function(seq,...)
local b=python.builtins()
return b.tuple.__getitem__(seq,b.slice(...))
end

--# Program Start #--
pygame = python.import("pygame")
pygame.init()
size=t{640,480}
screen = pygame.display.set_mode(size)
font=pygame.font.Font(u"C:\\WINDOWS\\Fonts\\simsun.ttc",32)
text=font.render(u"你好啊",1,t{255,255,255})
pygame.display.set_caption("LuaDemo")
while 1 do
event = pygame.event.wait()
if event.type == pygame.QUIT then
break
end
screen.fill(t{0,0,0})
screen.blit(text,t{64,64})
pygame.display.flip()
end
LunaticPython本身的代码不复杂来着,直接用了python和lua给C提供的接口。
感觉上用起来觉得lua的table比python的dict轻量好些,就这样了。
有个bugfix的分支在 https://code.launchpad.net/lunatic-python ,还是建议用这个版本吧。


E
曾试过用swig给lua做过一个绑定,是拿之前写的C++框架改的,想Override虚函数是很麻烦的。
不过参看Pygame的实现,可以分为三个部分:SDL的函数补充,到python的绑定,python写的框架(比如Sprite的实现)。
所以绑定部分可以直接function2function,class2userdata,然后框架部分用lua实现,SDL的C函数直接移植。
这样看给lua重写pygame也不算很费事的,不过暂且还是先拿py的库来直接用吧。
况且Model和View要分离呢,用什么库和逻辑部分的代码间影响不大。。。

F
下面备份用过的makefile,很dirty的。

SRCS = $(wildcard *.cpp)
OBJS = $(patsubst %c,%o,$(SRCS))
LIBS = -lSDL -lSDL_image -lsmpeg -lSDL_mixer -lSDL_ttf -lSDLmain
all: svl
svl:
swig -lua -c++ svlua.i
g++ -shared -O2 $(SRCS) svlua_wrap.cxx $(LIBS) -o svl.so -I/usr/include/lua5.1 -I/usr/include/SDL -L/usr/lib/lua5.1
win:
C:\workspace\lib\swigwin\swig -lua -c++ svlua.i
g++ -shared -O2 $(SRCS) svlua_wrap.cxx $(LIBS) -o svl.dll -IC:\workspace\lib\lua\include -IC:\workspace\lib\SDL\include -LC:\workspace\lib\lua -LC:\workspace\lib\SDL\lib -IC:\workspace\lib\lua\ -l C:\workspace\lib\lua\lua51.lib
copy:
cp ./* /home/zhang/workshop/linux/lua/sdl/
clean:
rm -f *.o
rm svlua_wrap.cxx
这个是以前给Lua做了一个SDL的绑定,比较失败的地方是把框架部分写在CPP代码里了。其实cpp部分只要提供函数和具体类。然后什么对象层次之类的东西用脚本去扩充,然后最多在C代码写点有性能要求的Helper函数。SWIG对lua不支持参数传入派生出的对象的,对Python支持更好一些。不过作为根据头文件生成自动绑定的工具,已经很好用了。

编译LunaticPython,它自带的参数是用于Linux下gcc的(LP上的补丁版本编译没有任何问题),因为我python和lua都是VC编译的,所以就手写一回参数吧:
set INCLUDE=%INCLUDE%;D:\VS.NET\VC7\INCLUDE;C:\workspace\lib\lua\include;C:\Python26\include;C:\workspace\lib\lua\
set LIB=%LIB%;C:\workspace\lib\lua\;C:\Python26\libs;C:\workspace\lib\lua
cl pythoninlua.c luainpython.c pylua.def /opylua.dll /O2 /GD /LD /MD /link lua51.lib python26.lib
文件pylua.def里面是
LIBRARY pylua.dll
EXPORTS
luaopen_python
initlua
如果在Py中用的话,就文件名写成"lua.pyd"。Lua中用的话,就用 package.loadlib("pylua.dll", "luaopen_python") 。

G
tcc与cint,这是分别来对付C和C++的。除编译功能外,都可以对源代码直接执行。
tcc之前一直用的,它依照C99并兼容gcc的语法。它以轻巧的代码实现了一个c语言的编译器。
这也就是说在需要编译速度时可以用tcc来写,然后gcc编译时再估计代码生成优化。
cint是C与C++的解释器,并实现的一些编译时间优化。
不过它源于ROOT项目,定位是用于工程计算,实现了一个涵盖C与C++大部分特性的子集。
用于尝试算法还是很适合的,STL也是有的。不过我拿写过的CPP特性的练习代码,和一些第三方代码来跑,可惜没编译通过。不过Cint实现了一些它自己的绑定来着。

H
拿Python来写简单的和网络有关的应用,这里分几方面来说:
首先是抓取网页有关的。
最基本的是用来访问网页的话用urllib2,解析HTML用HTMLParser。
还有的就也有一些以lib结尾的各种协议客户端了。
另一方面是实现服务器的角度。
Python里自带了一个Http的文件服务器实现,使用方式是"python -m SimpleHTTPServer"。它一层层地基于另一个库SocketServer,也有CGI支持和多线程功能。
不过这些相对于socket已经是比较高层的封装了。
以上这些库在在2to3里有了名称上的重新分类。
写Web应用的话,处去直接写服务器。有cgi和使用Web框架两种方式
前者基于cgi和cgitb这两个库,后者就是像web.py那样的,Django更普及一点,不过还功能不够集成来着。

I
打包工具
pyinstaller
http://www.pyinstaller.org/
果然很好用的一个东西,不过由于编译器(Py2.5是用VC2003的)的关系,对Py2.5分支的支持目前最好来着。我这Py2.6也Work来着。
它可以分析py文件的依赖关系,然后生成一个不对本地有依赖的绿色版本。使用Pygame和PyQt等第三方库都可以的。
使用步骤是
Configure.py
Makespec.py /path/to/yourscript.py
Build.py /path/to/yourscript.spec
其中在Makespec.py时,可以附加一些参数。
我无聊地试了试
import lua
import pygame
lua.require("__init__")
然后打了一个onefile包,居然也工作。

J
来发泄下不满,既然决定做Python黑了,就要做到底!
严格的缩进确实很有必要,即使是C我就很喜欢K&R风格缩进(别的风格也可以来着)。不过这里的关键是,缩进是否影响语义。在Py的缩进的语义下,写代码是明了了,不过在代码修改时就是有在找麻烦的感觉。移动一段代码的话,为了让他语义不变,就要去调整一下缩进。
下面说的是,Py对闭包支持有限,而且没有原型继承。首先是闭包这东西,其实C也没有,而且C里面为了实现的简单,把函数定义嵌套也免了。对于整个C代码,就是由main,可导出,未导出的函数和全局变量组成。然后嘛,函数名是作为指针来用的,这里没感觉什么来着。不过问题在于,Guido自己也说过,Python对函数编程的支持就是残缺,而且也不鼓励那种风格。话是没错,不过写着过程式的代码,还不时写点不大好用的闭包,这种感觉,唉。
关于原型继承,这东西绝对是很有用的东西。单实例类和类型的膨胀问题在原型继承下都可以很不错的解决。而且最关键的是,开发效率。这不是说写代码的效率,而是说思考的效率。是去思考一个实际的对象原型容易,还是去设计或派生一个类简单呢。有时候用过好用的东西,以一个现代语言的态度去用却用不到,就也是种别扭。
接口!对Python的Duck Type确实是接口上的便利,一个对象能拿来用,而不用管它是什么类型。不过如今这种动态语言成为“标配”的特性,已经不值得炫耀了。但是看看Py代码中那种一行行代码像面条一样的拖下来就感觉恶心。对于一大堆代码,值得关心的是它在做什么,有如何的结构,能跑过哪些测试,而不是在有闲暇去看他怎么一步步做。对于写算法,或写命令Py的风格是合适的,但是想写出难以重构的结构混乱的代码却是容易的。当对代码的关注点转移时,Py的风格是否同样仍是最有效的方式呢。
恩,今天就黑至此。因为把Python拿出来用用确实挺省事的。
----
我还想要php/Javascript里那种list和dict混合型的array来着。。。

K
年初尝试过一个简单的脚本解析模块,目的是可用于AVG和RPG的数据文件。
虽然已经把能用和实现简单作为要点了,直接把脚本当批处理一样用。
不过实在功能太残,用起来就是自找麻烦啊。不过想在会看那种诡异的语法还挺有趣的。
基本语法是[obj msg arg],脚本的存在和脚本的执行都是在一个嵌套的Map上进行的。
然后又多重预处理器,让"$a=$b+1"先是优先级"[$a]=[$b+1]"在分析出[$a]是[$ a]对$发出一个无参数的消息a,那么执行结果是找到变量a来着。然后本身执行环境也是一个嵌套Map来着。一个变量在自己这个Map里找不到,就去__path__里找,用$new的话,则在当前Map建立一个空值。
不过问题是怎么实现闭包来着,还没那么考虑过。那样的话Map的成员就都是引用了,看那个对象引用数为零在删掉?
嘛,这样的脚本的好处是可以这样写:
map npc1:var __ npc;var pos 2 1;var touch [say "yOu CatCH Me!!"];
map npc2:...
貌似很直观来着。

L
Python Documentation
http://www.python.org/doc/
Python 绝对简明手册
http://wiki.woodpecker.org.cn/moin/PyAbsolutelyZipManual
Python 2.4 Quick Reference Card
http://www.digilife.be/quickreferences/QRC/Python%202.4%20Quick%20Reference%20Card.pdf
除去dir/help命令,或许可以用来速查的东西。
然后这个也有时是值得看看的 http://www.debian.org/doc/

M
在Py字典和对象的关系,估计有人写过这个话题,而且肯定写得还不错的。
我只能来列举点我暂时知道的了(貌似文档里提到的),还很可能有差错:
可以有dict['__metaclass__']存在,对我下面用不到来着。
对象方法的第一个自动的参数self即是自己,
当类方法用时则不会隐式在调用时添加这个参数。
在py中字典[]和.貌似(?)不通用的,属性的操作方法不等于它的Key。
setattr方法等价于使用self.name = value,可由Object来override属性__setattr__
x[i]等价于x.__getitem__(i)和type(x).__getitem__(x, i)。
在class定义的变量函数什么的属于class这个包,不是实例的。
下面的两种写法是等价的
>>> class X(object):
... a = 1
...
>>> X = type('X', (object,), dict(a=1))

type函数的三个参数将分别给对象的__name__,__bases__,__dict__属性
在2.5中是混合着两用对象style来着。
最后来说在Py中字典和对象的关系:它们是不同的类型,但用法有点像。
恩,去看 http://docs.python.org/reference/datamodel.html 吧。

N
ECMA-262
http://www.ecma-international.org/publications/standards/ecma-262.htm
目前比较值得看的是第三版和第五版
是叫ECMAScript来着,做嵌入脚本也相当不错。
有点是简单高效,不足是没有统一的实现(不过也有很好的实现来着)。
有个第三版Html格式的:http://bclary.com/2004/11/07/

O
Java里
有jPython,可以用Py写成的库,以及Java的库,然后编译成class文件
Javascript的嵌入貌似是j2se自带什么Engine?暂且还不熟悉。Qt也像是学着来着。
Lua有个luaj,是其解释器的Java实现,分J2ME&J2SE版本,用于解释嵌入或使用Java库。
不过Lua目前没有统一的继承实现方法,虽然有调用对象方法的语法,但实现对第三方库继承还是不便的。

P
Python的正则表达式的库叫re。正则表达式可以编译为状态机,所以使用正则表达式的话有表意和性能上的优势。
不知什么时候这就成成了大多语言的标配了。
虽然Perl的风格可能影响更大一点,不过对照来 http://docs.python.org/library/re.html 来编写字符串相关功能的话也不是很大的负担。
平时的话在文本编辑器里执行搜索替换操作,re也是会用到的东西。
正则表达式在Py用r""传给re库,字符串里大多字符就是字面的意思,还有一些符号表示扩充的意思(如在宽度和长度上代表多个值),也有向表示或者或括号的运算符号。

Q
感觉使用Python的时候,要陷入他的审美观中,才能用起来有点感觉。
比如Py的没有Scheme那种严格的静态作用域的可是规则,变量就全局和局部按模块分,在继承时就是按照名字来的《---这是在说什么。
然后另一个审美上的就是带了一个大库,一大包的话一个HelloWorld都是庞然大物。
用Emacs默认的低行数看面条式代码可以减少视觉压力。
不过这年头库多了,也算一种种语言了。

R
Emacs-w3m居然在windows下可以用,配合一个color-mode的好主题还是蛮好看的。
Emacs和Python的各自制作的mode区别是,前者维护较积极,后者与Python集成好。自己目前在用后者,不过觉得他的语法高亮好基本,就是也算清爽啦。
不过我就看Emacs是快捷键多的可扩展文本编辑器来着,看了别人的配置文件就有引用了几行。
注目:换行是C-j来着。

S
Lua的社区,官方自己更加关注一个灵活高效便携的核心。所以Lua更适合做嵌入使用,不过也有一些那Lua写应用的项目。
可以参考的是:
lua官方的社区的目录
http://www.lua.org/community.html
一个用lua写网络应用的集合项目
http://www.luarocks.org/
一个集成的包管理系统
http://www.keplerproject.org/
不过都到综合应用的层面,语言纯洁倒成为次要的了。

T
再写就真的却来却杂乱了。嘛,其实也只是以记录一时感受来开这个贴子的。
意义也就是纪念价值吧,离写满26个字母还有一段距离的,我还是去开开别的帖子吧。
不然就不是叫写补充,而是在堆杂货了。况且一直写下去,自己还是所知有限。
貌似之后写的东西没来贴代码来着。

U
有吗,这部分有吗?

2010年5月6日星期四

用Qt写GalGame

虽然向PyGame这样东西的东西来写个交互娱乐作品会简单一点,而且也有了像RenPy这样的东西。
不过Qt作为一个跨平台的Qt库兼C++开发框架,用来写一个简单视觉类型的东西还是很方便的。
况且除了其Core和GUI部分,还为我们提供了Paint System,Graphics View,Script Module,Phonon Multimedia Framework这些图形脚本多媒体支持,还有Network Module和OpenGL Module这些会很有用的东西。

以下内容是按照Qt4.5的API来写的,因为这是一个向后兼容LGPL版本。不过实例是在Qt4.6下测试的(Qt4.5下Phonon没编译好的后端支持),并且暂不过考虑在Qt4.7上会有更好的实现方法(对Script有更强大的支持了)。

写此文目的仅仅是为了尝试一种可行的方案罢了。#include部分被我略去了,不要忘记写哦。一般用一个Class就对应写一个,也就是像"#include <QtGui>"这样的东西。这里也没处理空格来着。


QWidget & Events

这部分先考虑显示出基本的界面,以及实现事件处理。
可以通过继承QWidget来实现一个Scene:
class Scene : public QWidget
{
public:
Scene(QWidget *parent);
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent ( QMouseEvent * event );
void keyPressEvent ( QKeyEvent * event );
void timerEvent(QTimerEvent *event);
};
然后mian.cpp里可以写:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
QWidget window;
window.setWindowFlags(Qt::MSWindowsFixedSizeDialogHint);
window.setWindowTitle("演示程序");
Scene *s=new Scene(&window);
s->resize(800,600);
window.show();
return app.exec();
}
这样就能得到一空白的窗口了,我们事件方面的功能是通过重写QWidget的虚函数来实现的。

首先来看"paintEvent",对QWidget的绘制操作就将在这里进行。需要时可以用"update();"来使这个方法被调用。
方法"timerEvent"是继承自QObject(QWidget的基类),用"int QObject::startTimer(int interval)"来开始一个QTimer。这里interval通常取20的倍数。
一个通常的交互场景的update(逻辑的更新)和draw(图像的绘制)方法,就可以通过重写这项个方法实现。

然后来看对鼠标键盘事件的处理,我以上只是列举了部分可重写的函数,详见的QWidget文档的Events部分。例如:
void MyWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
// handle left mouse button here
} else {
// pass on other buttons to base class
QWidget::mousePressEvent(event);
}
}
也可以只重写(在消息链中添加一环)"event(QEvent *event)"方法,再对其"type()"进行判断后对不同类型的消息处理。不过不调用"setFocusPolicy(Qt::StrongFocus);"的话,会收不到键盘消息的(还有"setMouseTracking(true);"鼠标移动消息)。

QWidget本身还有方法"show()"和"hide()"来改变自身的"visible"属性。
在QWidget上放另一个QWidget也是可行的,不过一般只有一个是活动的吧。


Paint System

QWidget是继承自QPaintDevice,这样就可以在"paintEvent"方法中用"QPainter"对象来实现图形的绘制了:
void SimpleExampleWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(Qt::blue);
painter.setFont(QFont("Arial", 30));
painter.drawText(rect(), Qt::AlignCenter, "Qt");
}
通常还有"painter.setRenderHint(QPainter::Antialiasing);"。

这里绘制图形的效果的设置中线条是"QPen"而填充是"QBrush"决定的字体则是"QFont"。
还有"rotate","scale"和"translate"用于绘制前对图形变换(底层方法是执行矩阵运算)的设置。
具体的绘制操作是一组以"draw"为前缀的方法,有图形,路径或图像。
绘制像素的混合模式参见文档"QPainter"的"Composition Modes"部分。
至于绘制图层的透明度是由被绘制物的材质的透明度决定的,比如"QColor"就可以指定"alpha"值图像也可以"setMask"。


Graphics View

这部分是个可选的框架,用来实现管理2D图形物件的场景。
不过是实现Sprite精灵对象是最好最直接的方式了。
首先是一个基本的mian.cpp:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene;
scene.addText("Hello, world!",QFont("",128));
QGraphicsView view(&scene);
view.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view.resize(800,600);
view.show();
return a.exec();
}
这里是把派生自"QGraphicsItem"的对象添加("addItem()"或使用创建加添加的方法)入"QGraphicsScene"(负责管理),然后由"QGraphicsView"来渲染和显示(也可以对Scene使用QPrinter)。
这里我设置滚动条为不显示,并且默认"DragMode"为"NoDrag",所以需要之后控制渲染的场景区域。
还有在View中提供了鼠标选择Item的方法,同时它会把消息传递给Scene。我们对Event的处理就通过重写(依旧是消息链,可调用父类方法)Scene的方法来实现,默认的是选择或把消息提交给当前"focus"的item,而item可以决定它能否被拖拽。

下面来从Scene派生,用于处理事件:
class scene : public QGraphicsScene
{
public:
scene();
protected:
void keyPressEvent ( QKeyEvent * keyEvent );
};
void scene::keyPressEvent ( QKeyEvent * e){
switch(e->key()){
case Qt::Key_Up:
qDebug("Key_Up");
break;
case Qt::Key_Down:
qDebug("Key_Down");
break;
default:
;
}
}
这里截取了消息,没有向父类传递,所以item不会获得消息了。
我是偏向于只保留键盘控制的,就有点像传统NES手柄的感觉。

而Sprite对象则可以从QGraphicsItem派生,然后重写"boundingRect()"和"paint()"。
也可以直接去继承"QGraphicsItem"的子类,可以省些事儿,比如用"QGraphicsPixmapItem"。
class sprite : public QGraphicsItem
{
public:
sprite(QGraphicsItem * parent=0);
//...
};
一组Item可用"QGraphicsItemGroup",不过Item本身就是Qt传统的树状关系了。

每个sprite以new创建,然后交给父Item或Scene管理。:
scene::scene()
{
this->player=new sprite();
this->addItem(player);
}
如果需要控制sprite的属性(比如Pos方位)则使用一个私有的指针成员,然后在处理事件时跟新。
至于sprite的属性和运动角度之类,则是sprite对象的私有属性了,sprite的update(逻辑上的根更新)则通过重写"advance"方法:
void QGraphicsItem::advance (int phase){
if (!phase)return;
//更新自己状态
//进行碰撞检测,这里是复制了Qt自带Demo的代码片段
QList dangerMice = scene()->items(QPolygonF()
<< mapToScene(0, 0)
<< mapToScene(-30, -50)
<< mapToScene(30, -50));
foreach (QGraphicsItem *item, dangerMice) {
if (item == this)
continue;
//在这里判断
}
}
碰撞检测可以用手工根据"QGraphicsItem::contains"判断,或者用"QGraphicsScene::collidingItems","QGraphicsItem::collidesWithItem"。
还有个"QGraphicsItemAnimation"用来实现缩放移动等随时间的变换。

显示区域在View设置,可用View的"setSceneRect"来设施显示区域的范围。
有个"centerOn"用于点居中,不过要注意Scene的"setSceneRect"来设置大小,默认是Scene大小小于View大小时Scene居中于View。

这是个带来方便的可选框架,我这里也没多说清楚。不过
如果需要的功能简单的话也可以手工用"QPainter"来实现Sprite精灵。


Script Module

QtScript模块目前还不能代替C++来使用Qt库,不过由于moc预编译器的存在,QObject的绑定也变成了一件很方便的事:
QScriptEngine engine;
qDebug() << "the magic number is:" << engine.evaluate("1 + 2").toNumber();
比如在Script里写AI脚本,提供一个函数给Host使用。
当然Qt的信号槽机制也很好的支持,这算是最方便的一种用法了。

用"QScriptEngine::newQObject"的话是绑定QObject的 Signals and slots, properties and children:
QScriptEngine engine;
QObject *someObject = new MyObject;
QScriptValue objectValue = engine.newQObject(someObject);
engine.globalObject().setProperty("myObject", objectValue);
这里是通过个QObject设计一组API(包括绘图操作,事件绑定)给脚本,然后把整个程序的逻辑写在.js文件里。

下面来看Qt示例里的这段代码:
QApplication app(argc, argv);
QScriptEngine engine;

QScriptValue Qt = engine.newQMetaObject(QtMetaObject::get());
Qt.setProperty("App", engine.newQObject(&app));
engine.globalObject().setProperty("Qt", Qt);

evaluateFile(engine, ":/tetrixpiece.js");
//...
QScriptValue ctor = engine.evaluate("TetrixWindow");
QScriptValue scriptUi = engine.newQObject(ui, QScriptEngine::ScriptOwnership);
QScriptValue tetrix = ctor.construct(QScriptValueList() << scriptUi);
它把Qt命名空间传给脚本,并用脚本提供的构造函数创建了一个对象。

不过最主要还涉及各种connect,以及C++和脚本中函数的互相调用(比如脚本中函数对象的call方法)啦。
这里问题在于怎么划分出脚本的职责,因为绑定方法是都可以直接使用文档上的示例代码的。
或QScriptEngine::fromScriptValue()在C++与Script间传递。


Phonon Multimedia

最简单的用法是:
Phonon::MediaObject *music =
Phonon::createPlayer(Phonon::GameCategory,
Phonon::MediaSource("/path/mysong.wav"));
music->play();
这里MediaObject是可以存放一个序列的。
然后使用"setCurrentSource",音乐播放是不需要全部载入内存的。
循环播放的话,用finish信号。

也有视频支持来着。


GUI

按钮啦文本框啦什么的也是会用到的,这些就按Qt本来的方式处理吧。
然后也可以渲染到View里(QGraphicsScene::addWidget),再就指定一下"Style Sheets"显示样式。

不过Qt毕竟是GUI库,其显示界面是基于模式和消息循环的。也就是说对于一个画面显示的状态,有一个个对应的Widget的mode,要处理界面的重绘(比如窗口覆盖)并重写处理事件的消息。
而不是像SDL那样有个全局的Surface供绘制并可随时在脚本中间进行一个消息循环。虽然说像STG那种也通常就一个主模式和主循环。当然Qt中也可用一个QImage作Surface用。
QPainter一般是让它在析构的时候才自动执行实际的绘制工作。


Resource System

Qt提供嵌入Resource,也就是qrc资源文件。资源文件编译有可以绑如可执行文件中,或者单独的.rcc文件。
QResource::registerResource("/path/to/myresource.rcc");
cutAct = new QAction(QIcon(":/images/cut.png"), tr("Cu&t"), this);
如果用"RESOURCES = application.qrc"捆绑的话,是会自动初始化资源文件的。


其他

qDebug() 用于输出调试文本
qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); 初始化随机数
OpenGl可以用来2D加速,或3D支持。
Qt4.6有些状态机模式的示例,原理可用到。

额,就是暂且列了些可用的东西而已,没出现什么实际的代码。
最关键一点,详见Qt文档。
然后,PyQt貌似也能用来着。


----
荐歌环节:中岛美雪《骑在银龙背上》

====
Part.2
现在到文本的补充环节了,上面的说的内容,因该是算Qt的相关API部分了。
然后我也确实那它来写了一个简单的GalGame,最初的样子就是单个QWidget然后一个执行脚本序列(最初只是简单解析下文本而已,)。
不过这种方案有两个问题:
一是这样只用一个模式,像俄罗斯方块那样的确实一个模式就可以了,不过对于一个GalGame来说单模式还是很单一的。
另一个是消息循环在Host程序中,脚本序列只是一串孤立命令,无法实现丰富的脚本功能,引入QtScript的好处被限制了。

一般的GalGame专用引擎都会实现自己的脚本,以行为单位依次执行命令,控制流程则通过古典的GOTO跳转语句。这样的好处是没有的局部作用域,行的存在就是脚本执行的一个状态。这样GalGame的程序的save&load来保存状态就只需要保存行号和所有变量的值。
脚本文件的存在,即是数据也是程序。不过由于GalGame功能上的东西往往都是有引擎实现好的,或者可由插件支持的。更多的是把脚本作为数据看待,作为引擎解析的文本而不是一组命令。
在古董(因为简单嘛)级别BASIC小游戏中,数组是唯一的数据结构,然后程序和游戏逻辑的代码就全混合在一起了。
在C语言中,虽然也是有goto语句的,不过这样就无法获得程序执行的状态。此时可以转用switch来实现:
enum Map{ROOM,TREE,OTHER};
switch(MapId){
case ROOM:
say("欢迎回家");
say("1.去森林");
n=key();
if(n==1)return TREE;
break;
case TREE:
say("欢迎森林");
say("1.回家");
n=key();
if(n==1)return ROOM;
break;
case OTHER:
say("欢迎回家");
break;
default:
;
}
这只是一种重写在中间状态的代码,每个key()的存在都是单独的状态。
这在传统的Goto式脚本中是可行而且简便的,不过在有统一消息循环的程序中,将会带来频繁的模式mode切换。
所以这里可以再修改一下,建立一个跳转表,把switch放在while内,并每次循环只调用一次key()。不过坏处就是,我们有时确实是需要频繁的模式切换的。
从编译器的角度说switch就会被优化为一个跳转表的,于是对于一些轻量的脚本语言(例如Javascript和lua)就有了另一种解决方案(它们要们switch有问题,或干脆就没定义这条语句),用字典类型的数据结构(json和table)。
并且由于函数的first class value的存在,语句块是可以作为一个key对应的值,于是语句也就天然地作为了数据的一部分了:
#lua#
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
end
},
shop={
name="商店",
goto={"home","tree"},
has={},
run=function(self)

end
}

}
obj={

she={
name="女友",
type="human",
run=function(self)
say("你好啊")
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

}

}
这只是实验性质代码的DATA部分(半成品,分离的程度还不够),然后可以根据需要去实现若干View视图。
从做法上来说,感觉这里的代码分离是很具有个人喜好的美感的,而且此时去实现View部分的代码也是非常具有效率的。

啦,可以回到之前的关于Qt的消息循环与模式切换的问题上,一个还算可行的方案是用设计模式中的状态模式以及模拟Stack。
因为我之前说到的那个所写的Qt代码是单模式的只使用的一个QWidget,所以来发扬一下重构精神。用消息托管的方法分离一个state的界面处理QWidget来:
class world;
class Scene :public QObject
{
protected:
world* w;
public:
bool pop;
Scene(world* parent);
virtual ~Scene(){};
virtual void paint (QPaintEvent *event) =0;
virtual bool event ( QEvent * event );
//virtual void timer ( QTimerEvent *event){};
//virtual void update();
//virtual void mousePressEvent ( QMouseEvent * event ){};
//virtual void keyPressEvent ( QKeyEvent * event ){};
};
这里的class world就是我先前的那个单一QWidegt,不过显然职责分配的过重了。它本身承担了QWidet本身要处理的消息和绘制操作,而mode间交换的全局环境(先叫env)以及script的处理代码居然还混在里面。不过暂且目标是实现多mode,这里是重构了一半的代码,不过工作良好(往后的细节问题还没考虑好来着)啊。
然后,模式间的切换就交给单实例类(实现单实例类,那么env就是全局的了,所以现在的传指针的方案是有好处的。)env的成员stack或map来。
如果使用stack那么每创建一个mode就是把旧mode给push了,然后消息托管给栈顶的mode处理。
如果mode设置退出消息,那么就把它pop掉。
用map的话,就是同时存在多个mode,但在一个时刻里,只有一个mode是活动的。mode切换的方法和上面stack的类似。例如用map,这里string和enum都行。

这样的话,我们的脚本部分就终于又有一种用法了,而且OO到这种别扭的地步下还能算是优雅吧:用来实现mode。
对于QtScript可以用"QScriptValue::construct"由javascript脚本中的构造函数来创建对象。那么接下来,只要在js对象的Prototype中添加mode子类需要重写的若然方法就行了。
这样的话消息循环依旧由QWidget去做,但是已经给脚本部分很大的自由度了。
Qt自带有个Demo是去实现一个canvas供Script使用,它在界面重绘时不是去调用脚本。而是脚本在绘制时把图像绘制在一个QImage上,然后再需要时把QImage的内容绘制到显示用的QWidegt上。
这里我们脚本的职责是定义对象(就是相当于用new语句创建对象到环境里一样啦)。

再然后嘛,这里和前面写到json部分结合起来有点不够协和的感觉,那就有必要去再扯一扯关于对象实现方面的事情了。
Javascript(QtScript里用到的)的继承是基于原型,用new的方法来创建对象,用函数的形式来写对象的构造器。于是可以写出:
function Person(name)
{
this.name = name;
}
var p1 = new Person("John Doe");
Person.prototype.toString = function() { return "Person(name: " + this.name + ")"; }
print(p1 instanceof Person); // true
function Employee(name, salary)
{
Person.call(this, name); // call base constructor

this.salary = salary;
}
Employee.prototype = new Person();
var e = new Employee("Johnny Bravo", 5000000);
print(e instanceof Employee); // true
print(e instanceof Person); // true
print(e instanceof Object); // true
(示例取自Qt文档)
这里隐含实现了对象的方法查找链,以及对象派生关系的判断。如果要写成类似json的形式的话,就要自己来实现一些机制了。
比如在还很淳朴的Lua中(这两种语言的优雅支持就在于废话写多了不觉得累啊)用table和metatable机制(有机制但没自带oo实现):
比如学Python(令人印象深刻的"__init__"双下划线写法)把table的metatable都设成(在Lua中也就额外遍历一下)自己?
这样就实现了居于原型的查找,然后还要实现一个类似instanceof的函数吧。
额。。。我也只是想象而已。相比写成一序列new构造(貌似可以玩出复杂的制造构造的构造来着),个人还是觉得写成这种嵌套字典的形式好看一点(况且可用)。
个人评价标准嘛,依旧是以前一篇里提过的"声明>命令",以及Scheme的语言最小话的原则。

恩,这部分补充的东西乱了一点。大体上说是关于脚本和程序混合的问题。
当然如果纯粹像插件类型的或批处理类型的,实现会简单很多的感觉来着。
还有一种更简单的分割原则就是,以脚本为主体,制作二进制库来扩展。使开发偏重脚本,必要时以C语言等重写或扩展组件。
本篇的第二部分就暂且不独立成篇了。
#100513
----
其实上边的脚本最初是写成这种样子的:

on choose 床 水池
case 1
player.hp+=1
case 2
player.mp+=12
纯手写风格,好处是可以作为思维的开始。
不然就总觉得在哪里被框住了。

2010年5月1日星期六

五月啦!!!

想写短一点。
因为最近是字堆了好些,但是表达的内容却有没有展开。
所以也就是要把内容写集中,而在表达要充分的意思。
说到5月,是先想到五月病来着。
虽然这或许是一年中让人感到懈怠的一段时间(其实按我这的放假周期不是那样的啦),可是毕竟又是新的一月嘛。
至少可以说点希望,作些期待吧。
恩,那下面就进入正文看喽~~

"明天是新的一天了。"
话是没错,
不过在第二天醒来后发现,
原来又是如同往常的一天啊。
那么此时,
这一天要以如何的心态来度过呢?
照旧?不对啦!
每天虽然可能真的感觉大同小异。
但是,对,就是但是,
不要忘记这真的是新的一天哦。
新的一天是一个孕育新生的时刻,
一个从无到有的过程。
所以,
新的一天就要以认真的态度来对待它。
即使在早晨睁开眼觉得一切如故的时候,
也千万要相信,这就是新的一天喽!

恩,下面是月初祝福的第二条:
要学会品尝收获的喜悦哦。
收获不论大小,都是快乐的事情,
都是值得为之庆祝的事情。
或许收获在内心只是一个被视作应当的结果,
但是这并不足掩盖我们一路上所做的付出。
所以,在收获到来的时候,
请一定要真诚地敞开心扉迎接它的到来。
那么,
每天也可以用一个迎接收获的心态来度过。
去寻找丰收存在地方,
并尽力描绘出切实可行的一步步脚印,
相信存在过的行动是
至少可以有其存在的价值的。

接下来的一条很简单,就是
请不要总挂念失去哦。
得到和失去,
有时就是一个硬币的正反两面。
就像做单选题一样,
我们必然要放弃一些可能,
或者做出了未必正确的选择。
那么,在体会到失去时要记得,
我们依然是在做出选择,
依然可以去判断接下来要如何做,
我们是在为了我们将得到的东西。
同时,失去也并不意味着永远的失去。
因为这可能只是暂时不需要了,
可能只是暂时不必背负那么多了,
可能只是暂时的自然性遗忘了。
那么,请不必在此刻难过,
这只是为了让我们可以去收获更多。

最后,也很简单但会被忽视,
请保持一颗与世界接触的心。
人接受的信息是有限,
有时会固执地抱怨所做的还不够。
那么,在这个关注于自我的时刻,
不要忘记仍去接触外面的世界。
其实这也是关于视野的话题,
不要认为很多事是与己无关,
就去仅在意个人行为是什么。
世界是广阔的,
也是充满未知与启示的。
在心的内外接触之中,
请相信世界会展现出它的惊奇。

现在已经过了零点好一会儿了,
写了些文字也先暂且放这儿再温暖一会儿吧。
或许我是在写什么错误的想法了吧,而且还依旧那么空泛而不具体呢。
不过这仅是在这个崭新的5月里一个新的脚步,
之后还可以期待更美好的步伐。
此刻,可以安眠。
这并不是告别今日,
而是为了新的一天。
----
乘还能写得出,写个天真风格的。