很多中国人的习惯是按照农历过生日的,但是在Google Calendar里添加农历生日只能一年一年的添加,而不能一次添加多年,这样就很不方便。为了避免这个麻烦,在几个月前写了一个程序,用来生成按年重复的农历生日的ICS文件,导入Google Calendar里,避免一年一年的添加带来的麻烦。程序是一个Web应用,运行在GAE(Google App Endine)之上,地址在这里: http://spur.appspot.com
前几天有网友给我发信说他在用这个工具,问我能不能开源,因为appspot随时可能会被封掉,这样就用不了了。如果开源的话别人拿到源代码至少可以自己部署起来使用。我想这也是个不错的主意,本来这只是我个人写着玩的东西,能对人有帮助还是很高兴的。
于是在Google Code上建了个项目,把源代码放了上去,地址: http://code.google.com/p/lunar-cal-ics-gen/ 。
简单说明一下:
- 程序使用python语言编写,运行于GAE平台, 要想在本机运行,需要下载GAE SDK。当然,也可以修改代码改造成不依于GAE的Web应用。
- Web框架使用了从friendfeed开源出来的tornado,具体介绍可见这里 。
- 生成日历使用的是icalendar 库,计算农历的代码取自pyzh 项目。
目前添加日历的方式是以ics文件做为中介,最初曾设想通过GData API直接将日历添加到Google Calendar,不过由于时间关系,一直没来得及做这件事,也不确定这个想法是否可行。
程序截图
起了个名叫做segcala, 地址 http://segcala.googlecode.com。
这几天对Scala比较感兴趣,看了一些资料,不过语言这东西光看不练还是没感觉,就写了这样一个分词程序。使用了Chih-Hao Tsai的mmseg分词算法。做为依赖注入的粉丝,程序里使用了google guice做为依赖注入容器。
Scala在JVM上的运行效率可以达到和Java差不多的程度。做为同时支持面向对象和函数式编程思想的语言,其强大的表达能力则是Java所忘尘莫及。
以mmseg算法中求一个chunk的自由语素度的代码为例,看看Scala如何使程序变得更加简捷和清晰:
Scala版:
def largestSumMorphemicFreedomDegreeRule(chunks: List[Chunk]): List[Chunk] = {
val c = chunks.reduceLeft((c1, c2) => {if (c1.degreeOfMorphemicFreedom > c2.degreeOfMorphemicFreedom) c1 else c2})
chunks.filter(chunk => (chunk.degreeOfMorphemicFreedom == c.degreeOfMorphemicFreedom))
}
再看看Java版(取自solo L的mmseg库):
public class LSDMFOCWRule implements IRule {/* (non-Javadoc)
* @see org.solol.mmseg.core.IRule#invoke()
*/
public final IChunk[] invoke(final IChunk[] chunks) {
LSDMFOCWRuleComparator[] orderedChunks = new LSDMFOCWRuleComparator[chunks.length];
for (int i = 0; i < chunks.length; i++) {
orderedChunks[i] = new LSDMFOCWRuleComparator(chunks[i]);
}
Arrays.sort(orderedChunks);
int index = 0;
double degreeOfMorphemicFreedom = orderedChunks[index].getChunk().getDegreeOfMorphemicFreedom();
List list = new ArrayList(1);
list.add(orderedChunks[index].getChunk());
index++;
while (index < orderedChunks.length) {
if (orderedChunks[index].getChunk().getDegreeOfMorphemicFreedom() == degreeOfMorphemicFreedom) {
list.add(orderedChunks[index].getChunk());
} else {
break;
}
index++;
}
IChunk[] degreeOfMorphemicFreedomChunks = new IChunk[list.size()];
list.toArray(degreeOfMorphemicFreedomChunks);
return degreeOfMorphemicFreedomChunks;
}
static class LSDMFOCWRuleComparator implements Comparable {
private IChunk chunk;
public LSDMFOCWRuleComparator(IChunk chunk) {
this.chunk = chunk;
}
public IChunk getChunk() {
return chunk;
}
public int compareTo(Object obj) {
IChunk another = ((LSDMFOCWRuleComparator) obj).getChunk();
double temp = another.getDegreeOfMorphemicFreedom()
- chunk.getDegreeOfMorphemicFreedom();
if (temp > 0D) {
return 1;
} else if (temp < 0D) {
return -1;
} else {
return 0;
}
}
}