-
2009-06-02
基于Subversion的分布式版本控制系统SVK宣布停止开发 - [技术]
上周(5.28)在邮件列表中看到这封邮件“The Future Of SVK”,在邮件的一开始,SVK的创始人Chia-liang Kao (高嘉良)就开门见山地宣布“We at Best Practical will no longer be actively developing SVK”。
在这封邮件里,高嘉良提到他自己在2003年开始SVK的开发的时候,只是想着能够在离线或旅游的时候继续进行,从“让Subverion支持分散式开发”的角度来看,可以认为SVK是任务完成了。去年我在找寻Subversion离线使用方案的时候,写了这篇blog“通过svk,离线使用subversion”简单介绍了SVK的用法。
高嘉良没有提到停止开发SVK的具体原因,倒是说一开始决定采用Subversion的文件系统作为SVK的后端,最终变成了SVK最主要的技术问题,因为发现它不适合于复杂合并的情况。
目前在分布式版本管理工具这个领域,引人注目是主要是Git和Mercurial,SVK的用户应该很少,从5月28日高嘉良在邮件列表宣布这一消息后,只有为数不多的回应,同时我也没有在其他什么地方看到相关新闻。但无论如何,SVK的确是一个能够满足“让Subverion支持分散式开发”要求的工具,写这篇新闻,算是一个小小的纪念吧。
后记:发现Mercurial已经获得Google Code的官方支持,在Analysis of Git and Mercurial这篇文章中,Google Code团队对两个工具进行了分析比较,并解析选择Mercurial的原因。 -
2009-05-24
纸张大小及其相关标准 - [技术]
在写《Amazon宣布Kindle DX,启动大屏幕电子阅读器时代》这篇blog的时候,为了对Kindle DX大小有个感性认识,就想知道所谓A4纸、16开、32开等单位有多大,结果找到一些关于纸张大小及其相关标准的资料,整理如下:
1、一般的书籍是32开,而很多电脑书,还有杂志就是16开的,但是32开、16开具体的大小是多少呢?
造纸厂所生产的印刷纸一般长宽都在一米左右,因此需要裁切成较小的面积来使用。分成多少份一样大小就是多少开,为了易于折叠一般都是以2的倍数来裁切,所以16开、32开,就是一张大纸裁切为16份、或32份的大小。
然而印刷用纸的出厂大小有很多中规格,大类分为卷筒纸和平板纸两种,每种又有五六种规格。所以尽管同样是32开的书籍,却出现各种不同的大小,我们的书架永远都是参差不齐的。
2、著名的A4纸
A4纸相信是无人不知了,它是在国际标准ISO216中定义的,这个标准定义了A、B、C三个系列的,其主要特点是长宽比是“
: 1”,也就是说长:宽=宽×2:长。因此两张A4可以拼成一张A3(每两张An正好拼成一张An+1),也可以将两张A4按比例缩小成两张A5然后拼成一张A4。所以我们的Word文档,在一张纸上打一页,或者两页、四页,都能刚刚好用完一张A4纸。
3、纸张大小相关的一些国家标准
我首先是在网上找到这样一篇文章——99版《国家行政机关公文格式》国家标准与88版的比较,其中提到的主要变革是以国际标准A4型作为公文用纸的新式公文样式将替代16开型,然后在网上找到了“国家标准化委员会”以“gov.cn”结尾的的官方网站(http://www.sac.gov.cn),这个网站的维护很有问题。
在其首页的右上角的“政务公开”,还有下方的标准化管理“国内、国际、区域、国外”四个栏目空空如是,点击"more"进去一看,最新的内容也是两年前的了,真不晓得这帮由我们供养着的公务员两年来有否去看过自己的网站。
在这个网站上,我只能查询到关于《国家行政机关公文格式》(请用鼠标猛击 GB/T 9704-1999)的基本信息,无法看到标准内容。
不过在维基百科的ISO216介绍中,提到中国国家标准 GB/T 148-1997 《印刷、书写及绘图纸幅面尺寸》与ISO216是“非等效采用”。而我在百度百科上找到了《国家行政机关公文格式》的内容信息,里面也提到“公文用纸张采用GB/T 148中规定的A4型纸”。
4、标准何其多
根据国家标准化委员会非常慷慨大方给出GB/T 148-1997的摘要信息说明,GB/T 148这个标准是由“全国造纸工业标准化技术委员会”负责的,我顺藤摸瓜到了其官方网站——中国造纸工业标准信息服务网,发现关于“纸”的标准可真是不少,仅仅是GB/T 148所属地“基本标准”就有30个,按造纸标准体系表的说明,还包括了与纸的“物理性能”、“光学性能”等等标准。
5、作为一个公民,为什么不能免费获得国家标准?
在查找过程中,我觉得很郁闷就是,在国家标准的政府网站,找不到标准的内容。在网上一些所谓“免费下载国家标准”的网站,其实大部分都是在贩卖国家标准,例如这个地方,GB/T 148-1997标准,阅读版(也就是看一看)要人民币8元。作为纳税人,我们已经为写标准付钱了,现在看标准,居然还要再次付钱,这是典型的重复收费。就像你每个月已经为所在小区交付了管理费,然而要查看小区管理条例的时候,保安对你说:“不好意思,请付钱!”。
-
2009-04-20
Andriod应用设想:GTD的位置相关任务自动提醒 - [技术]
朋友aleung前段时间入手了一个Andriod手机,不满上面的地图偏差现象,于是开发了一个MyBetterLocation程序能够调整偏差,从而在手机地图上显示正确的位置。今天一看,该软件已经更名为“Here I'm”,具体介绍请猛击官方网站!
我已经占了网站论坛的沙发以示支持,不过aleung交代还要帮忙想Idea,上午开会期间憋了很久,终于想到一个Idea了——就是GTD的位置相关任务自动提醒。
在 GTD中,任务有一个场所的概念,例如某些代办事宜是适合在“办公室”完成,某些应该在“超市”完成。当你将代办事宜录入GTD后,则可以放松你的头脑, 等到了相应的场所,再来查看应该做什么事情。但是在以前任何的GTD应用,还是要依赖使用者自己来根据当前场所筛选代办事宜,不方便,则容易遗忘。例如你 某次记录了需要购买一个小物品,可能去了几次超市都不记得。
现在Andriod G1手机既然可以知道我们在什么地方,则可以让它来提醒这个事情。首先将一些地点记录并且分类,例如某个地点(或者范围)是超市、办公司、或者加油站等 等;然后在录入代办事宜的时候,就可以将任务与这些地点相关联;当手机进入这些地点(或者接近某个范围),就自动提醒相关事宜。
现在的任务提醒软件基本上都只有根据时间提醒这个功能,有了GPS手机后,就可以加上根据位置提醒,算是一个不错的idea吧。 -
2009-04-13
Google App Engine支持Java,个人的一些想法 - [技术]
早在3月份,就有传言说Google愚人节那天将在GAE中增加Java支持,结果我在4月1日不停的查看各个网站,最终还是要到4月7日才在看Guillaume Laforge的这篇blog “Write your Google App Engine applications in Groovy” 时获知这一消息。
1、对Java社区的影响
GAE支持Java的消息可谓是一石激起千层浪,在“云计算”越来越重要的今天,谁不想能够乘“云”而起呢?相比GAE支持的第一种语言Python而言,Java的应用层面更广,更重要的是Java不仅仅是一门语言,而应该被看成是一个平台,例如基于JVM开发的各种动态语言,例如Groovy、JRuby等都可以运行在GAE之上了。
运行在GAE上,毕竟跟普通的J2EE/J2SE环境不一样,GAE本身对JRE Library的访问有一定的限制,这里列出可以在GAE中能否访问的基本API(The JRE Class White Lis t),在“Write your Google App Engine applications in Groovy”也提到Groovy是经过一些补丁修改后才能够运行在GAE上。
已经有有心人整理了"Frameworks and libraries supported by Google App Engine Java"的列表,这个列表应该会不断增长,大量的Java开源项目都会为了能够在GAE上运行而努力的。而一个开源项目,能否方便地用在“云”中,也将会成为一个重要的选择标准。
2、 Google的“操作系统”
如果把操作系统定义为应用的运行平台,我认为Google已经实现了一个广义的操作系统,其中Java占据了重要的地位。你可以将客户端应用(Android)、着重展现的Web应用(GWT)和着重服务端的应用(GAE)都部署到Google的“操作系统”上。
我去Google I/O网站看了即将在5月份讲演的主题列表,都是关于如何更好地利用Google的各种技术来开发应用。现在大家提到“搜索”自然而然会想起Google,Google正在努力让大家在提到“应用”的时候,也会想起Google。
我 觉得很快可能会有这样的故事发生,某人开发某个应用,运行在Andriod的手机和上网本上,该应用依赖的后端服务则是部署在GAE上。随着用户量的增 加,突破了GAE的免费限制,但是开发者同时在应用上放置Google AdSense,用Google广告挣钱支付GAE费用,还另有盈余。
3、对中间件、数据库市场的影响
这个也许是我想的有些远了。我猜想企业应用还是J2EE+DB的结构为主,短期内运行在“云”上的估计以个人、娱乐应用为主。但是如果Google操作系统越来越成熟后,不排除两种情况:
1)企业将自身的应用部署在GAE上,或者Google专门为企业提供单独的“云”部署
2)Google将“云”本身拿出来出售,例如银行、电信这类用户应该很有兴趣搭建自己的“云”
当上述情况发生时,对目前的中间件产商、数据库产商可是一招釜底抽薪。
一些个人想法,抛砖引玉了:) -
2009-03-06
[笔记]Loose Couple Dimensions,松耦合的维度 - [技术]
在Infoq的文章SOA的十大原则,专门提到了松耦合。松耦合是老生常谈了,不过这篇文章则用了好几个维度来描述:
时间:当参与者在时间上是松耦合时,它们不需要在同一时间启动并进行通讯。这要求两者之间采用某种缓冲或队列机制,尽管这种机制与松耦合无关。当参与的一方向另一方发送消息时,交互的继续不依赖于逻辑上或物理上能否立即返回应答消息。
在互联网时代,这个需求逐渐明显,因为我们已经习惯了各种网络应用,例如我自己深度依赖Gmail, Google Docs。然而一旦断网,就只能干瞪眼。幸好Google推出Gear,成功将互联网应用与浏览器松耦合,极大的扩充了互联网应用的可用性。
另外在企业应用中,同一时段会收到不同类型的用户请求。如果某些请求的及时性要求不迫切的话,将其置入队列中,就能在在保证业务功能完整的情况下,无需其他投资而提高了应用的性能。位置:如果一方参与者查询与之通信的另一方参与者的地址,另一方的地址可以透明地进行变更,不需要重新编程、重新配置或者甚至不需要通信伙伴的重新启动。这意味着查找(lookup)过程采用某种目录或地址来存储服务终端的地址。
这里提到不需要“重新配置”,在企业应用中我觉得需要商榷。因为这的确就以为需要一个目录服务,而且目录服务本身的位置永远不能改变。而应用每次访问服务之前都要执行一次查找动作是否值得呢?我可以倾向于可以重新配置,但是要做到动态配置,即无需重启应用。
类型:同静态与动态,弱类型与强类型这些编程的概念类似,参与者既可以全部依赖也可以部分依赖文档结构来实现它的功能。
在ESB的项目中这倒是比较常见,我们使用的是Oracle Service Bus,一般采用xml作为交换格式。既然实际格式是文本,那么两边应用的具体类型就无所谓了,例如字符串“2009-03-06 21:24:20”,在服务端是日期类型,而在客户端是字符类型。
版本:参与者可以依赖服务接口的特定版本,也可以兼容某个范围内的版本。所需匹配的版本越确切,参与者在这个方面上的松耦合性就越差。一个好的原则是遵循Postel法则(译 者注:Postel’s Law——“Be liberal in what you accept, and conservative in what you send.”):服务提供者应尽可能兼容许多不同的版本,这将使它更加健壮(可能甚至需要容错),服务使用者应尽可能遵循精确的语法和文档类型。这将增加 整个系统的稳定性和灵活性。
在SOA的首期项目,根本没有所谓的“版本”问题,而随着项目的渐渐演变,版本问题就渐渐暴露出来了。尽管基于Oracle Service Bus的丰富特性可以应付一些版本问题,但还是服务接口的初始设计其决定作用。我觉得解决“版本”问题,SOA的十大原则提到的“面向文档”是一个很好的工具,因为面向文档能够灵活应对接口的变化。
这里的Postel’s Law——“Be liberal in what you accept, and conservative in what you send ",中文意思应该就是我们常常说的“严以待己,宽以待人”。服务端的程序设计必须严格遵循相应的架构规范、设计模式和最佳实践,但是就要允许客户端以各种古灵精怪、不合逻辑的方式进行交互,这样的服务端就会更将强壮,而版本变化则不在话下了。基数:服务消费者和提供者可能是1对1的关系,尤其是在请求或响应交互发生时,或队列被明确使用的情况下。在别的情况下,服务使用者(在这种情况下,称作“消息发送者”或“事件源”更为合理)可能既不知道也不关心有多少人接受了消息。
这段文字有点不容易读,我理解为交互双方是一对一,或者一对多的关系。现在一般都是通过“主题队列”的订阅机制来实现一对多的关系。
查找:参与者打算调用服务时,既可以依赖服务提供者的物理名或逻辑名,也可以先通过一组功能描述来执行查找(lookup)操作。这意味着存在一个注册表和(或)仓库,对存储其中的使用者需求和提供者能力进行直接或间接的匹配。
这段话讲的不够直白,我的理解就是如果应用要使用一个天气预报的服务,那么应用可以首先到某个地方查找能够提供天气预报能力的服务,然后使用它。说实话,我还从来没有看过能够在“运行时”做到这一点的,曾经有一些SOA的项目标书提到这个特性,除了让销售多买一些Repository Server外,就不曾在项目实施中出现过了。
对于将使用者需求和提供者能力进行直接或间接的匹配,我认为只是应该在“设计时”进行,而且当企业部署了上百个服务后是很有必要的。不过我这里着重的是企业应用,不晓得互联网应用中有否在运行时进行功能查找的真实案例。接口:参与者可能要绑定到一个特定的服务接口或是支持一个通用的接口。如果使用通用接口,所有该接口的使用者都能与所有该接口的提供者进行交互。尽管可能乍看起来这有些笨拙,但单一通用(统一)接口的原则就是WWW架构的核心
通用接口的确是WWW的核心,不同产商的浏览器可以自如的和不同产商的Web服务器进行交互,这就是通用接口带来的奇迹。不过我对通用接口的理解还不够深刻,这几天在看一些关于REST的争论也常常提到统一接口的问题,需要做进一步了解。我对如何在企业应用中的采用单一通用接口比较感兴趣。
---------------------------感想分割线 ---------------------------
其实松耦合真的是老生常谈,但这种多维度的分析还是让人眼前一亮的。将问题进行分解,然后分别描述是一种简单而有效的方法。在讨论问题的时候,容易泛泛而谈没有重点,或者紧紧揪住某个细节陷入其中,这个时候,如果有人说“这个问题嘛,应该分成几个方面来看,……”,这个人往往会被大家佩服。
其实也是,当我们开始试图从多个角度看待一个问题时,就更加能够看清问题的真相,找到相应的解决方法,而如果能够做到麦肯锡所倡导的“相互独立,完全穷尽”就更加完美了。 -
2009-02-28
自动生成Powerpoint - [技术]
我在“试用mindmap(脑图)工具xmind3”一文中提到:
非常非常希望有这样的工具,用Mind Map来制定Presentation的结构,每个叶子节点就是一张Slide,可以在PowerPoint中对这些叶子节点进行编辑。然后可以选择Mind Map中的某些节点输出为一个ppt,其中包含自动按照Mind Map结构生成的Agenda!
今天看到这篇文章“Groovy PowerPoint DSL”,作者用Groovy做了一个能够生成Powerpoint的builder,十分简单:
builder.slideshow(filename:'Test.ppt') {
slide(title: 'Introduction') {
bullet(text: 'Bullet 1')
bullet(text: 'Bullet 2')
}
slide(title: 'Slide 2') {
bullet(text: 'Bullet 3')
bullet(text: 'Bullet 4')
}
slide(title: 'Example') {
textbox("""This is a slide
With a lot of extra lines
Which make no sense
At all""")
}
imageslide(src:'background.png')
}希望作者尽快完善这个builder,这样就可以用DSL来编写ppt,然后自动生成。再另外做一个读取xmind格式的程序就可以达到我的想法了。不过我觉得OOO的开放性更佳,也许应该开发一个基于Open Office Impress的DSL。
-
1、起缘
上周在深圳培训,无意看到一位台湾同事的电子书阅读器——Sony 505 ,因为之前看了不少Amazon Kindle的新闻,对这类产品挺感兴趣,就忍不住把玩了一会。然后在网上查看了不少相关资料,看到汉王将自己的产品称之为“电纸书”,觉得这个称谓特别贴切,就用了在我这篇blog的标题。
2、电纸书的特点
- 纸质:此类产品采用所谓的“电子墨水”(E- Ink)技术,按我的理解电纸书屏幕的每个像素就是一小块平板,正反面涂了黑白色。缺省全部显示白色,然后通电将特定的像素翻过来,显示一个黑点(当然真 正的电子墨水技术是做成胶囊状,一般会有多级灰度)。电子墨水力图达到如果墨水写在纸上的显示效果,不需要屏幕刷新,自身不发光,所以保护眼睛,不怕强 光。
- 省电:电纸书的功能非常单一,因此硬件软件功耗都可以设计得非常低;特别是得益于电子墨水的技术,电纸书的屏幕可以不再加电的情况下保留住原先显示的图片和文字状态,只有在翻页的时候才耗电,因此电纸书往往充一次电可以用两个星期左右。
- 轻薄:同样由于功能专一、简单,可以设计得相当轻薄。
正是这些特点将电纸书和笔记本电脑、PDA区别开来。说实话,电纸书捧在手上的感觉真不错,那个屏幕真的跟纸差不多,用它看书比起笔记本电脑、PDA、手机舒服多了,我都动心了。3、我认为的购买注意事项
如果真的要购买,我觉得要注意一下几个方面:
- 屏幕大小:一般而言是越大越舒服,当然也越贵。目前主流的大小是6寸,包括Amazon Kindle和Sony 505,还有国内各个电纸书产商都有6寸产品。
- 支持格式:支持的格式越多越好,重点是pdf, chm, doc还有各种图像、以及zip、rar等压缩格式,这方面是国产电纸书明显占优,其中翰林V3似乎是支持格式最多的(这里有一个部分国产电纸书列表)。相反国外的产品支持格式较少,像Amazon Kindle几乎是只支持专有格式。
- 翻页黑屏:在用同事505的过程中,我发现每次翻页,它是整个屏幕首先黑屏,然后再重新以“白底黑字”的方式显示,这里有一个V3、Kindle、Sony 505翻页对比视频可以看看具体效果。这种翻页黑屏的效果是我不能忍受的,在这里提到汉林N516是目前全球唯一一款翻页不黑的产品,不过我相信这个解决方法很快就会普及到各个产品。
- 阅读方便:例如将pdf自动切白边、书签、放大、旋转还有查询等功能。
4、对国内市场的想法
电纸书——电子阅读器这类产品,感觉之前一直属于阳春白雪、少人问津,直到Kindle横空出世才引起众多眼球的关注。相对于一般的电纸书,Kindle的特别之处在于:
1)Amazon庞大的在线商店:在Kindle Store上宣称有超过23万本书,这个商店不仅容量大,而且还新,能够用纸质书几分之一的价格买到刚刚上市的畅销书,还有报纸、杂志等等。
2)免费的无线连接:用户无需付费,无需通过电脑,即可直接使用随机附带的无线模块上网下载书籍。
所以Amazon Kindle不仅仅是提供了一个杰出的硬件产品,更加重要提供了一种新的阅读体验——手捧着Kindle,无需再考虑其他,即可读到你所想读的。而作为Amazon本身,也同时挖掘到一个庞大的销售平台,销售更多的书籍,形成一个双赢的生态圈。
反观国内的电纸书市场,则基本上是硬件产商在互相PK,看不到出版业的影子。其实国内产商的产品并不差(应该说电纸书这类产品本身的技术门槛不高),而且国内产品的软件能力往往更强,但由于没有Kindle Store这样的后台支持,使得:
1)使用者需要使用电脑(就无法做到随时随地),才能更新电纸书上的内容
2)由于没有正规电子出版物的支持,大家正在看的电子书绝大部分都是盗版的
3)同样,由于是盗版,用户很难获得最新的畅销书籍,也难得有制造精良的盗版书
我想,正是这些因素使得电纸书在国内还是属于“小众”用品。5、理想的电纸书
我认为理想的电纸书产品,除了有良好的阅读体验外,同时还应该是一个良好的写作平台。除了对书籍、报纸本身做注释之外,也能进行文章撰写,画图什么的。也许,将上网本和电纸书两者相结合,才是一个理想的产品!
附注1:两个关于电子阅读器的国内网站:1)一路书香网;2)百战商盟(这个名字怪怪的)
附注2:值得一提的是,我发现有一个专门为电纸书产品打造的开源操作系统——OpenInkpot.org,而它目前支持最好硬件就是我们国产的翰林V3,看来V3的用户量还是很大的! -
接着上一篇博客“卓越、淘宝等网购站点应该改进的地方”,整理了一下自己的想法,着重购物网站如何帮助顾客找到想要的商品。我将顾客的类型分成三种:
1、顾客明确知道商品名字、型号
例如你要买《神奇的分子:药物是如何起作用的》这本书(推荐一下,我正在看这本书,增长健康、医学知识,而且也挺有趣的),或者你要买一部诺基亚N95手机。
这种需求是十分明确的,对于购物网站来说是最简单的,一般都可以得到准确的搜索结果。不过对于这类顾客,购物网站的差异性只是体现在“价格”,所以顾客很可能去类似“大拿网”这种价格比较网站。这时候购物网站对于顾客来说只是一个买便宜货的地方,没有任何忠诚度可言,哪家便宜就去哪里。
2、顾客对商品有明确要求
就像我在上一篇文章我提到对钱包的需求——需要有活页的。
购物网站能否满足这种需求的查询,取决于对商品的属性记录是否足够详尽。这种记录不是指单纯的文字记录,例如说我要买重量在1KG以下的双肩电脑包,尽管可以通过google查到几乎所有双肩电脑包的资料(包括重量),但是就没法在google进行“<= 1KG”的查询。
要完成这样的查询,商品属性就要以“元数据”的形式存在。商品的元数据越是丰富,能够完成搜索条件就越复杂,就更加能够满足顾客的各种稀奇古怪需求。在网络销售中,“长尾”是盈利的重要组成部分,改变了原有的“二八定律”;但是要让长尾真正长起来,就让要顾客能够找到各种偏门的商品,因此一个详尽的商品元数据是至关重要。
在我看来,目前最有能力、也是最适合做这个事情的就是淘宝。因为:1)淘宝上的物品数量庞大,相信比任何其他的购物网站都要多;2)淘宝是C2C网站,只要能够设计出鼓励店主输入详细商品属性的机制,就有无限的人力在做这个事情。
淘宝要做的事情有:
1)商品分类。这个是大部分购物网站都已经完成的
2)针对不同的商品类别,定义公共的元数据。鼓励店主创建和重用商品元数据。
3)每个店铺允许为自己的商品添加私有的元数据;提供将私有元数据升级到公共元数据的机制
4)提供按照商品元数据进行组合查询的方式一旦建成,淘宝就等于拥有了世界上最大的商品元数据库。许多消费者在别的网站都无法查询到商品,则可以在淘宝找到,这就是差异性所在。而且在这个基础上与销售数据结合,淘宝可以做进一步的挖掘,例如各种属性的差异是如何影响销售量的等等,这是一个无价的数据库。
在互联网上,有一个freebase.com(阮一峰的介绍)的网站,致力让用户创建任何事物的元数据,淘宝可以考虑创建商品版的freebase。这时候购物网站对消费者来说就是一个卓越的信息中心,能够帮助找到想要的商品。
3、顾客对商品只有模糊的要求
比如说女孩要买一件外套,一般情况下是不会有明确的概念就是要买怎么怎么样的衣服。很可能是去商店东看看、西看看,看到某种喜欢的款式,然后再看看这个款式的系列等等。对于这种需求的顾客,购物网站如果能够帮助她/他找到心头所好,就等于成为了顾客的逛街好友。
我觉得可以借鉴那些音乐分享、推荐网站的做法,例如我之前记录过的Yobo,你选择了蔡琴的《被遗忘的时光》之后,网站就会推荐其他风格相近的歌曲给你。购物网站也可以与顾客进行交互,逐步帮助顾客缩小选择的范围,最后达成满意的交易。
我构思的一种方式如下,例如女孩要买一件外套,那么她进入女上装的分类。网站一开始推荐20款各自不同风格的外套,女孩挑选其中的一套,网站根据女孩挑选的风格,再推荐与之相近的20款外套,女孩再挑选一直到最终确认。
在这个过程中,女孩也可以一次挑选多件外套,网站可以根据多件外套的混合风格进行推荐;另外除了简单点击挑选之外,网站应该让女孩细化自己的爱好,例如是喜欢外套的颜色、品牌,还是某个部位(例如袖子)的款式等,这样网站就能够做更加精确的推荐。
要能够完成这样的推荐,其中的主要难点有:
1)需要更加详尽的元数据,特别是设计到商品外观的细节,例如衣服的款式等等,这方面的工作量浩大无比。
2)分类的规则。及时有了十分详尽的元数据信息后,当用户选择了一件商品,如何找出其他“相近的商品”?特别是能否做到个性化的推荐?在购物网站查看商品,常常会看到“80%购买了这件商品的顾客,还会购买以下商品”,这实际上是利用顾客的参与来识别“相近的商品”,这种机制再加上对顾客购买历史的分析,应该可以做出比较有效的推荐。 -
2008-11-22
卓越、淘宝等网购站点应该改进的地方 - [技术]
在淘宝、卓越买过几次东西,体会到了网购的好处:便宜、方便、选择多,现在要买什么东西,都会先到网络上查查。
最近正好想要买两样东西,一个是钱包,一个是小斜挎包。
买钱包是因为最近领了驾照,现在钱包太薄塞不下这么多东西,另外觉得平时要拿小区IC卡、羊城通出来不方便。我还是想把常用的东西都放在钱包,所以想买一个带中间活页的钱包。
平时着休闲装时,我一般都是用腰包,不过在冬天穿厚外套的情况下就不大方便。因此想买一个斜挎包,但是不希望想公文包那么大,比腰包大一点点就好,也就是用来放钱包、手机、钥匙什么的。
但是这次在网上查询的体验就糟透了。
在淘宝、卓越输入“钱包”的查询结果对我来说几乎没用,根本无法判断钱包是否用活页。我后来发现,所谓有活页,基本上在商品描述中就会有“2个透明窗”,我不得不用google来查询。而查询“斜挎包”得到的搜索结果根本无法精确判断包的大小,只能一个个点击进入相应的商品网页,在商品描述里面看到长宽数据。
如果网站上有我想要的商品,而我却无法找到该商品进行购买,对我来说是一个不便,对网站来说就是一个损失!
从目前来看,卓越提供的查询手段相当简陋,只有:
1、在商品标题中查询用户输入的关键字,可以限制在某些分类中查询
2、按照热销程度、价格等排序淘宝相对要好一些,比如提供款式、风格、颜色等选择,分类更加细致。例如我后来就发现有这么一个“三折短款”的分类符合我的要求。但是就不能进行“长<=20cm and 宽<=20cm”这样的搜索。或者说大部分商品描述都提及到重量,同样也无法对重量进行查询。
实际网站都已经拥有了这些商品的数据,却无法提供良好的用户体验。不过我也是技术人员,不能光是抱怨,回头整理一下这方面的解决方案才行。
-
2008-11-16
通过svk,离线使用subversion - [技术]
我的需求很简单,就是:
1、希望将源代码放到Google Code上;
2、在没有连接网络的时候,依然可以commit在网上查询一下,发现这其实是对“分布式版本控制”的需求。因为Google Code用的是subversion,而我自己最熟悉的客户端工具也是subversion,所以我最终选择了SVK。
SVK与subversion的关系密切,它是采用subverion respository作为存储后端,所以可以直接用利用subclipse, tortoiseSVN来操纵svk respository,这也是我所希望的。SVK最重要的作用就是可以将本地的respository与远程的respository进行同步,这样平时我可以只是在本地respository进行编程,多次本次提交之后,在适当的时候将变更同步到google code上。
具体的做法是:
1、为远程respository建立本地镜像。
svk mirror //mirror/myproject https://myproject.googlecode.com/svn/
这个语句的作用是将两个respository关联起来svk sync //mirror/myproject
这个语句的作用是将指定的镜像目录与远程respository同步,这是在建立镜像关系后第一个要运行的命令。2、建立本地分支。虽然//mirror/myproject位于本机,但是不建议对//mirror/myproject目录直接操作。主要的原因有两个:
1)对mirror修改的一切,都会自动同步到远程respository。如果网络不通,对镜像目录的改变就会失败,这就失去了离线提交的特性。
2)我用subversion客户端工具对mirror直接操作后,结果在sync的时候得到了“is not a mirrored path”的错误,在网上查询发现这个问题已经被讨论过:Basically any dealing with mirrored paths with a non-svk tool (i.e. svn) is going to cause issues due to some internal properties that svk uses.
因此必须建立自己的分支:
svk copy -p -m "Make local repo" //mirror/myproject //local/myproject建立本地分之后,则可以随意用subversion对//local/myproject进行操作了。
这个时候其实你一共有三个存放代码的地点:1)远程googel code;2)mirror目录;3)本地分支。你的工作则是在本地分支上面进行。需要知道:
1)svk提供smerge命令,将一个分支的变化合并到另外一个分支
2)对镜像目录的任何改变,都会自动实时同步到远程respository
3)svk sync的命令用于同步镜像目录3、将本地分支的变化提交到远程respository。通过上面的知识,你就知道只需要执行以下命令,就会知道更新远程的google code。
svk smerget -I //local/myproject //mirror/myproject
一般情况下你是在对本地分支进行多次提交后才去执行这个命令,如果不使用“-I”(--incremental)参数,svk只会将本地分支的当前状况合并到//mirro/中,这样镜像目录会丢失你之前每次提交的信息(例如注释等)。
4、将远程respository的变化更新到本地。由于我目前是一个人玩,就不存在这个动作,但是根据上面的知识,我们知道需要两个命令
svk sync //mirror/myproject
svk smerget -I //mirror/myproject //local/myproject遗留问题:
我有时候会在proxy环境下工作,不清楚如何对svk进行proxy设置。在SVKFAQ上发现说svk还不支持proxy,不过可以通过subversion,但我就是找不到具体应该怎么弄。在网络上发问,也没有人理睬,更加诡异的是死活无法加入svk maillist。参考资料:
1. Version Control with SVK
2. Google Code: Getting started with svk
3. Explaining the SVK Workflow -
2008-04-22
Google中国地图的偏差 - [技术]
在中国Google提供地图相关服务其实有三种,Google Earth, Google Map和Google地图。Earth和Map一个是客户端软件,一个是Web服务,在操作上有所不同,但他们的数据几乎是一样的,主要是一个卫星图层加上例如道路、地点信息等数据。而Google地图则是专门针对中国推出的Web站点,缺少最重要的卫星图,主要是城市的街道图而已。
另外Google Map去年推出一个“My Maps”功能,让用户能够在创建自己的地图信息,例如你可以将自己小区的一些主要地点标志在上面,创建自己的小区地图。我最近才发现,这个My Maps功能和Google地图是通用的,从而发现原来Google中国地图存在比较大的偏差,例如下图是我在Google Map中标志的广州中信广场:
(上图的小黄房子标志就是中信广场所在地)
然在在Google地图打开“My Maps”之后,却发现小黄房子与地图上所标示的中信广场偏移几百米的距离(西北方向),也证实了“根据国家相关技术规范的规定,导航电子地图强制安全处理指的是国家有关部门从维护国家秘密和国家安全的目的出发,对导航电子地图进行的空间位置变型技术处理,使地图上地物的地理坐标的经纬度与实际发生偏差。”这个说法。
怪不得Google中国地图没有卫星图,估计一是中共不容许,另外如果把卫星图和地图一重叠,就更加一团糟。
最后,附新闻一则:《电脑报》:99%互联网地图签生死薄 Google将被重点监管
20090415 Update:朋友llz写了一个软件,能够解决Andriod上Google地图偏差的问题(http://aleung.blogbus.com/logs/37558170.html)
-
2008-03-08
Ramdisk,以及其他 - [技术]
上周公司给我发了新电脑,Dell D630,更换我已经用了4年多的Dell D600。新电脑到手后,主要是安装常用软件,例如FireFox、Evernote、iTune等,还好有不少常用开源工具无需重装,拷贝即可。这台电脑本身不算突出,但公司这次比较豪爽地给了4G内存,我应该怎样对待这4G内存呢。我首先想到的是,应该不需要设置虚拟内存了吧,但如果将虚拟内存设置为0,对系统有没有影响呢?知之为知之,不知google之,让我找到一篇不错的文章“谈大内存系统设置优化”。原来更重要的不是虚拟内存,而是如何利用内存减少磁盘IO。想想也是,现在一部电脑最慢的部分就是硬盘了,如果能够将频繁读写硬盘的操作转移到内存上必然对性能就较大的提高,而且还能减少磁盘碎片的产生。我参照文章所述设置了系统,感觉还不错,我采用是一个免费的软件“Gavotte Ramdisk”。除了设置系统临时目录外,我还想到,其实也可以将数据库也存放到内存中去提高应用的性能。应用中常常有需要将存放大量临时数据的需求,一般都是采用“缓存”方式,如果要处理并发读写,相对比较麻烦。而数据库的功能已经非常成熟,将数据库建立在内存里,就是一个功能超级强大的缓存系统。虽然重新开机数据会丢失,但是这对缓存是无所谓的。其实这种做法自己以前也有所了解,不过亲手发现RamDisk是如此简单实现,大大加深了我的印象。想想网上流传的比尔盖茨那个“640K内存足以”预言,技术的发展真是往往出乎人的意料之外。以前采用“虚拟内存”技术,用硬盘来弥补内存的空间;现在反过来,用内存来代替硬盘。 -
2007-11-01
提交句库的firefox search plugin - [技术]
老艳刚刚找我抱怨,说要用这个句库search plugin 有点麻烦,需要手工下载文件、放置到特定目录等。
本着服务大众的精神,呵呵,我就到mozilla上提交了该插件,现在你只需要到(http://mycroft.mozdev.org),输入“jukuu”查询,就能直接安装,欢迎试用 :)
-
2007-10-31
ZK plugin for Grails - [技术]
(update 20071102: I have just released the 0.2 version of ZK plugin, which allows Grails controllers and ZK work together now, you can download the new version on (http://docs.codehaus.org/display/GRAILS/ZK+Plugin ).)
Grails : agile, industrial strength, rapid web application development made easy!
ZK: the simplest way to make Web applications rich!Do you want to combine both of them?1. Install & test the ZK plug-in1) Install Grails first (http://grails.codehaus.org/Installation)2) Follow the Grails Quikc Start (http://grails.codehaus.org/Quick+Start), to build a simple Grails project with the Book domain class3) Download ZK plugin (http://flyisland.blogbus.com/files/11938117910.zip), and rename the file to "grails-zkplugin-0.1.zip", make sure you are in the root directory of your project, typegrails install-plugin full_path_to_zk_plugin4) create a new file "list.zul" under "your_project\web-app" with the following content--------------------------list.zul-------------------------------<?xml version="1.0" encoding="UTF-8"?><?page zscriptLanguage="Groovy"?><window border="normal" title="Groovy Test" id="MainWindow" width="400px">
<zscript>
books = Book.findAll()
</zscript>
<listbox>
<listhead>
<listheader label="ID"/>
<listheader label="Title"/>
<listheader label="Author"/>
</listhead>
<listitem forEach="${books}">
<listcell label="${each.id}"/>
<listcell label="${each.title}"/>
<listcell label="${each.author}"/>
</listitem>
</listbox>
</window>
-----------------------------------------------------------------5) test "list.zul", make sure you are in the root directory of your project, type "grails run-app", then browse to "http://localhost:8080/your_project/list.zul", you should be able to view the following list.
2. How does ZK plugin do it?Actually, you can find the whole source code of this plugin by browsing to "your_project\plugins\zkplugin-0.1", right now, ZK plugin is made up of three parts.1) maintaining the basic ZK realted java libaries2) Participating in web.xml Generation to add ZK related servlets3) Participating in web.xml Generation to modify the "url-pattern" of Grails filter "sitemesh"I find that accessing the "zul" file will just return a blank page with "sitemesh" filter mapped to "/*", so I have to modify the "url-pattern" of Grails filter "sitemesh". It means that you can NOT use Grails controller after you installed the ZK plugin!I'm still working on it to see if there is a way to let Grails filter "sitemesh" to ignore some particular url-patterns, your comments are welcome!3. Accessing the domain classAs you can see in the above sample, accessing domain classes in ZK is very simple, just as what you can do in Grails controller.4. Accessing the serviceAccessing grails services in ZK is also very easy. Grails will define a bean with name "xxxService" of each service "XxxService", and you can access the Services in zscript directly with the help of DelegatingVariableResolver .1) create a service.create a new file "HelloService.groovy" under "your_project\grails-app\services" with the following content-------------------HelloService.groovy-----------------------class HelloService {boolean transactional = truedef serviceMethod(username) {return username+", welcome to ZK&Grails world!"}}-----------------------------------------------------------------2) create a zul filecreate a new file "service.zul" under "your_project\web-app" with the following content-------------------------service.zul---------------------------<?xml version="1.0" encoding="UTF-8"?>
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<?page zscriptLanguage="Groovy"?>
<window border="normal" title="Groovy Test" id="MainWindow" width="400px">
<textbox id="username"/>
<button label="Call HelloService"
onClick="result.value=helloService.serviceMethod(username.value)"/>
<separator/>
<label id="result"/>
</window>
-----------------------------------------------------------------
3) test it
Browse to "http://localhost:8080/your_project/service.zul", input a name and click the button!
Enjoy it, and any comments is welcome! -
2007-08-07
[groovy]通过builder了解groovy的动态性 - [技术]
Builder是Groovy相当有用的一个特性,样例常常用生成XML来展现Builder所带来的便利性,例如要生成下述的XML文档:- <books amount='2'>
- <description>books to lean groovy<description>
- <book1 name='Groovy in Action' ISBN='1-932394-84-2' />
- <book2 name='Getting Started with Grails' ISBN='978-1-4303-0782-2' />
- </books>
所使用的Groovy代码是:import groovy.xml.MarkupBuilderdef xml = new MarkupBuilder()xml.books(amount:2) {
description 'books to lean groovy'
book1 (name:'Groovy in Action', ISBN:'1-932394-84-2')
book2 (name:'Getting Started with Grails', ISBN:'978-1-4303-0782-2')
}xml.println()从例子可以看到,在groovy中创建xml是相当的便利,你可以写一个相应功能的java程序来对照。实际上这个groovy代码结构跟所生成的html结果十分相似,从某种意义上来说,这像是在使用创建"BOOK XML"的专用的DSL了。我第一次看到这样代码的时候,心里很是疑惑,这是什么语法,为什么groovy自身的“groovy.xml.MarkupBuilder”类 会有books这个方法?慢慢了解其中的机制后,觉得builder真是groovy精华的集大成者,所谓“解脱之味不独饮”,在此与朋友分享。1. groovy中是如何调用方法的?groovy基本上是兼容java语法的,但是为了更加方便开发人员,groovy作了许多便利的改进,比如说方法调用中省略括号description 'books to lean groovy' 等同于description('books to lean groovy')所以在java中常用的打印语句System.out.println("Hello World!"); 可以简写为:println "Hello World!"同时调用方法的时候,传递参数可以加上参数的名字,例如def method1(String name, int age) 的调用方式可以是:method1 (age:30, name:"Tom")而groovy方法调用中与我以前所接触语言的最大不同就是Closure作为参数,而为了方便编写代码,一般也是将Closure写在方法调用的最后,因此上述代码的:xml.books(amount:2) {...}等同于xml.books(amount:2, {...})这样一来上述代码的字面意思就明白了,是方法调用中包含Closure,Closure中又包含方法调用的混合体,不过用了groovy的特殊写法。(注:关于groovy中方法调用的更详细说明,请参考Groovy Statmements)2. 无中生有的方法知道上述语句的字面意思后,但还是不知道为什么这些方法调用会成功呢,因为我们的代码并没有定义这些方法。在groovy中,所有的东西都是对象,而所有的对象都必须实现GroovyObject接口,该接口定义的方法不多,其中一个是:Object invokeMethod(String name, Object args)。原来在groovy中,编译器会将所有的方法调用转换成对invokeMethod的调用,例如:xml.books(amount:2) {...}会转换成xml.invokeMethod("books", list of parameters)groovy会将方法中的参数放入一个列表,作为invokeMethod()方法的第二个参数,invokeMethod()缺省实现将调用对象的同名函数,所以平时你对此是没有察觉。也就是说MarkupBuilder不必事先定义books(), book1()这些方法,它可以假装拥有这些方法,只需要在invokeMethod()的实现中根据name和args的值生成相应的xml代码即可。实际上,在GroovyObject接口背后还有一个更加重要的MetaClass,从而使得groovy对象可以在运行时更改对象和类的行为,在groovy中可以轻易做到的有:1)假装拥有某些方法,这就是MarkupBuilder干的,我觉得这一点在GPath中发挥的淋漓尽致!2)在语言级别支持Intercept模式3)将对自身方法的调用委派给其他对象完成(Delegate模式)3. 更加灵活的方法名注意到invodeMethod()的name参数是String类型,我第一个想法就是是否能够用字符串来做方法名呢,例如:xml."books"(amount:2) {...}实验结果证实这样的写法是OK的,而因为groovy中字符串的特性,上述代码还可以这么写:import groovy.xml.MarkupBuilderdef xml = new MarkupBuilder()
def names = ['Groovy in Action', 'Getting Started with Grails']
def isbns = ['1-932394-84-2', '978-1-4303-0782-2']xml.books(amount:names.size()) {
description 'books to lean groovy'
for(int i in 0..(names.size()-1)){
def s = "book"+(i+1)
"$s"(name:names[i],ISBN:isbns[i])
}
}xml.println()我最近在编写一个数独解题程序,就大大享受到字符变量做方法名的好处。因为在解题中往往要对“行/列”两种情况都做一次,代码结构基本一致,只 是多处调用的方法名和属性名不同。在普通Java程序中,要将这样的两段代码合并成一个方法少不了一大堆的if..then语句,而groovy只需要在 开头设置好方法名即可,极为方便。4. 创建自己的builder如果你喜欢groovy builder这种模式,也可以创建自己builder,只要你的程序中存在用Builder模式可以解决的问题,groovy必然能帮上大忙,而且十分简便、优雅。创建自己的builder也很简单,只需要继承BuilderSupport类,并实现其中几个抽象方法包括:1)四种形式的createNode方法,groovy会根据你使用的形式自动调用相应的方法方法名 参数形式 使用样例 createNode Object name foo() createNode Object name, Object valuefoo('x') createNode Object name, Map attributes foo(a:1) createNode Object name, Map attributes, Object value foo(a:1, 'x') 2) void setParent(Object parent, Object child)设置树状继承层次,当你创建子元素的时候,该方法就会被调用3)void nodeCompleted(Object parent, Object node)在子元素定义完毕后,该方法也会被自动调用。例如上述生成xml的代码会被转换成以下的调用方式:import groovy.xml.MarkupBuilderdef xml = new MarkupBuilder()
def names = ['Groovy in Action', 'Getting Started with Grails']
def isbns = ['1-932394-84-2', '978-1-4303-0782-2']def books = xml.createNode('books', [amount:names.size()])
def des = xml.createNode('description', 'books to lean groovy')
xml.setParent(books, des)
xml.nodeCompleted(books, des)
for(int i in 0..(names.size()-1)){
def s = "book"+(i+1)
def book = xml.createNode(s, [name:names[i],ISBN:isbns[i]])
xml.setParent(books, book)
xml.nodeCompleted(books, book)
}
xml.nodeCompleted(null, books)xml.println()在Groovy中令人感兴趣的Builder还有:1)SwingBuilder和GroovySWT,用于生成Swing/SWT界面,UI界面用Builder模式来产生是再合适不过的了。不过GroovySWT的发展似乎不如SwingBuilder好,在用户邮件列表中常常提到SwingBuilder,而GroovySWT则好长时间没有被人关注了。2)属于Grails项目的Spring Bean Builder,通过Builder来编写Spring的配置文件要比写XML简洁一百倍,而且更重要的是你可以很方便地在运行时动态生成Spring配置。通过这些精彩例子,相信你不难发现groovy builder能够在你程序中大展身手的地方。这篇文章就到这里,希望我所描述的能够引发你去了解groovy的兴趣,groovy的确是个好东西!注:在本文中还有一个关键的地方没有说明,就是Cloure中的方法调用{description 'books to lean groovy'}为什么会触发调用xml.createNode()方法?说明这个问题的主要关键有两个:1)Closure中变量、方法的作用范围,我们在Closure中能够访问到谁的变量、方法,为什么?2)Closure是如何将对自身方法的访问“委派”给其他对象的?如果我够勤奋的话,我会在另一篇blog讨论这两个问题 :)










