把函数的返回值设计成Boolean或者MemPtr在使用的时候是非常方便的。而且也容易理解,返回true就是成功或真, false就是失败或假。返回指针的时候,如果返回空指针就是失败了,如果是实指针就是需要的地址。
但是这两种情况就不能处理错误,除非使用C++的异常处理机制。如果把返回值设计成Err,那么错误终归是可以处理的,但是这样使用的时候不方便,而且需要处理多少错误也是个问题。
通常,私有函数可以偷偷懒,因为你很清楚这些函数在什么地方会被调用,可以约定参数都是有效的(看看参数的来源,不要有从公有函数传递过来的外部参数,否则就不能做这样的假定),这样可以用Boolean或者MemPtr这样易用的形式;但是对于公有函数,在写这个类的时候没法预料将来会怎样被调用,所以假定所有可能的参数比较好,考虑所有的错误发生情况并返回正确的错误信息。
如果用了Boolean或者MemPtr这样的形式,要看看函数内部资源失败的时候会有什么后果(比如Malloc),是否会导致数据不一致,包括内存对象和存储对象。如果会,那还是不要偷懒,否则因为小几率事件导致数据损坏的时候就追悔莫及了。
2006/12/27
2006/12/26
数据完整性
在数据库中,有一个功能叫做Transaction,它的意思是,如果要做一系列的更新、创建、或者删除操作的话,应该有一个显示Commit;如果Commit成功,则所有的改动都生效,如果Commit失败,则没有任何改动生效----而不是部分生效。
在涉及到数据存储的时候,通常也会很好的考虑这个问题,简单的办法就是做一个缓冲出来,全部写完之后一次性写入文件;这也要求尽量把有相关性的数据写在一个地方,否则很难保证不在多个文件写入的时候发生错误,导致数据不一致。
在对象中同样要考虑这个问题。虽然我们可以假定在函数返回错误的时候,传递的参数变量会失效(如果对象较大又不希望被改变的话,传递对象的Copy的引用,而不是直接传递引用),但是在返回错误的时候,如果本来有效的数据对象本身同样被摧毁,是不能接受的。所以不要在函数中直接改动成员变量,除非你明确知道不再可能有错误发生。不要做太大胆的假设,实际上除了MemMove这样屈指可数的几个函数之外,即使是内存分配也有发生错误的可能。较好的办法是做出一个成员变量的复制品,对这个复制品进行改动,如果过程中出现错误,丢弃这些复制品并返回错误;如果没有出现错误,那么用复制品对成员变量赋值,或者把指针指向复制品,把原件丢弃。
如果每个过程都遵循这样的原则设计,那么就不用为对象内部出现数据不一致担心了。
在涉及到数据存储的时候,通常也会很好的考虑这个问题,简单的办法就是做一个缓冲出来,全部写完之后一次性写入文件;这也要求尽量把有相关性的数据写在一个地方,否则很难保证不在多个文件写入的时候发生错误,导致数据不一致。
在对象中同样要考虑这个问题。虽然我们可以假定在函数返回错误的时候,传递的参数变量会失效(如果对象较大又不希望被改变的话,传递对象的Copy的引用,而不是直接传递引用),但是在返回错误的时候,如果本来有效的数据对象本身同样被摧毁,是不能接受的。所以不要在函数中直接改动成员变量,除非你明确知道不再可能有错误发生。不要做太大胆的假设,实际上除了MemMove这样屈指可数的几个函数之外,即使是内存分配也有发生错误的可能。较好的办法是做出一个成员变量的复制品,对这个复制品进行改动,如果过程中出现错误,丢弃这些复制品并返回错误;如果没有出现错误,那么用复制品对成员变量赋值,或者把指针指向复制品,把原件丢弃。
如果每个过程都遵循这样的原则设计,那么就不用为对象内部出现数据不一致担心了。
2006/12/25
拼音与五笔的统一
一直都被这样一个问题困扰着:既然以记忆预测作为输入法的基础构架,不同编码方式的输入法应该能统一在一个同样的框架下,因为语言蚊子本身是不依赖于编码的。
今天终于想明白了这个问题。
巨硬把同音字作为不同的字处理。其实可以不这样做。把拼音拆开成为子数组。然后在SeqNode上加一个modifier的标志,另外建一个数据库存储特殊的拼音序列,供匹配的时候查询,这样就可以依据字,而不是多音字,建立树库。可以实现不同编码输入法的词组、序列和频度的共享。
不过这是巨硬IV的任务了。巨硬III还是按照目前的算法继续下去,否则永远也无法完成可用版本了,改进永远是无止境的。
今天终于想明白了这个问题。
巨硬把同音字作为不同的字处理。其实可以不这样做。把拼音拆开成为子数组。然后在SeqNode上加一个modifier的标志,另外建一个数据库存储特殊的拼音序列,供匹配的时候查询,这样就可以依据字,而不是多音字,建立树库。可以实现不同编码输入法的词组、序列和频度的共享。
不过这是巨硬IV的任务了。巨硬III还是按照目前的算法继续下去,否则永远也无法完成可用版本了,改进永远是无止境的。
2006/12/19
改动
据说这叫做AGILE。:)
对于这个小项目来说也许不是什么大问题,只是重写几个函数而已;如果是大项目,这个就要吐血了。所以我最终理解了,我在软件上最欠缺的是设计能力;不知道是不是有人能够做到一次设计不再改动的,至少对我来说这够匪夷所思的。
今天做了一个改动,摒弃了之前关于sequence of sequence的设计,抛弃在Sequence node中嵌入Sequence reference的做法。这主要是考虑到两点:一个是增加用户输入的随意性,比如用户输入嫁,可以提示鸡随鸡,输入嫁鸡,可以提示随鸡,输入嫁鸡随,可以提示鸡;另一点很重要的,是提高性能,只要抽出一个树就可以得到所有Node的展开结果,不需要查询其他树,这对于symbian或者wm这种以flash作为存储介质的系统来说非常重要。
对于这个小项目来说也许不是什么大问题,只是重写几个函数而已;如果是大项目,这个就要吐血了。所以我最终理解了,我在软件上最欠缺的是设计能力;不知道是不是有人能够做到一次设计不再改动的,至少对我来说这够匪夷所思的。
今天做了一个改动,摒弃了之前关于sequence of sequence的设计,抛弃在Sequence node中嵌入Sequence reference的做法。这主要是考虑到两点:一个是增加用户输入的随意性,比如用户输入嫁,可以提示鸡随鸡,输入嫁鸡,可以提示随鸡,输入嫁鸡随,可以提示鸡;另一点很重要的,是提高性能,只要抽出一个树就可以得到所有Node的展开结果,不需要查询其他树,这对于symbian或者wm这种以flash作为存储介质的系统来说非常重要。
2006/12/18
低级错误
对于非职业程序员来说,经常重复低级错误就是编程的一大乐趣之一。我甚至在想,如果成立一个收录低级错误的网站,对Coder来说也许是很不错的去处。
今天就遇到了两个很低级的错误,其一是忘记初始化一个变量(把初始化写入Constructor这时就是一个好主意,当资源还不存在的时候这么干就不是一个好主意);其二是没想到bitwise operator的优先级竟然不如+-*/。
ANYWAY,一边搭脚手架一边盖房子的过程开始了。
今天就遇到了两个很低级的错误,其一是忘记初始化一个变量(把初始化写入Constructor这时就是一个好主意,当资源还不存在的时候这么干就不是一个好主意);其二是没想到bitwise operator的优先级竟然不如+-*/。
ANYWAY,一边搭脚手架一边盖房子的过程开始了。
2006/12/10
Coding的周末
这个周末写了不少代码。
上周看到同事演示了一下梅花在Dopod无键盘机器上的使用情况,设计得非常不错。不过我还是坚持硬按键的想法。硬按键和软键盘相比,后者的一个优势是可以动态更改键盘显示内容,梅花的作者在这方面颇有造诣。
巨硬III的算法是很独特的,目前我也不知道最终的结果是否如想象的好用,只能等做出来再看。我也在考虑用这个算法去解决一些输入法之外的智能问题,上周想好了一个例子,就是中国古老的围棋。
目前我越来越相信基于记忆树的预测方式是好过人工智能中广泛应用的盲搜路径的。但是关于严格序列的假设是否能击败Pattern识别方式就不得而知了。这里面还有一个Pattern形成的问题比较头疼,这涉及到人脑是如何发现和形成一个未知的概念。但总体来说,如果能克服这个问题,程序就可以象人脑一样去识别和学习了。围棋中有很多模糊的东西,这基本上就是一种概念形成的结果。在不同的阶段有不同的概念,比如布局、势和实地、形和子效、手筋、死活、先后、定势、管子等等。对形势判断的绝对的优劣是没有的,往往不同的人判断的结果不同,这是很有趣的一个方面。在人实际学习围棋的过程中,这些概念都是通过老师的教导和实际的感悟逐渐形成的模糊概念。
上周看到同事演示了一下梅花在Dopod无键盘机器上的使用情况,设计得非常不错。不过我还是坚持硬按键的想法。硬按键和软键盘相比,后者的一个优势是可以动态更改键盘显示内容,梅花的作者在这方面颇有造诣。
巨硬III的算法是很独特的,目前我也不知道最终的结果是否如想象的好用,只能等做出来再看。我也在考虑用这个算法去解决一些输入法之外的智能问题,上周想好了一个例子,就是中国古老的围棋。
目前我越来越相信基于记忆树的预测方式是好过人工智能中广泛应用的盲搜路径的。但是关于严格序列的假设是否能击败Pattern识别方式就不得而知了。这里面还有一个Pattern形成的问题比较头疼,这涉及到人脑是如何发现和形成一个未知的概念。但总体来说,如果能克服这个问题,程序就可以象人脑一样去识别和学习了。围棋中有很多模糊的东西,这基本上就是一种概念形成的结果。在不同的阶段有不同的概念,比如布局、势和实地、形和子效、手筋、死活、先后、定势、管子等等。对形势判断的绝对的优劣是没有的,往往不同的人判断的结果不同,这是很有趣的一个方面。在人实际学习围棋的过程中,这些概念都是通过老师的教导和实际的感悟逐渐形成的模糊概念。
2006/12/09
Doxys
今年对我的编程生涯来说简直是一个工具年。从MindMap工具MindManager,到UML工具DIA和Visio,都对我的coding方式有很大改善。眼看着年底将至,末世情结再现,这个节骨眼上,遇到了Doxys。
Doxys是一类被称为Documentation Extraction Tools的工具。它的工作原理其实很简单,就是在代码中有一些注释的约定,符合约定的注释可以直接被转换成html的文档。Doxys基于另一个DET项目Doxygen,不过我没试过那个,据说注释风格有些怪异。这是我目前看到的最好的代码维护工具。UML更多是用于理解代码,尤其是关键代码的,把所有的代码,尤其是例程,都UML出来很不现实。而且没有直接对应关系,要保持两个地方的逻辑一致,这才是维护的时候最难的地方。事实上,无论是MindMap工具,还是UML图,还是Word一类的RTF文档编辑器,都存在这个问题。从这个意义上说,Doxys是最好的方式。
Doxys是一类被称为Documentation Extraction Tools的工具。它的工作原理其实很简单,就是在代码中有一些注释的约定,符合约定的注释可以直接被转换成html的文档。Doxys基于另一个DET项目Doxygen,不过我没试过那个,据说注释风格有些怪异。这是我目前看到的最好的代码维护工具。UML更多是用于理解代码,尤其是关键代码的,把所有的代码,尤其是例程,都UML出来很不现实。而且没有直接对应关系,要保持两个地方的逻辑一致,这才是维护的时候最难的地方。事实上,无论是MindMap工具,还是UML图,还是Word一类的RTF文档编辑器,都存在这个问题。从这个意义上说,Doxys是最好的方式。
2006/12/01
UML的类图
在visio中这个称为静态结构图。
两年前我就买过一本UML。当时很起劲,还拉了一个朋友一起探讨code generation技术,UML的一个非主流的流派就是xUML, executable UML,换言之就是让UML可以自动生成可执行的程序。因为UML是平台无关的东西,这个想法很有吸引力,如果可行,程序员就不必探究平台差异的编程了。
UML的起源很复杂,所以学习UML就象学习计算机网络一样,林林总总的标准和意图,让这个技术变得不那么纯粹,甚至是颇为混乱。同样和网络一样,UML也是由为了解决很多工程上实际遇到的问题出现的,它更像一箱子组合工具而不是一把万能工具。
我一直被这样的问题困扰着:随着程序不断膨胀,类和函数越来越多,文件也越来越多;类之间的关系让人很头疼,无论代码还是文档都不直观。我也尝试过用powerpoint和mindmanager画了很多图,仍然不方便,所以这两天又把UML的书翻出来看了看。有点心得了。
好处:
1。UML类图可以很好的给程序结构一个总览。这是其他工具很难做到的。
2。UML有关于类之间的关系的严格定义,这比MindManager或者Powerpoint流程图那种不规范的东西好的多,规范的一个好处就是强迫你去按照严格的逻辑去思考,而不是在字面的意思上较劲。
缺点:
1。UML关于关系的定义需要花满多时间去学习。
2。UML并不是能表达所有的关系
底限:
1。不要试图把代码中的所有东西都放到UML中去,一些一望可知的东西和次要的东西不必放进去。
2。UML的目的是帮助理解代码,而不是和代码建立严格的逻辑关系或者生成代码,简单、直观、没有歧义是最重要的。
UML类图对开发进度的管理也因该有很大帮助,可以在代码和UML图之间往返工作:
1。先根据文档想清楚关键的算法和逻辑
2。在UML中画出除了基础类之外的类,尤其是那些需要在不同类之间作为参数传递的类,在开始的时候就统一一下,不要在写程序的时候不断添加这样的类。
3。把这张图画的平衡一些,把一些很小的、有强依赖关系的类合并到其他类中去,可以作为一个public成员存在。这样可以有效减少类之间的关系数量。
4。仔细考虑一下那些会很很多的类打交道的类,看看这样的类是不是功能很单一,是不是可以作为一个包中最大的容器类的一个函数存在,而不是作为一个类存在。
减少类之间的关系有助于理解和简化结构,当然,不能为了这个目的把类中大量添置操作,把不相关的操作都放到一个类中去,这也违反封装的独立性原则。要平衡类的操作数量和类的关系数量。
两年前我就买过一本UML。当时很起劲,还拉了一个朋友一起探讨code generation技术,UML的一个非主流的流派就是xUML, executable UML,换言之就是让UML可以自动生成可执行的程序。因为UML是平台无关的东西,这个想法很有吸引力,如果可行,程序员就不必探究平台差异的编程了。
UML的起源很复杂,所以学习UML就象学习计算机网络一样,林林总总的标准和意图,让这个技术变得不那么纯粹,甚至是颇为混乱。同样和网络一样,UML也是由为了解决很多工程上实际遇到的问题出现的,它更像一箱子组合工具而不是一把万能工具。
我一直被这样的问题困扰着:随着程序不断膨胀,类和函数越来越多,文件也越来越多;类之间的关系让人很头疼,无论代码还是文档都不直观。我也尝试过用powerpoint和mindmanager画了很多图,仍然不方便,所以这两天又把UML的书翻出来看了看。有点心得了。
好处:
1。UML类图可以很好的给程序结构一个总览。这是其他工具很难做到的。
2。UML有关于类之间的关系的严格定义,这比MindManager或者Powerpoint流程图那种不规范的东西好的多,规范的一个好处就是强迫你去按照严格的逻辑去思考,而不是在字面的意思上较劲。
缺点:
1。UML关于关系的定义需要花满多时间去学习。
2。UML并不是能表达所有的关系
底限:
1。不要试图把代码中的所有东西都放到UML中去,一些一望可知的东西和次要的东西不必放进去。
2。UML的目的是帮助理解代码,而不是和代码建立严格的逻辑关系或者生成代码,简单、直观、没有歧义是最重要的。
UML类图对开发进度的管理也因该有很大帮助,可以在代码和UML图之间往返工作:
1。先根据文档想清楚关键的算法和逻辑
2。在UML中画出除了基础类之外的类,尤其是那些需要在不同类之间作为参数传递的类,在开始的时候就统一一下,不要在写程序的时候不断添加这样的类。
3。把这张图画的平衡一些,把一些很小的、有强依赖关系的类合并到其他类中去,可以作为一个public成员存在。这样可以有效减少类之间的关系数量。
4。仔细考虑一下那些会很很多的类打交道的类,看看这样的类是不是功能很单一,是不是可以作为一个包中最大的容器类的一个函数存在,而不是作为一个类存在。
减少类之间的关系有助于理解和简化结构,当然,不能为了这个目的把类中大量添置操作,把不相关的操作都放到一个类中去,这也违反封装的独立性原则。要平衡类的操作数量和类的关系数量。
2006/11/30
UML的活动图
这是一个特高雅的说法,说白了,就是传统的流程图,只不过更严密一些。
巨硬颇有几个搞脑子的流程,今天遇到的是第一个,Composition如何响应各种按键输入事件、分隔拼音、判断中英文状态,在Visio的帮助下,画了两个UML活动图,现在感觉比较清楚了。这两天睡眠不好,总是不能静心下来高效工作(别说让我喝静心口服液),今天画了两张图,感觉武功精进,状态在提升了。是个好的开始。希望这次代码工作能进行得比上次顺利。
BTW:这几天都在强制自己使用微软拼音输入法,如果能强制自己整句输入的话,准确率还是很不错的,只是不符合用户的输入习惯,而且万一匹配出错的时候,修改的代价很高。另外就是这个输入法对减少击键次数没什么帮助,比较适合touch typing。
巨硬颇有几个搞脑子的流程,今天遇到的是第一个,Composition如何响应各种按键输入事件、分隔拼音、判断中英文状态,在Visio的帮助下,画了两个UML活动图,现在感觉比较清楚了。这两天睡眠不好,总是不能静心下来高效工作(别说让我喝静心口服液),今天画了两张图,感觉武功精进,状态在提升了。是个好的开始。希望这次代码工作能进行得比上次顺利。
BTW:这几天都在强制自己使用微软拼音输入法,如果能强制自己整句输入的话,准确率还是很不错的,只是不符合用户的输入习惯,而且万一匹配出错的时候,修改的代价很高。另外就是这个输入法对减少击键次数没什么帮助,比较适合touch typing。
2006/11/29
巨硬III
可能很多人会以为这个标题是文字游戏,它不是。
过去的两周我已经仔细写完了巨硬III的逻辑文档,大部分问题都考虑完毕,这周开始进入代码阶段。上一次写代码大约是今年6-7月份,把这些代码翻出来看了看,庆幸当时的结构化设计,很多代码都可以重用,包括数组类、字串类、文件操作类;数据封装方面,原来的PalmOS数据库和记录操作代码可以完全不用修改,直接拿过来用;引擎和框架方面,上次的代码并没有最后完工,但是仍然有相当多的部分可以重用;只有数据结构方面,因为改动很大,需要全部重写。就代码量而言,现在保守估计应该有30%-40%的水平。
大家会问为什么跳过了2.0版本。对这个问题我会在软件发布的时候仔细解释,跳过2.0是有充分的理由的--或者把前面这个进度80%左右的版本称为2.0。3.0在逻辑上有质的飞跃。
关于输入法,目前应该说有两大流派:一个是规则派,大量使用技巧和规则,绝大多数基于词组、简拼、联想的中文输入法都是这一派的,这个路已经随着计算机中文应用进化了十几年,非常成熟,紫光拼音、拼音加加和新秀搜狗拼音都是典范;剩下的能做的改进已经很有限,无非是在词组和词频上做文章,以及增加一些特殊输入方式。当然这些输入法都有一些闪光点,比如词频调整和自动造词,确实方便。但是其发展空间的限制也非常明显,词组和词频的问题其实是非常个性化的,而特殊输入方式需要学习,我相信拼音加加的用户中90%以上都不会用到它的很多快捷输入功能。
另一类就是智能派(整句输入法),以微软拼音为代表,国内还曾经有个黑马输入法,以及口碑不错的智能狂拼。这一类输入法的理论依据很厉害,是贝叶斯模型。但是这个模型是否能适用输入法,是个疑问,我发现在现有的贝叶斯模型整句输入法中,或多或少都有些错误假设。这或者影响性能,或者不符合用户的心理习惯。
巨硬3.0不同于以上两类输入法,当然它不是说抛弃了上述输入法的优点,而是继承了传统规则派输入法的绝大多数优点,包括词频调整、自造词、简拼等等。但是在逻辑上,它大胆的采用了Jeff在On Intelligence中首次提出的Memory Prediction模型。该模型对用户来说的好处在于大大降低无效重码,在数字键盘设备上尤其明显。形象的说,MP模型就是词组的词组,再加上词组的联想,同时不要求用户的输入方式,比如连续输入几次蒲巴甲,即使是一个字一个字输入的,系统也会把他们组合成词。至于怎么从用户输入的序列中抽取出来组合的开始和结束,正是MP模型的闪光之处。实际上这个想法在2.0版本中就想实现了,但是直到最近灵光频现,才想明白了其中的一些关键问题,2.0中的一些思路是错误的,于是推倒重来。和商业软件相比,个人软件的魅力也许就在与此,It's ready when it's ready,你不需要把半生不熟的东西端上客户的餐桌。至于这个模型和现有模型能有多少进步。我的预测大概是这样的,如果是PC输入法,因为touch typing的速度是接近甚至超过人的口语速度的,用不用MP模型都没所谓;在thumb qwerty kb上,现有的巨硬和梅花都没有联想,如果采用MP能直接补足这个问题,如果是双手操作,速度应该能有50%左右的提升,如果是单手,还要多些,但是不会达到100%;在numpad kb上,传说中的Nokia神奇T9在我看来根本不怎么样,MP模型绝对可以让你shit in your pants。它应该可以让num kb超越现有qwerty巨硬的速度。
最后说说进度的预期,老实说,PALMOS是开发输入法最恶心的,没有好的系统框架,稳定性大成问题,debug也很困难,但是它在存储设计和运行效率上最好,而且我也最熟悉PalmOS开发,所以仍然从这里开始;最近也在看Windows Mobile和Symbian的开发,Windows Mobile开发也不难,有很好的输入法框架,就是效率上不知道怎么样,但是WM PPC设备的大内存是不错的,WM Smartphone就难受一些,不过总的来说差异不大;Symbian的系统在编程习惯上差异比较大,好处是FEP框架和C++类都很好,惟一的缺点是我对它的Stream类效率担忧,巨硬III的动态读取数据在M量级,频繁的读写操作系统能否高速完成是个未知数,如果要自己设计cache系统来解决这个问题的话,就太悲凉了。不过我现在就在使用E60,所以无论困难多大,我都会完成的。PalmOS的版本应该在春节前有Alpha,但是我不打算立刻发布;PalmOS版本完成之后会马上进入Symbian开发,我希望是在明年春暖花开的时候(大约4月吧)能同时发布PalmOS的QWERTY版、 Symbian的QWERTY(E61/E62)和NUMPAD(S60 3rd Edition)版。正式发布之后就进入Windows Mobile系统的移植,这个应该很快,再多花2个月吧。
过去的两周我已经仔细写完了巨硬III的逻辑文档,大部分问题都考虑完毕,这周开始进入代码阶段。上一次写代码大约是今年6-7月份,把这些代码翻出来看了看,庆幸当时的结构化设计,很多代码都可以重用,包括数组类、字串类、文件操作类;数据封装方面,原来的PalmOS数据库和记录操作代码可以完全不用修改,直接拿过来用;引擎和框架方面,上次的代码并没有最后完工,但是仍然有相当多的部分可以重用;只有数据结构方面,因为改动很大,需要全部重写。就代码量而言,现在保守估计应该有30%-40%的水平。
大家会问为什么跳过了2.0版本。对这个问题我会在软件发布的时候仔细解释,跳过2.0是有充分的理由的--或者把前面这个进度80%左右的版本称为2.0。3.0在逻辑上有质的飞跃。
关于输入法,目前应该说有两大流派:一个是规则派,大量使用技巧和规则,绝大多数基于词组、简拼、联想的中文输入法都是这一派的,这个路已经随着计算机中文应用进化了十几年,非常成熟,紫光拼音、拼音加加和新秀搜狗拼音都是典范;剩下的能做的改进已经很有限,无非是在词组和词频上做文章,以及增加一些特殊输入方式。当然这些输入法都有一些闪光点,比如词频调整和自动造词,确实方便。但是其发展空间的限制也非常明显,词组和词频的问题其实是非常个性化的,而特殊输入方式需要学习,我相信拼音加加的用户中90%以上都不会用到它的很多快捷输入功能。
另一类就是智能派(整句输入法),以微软拼音为代表,国内还曾经有个黑马输入法,以及口碑不错的智能狂拼。这一类输入法的理论依据很厉害,是贝叶斯模型。但是这个模型是否能适用输入法,是个疑问,我发现在现有的贝叶斯模型整句输入法中,或多或少都有些错误假设。这或者影响性能,或者不符合用户的心理习惯。
巨硬3.0不同于以上两类输入法,当然它不是说抛弃了上述输入法的优点,而是继承了传统规则派输入法的绝大多数优点,包括词频调整、自造词、简拼等等。但是在逻辑上,它大胆的采用了Jeff在On Intelligence中首次提出的Memory Prediction模型。该模型对用户来说的好处在于大大降低无效重码,在数字键盘设备上尤其明显。形象的说,MP模型就是词组的词组,再加上词组的联想,同时不要求用户的输入方式,比如连续输入几次蒲巴甲,即使是一个字一个字输入的,系统也会把他们组合成词。至于怎么从用户输入的序列中抽取出来组合的开始和结束,正是MP模型的闪光之处。实际上这个想法在2.0版本中就想实现了,但是直到最近灵光频现,才想明白了其中的一些关键问题,2.0中的一些思路是错误的,于是推倒重来。和商业软件相比,个人软件的魅力也许就在与此,It's ready when it's ready,你不需要把半生不熟的东西端上客户的餐桌。至于这个模型和现有模型能有多少进步。我的预测大概是这样的,如果是PC输入法,因为touch typing的速度是接近甚至超过人的口语速度的,用不用MP模型都没所谓;在thumb qwerty kb上,现有的巨硬和梅花都没有联想,如果采用MP能直接补足这个问题,如果是双手操作,速度应该能有50%左右的提升,如果是单手,还要多些,但是不会达到100%;在numpad kb上,传说中的Nokia神奇T9在我看来根本不怎么样,MP模型绝对可以让你shit in your pants。它应该可以让num kb超越现有qwerty巨硬的速度。
最后说说进度的预期,老实说,PALMOS是开发输入法最恶心的,没有好的系统框架,稳定性大成问题,debug也很困难,但是它在存储设计和运行效率上最好,而且我也最熟悉PalmOS开发,所以仍然从这里开始;最近也在看Windows Mobile和Symbian的开发,Windows Mobile开发也不难,有很好的输入法框架,就是效率上不知道怎么样,但是WM PPC设备的大内存是不错的,WM Smartphone就难受一些,不过总的来说差异不大;Symbian的系统在编程习惯上差异比较大,好处是FEP框架和C++类都很好,惟一的缺点是我对它的Stream类效率担忧,巨硬III的动态读取数据在M量级,频繁的读写操作系统能否高速完成是个未知数,如果要自己设计cache系统来解决这个问题的话,就太悲凉了。不过我现在就在使用E60,所以无论困难多大,我都会完成的。PalmOS的版本应该在春节前有Alpha,但是我不打算立刻发布;PalmOS版本完成之后会马上进入Symbian开发,我希望是在明年春暖花开的时候(大约4月吧)能同时发布PalmOS的QWERTY版、 Symbian的QWERTY(E61/E62)和NUMPAD(S60 3rd Edition)版。正式发布之后就进入Windows Mobile系统的移植,这个应该很快,再多花2个月吧。
2006/11/14
SEQUENCE & FORGETTING
说起来有趣,一个以智能为目标的输入法设计,其核心的数据结构要考虑一个遗忘的问题。
巨硬一中没有特别设计遗忘算法,不过如果要实现的话并不复杂;因为巨硬一中没有设计记忆序列,所以所有词组可以放在26个序列中,至于为什么以S开头的字词和以K开头的字词都要遵守同样的空间限制,并不是一个非要解决的问题,即使K开头的字词浪费了存储空间,用户并不会有什么实际的使用障碍,浪费的那些存储空间也无关紧要。
但是巨硬二不同了,对于记忆序列的存储需要解决这个问题,不然浪费的空间就不是几百K的问题,而可能是几百M的问题。所以巨硬一的那种简单算法不再够用了。
解决这个问题有两个思路,一个是在记忆序列的时候记录使用次数,另一个是对所有记忆序列整体排序。前面一种方式比较可行。需要注意的地方是(1)排序的依据并不是使用次数,(2)该值的目的是遗忘,所以能记录有限的几次就可以了。基本上所有曾经被多次使用的记录都不应该被遗忘。
巨硬一中没有特别设计遗忘算法,不过如果要实现的话并不复杂;因为巨硬一中没有设计记忆序列,所以所有词组可以放在26个序列中,至于为什么以S开头的字词和以K开头的字词都要遵守同样的空间限制,并不是一个非要解决的问题,即使K开头的字词浪费了存储空间,用户并不会有什么实际的使用障碍,浪费的那些存储空间也无关紧要。
但是巨硬二不同了,对于记忆序列的存储需要解决这个问题,不然浪费的空间就不是几百K的问题,而可能是几百M的问题。所以巨硬一的那种简单算法不再够用了。
解决这个问题有两个思路,一个是在记忆序列的时候记录使用次数,另一个是对所有记忆序列整体排序。前面一种方式比较可行。需要注意的地方是(1)排序的依据并不是使用次数,(2)该值的目的是遗忘,所以能记录有限的几次就可以了。基本上所有曾经被多次使用的记录都不应该被遗忘。
2006/06/21
CMS, AMP Stack, Windows Service
开始为新网站做准备,域名已经想好了,只有cn的没被注册了,暂时保密。
CMS系统的选择有点头疼,我一直不喜欢Nuke系列,包括phpNuke, postNuke和Xoops;以前一直用Drupal,但是Drupal的论坛很弱,虽然有个vB Drupal,但是这样以后的升级成了问题;Xaraya没用过,ezpublish有些杀鸡用牛刀,最终决定先尝试Joomla!,这是大名鼎鼎的Mambo的一个分支,只是我没搞明白为什么会有这么一个分支出来。
Drupal的Collaborative Book功能会让我怀念的;但是Mambo/Joomla的简洁和它的社区支持更让人放心。
Apache/Mysql/Php的安装一直是个不大不小的麻烦,每次安装都是看文档,还好没遇到过问题;这次偷懒找了一些做好的东东,有三个选择,XAMPP,EasyPHP和apache2triad,最后一个不知道为什么下载有问题,sourceforge上所有的镜像都是下载到30%就挂了。于是下了一个XAMPP,也许这个更适合我,除了AMP之外,只装了PHPMYADMIN,够用了。
上次本机安装Ezpublish,反安装之后在Windows Service里留了两个空的apache和mysql服务,开动GooGle找了一下,发现手工删除服务也很简单.
停止服务是:
net stop
删除服务是
sc delete
CMS系统的选择有点头疼,我一直不喜欢Nuke系列,包括phpNuke, postNuke和Xoops;以前一直用Drupal,但是Drupal的论坛很弱,虽然有个vB Drupal,但是这样以后的升级成了问题;Xaraya没用过,ezpublish有些杀鸡用牛刀,最终决定先尝试Joomla!,这是大名鼎鼎的Mambo的一个分支,只是我没搞明白为什么会有这么一个分支出来。
Drupal的Collaborative Book功能会让我怀念的;但是Mambo/Joomla的简洁和它的社区支持更让人放心。
Apache/Mysql/Php的安装一直是个不大不小的麻烦,每次安装都是看文档,还好没遇到过问题;这次偷懒找了一些做好的东东,有三个选择,XAMPP,EasyPHP和apache2triad,最后一个不知道为什么下载有问题,sourceforge上所有的镜像都是下载到30%就挂了。于是下了一个XAMPP,也许这个更适合我,除了AMP之外,只装了PHPMYADMIN,够用了。
上次本机安装Ezpublish,反安装之后在Windows Service里留了两个空的apache和mysql服务,开动GooGle找了一下,发现手工删除服务也很简单.
停止服务是:
net stop
删除服务是
sc delete
2006/06/09
巨硬,J2ME,Symbian...
今天Blogger竟然挂了,只好打开Writely写Blog.
巨硬还在胶着状态,改来改去,当然越改越简单。事实上,代码很早就接近30K了,然后就不停的合并修改删除,现在还保持在28K的标准,估计最终也不会超过35K。
前 面提到过一个键盘模型的问题,在巨硬的构架中,键盘模型仅仅影响零级序列,即传统意义上的词库。为了提高搜索速度,不会把所有词都存在一张表中,通常都会 按照首字母分开以提高检索速度。如果要兼顾数字键盘和qwerty键盘,最简单的办法就是按照手机键盘的分组方法把首字母相同的词合并在一个表中,也是传 统的处理声母模糊音的办法,粗略估算了一下,这个办法应该可行,副作用主要是降低了词库的最大容量,不过巨硬二本来就不是为大词库设计的,这样做也算是一 个不错的办法。方便用户迁移数据库。
这段时间还抽空看了看Symbian系统和J2ME系统的开发。Symbian是很彻底的C++,在 流存储上考虑的极为周密,而且也把C++的继承特性,或者说代码重用特性,发挥到了极致。Java实际上是我学编程的入门语言,比C++更简洁优雅,只是 效率差了些。J2ME中设计了一个Record Management System,我不知道PC程序员或者数据库开发人员会怎么看这个玩意,但是作为PalmOS开发者,我特别喜欢它。它的实现几乎和PalmOS数据库如 出一辙,在简单的数据库驱动的应用中,这种存储又灵活又高效;在Symbian系统中有个嵌入式存储,也是这类的“单表”“非严格字段”数据库。之所以关 心这个问题,是因为巨硬二仍然没有改掉巨硬一的记录数庞大的问题,不过这次不是基础词库庞大,而且词序库庞大,如果系统级不支持这种数据库存储的话,就得 自己些个小型的文件数据库了,虽然不是不可行,但是效率就差多了,而且光是表头就要Cache掉不少的内存,因为这个原因,巨硬二应该不会有 Windows Mobile版本了,Windows Mobile提供的数据库太肿了,又浪费空间速度又慢,只适合做标准的数据库应用程序。
上 周把娘子的Moto E398刷了E1,为的是有iTunes。感觉非常不错--我是iTunes/iPod的狂热拥护者。但是Moto的输入法烂到极点了。因此萌生了一个在 Moto手机上开发输入法的想法,于是引出了J2ME的话题,再然后想到可以开发一个把中文输入和短信结合在一起的程序,这个东西市场应该不错。巨硬+ Threaded模式+狂多的J2ME手机,蛮有趣的吧。其实我还有很多关于手机和internet结合的应用构思,先用一个程序打出一片用户群体,然后 就可以逐步实施这些想法了。能跨出Treo用户群体,对巨硬来说也是很重要的一步吧。巨硬II的PalmOS版完工之后应该很快就会投入J2ME版本的开 发,至于Symbian版,要看移植的工作量了,但是我现在还没想好是先把Symbian的做了再说,还是先写J2ME的。
Evan一直 在鼓动我把输入法卖给手机厂商,不过我的想法是越来越相反。输入法就像GPS导航地图一样,只是一个技术,不是一套完善的应用;基于这个东西积累用户群 体,然后融入周边应用,譬如短信,IM,Email等等,才是输入法的出路。我喜欢能够接触终端用户并且持续挖掘用户需求的商业模式,而不是专注于技术产 品,当然,我不是说技术不重要,但是从商业角度或者从发展的角度来说,技术应该最大限度的转化为应用。
巨硬还在胶着状态,改来改去,当然越改越简单。事实上,代码很早就接近30K了,然后就不停的合并修改删除,现在还保持在28K的标准,估计最终也不会超过35K。
前 面提到过一个键盘模型的问题,在巨硬的构架中,键盘模型仅仅影响零级序列,即传统意义上的词库。为了提高搜索速度,不会把所有词都存在一张表中,通常都会 按照首字母分开以提高检索速度。如果要兼顾数字键盘和qwerty键盘,最简单的办法就是按照手机键盘的分组方法把首字母相同的词合并在一个表中,也是传 统的处理声母模糊音的办法,粗略估算了一下,这个办法应该可行,副作用主要是降低了词库的最大容量,不过巨硬二本来就不是为大词库设计的,这样做也算是一 个不错的办法。方便用户迁移数据库。
这段时间还抽空看了看Symbian系统和J2ME系统的开发。Symbian是很彻底的C++,在 流存储上考虑的极为周密,而且也把C++的继承特性,或者说代码重用特性,发挥到了极致。Java实际上是我学编程的入门语言,比C++更简洁优雅,只是 效率差了些。J2ME中设计了一个Record Management System,我不知道PC程序员或者数据库开发人员会怎么看这个玩意,但是作为PalmOS开发者,我特别喜欢它。它的实现几乎和PalmOS数据库如 出一辙,在简单的数据库驱动的应用中,这种存储又灵活又高效;在Symbian系统中有个嵌入式存储,也是这类的“单表”“非严格字段”数据库。之所以关 心这个问题,是因为巨硬二仍然没有改掉巨硬一的记录数庞大的问题,不过这次不是基础词库庞大,而且词序库庞大,如果系统级不支持这种数据库存储的话,就得 自己些个小型的文件数据库了,虽然不是不可行,但是效率就差多了,而且光是表头就要Cache掉不少的内存,因为这个原因,巨硬二应该不会有 Windows Mobile版本了,Windows Mobile提供的数据库太肿了,又浪费空间速度又慢,只适合做标准的数据库应用程序。
上 周把娘子的Moto E398刷了E1,为的是有iTunes。感觉非常不错--我是iTunes/iPod的狂热拥护者。但是Moto的输入法烂到极点了。因此萌生了一个在 Moto手机上开发输入法的想法,于是引出了J2ME的话题,再然后想到可以开发一个把中文输入和短信结合在一起的程序,这个东西市场应该不错。巨硬+ Threaded模式+狂多的J2ME手机,蛮有趣的吧。其实我还有很多关于手机和internet结合的应用构思,先用一个程序打出一片用户群体,然后 就可以逐步实施这些想法了。能跨出Treo用户群体,对巨硬来说也是很重要的一步吧。巨硬II的PalmOS版完工之后应该很快就会投入J2ME版本的开 发,至于Symbian版,要看移植的工作量了,但是我现在还没想好是先把Symbian的做了再说,还是先写J2ME的。
Evan一直 在鼓动我把输入法卖给手机厂商,不过我的想法是越来越相反。输入法就像GPS导航地图一样,只是一个技术,不是一套完善的应用;基于这个东西积累用户群 体,然后融入周边应用,譬如短信,IM,Email等等,才是输入法的出路。我喜欢能够接触终端用户并且持续挖掘用户需求的商业模式,而不是专注于技术产 品,当然,我不是说技术不重要,但是从商业角度或者从发展的角度来说,技术应该最大限度的转化为应用。
2006/06/06
巨硬II进展
巨硬II的进展依旧缓慢。
周末偷懒休息了两天,看prison break到天昏地暗,没写代码,罪过罪过。
今天挤出了点儿时间修改了Filter类的设计,原来的Filter类是Attach到数据库的一张表上,其结果是在查询过程中不能改动表,在目前的假设中这不构成问题,但是以后就不好说了。当前的假设是在用户部分选定了候选词之后,预测词库自动失效,检索被设定为精确匹配状态,但是我发现在实际的输入过程中,有时会有把一些本不该划为一个词组的内容一起输入的情况,在这种情况下,能启用预测词库对减少选字还是有帮助的。
另外一个小小的改进是把多级预测词库并为一个数据库,其好处是,在遗忘的时候可以体现出不同级别的预测序列之间的频率比较;否则分开存储的话,那些本该属于短期记忆的序列会大量挤占存储空间,不上算。虽然还没有实际输入过,我估计1,2级预测序列肯定是最多的,3级应该比较少才对。
总的来说,目前已经完成了大部分框架类,包括所有的数据库存储,文件存储,字库词库Alpha库,统一的dataStore界面,0级和n级序列库,查询填充队列,过滤器,以及composition/candidate/nominee/history工具类,剩下的主要类就只有popup类和window类,对巨硬来说,这都是比较简单的UI和事件处理了,就是要花些时间上去。理论上说,这些类都完成之后就可以工作了,但实际上还有不短的路要走,因为巨硬II的算法和传统输入法改动很大,需要准备一个非常好的初始化序列数据库,才能比较理想的工作。另外我还在犹豫是不是在第一个版本中就加入支持键盘对象和被动匹配的代码,这样做的好处是在training过程中可以直接得到一个词频排序良好的0级词库。ANYWAY,离进入调试阶段不会很远了。
周末偷懒休息了两天,看prison break到天昏地暗,没写代码,罪过罪过。
今天挤出了点儿时间修改了Filter类的设计,原来的Filter类是Attach到数据库的一张表上,其结果是在查询过程中不能改动表,在目前的假设中这不构成问题,但是以后就不好说了。当前的假设是在用户部分选定了候选词之后,预测词库自动失效,检索被设定为精确匹配状态,但是我发现在实际的输入过程中,有时会有把一些本不该划为一个词组的内容一起输入的情况,在这种情况下,能启用预测词库对减少选字还是有帮助的。
另外一个小小的改进是把多级预测词库并为一个数据库,其好处是,在遗忘的时候可以体现出不同级别的预测序列之间的频率比较;否则分开存储的话,那些本该属于短期记忆的序列会大量挤占存储空间,不上算。虽然还没有实际输入过,我估计1,2级预测序列肯定是最多的,3级应该比较少才对。
总的来说,目前已经完成了大部分框架类,包括所有的数据库存储,文件存储,字库词库Alpha库,统一的dataStore界面,0级和n级序列库,查询填充队列,过滤器,以及composition/candidate/nominee/history工具类,剩下的主要类就只有popup类和window类,对巨硬来说,这都是比较简单的UI和事件处理了,就是要花些时间上去。理论上说,这些类都完成之后就可以工作了,但实际上还有不短的路要走,因为巨硬II的算法和传统输入法改动很大,需要准备一个非常好的初始化序列数据库,才能比较理想的工作。另外我还在犹豫是不是在第一个版本中就加入支持键盘对象和被动匹配的代码,这样做的好处是在training过程中可以直接得到一个词频排序良好的0级词库。ANYWAY,离进入调试阶段不会很远了。
2006/06/02
Filter,友元函数,MindManager
今天没有任何功能性的进展,仅仅解决了一个很小但是异常复杂的问题。
在一次搜索中会遇到多种情况,包括联想状态(用户尚未输入任何字母),首次选字,以及已经选字;巨硬允许用户选择是否混合匹配中英文(比如输入br的时候匹配brighthand.com);允许用户选择使用和不使用预测词库;是否允许longer匹配(输入s的时候匹配“上海”,这是词频高于字数的匹配方式),以及自动切换longer设置(比如在用户已经输入超过一个音节的情况下自动关闭longer匹配);是否允许尾码渐进匹配(比如输入了kun ji的时候匹配“困窘”);还要照顾到自动分隔匹配(几乎所有的拼音输入法都这样匹配)和被动匹配(适用于数字键盘或者有按键合并的half qwerty);在输入的不同阶段,这些匹配设置可能会动态打开或关闭,还会遇到一些冲突情况(比如用户已经选了一个字,然后继续输入导致编码不再符合拼音规则)。
在MingManager的帮助下,这个问题最终通过一个简单模型实现了。我设计了一个Filter类,该类是可以attach到数据库上去的,从Filter类派生了很多行为不同的类,比如只匹配英文词汇,只匹配单字,词频优先或者字数优先等等,用于匹配的不同阶段和不同环境,这样就不用把所有的配置和环境变量放到一个由if语句堆积而成的函数中折腾,使得代码更容易实现,更容易理解,同时大大减少犯错误的机会,需要改动的时候也很方便。
设计Filter类的另外一个原因是为了提高检索速度,巨硬II使用了保留搜索断点的方式来获得候选字词,而不是一次性完成所有搜索填入buffer,事实上按照巨硬的数据结构设计,一次性填充Buffer几乎是不可行的,用户输入单一字符时,如果打开longer匹配和中英文混合匹配的选项,符合的选择可能多达上万个。我试了一下拼音加加,它不允许longer匹配也没有英文匹配,输入y的时候候选字有729x3个,而且要考虑到这是PC上的结果,手持设备上一次遍历匹配2000多个字很吃不消了。
这个断点状态的保留也是个很麻烦的问题,所以Filter类不仅仅是处理各种不同的匹配设置,它还负责维护搜索状态,最早的代码把这个工作交给了数据库自己来完成,但是后来发现很难处理那么多种情况,还是分到不同的Filter派生类中处理更清晰。最终在MindManager中得到了一个四级的二分树来表示不同的输入过程,再混合4个排列组合的设置(longer, longer auto off, progressive, alpha match),这回让我庆幸自己是用C++来写巨硬II的,可以利用virtual特性为所有的Filter类提供一个统一的Read函数供填充队列使用;如果是C,我真不知道怎么处理才好了。
以前一直鄙视C++的友元函数设计。但是今天派上了用场。
通常在程序中,有很多类提供了类似API一样的基础功能,而另外一些类封装了几乎所有核心数据结构,一些核心算法要同时访问这些东西。就比如前面说到的Filter类,它要访问字词库符号库以获取字词的音标并判断是否匹配,要访问输入过程的当前数据,包括Composition(用户输入),Candidates(候选词),和Nominee(用户已经选择的字词,但是还没有完成输入过程),还要访问当前的队列填充状况(比如这样的设置,在用户翻页两次之后关闭所有词频优先选项,改为精确匹配优先),在这种情况下有两个办法来处理。一种是C++中的Composition方式,即类套着类层层封装,以前我很喜欢这个方式,它使程序看起来很优雅,结构清晰,尤其是写UI程序时,把数据和UI层层绑定,是非常方便的做法;但是这种封装会牺牲灵活性和效率,就像MFC那样,而且对于错误处理也很麻烦(很不幸我没能在巨硬中使用C++的异常处理,这个Metrowerks提供的特性让我担心在弹出程序中会带来一些难以预料的问题,我还没能力在Debug窗口中看那些汇编代码并从中找出错误)。
除了层层封装之外的一个做法是,用一个容器类把这些东西统统丢进去,然后把所有需要用到这些数据或API的类都声明成友元类,这样可以大大减少参数传递和数据成员,让类简化很多,函数参数少了,代码也好看很多(不然要传递一长串参数进去);而且更重要的是,我觉得这样做在开发阶段是方便的,很多想法往往在调试时发现效果不佳,要采用另外的逻辑,如果是这样设计的类结构,那改起来也很方便,真的功能定型了之后,在refactoring阶段再考虑如何更优雅的封装也来得及。
另外这几周来,我对MindManager是越来越崇拜。以前看过一些Mind Map方法的介绍,不以为然。不就是拿张白纸从中心向四周画圈圈吗?我从来没想过用这类工具作为程序开发的辅助工具。
我想和我遇到一样问题的个人开发者应该有很多,为了解决程序结构和代码控制问题,我学了也试了很多工具,包括Case Tool,UML工具,大量撰写注释,大量撰写文档(包括需求和设计)。但是最终没有适合的。
从这次借助MindManger开发,我明白了这里面的问题所在。Case Tool,UML工具,注释,文档这些东西,都是维护需求、设计或者代码用的;个人开发者和那种大型商业软件开发的很大区别在于,前者并没有特别明确的用户需求,往往是靠算法、数据结构或特殊功能设计来取胜,软件开发过程的实验性很强,这时候最需要的并不是很高级的维护代码的工具,而是有效的分析和辅助思考的工具,MindManger正是这样的东西。在功能最终趋于大而全的时候,那些需求和代码维护工具才派得上用场。
在一次搜索中会遇到多种情况,包括联想状态(用户尚未输入任何字母),首次选字,以及已经选字;巨硬允许用户选择是否混合匹配中英文(比如输入br的时候匹配brighthand.com);允许用户选择使用和不使用预测词库;是否允许longer匹配(输入s的时候匹配“上海”,这是词频高于字数的匹配方式),以及自动切换longer设置(比如在用户已经输入超过一个音节的情况下自动关闭longer匹配);是否允许尾码渐进匹配(比如输入了kun ji的时候匹配“困窘”);还要照顾到自动分隔匹配(几乎所有的拼音输入法都这样匹配)和被动匹配(适用于数字键盘或者有按键合并的half qwerty);在输入的不同阶段,这些匹配设置可能会动态打开或关闭,还会遇到一些冲突情况(比如用户已经选了一个字,然后继续输入导致编码不再符合拼音规则)。
在MingManager的帮助下,这个问题最终通过一个简单模型实现了。我设计了一个Filter类,该类是可以attach到数据库上去的,从Filter类派生了很多行为不同的类,比如只匹配英文词汇,只匹配单字,词频优先或者字数优先等等,用于匹配的不同阶段和不同环境,这样就不用把所有的配置和环境变量放到一个由if语句堆积而成的函数中折腾,使得代码更容易实现,更容易理解,同时大大减少犯错误的机会,需要改动的时候也很方便。
设计Filter类的另外一个原因是为了提高检索速度,巨硬II使用了保留搜索断点的方式来获得候选字词,而不是一次性完成所有搜索填入buffer,事实上按照巨硬的数据结构设计,一次性填充Buffer几乎是不可行的,用户输入单一字符时,如果打开longer匹配和中英文混合匹配的选项,符合的选择可能多达上万个。我试了一下拼音加加,它不允许longer匹配也没有英文匹配,输入y的时候候选字有729x3个,而且要考虑到这是PC上的结果,手持设备上一次遍历匹配2000多个字很吃不消了。
这个断点状态的保留也是个很麻烦的问题,所以Filter类不仅仅是处理各种不同的匹配设置,它还负责维护搜索状态,最早的代码把这个工作交给了数据库自己来完成,但是后来发现很难处理那么多种情况,还是分到不同的Filter派生类中处理更清晰。最终在MindManager中得到了一个四级的二分树来表示不同的输入过程,再混合4个排列组合的设置(longer, longer auto off, progressive, alpha match),这回让我庆幸自己是用C++来写巨硬II的,可以利用virtual特性为所有的Filter类提供一个统一的Read函数供填充队列使用;如果是C,我真不知道怎么处理才好了。
以前一直鄙视C++的友元函数设计。但是今天派上了用场。
通常在程序中,有很多类提供了类似API一样的基础功能,而另外一些类封装了几乎所有核心数据结构,一些核心算法要同时访问这些东西。就比如前面说到的Filter类,它要访问字词库符号库以获取字词的音标并判断是否匹配,要访问输入过程的当前数据,包括Composition(用户输入),Candidates(候选词),和Nominee(用户已经选择的字词,但是还没有完成输入过程),还要访问当前的队列填充状况(比如这样的设置,在用户翻页两次之后关闭所有词频优先选项,改为精确匹配优先),在这种情况下有两个办法来处理。一种是C++中的Composition方式,即类套着类层层封装,以前我很喜欢这个方式,它使程序看起来很优雅,结构清晰,尤其是写UI程序时,把数据和UI层层绑定,是非常方便的做法;但是这种封装会牺牲灵活性和效率,就像MFC那样,而且对于错误处理也很麻烦(很不幸我没能在巨硬中使用C++的异常处理,这个Metrowerks提供的特性让我担心在弹出程序中会带来一些难以预料的问题,我还没能力在Debug窗口中看那些汇编代码并从中找出错误)。
除了层层封装之外的一个做法是,用一个容器类把这些东西统统丢进去,然后把所有需要用到这些数据或API的类都声明成友元类,这样可以大大减少参数传递和数据成员,让类简化很多,函数参数少了,代码也好看很多(不然要传递一长串参数进去);而且更重要的是,我觉得这样做在开发阶段是方便的,很多想法往往在调试时发现效果不佳,要采用另外的逻辑,如果是这样设计的类结构,那改起来也很方便,真的功能定型了之后,在refactoring阶段再考虑如何更优雅的封装也来得及。
另外这几周来,我对MindManager是越来越崇拜。以前看过一些Mind Map方法的介绍,不以为然。不就是拿张白纸从中心向四周画圈圈吗?我从来没想过用这类工具作为程序开发的辅助工具。
我想和我遇到一样问题的个人开发者应该有很多,为了解决程序结构和代码控制问题,我学了也试了很多工具,包括Case Tool,UML工具,大量撰写注释,大量撰写文档(包括需求和设计)。但是最终没有适合的。
从这次借助MindManger开发,我明白了这里面的问题所在。Case Tool,UML工具,注释,文档这些东西,都是维护需求、设计或者代码用的;个人开发者和那种大型商业软件开发的很大区别在于,前者并没有特别明确的用户需求,往往是靠算法、数据结构或特殊功能设计来取胜,软件开发过程的实验性很强,这时候最需要的并不是很高级的维护代码的工具,而是有效的分析和辅助思考的工具,MindManger正是这样的东西。在功能最终趋于大而全的时候,那些需求和代码维护工具才派得上用场。
2006/05/31
键盘抽象与机器人
今天在考虑键盘抽象和机器人问题。
为设备按键映射和键盘种类抽象并不难。键盘只有两种,数字键盘和QWERTY键盘,前者内嵌字母;后者内嵌数字。QWERTY的变种键盘,BB7100那种,就是一个减少了字母表的键盘。只有在全QWERTY的时候,能采用自动分隔composition的办法来处理,即使有模糊音的时候也是如此,因为前置模糊音并不会破坏分隔原则;但是其它的情况都只能被动匹配,即把字词的编码翻译成键盘码(字母或数字)然后匹配composition,这能处理字数>=或=问题(Longer),但是无法快速处理尾码渐进匹配(progressive),需要额外的数据表才行,否则就要遍历拼音表,这需要再考虑一下,暂无很好的解决方案。
现有的代码把匹配分到了不同的对象中完成,譬如字数过滤和Alpha过滤(允许匹配非拼音规则的英文、符号、网址、Email地址等等)在数据库搜索中解决。但是看起来这个代码得挪个地方了。因为在被动匹配的情况下,字数是在匹配之后才知道的。:(。这回到了我昨天写的C++封装心得:把类拆小,每个只实现单一功能。
按照你告诉别人的东西去做,你就成功了--米兰·昆德拉
不过在现阶段,似乎没有必要把这个问题搞的很复杂。基本上考虑两种键盘就可以了,一种是Qwerty,包括Treo, HP65/69,Nokia E61,Moto Q,BenQ,Dopod,BB等都是这种键盘,区别只是内嵌数字键的方法不同;另外就是手机上的Numpad,好在手机上的Numpad内嵌字母的方式都是一样的,数据库无需更改。至于BB7100的键盘,其实也是不错的设计,可惜支持的手机不多,但是随着SE M600系列的发布,这种情况也许会有改观,毕竟SE品牌有着大批拥趸而且UIQ是Symbian最好的分支,只是不知道内置的中文输入法会不会让中国用户接受。
机器人问题比较麻烦。汉字分隔和拼音标注不是在一个小小的巨硬程序中能实现的,事实上,前者是中文搜索引擎的核心技术之一,后者是汉语言计算机处理上的难点课题。好在我要求并不高,前者在网上找到了一个免费程序,而后者可以购买拼音大师。除此之外还需要对格式做一些简单的正则表达式替换,工作量不大,用Editplus就可以解决,或者用Turbo C++写个简单的命令行程序就可以了。不过,这基本上可以让巨硬II具有主动学习功能(根据用户的短信和Memo来学习词频)的想法破灭了。:(。或者退一步,只能根据已有词组调整词频,不能通过学习自动造词了。
机器人问题还涉及到英文,数字,和符号的自动处理。其实这也和巨硬II中的保留History问题类似,只是以前没仔细考虑过。数字用通配符是比较好的做法。英文可以考虑成自动造词。但是符号问题比较棘手,尤其是混合在英文数字之中的符号。大致来说,符号分为三类,一类是中止符,比如逗号句号,会清除History;另一类是含义符,比如$,可以当作英文字符处理,还有一类是强制分词符号,可以当作是词与词之间的分隔,包括引号双引号书名号等等。在最初的时候,我只会考虑处理ASCII中的所有可打印符号和GBK中定义的可打印符号,至于Unicode中浩如烟海的符号,以后再说吧。:(
为设备按键映射和键盘种类抽象并不难。键盘只有两种,数字键盘和QWERTY键盘,前者内嵌字母;后者内嵌数字。QWERTY的变种键盘,BB7100那种,就是一个减少了字母表的键盘。只有在全QWERTY的时候,能采用自动分隔composition的办法来处理,即使有模糊音的时候也是如此,因为前置模糊音并不会破坏分隔原则;但是其它的情况都只能被动匹配,即把字词的编码翻译成键盘码(字母或数字)然后匹配composition,这能处理字数>=或=问题(Longer),但是无法快速处理尾码渐进匹配(progressive),需要额外的数据表才行,否则就要遍历拼音表,这需要再考虑一下,暂无很好的解决方案。
现有的代码把匹配分到了不同的对象中完成,譬如字数过滤和Alpha过滤(允许匹配非拼音规则的英文、符号、网址、Email地址等等)在数据库搜索中解决。但是看起来这个代码得挪个地方了。因为在被动匹配的情况下,字数是在匹配之后才知道的。:(。这回到了我昨天写的C++封装心得:把类拆小,每个只实现单一功能。
按照你告诉别人的东西去做,你就成功了--米兰·昆德拉
不过在现阶段,似乎没有必要把这个问题搞的很复杂。基本上考虑两种键盘就可以了,一种是Qwerty,包括Treo, HP65/69,Nokia E61,Moto Q,BenQ,Dopod,BB等都是这种键盘,区别只是内嵌数字键的方法不同;另外就是手机上的Numpad,好在手机上的Numpad内嵌字母的方式都是一样的,数据库无需更改。至于BB7100的键盘,其实也是不错的设计,可惜支持的手机不多,但是随着SE M600系列的发布,这种情况也许会有改观,毕竟SE品牌有着大批拥趸而且UIQ是Symbian最好的分支,只是不知道内置的中文输入法会不会让中国用户接受。
机器人问题比较麻烦。汉字分隔和拼音标注不是在一个小小的巨硬程序中能实现的,事实上,前者是中文搜索引擎的核心技术之一,后者是汉语言计算机处理上的难点课题。好在我要求并不高,前者在网上找到了一个免费程序,而后者可以购买拼音大师。除此之外还需要对格式做一些简单的正则表达式替换,工作量不大,用Editplus就可以解决,或者用Turbo C++写个简单的命令行程序就可以了。不过,这基本上可以让巨硬II具有主动学习功能(根据用户的短信和Memo来学习词频)的想法破灭了。:(。或者退一步,只能根据已有词组调整词频,不能通过学习自动造词了。
机器人问题还涉及到英文,数字,和符号的自动处理。其实这也和巨硬II中的保留History问题类似,只是以前没仔细考虑过。数字用通配符是比较好的做法。英文可以考虑成自动造词。但是符号问题比较棘手,尤其是混合在英文数字之中的符号。大致来说,符号分为三类,一类是中止符,比如逗号句号,会清除History;另一类是含义符,比如$,可以当作英文字符处理,还有一类是强制分词符号,可以当作是词与词之间的分隔,包括引号双引号书名号等等。在最初的时候,我只会考虑处理ASCII中的所有可打印符号和GBK中定义的可打印符号,至于Unicode中浩如烟海的符号,以后再说吧。:(
2006/05/30
C++封装
巨硬的进度令人满意,不过在进入Debug之前,就进入了Refactoring阶段。Refactoring是软件工程的一个术语,我不知道怎么翻译,它的意思是在不改动需求和主体数据结构的情况下,对软件做优化,包括更清晰的组织代码,划分类功能,组织函数,重命名类和函数名等等。
Wikipedia的解释在这里:en.wikipedia.org/wiki/Refactoring (如果你能访问这篇Blog,那你也应该可以看到Wikipedia)。
之所以这样做是因为我并不着急launch这个程序,就像和newaa以及evan在网上聊天时所说到的,我本身并不把这个程序当成一个商品--确切的说是仅仅当成一个商品。巨硬带有很多实验主义的色彩,也倾注了我对手持计算设备的热爱,也是我不断学习软件开发乃至软件工程的一个课程,同时它还是WEB2.0时代的一个契机--虽然这只是一个输入法,但是它带来的更快捷的输入会改变人民对文字沟通的使用方式,而通过网络的文字沟通无疑是我们生活的这个时代发展最快的沟通手段之一。
一些软件工程高手常挂在嘴边的一句话是:软件工程从发布才开始。它指的是软件的功能维护,代码树维护,以及开发新的应用,拓展新的市场等等。至少到现在为止,我是打算接受这个挑战的,这也是我愿意重新打开CodeWarrior,重写巨硬II的最主要动因。从我的个性来说,我相当懒惰,而且不喜欢做重复的事情,如果只是用C重新写过巨硬并继续卖28元(或者一个美元)的话,我大概是不会做的。:)
Anyway,言归正传,如果你在看这个Blog的话,请不要催促我尽快发布巨硬II,也不要宣传巨硬II即将诞生。我会在适当的时候--当我觉得它很完美的时候,虽然事实上它不可能是完美的--发布它。这个时间不会很远,但是我不承诺一个严格的Deadline,这种压力只会使我在射门的时候动作变形,没什么其它的益处,我喜欢在最自由的空间里踢出最漂亮的弧线球--相信我,我会踢出来的。我能承诺的只是,我每天至少拿出3个小时在代码工作上,无论工作和生活多紧张,事实上在过去的2-3个星期我就是这样做的,我还会继续这样做下去,直到巨硬II的诞生。
好了,回到今天Blog的主题,说说我对C++封装的浅薄理解--你千万不要把它当作高手箴言,如果你想看高手箴言,我推荐你看豪杰解霸作者梁肇新撰写的《编程高手箴言》,或者10余年畅销不衰的Code Complete。我愿意写在这里,或者说写在这个几乎没人能看到的Blog里,就是颇有露怯之意,但是我还是愿意写下来,让明天的我看看昨天的我在想什么--这也是Blog的原本之意吧。
C++的结构非常灵活,你可以用很多种方式封装类,但是不幸的地方在于类是静态的,而实际的程序中要考虑对象的生存周期。所以第一个原则就是,生存周期不同的功能组,应该封装到不同的类里面去。
常常会遇到这样的情况,很多不同的功能都和一个资源有关,所以容易产生这样的想法,把所有和该资源有关的功能都统统放到这个类里面去,渐渐的,这个类就膨胀起来了,会出现很多的工作状态,这些状态之间彼此制约,结果不但是容易产生错误,而且对使用这个类的对象来说,要深刻理解这个类的对象的工作状态,这就违反了封装的本意--封装就是要只看到界面,而不必深入内部。
在一个类中留一个另一个类的指针并且在类初始化的时候赋值并不是什么坏主意,虽然这让人担心在指针实效的时候发生种种错误,但是在代码角度看,处理这些错误并不麻烦,而好处是,可以把对象的状态分开,容易理解。事实上那些被很多类调用的、用于封装排他资源的对象往往只有一个,在程序开始的时候初始化一下,然后传递给所有需要用到该资源的类,在这个类中可以很容易处理资源冲突情况,最后在程序退出前销毁;这是应付资源冲突的最好办法。资源冲突往往就像操作系统中的死锁问题一样,很多时候是不可避免的,用简单的失败处理代码来应付这种情况就可以了,这远比把事情搞的很复杂最终还是没有解决问题强。
C++中提供了虚函数和多态,这让代码看起来很优雅,但是虚函数往往需要实现该函数的类了解在基类中该函数被调用的具体过程,所以,不要写那些看起来很不易懂的虚函数,也不要让虚函数之间产生逻辑上的关联,不要为了让代码看起来更优雅一致或者更高深,而牺牲代码的易读性,易维护性,以及留下更多的犯错误的机会。通常在资源访问的问题上,虚函数是一个好主意,比如流,缓冲,或者数据库,它增加了代码的移植能力,因为大多数程序都必须和这些东西打交道。但是把程序自身的数据结构层层封装起来就不象是一个好主意,这需要使用者了解很多数据结构的含义和数据对象的状态--和状态打交道是程序设计中最复杂的事情,尽量不要处理组合状态,否则一定会死的很难看。
在参数的传递上,要避免传递复杂的数据结构,应该只传递三种基本的东西:(1)基本数据类型,含义和范围明确(2)简单的数据结构,它们封装了程序的核心数据,到处都用得到(3)对象指针,这些对象是作为一个占有资源的"服务器"存在的,很多时候要用到它们提供的服务,但是,不要试图了解对象的状态。这可能会破坏过程的原子性,在多线程或者多进程的环境下产生莫名其妙的错误。
总而言之,不要惧怕写很多的类,类越小越好;也不要惧怕类之间的依赖性,这是很常见的,而且它是由程序的核心逻辑决定的,不是说你少写了几个类就可以让问题变得简单;类之间的关系应该是松散集成的,彼此了解的越少越好。状态之间不要关联,不要在多个对象之中试图维护步调一致的数据。多画一些图来研究状态之间的关联性,直到你确定可以把每种状态都分离出来,再开始Coding,如果在Coding过程中发现了状态之间的耦合问题,那就应该回到设计上来解决这个问题,然后再开始Coding。
好了,该去写代码了。
Wikipedia的解释在这里:en.wikipedia.org/wiki/Refactoring (如果你能访问这篇Blog,那你也应该可以看到Wikipedia)。
之所以这样做是因为我并不着急launch这个程序,就像和newaa以及evan在网上聊天时所说到的,我本身并不把这个程序当成一个商品--确切的说是仅仅当成一个商品。巨硬带有很多实验主义的色彩,也倾注了我对手持计算设备的热爱,也是我不断学习软件开发乃至软件工程的一个课程,同时它还是WEB2.0时代的一个契机--虽然这只是一个输入法,但是它带来的更快捷的输入会改变人民对文字沟通的使用方式,而通过网络的文字沟通无疑是我们生活的这个时代发展最快的沟通手段之一。
一些软件工程高手常挂在嘴边的一句话是:软件工程从发布才开始。它指的是软件的功能维护,代码树维护,以及开发新的应用,拓展新的市场等等。至少到现在为止,我是打算接受这个挑战的,这也是我愿意重新打开CodeWarrior,重写巨硬II的最主要动因。从我的个性来说,我相当懒惰,而且不喜欢做重复的事情,如果只是用C重新写过巨硬并继续卖28元(或者一个美元)的话,我大概是不会做的。:)
Anyway,言归正传,如果你在看这个Blog的话,请不要催促我尽快发布巨硬II,也不要宣传巨硬II即将诞生。我会在适当的时候--当我觉得它很完美的时候,虽然事实上它不可能是完美的--发布它。这个时间不会很远,但是我不承诺一个严格的Deadline,这种压力只会使我在射门的时候动作变形,没什么其它的益处,我喜欢在最自由的空间里踢出最漂亮的弧线球--相信我,我会踢出来的。我能承诺的只是,我每天至少拿出3个小时在代码工作上,无论工作和生活多紧张,事实上在过去的2-3个星期我就是这样做的,我还会继续这样做下去,直到巨硬II的诞生。
好了,回到今天Blog的主题,说说我对C++封装的浅薄理解--你千万不要把它当作高手箴言,如果你想看高手箴言,我推荐你看豪杰解霸作者梁肇新撰写的《编程高手箴言》,或者10余年畅销不衰的Code Complete。我愿意写在这里,或者说写在这个几乎没人能看到的Blog里,就是颇有露怯之意,但是我还是愿意写下来,让明天的我看看昨天的我在想什么--这也是Blog的原本之意吧。
C++的结构非常灵活,你可以用很多种方式封装类,但是不幸的地方在于类是静态的,而实际的程序中要考虑对象的生存周期。所以第一个原则就是,生存周期不同的功能组,应该封装到不同的类里面去。
常常会遇到这样的情况,很多不同的功能都和一个资源有关,所以容易产生这样的想法,把所有和该资源有关的功能都统统放到这个类里面去,渐渐的,这个类就膨胀起来了,会出现很多的工作状态,这些状态之间彼此制约,结果不但是容易产生错误,而且对使用这个类的对象来说,要深刻理解这个类的对象的工作状态,这就违反了封装的本意--封装就是要只看到界面,而不必深入内部。
在一个类中留一个另一个类的指针并且在类初始化的时候赋值并不是什么坏主意,虽然这让人担心在指针实效的时候发生种种错误,但是在代码角度看,处理这些错误并不麻烦,而好处是,可以把对象的状态分开,容易理解。事实上那些被很多类调用的、用于封装排他资源的对象往往只有一个,在程序开始的时候初始化一下,然后传递给所有需要用到该资源的类,在这个类中可以很容易处理资源冲突情况,最后在程序退出前销毁;这是应付资源冲突的最好办法。资源冲突往往就像操作系统中的死锁问题一样,很多时候是不可避免的,用简单的失败处理代码来应付这种情况就可以了,这远比把事情搞的很复杂最终还是没有解决问题强。
C++中提供了虚函数和多态,这让代码看起来很优雅,但是虚函数往往需要实现该函数的类了解在基类中该函数被调用的具体过程,所以,不要写那些看起来很不易懂的虚函数,也不要让虚函数之间产生逻辑上的关联,不要为了让代码看起来更优雅一致或者更高深,而牺牲代码的易读性,易维护性,以及留下更多的犯错误的机会。通常在资源访问的问题上,虚函数是一个好主意,比如流,缓冲,或者数据库,它增加了代码的移植能力,因为大多数程序都必须和这些东西打交道。但是把程序自身的数据结构层层封装起来就不象是一个好主意,这需要使用者了解很多数据结构的含义和数据对象的状态--和状态打交道是程序设计中最复杂的事情,尽量不要处理组合状态,否则一定会死的很难看。
在参数的传递上,要避免传递复杂的数据结构,应该只传递三种基本的东西:(1)基本数据类型,含义和范围明确(2)简单的数据结构,它们封装了程序的核心数据,到处都用得到(3)对象指针,这些对象是作为一个占有资源的"服务器"存在的,很多时候要用到它们提供的服务,但是,不要试图了解对象的状态。这可能会破坏过程的原子性,在多线程或者多进程的环境下产生莫名其妙的错误。
总而言之,不要惧怕写很多的类,类越小越好;也不要惧怕类之间的依赖性,这是很常见的,而且它是由程序的核心逻辑决定的,不是说你少写了几个类就可以让问题变得简单;类之间的关系应该是松散集成的,彼此了解的越少越好。状态之间不要关联,不要在多个对象之中试图维护步调一致的数据。多画一些图来研究状态之间的关联性,直到你确定可以把每种状态都分离出来,再开始Coding,如果在Coding过程中发现了状态之间的耦合问题,那就应该回到设计上来解决这个问题,然后再开始Coding。
好了,该去写代码了。
2006/05/26
巨硬II,pDatabase & pRecord
又是一个不眠之夜。
巨硬的代码进度令人满意,虽然不快,但是在MM(MindManager)的帮助下清晰有序的进行着。先前写的一些基本类都派上了用场,对PalmOS Programming的理解也小有收获。
大致说来有这么几个问题:
1) PalmOS的存储方式非常特殊,高效,但不太适合C++封装,用C写怎么都思如涌泉,用C++就经常犯愁:尤其是用指针还是用集合类的问题。
2) CodeWarrior里面自带的POL类库是MFC风格,但有些封装过渡,大量的使用模板,提供RTTI能力,丰富的Stream类,对于大多数Palm风格的小程序来说,这个封装过于臃肿,反而带来不变,譬如弹出式程序就不能使用全局变量,POL的类就根本没办法使用;窗口也不能想弹就弹;截取硬按键也是个麻烦。还有就是对于Record的流式存储,乍一看,形式上很漂亮,但是不符合Storage Heap的数据库本质,密集访问的时候效率也成问题。
3) 自己从POL中抄袭篡改而来的pDatabase/pRecord类,个人认为更符合PalmOS的数据访问要求,提供的Insert,Overwrite,Replace,Erase等直接对Record写入的函数,远比流方便,效率也更高。
4) 数据库程序有个完整性要求,应避免多步更新永久存储数据,所以更新数据时务必要使用DmAttachRecord,而不是使用DmWrite多次写入;除此之外,pRecord类中还包含了一个MemHandle,允许用户多次写入然后一次提交生效,如果在写入过程中程序遇到错误,则不会修改任何存储数据。
5) 这种Commit方式,在不得不同时更新多个记录的时候更加重要,尤其是在遇到低内存的时候,如果依次更新,那可能DmNewHandle都会失败;如果要同时更新多条记录,可以用多个pRecord执行更新操作,然后全部CommitCheck--这个步骤是检查内存Lock情况,如果全部通过就一次全部Commit,虽然不是说绝对不会出问题,但是做到这个地步,出错的可能性和彗星撞地球差不多了吧。
6) PalmOS的数据库信息操作异常烦琐,pDatabase中规划的InitAppInfo通过三次调用才实现了高效封装--这是从POL里学来的。不过封装之后,派生类只要实现虚函数就可以了,用起来还是很方便的。
7) C++中的错误处理机制在PalmOS中不易实现,出了问题Debug也麻烦;所以还是使用了传统的返回Err方式。不过要小心的是构造函数和析构函数是不能返回值的,所以不要在构造函数和析构函数中调用任何可能返回Err的操作,包括打开数据库读写或者申请内存之类,可能后果严重。应该为类提供单独的Init或者Open函数来完成这个要求。
8) 数据库的工作模式很值得探讨。如果为了方便从pDatabase派生或者包含了该数据作为成员,很有可能为了提高速度把一些记录或者AppInfo锁定,但是这样就会给数据库的维护造成麻烦,把数据库的维护函数都写成static并不是好办法,这就走回C的老路上去了。更好的办法是把函数分为两类,一类工作在normal模式下,该模式下可以锁定数据库中的记录;另一类是维护模式,即把所有锁定记录的指针全部release掉,这在pDatabase中通过两个虚函数来实现,一个是InitInternals,一个是DeinitInternals。
9) 从效率出发的话,Normal模式下应该尽量多的锁定和传递指针;而Maintenance模式下,尽量避免使用指针,取而代之使用C++类,是更好的办法,依据这个原则就可以混合C/C++代码,写出既高效又易维护的代码来。
10) 最后一个问题是PalmOS的排序,如果使用C++,没办法传递动态的DmComparF函数给系统,这挺头疼的,不然在pDatabase里写一个Compare虚函数是非常漂亮的;如果非要这样用的话,没办法的办法是写一个Find虚函数,反正最终继承的数据库类知道自己的数据格式,不过这样做给人的感觉总象苹果里的虫子,所以,还是尽可能在数据库设计的时候把排序的Field直接写在Record头上,以避免需要从记录中提取出对象才能比较。
这两个类代码,在写完巨硬II之后好好整理一下,然后可以共享出来大家探讨了。
巨硬的代码进度令人满意,虽然不快,但是在MM(MindManager)的帮助下清晰有序的进行着。先前写的一些基本类都派上了用场,对PalmOS Programming的理解也小有收获。
大致说来有这么几个问题:
1) PalmOS的存储方式非常特殊,高效,但不太适合C++封装,用C写怎么都思如涌泉,用C++就经常犯愁:尤其是用指针还是用集合类的问题。
2) CodeWarrior里面自带的POL类库是MFC风格,但有些封装过渡,大量的使用模板,提供RTTI能力,丰富的Stream类,对于大多数Palm风格的小程序来说,这个封装过于臃肿,反而带来不变,譬如弹出式程序就不能使用全局变量,POL的类就根本没办法使用;窗口也不能想弹就弹;截取硬按键也是个麻烦。还有就是对于Record的流式存储,乍一看,形式上很漂亮,但是不符合Storage Heap的数据库本质,密集访问的时候效率也成问题。
3) 自己从POL中抄袭篡改而来的pDatabase/pRecord类,个人认为更符合PalmOS的数据访问要求,提供的Insert,Overwrite,Replace,Erase等直接对Record写入的函数,远比流方便,效率也更高。
4) 数据库程序有个完整性要求,应避免多步更新永久存储数据,所以更新数据时务必要使用DmAttachRecord,而不是使用DmWrite多次写入;除此之外,pRecord类中还包含了一个MemHandle,允许用户多次写入然后一次提交生效,如果在写入过程中程序遇到错误,则不会修改任何存储数据。
5) 这种Commit方式,在不得不同时更新多个记录的时候更加重要,尤其是在遇到低内存的时候,如果依次更新,那可能DmNewHandle都会失败;如果要同时更新多条记录,可以用多个pRecord执行更新操作,然后全部CommitCheck--这个步骤是检查内存Lock情况,如果全部通过就一次全部Commit,虽然不是说绝对不会出问题,但是做到这个地步,出错的可能性和彗星撞地球差不多了吧。
6) PalmOS的数据库信息操作异常烦琐,pDatabase中规划的InitAppInfo通过三次调用才实现了高效封装--这是从POL里学来的。不过封装之后,派生类只要实现虚函数就可以了,用起来还是很方便的。
7) C++中的错误处理机制在PalmOS中不易实现,出了问题Debug也麻烦;所以还是使用了传统的返回Err方式。不过要小心的是构造函数和析构函数是不能返回值的,所以不要在构造函数和析构函数中调用任何可能返回Err的操作,包括打开数据库读写或者申请内存之类,可能后果严重。应该为类提供单独的Init或者Open函数来完成这个要求。
8) 数据库的工作模式很值得探讨。如果为了方便从pDatabase派生或者包含了该数据作为成员,很有可能为了提高速度把一些记录或者AppInfo锁定,但是这样就会给数据库的维护造成麻烦,把数据库的维护函数都写成static并不是好办法,这就走回C的老路上去了。更好的办法是把函数分为两类,一类工作在normal模式下,该模式下可以锁定数据库中的记录;另一类是维护模式,即把所有锁定记录的指针全部release掉,这在pDatabase中通过两个虚函数来实现,一个是InitInternals,一个是DeinitInternals。
9) 从效率出发的话,Normal模式下应该尽量多的锁定和传递指针;而Maintenance模式下,尽量避免使用指针,取而代之使用C++类,是更好的办法,依据这个原则就可以混合C/C++代码,写出既高效又易维护的代码来。
10) 最后一个问题是PalmOS的排序,如果使用C++,没办法传递动态的DmComparF函数给系统,这挺头疼的,不然在pDatabase里写一个Compare虚函数是非常漂亮的;如果非要这样用的话,没办法的办法是写一个Find虚函数,反正最终继承的数据库类知道自己的数据格式,不过这样做给人的感觉总象苹果里的虫子,所以,还是尽可能在数据库设计的时候把排序的Field直接写在Record头上,以避免需要从记录中提取出对象才能比较。
这两个类代码,在写完巨硬II之后好好整理一下,然后可以共享出来大家探讨了。
2006/05/23
MindManager & 巨硬II (continued)
MindManager果然不是盖的,是我目前用过的最适合我的辅助设计工具,虽然它没有针对开发设计功能,但是在辅助设计方面非常棒。下周有时间的时候我写个详细的评测出来。
巨硬II在MindManager的帮助下迅速走上正轨,很快就可以再度回到代码阶段。大部分算法问题现在都有了着落。唯一遇到的问题是,新的巨硬设计大量统计汉语的词序相关性,目前找不到这方面的资料和数据,恐怕在完工之后得写个机器人程序才行。因为仅仅在设备上Training的话,多数用户的输入量很小,很难快速进入较好的预测状态。这是个不大不小的麻烦。
在巨硬II的设计过程中,我对新算法越来越有信心。举个例子,下班的时候我想发这样一条短信:晚上回来吃饭吗。在现有的巨硬中,
1)先按w键,出现候选字[我,为,完,晚,王],这里至少有两个明显的问题(1)[完]字出现在一句话的句首,是不是很罕见的情况?(2)[晚]其实是一个很少单独使用的字,为什么不能直接出现[晚上]呢?
2)输入[晚上]之后按h键,出现候选字[和,号,很,好,还],这里的[号]字几乎是不可能出现在[晚上]一词之后的,[和,很,还]也是可能性很低的。
3)输入[回来]之后按c键,出现候选字[陈,吃,车,从,程],除了[吃]字之外,其它几个字也相当莫名其妙。
4)输入吃饭之后按m键,出现候选字[吗,卖,买,没,马],同样,[买,卖,马]出现在[吃饭]一词之后也是不合常理的,象[买]和[卖]这样几乎只会单独使用的动词很容易以高词频姿态出现在前面,但是其实只有非常少的词可以放在前面搭配的。
在使用简码的情况下,这句话键入了ws#hl#cf#m,一共10键。而巨硬II追求的目标是:w#h#c#m,而如果这句话你经常写,那么最好的情况应该是w####,:)。能做到吗?现在还不知道,但是这绝对worth a trial。你觉得呢?
注:#代表选字键,在巨硬上可以是四大天王加空格键中的一个,视目标词语出现的位置而定。
巨硬II在MindManager的帮助下迅速走上正轨,很快就可以再度回到代码阶段。大部分算法问题现在都有了着落。唯一遇到的问题是,新的巨硬设计大量统计汉语的词序相关性,目前找不到这方面的资料和数据,恐怕在完工之后得写个机器人程序才行。因为仅仅在设备上Training的话,多数用户的输入量很小,很难快速进入较好的预测状态。这是个不大不小的麻烦。
在巨硬II的设计过程中,我对新算法越来越有信心。举个例子,下班的时候我想发这样一条短信:晚上回来吃饭吗。在现有的巨硬中,
1)先按w键,出现候选字[我,为,完,晚,王],这里至少有两个明显的问题(1)[完]字出现在一句话的句首,是不是很罕见的情况?(2)[晚]其实是一个很少单独使用的字,为什么不能直接出现[晚上]呢?
2)输入[晚上]之后按h键,出现候选字[和,号,很,好,还],这里的[号]字几乎是不可能出现在[晚上]一词之后的,[和,很,还]也是可能性很低的。
3)输入[回来]之后按c键,出现候选字[陈,吃,车,从,程],除了[吃]字之外,其它几个字也相当莫名其妙。
4)输入吃饭之后按m键,出现候选字[吗,卖,买,没,马],同样,[买,卖,马]出现在[吃饭]一词之后也是不合常理的,象[买]和[卖]这样几乎只会单独使用的动词很容易以高词频姿态出现在前面,但是其实只有非常少的词可以放在前面搭配的。
在使用简码的情况下,这句话键入了ws#hl#cf#m,一共10键。而巨硬II追求的目标是:w#h#c#m,而如果这句话你经常写,那么最好的情况应该是w####,:)。能做到吗?现在还不知道,但是这绝对worth a trial。你觉得呢?
注:#代表选字键,在巨硬上可以是四大天王加空格键中的一个,视目标词语出现的位置而定。
2006/05/22
MindManager & 巨硬II
周末卸载Visual Studio的时候,电脑的IE被击落,重装了系统。
巨硬没有任何进展。
和一个程序员朋友谈了巨硬II的构思,没有得到肯定也没有被否定,最后定位在实验主义程序上。
虽然一直极力想摆脱Palm的C-Style,但是考虑到最后发现,在PalmOS上还是用C最好,C++的封装益处并不大。这会导致前面很多代码要重新写过,不过好在工作量并不大。
遵循PalmOS的C-Style,原本设计的核心存储结构被推倒重来,用紧凑的C指针风格来处理最常见的数据库查询与遍历,而且有可能核心算法的代码可以写成Armlet,这会进一步提高执行速度。
还有一个搞笑且让人惭愧的事情。我企图在网上搜一些汉语字频词频的统计资料,发现很少,唯一找到的一个83年的资料,几乎没有任何价值,革命主义一类的词遥遥领先的排在前面,估计是用人民日报统计的。后来灵机一动,想到老外也要学汉语的,还要过有中文托福之称的HSK考试,于是以HSK Vocabulary作为关键词GooGle,找到了一份词汇表,共有1900个单字,6200个双字词,200个三字词和300个四字词,非常口语化,很适合巨硬。
最后应该记下来的一件事情是,为了让巨硬的开发比较有条理,试用了一下MindManager,第一印象非常不错,比用Word或者PPT写大纲方便多了。体现在两个方面:
1. 可以对整个项目有个总览。
2. 可以方便的Cut/Paste,用文本做这个很辛苦。
3. MindManager的设计很贴心,有大量的图标可用,标记优先级,进度,决策,状况。
4. 可以任意设定Relationship,这对程序设计来说非常重要,很多功能模块之间都有制约。
5. 直观。
I Love Colors!
巨硬没有任何进展。
和一个程序员朋友谈了巨硬II的构思,没有得到肯定也没有被否定,最后定位在实验主义程序上。
虽然一直极力想摆脱Palm的C-Style,但是考虑到最后发现,在PalmOS上还是用C最好,C++的封装益处并不大。这会导致前面很多代码要重新写过,不过好在工作量并不大。
遵循PalmOS的C-Style,原本设计的核心存储结构被推倒重来,用紧凑的C指针风格来处理最常见的数据库查询与遍历,而且有可能核心算法的代码可以写成Armlet,这会进一步提高执行速度。
还有一个搞笑且让人惭愧的事情。我企图在网上搜一些汉语字频词频的统计资料,发现很少,唯一找到的一个83年的资料,几乎没有任何价值,革命主义一类的词遥遥领先的排在前面,估计是用人民日报统计的。后来灵机一动,想到老外也要学汉语的,还要过有中文托福之称的HSK考试,于是以HSK Vocabulary作为关键词GooGle,找到了一份词汇表,共有1900个单字,6200个双字词,200个三字词和300个四字词,非常口语化,很适合巨硬。
最后应该记下来的一件事情是,为了让巨硬的开发比较有条理,试用了一下MindManager,第一印象非常不错,比用Word或者PPT写大纲方便多了。体现在两个方面:
1. 可以对整个项目有个总览。
2. 可以方便的Cut/Paste,用文本做这个很辛苦。
3. MindManager的设计很贴心,有大量的图标可用,标记优先级,进度,决策,状况。
4. 可以任意设定Relationship,这对程序设计来说非常重要,很多功能模块之间都有制约。
5. 直观。
I Love Colors!
2006/05/20
巨硬II
昨天和Newaa聊了很久,被拍了很多受用的MP,心里甜蜜蜜的。
但是他竟然是在用Treo和我MSN了两个小时,我都没有发现。这让我越发觉得有义务把巨硬的代码重新写过--无论自己对代码是多么的不在行。
话说回来,虽然很多人把巨硬推举为革命性的输入法,但是事实上它不是。受到用户追捧的原因是在巨硬之前没有一个手机或者PDA上的输入法可以把输入速度推向PC上输入速度的50%左右--这是我实际比较的结果,尽管一些热爱巨硬的用户声称巨硬的输入速度可以媲美PC,但是事实上它还做不到。如果把巨硬硬碰硬的和紫光或者拼音加加相比,它在算法上并没有明显高明的地方,也许里面有些自以为得计的小Trick,但是我相信每个程序中都有这类的东西,每个程序都是凝聚了程序员心血的东西--如果程序员思考的时间超过了编码时间的话。
作为作者,我认为巨硬在过去的两年中被追捧并不是软件本身的水平有多高,而是反映了这个领域的程序员缺乏钻研精神。用陈道明的话说,这属于水落石出,而不是水涨船高。
巨硬中真正有原创性的地方只有一个,就是用上下两排五个按键映射选字,这在手持设备上是革命性的,而且巨硬也充分利用了五向键,保持了Treo引以为毫的兼顾单双手操作特性,为掌上设备输入设立了标准。
巨硬的代码丢了之后,一直有想法重写,但是缺乏动力,尤其是650出来之后竟然巨硬仍然能用,这更加给了我偷懒的理由。
不过我是个手懒但是脑子不懒的人,在过去的两年中,我花了大量的时间考虑巨硬二代应该带给用户什么,曾经考虑过很多方向,也写了大量的文档,但是这个问题的真正答案,直到最近6个月才越来越清晰,直至驱动我心甘情愿的做下来,为了一个美好的愿景进入艰苦的编码工作。
Jobs说,你应该喜欢你的IDEA。窃以为然。
在原来庞杂的规划中,包括拓展平台和拓展输入法框架等等,都不会在巨硬二代中出现了,这些东西不该是目前一个个人程序员该做的,如果要做,或者有机会有资源来作,那我也会考虑成立一家公司来做这个事情,但是不是现在。
现在让我们仍然回到起点上来,我现在可以说的就是巨硬二代在界面上绝不会带来更多的视觉或者功能因素,它可能看起来比巨硬一更简陋(这取决于我有多懒)。巨硬二代的进步将是内在的,但是它绝对是你可以感觉到的,它可以在Qwerty设备上比巨硬一代提高50%左右的速度,可以在数字键盘设备上达到接近一代巨硬的速度。而且我很有把握的说,它把中文输入法带入一个全新的领域。这个飞跃就像从Windows内置全拼过渡到拼音加加一样。它会全面超越现在流行的拼音加加或者紫光拼音输入法。我不敢说我是第一个这样做中文输入法的人,中文输入法不下500种,但是在目前我用过的各种输入方法中,我从未见过这样的产品。
但是他竟然是在用Treo和我MSN了两个小时,我都没有发现。这让我越发觉得有义务把巨硬的代码重新写过--无论自己对代码是多么的不在行。
话说回来,虽然很多人把巨硬推举为革命性的输入法,但是事实上它不是。受到用户追捧的原因是在巨硬之前没有一个手机或者PDA上的输入法可以把输入速度推向PC上输入速度的50%左右--这是我实际比较的结果,尽管一些热爱巨硬的用户声称巨硬的输入速度可以媲美PC,但是事实上它还做不到。如果把巨硬硬碰硬的和紫光或者拼音加加相比,它在算法上并没有明显高明的地方,也许里面有些自以为得计的小Trick,但是我相信每个程序中都有这类的东西,每个程序都是凝聚了程序员心血的东西--如果程序员思考的时间超过了编码时间的话。
作为作者,我认为巨硬在过去的两年中被追捧并不是软件本身的水平有多高,而是反映了这个领域的程序员缺乏钻研精神。用陈道明的话说,这属于水落石出,而不是水涨船高。
巨硬中真正有原创性的地方只有一个,就是用上下两排五个按键映射选字,这在手持设备上是革命性的,而且巨硬也充分利用了五向键,保持了Treo引以为毫的兼顾单双手操作特性,为掌上设备输入设立了标准。
巨硬的代码丢了之后,一直有想法重写,但是缺乏动力,尤其是650出来之后竟然巨硬仍然能用,这更加给了我偷懒的理由。
不过我是个手懒但是脑子不懒的人,在过去的两年中,我花了大量的时间考虑巨硬二代应该带给用户什么,曾经考虑过很多方向,也写了大量的文档,但是这个问题的真正答案,直到最近6个月才越来越清晰,直至驱动我心甘情愿的做下来,为了一个美好的愿景进入艰苦的编码工作。
Jobs说,你应该喜欢你的IDEA。窃以为然。
在原来庞杂的规划中,包括拓展平台和拓展输入法框架等等,都不会在巨硬二代中出现了,这些东西不该是目前一个个人程序员该做的,如果要做,或者有机会有资源来作,那我也会考虑成立一家公司来做这个事情,但是不是现在。
现在让我们仍然回到起点上来,我现在可以说的就是巨硬二代在界面上绝不会带来更多的视觉或者功能因素,它可能看起来比巨硬一更简陋(这取决于我有多懒)。巨硬二代的进步将是内在的,但是它绝对是你可以感觉到的,它可以在Qwerty设备上比巨硬一代提高50%左右的速度,可以在数字键盘设备上达到接近一代巨硬的速度。而且我很有把握的说,它把中文输入法带入一个全新的领域。这个飞跃就像从Windows内置全拼过渡到拼音加加一样。它会全面超越现在流行的拼音加加或者紫光拼音输入法。我不敢说我是第一个这样做中文输入法的人,中文输入法不下500种,但是在目前我用过的各种输入方法中,我从未见过这样的产品。
Axosoft OneTime & MSDE 2000
今天想找一些软件开发用的项目管理软件。在网上看到了一些选择,不过大多不容易找破解版,而且目前几乎没有单机版的这类软件了。
最后选择了axosoft的OnTime 4.2.6,最新版是6.x,可惜没破解。
该软件需要MSDE或者其他版本的SQL Server作为数据库后台,于是下了一个MSDE装上,却不懂配置。照例GooGle了一下,发现需要运行svrnetcn.exe来配置。另外设置的时候如果setup.exe后没有配置security mode,那缺省就使用windows认证,而不是SQL认证。
下面这个链接是很不错的参考,对只是用MSDE作为程序后台的用户来说,够用了。
http://www.codeproject.com/database/ConfigureMSDE.asp
最后选择了axosoft的OnTime 4.2.6,最新版是6.x,可惜没破解。
该软件需要MSDE或者其他版本的SQL Server作为数据库后台,于是下了一个MSDE装上,却不懂配置。照例GooGle了一下,发现需要运行svrnetcn.exe来配置。另外设置的时候如果setup.exe后没有配置security mode,那缺省就使用windows认证,而不是SQL认证。
下面这个链接是很不错的参考,对只是用MSDE作为程序后台的用户来说,够用了。
http://www.codeproject.com/database/ConfigureMSDE.asp
Subscribe to:
Comments (Atom)
