技术,互联网,eLearning …

用Scala写了一个简单的分词工具

起了个名叫做segcala, 地址 http://segcala.googlecode.com。

这几天对Scala比较感兴趣,看了一些资料,不过语言这东西光看不练还是没感觉,就写了这样一个分词程序。使用了Chih-Hao Tsaimmseg分词算法。做为依赖注入的粉丝,程序里使用了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;
        }
    }
}

Web 2.0下的知识分享

上面这幅图来自这篇文章:Mosh Pit as Innovation Model

长期以来,知识是一种财富,掌握在少数人手中,很多“专家”乐此不疲的囤积知识,生怕别人偷走他们的想法。Web 2.0改变了这一切,我们可以看到,知识传播不在是少数知识所有者的专利,而是形成了一个知识网,每个人都可以成为知识分享者。

Karl Nelson提出了“开源知识(Open Source Knowledge)”的概念:“信息过时的速度是非常快的,特别是在技术领域,如果你将其存在你的头脑中不会产生什么价值,保存其价值的最好的办法是与他人共享你的知识 ”。

但是想真正使教育适应这一变化很难,这需要观念上的改变,教育本身就有一定的惰性,短时间内很难改变。但是对于那些出生于数字时代的小孩来说,他们都是数字原住民,他们习惯了网络,习惯了从网络上获取和分享。只是我们的老土教育还在束缚着他们。

试用Spring 3.0对于JSR 303(bean validation)的支持

JSR 303是标准的bean validation框架, 还没有发布正式版,最新的版本是1.0CR5。通过JSR 303,我们可以为域模型里添加@NotNull, @Email等注解,然后在应用程序的不同层里使用这些注解对于域模型做验证。

JSR 303目前有两个实现,分别是agimatec-validationHibernate Validator 4.0,这二者目前都没有正式版,但是测试版已经可以使用了。

Spring 3.0刚刚发布了RC1版,里面提供了对于JSR303的初步支持,试了一下,感觉还不错。

测试程序使用maven, 用的是hibernate的实现。首先,需要在pom里加入相关依赖:

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>1.0.CR5</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>4.0.0.CR1</version>
    </dependency>

在Spring的配置文件里加入validator factory:

<bean id="validator" >
  <property name="providerClass" 
            value="org.hibernate.validator.HibernateValidator"/>
</bean>

测试用的model:

public class User{
    private String username;

    @NotNull(message = "用户名不能为空!")
    @Size(min = 3, max = 16, message = "用户名最少 {min}, 最大 {max} 个字符")
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

测试用例:

@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
@TransactionConfiguration(defaultRollback = true)
public class TestValidator{

    @Autowired
    @Qualifier("validator")
    private Validator validator;

    @Test
    public void testValidator(){
        User user = new User();
        user.setUsername("a");

        Set<ConstraintViolation<User>> vals = validator.getInvalidValues(user);

        Assert.assertEquals(1, vals.size());
    }
}

运行 mvn test,测试通过。

update: 如果要配合Spring MVC合用,则需要在applicationContext里配置:

  <bean>
    <property name="webBindingInitializer">
        <!-- Configures Spring MVC DataBinder instances -->
        <bean>
            <property name="validator" ref="validator" />
        </bean>
    </property>
</bean>

这样,对于标注为@Controller类里的@Valid标记的对象,Spring会自动做验证。

Sakai的组件模型

从知名度和用户量来看,Sakai不算是一个成功的学习管理系统(LMS),从这里的比较就可以看出来。但Sakai有大量资金支持,是集众多专业开发者的力量打造而成的,其软件设计方面还是颇值得研究的。

Sakai有一个内核,负责提供公用的服务并且管理不同的组件。此外,还有一些独立的web应用来实现多种不同的功能,这些web彼此之间相互隔离,但是可以调用Sakai的公用服务。这一功能是通过Sakai自有的Component Manager机制实现的。

Component Manager

Component Manager负责将Sakai的各部分组装起来,它使得service之间能够通讯,并且各个web app能够使用这些service。在通常情况下,servlet 容器里的web应用是独立运行的,它们之间不能通讯,并且不能输出可共享的服务。Component Manager通过自定义ClassLoader的方式,提供了一些可共享的服务,以供各个web应用使用,同时,Sakai也提供了不用关闭整个服务而重启插件应用的方法。Sakai通过Component Manager来避开servlet容器的限制。Sakai里共享的服务API运行于servlet容器的特定位置,这个位置对所有的web应用可见。

与Component Manager功能相似的工业标准

Sakai的Component Manager是一个非标准实现,有两个实现类似功能的标准:

  • EJB容器,有JBoss, Weblogic, Websphere等相关实现
  • OSGI,有Apache felix, Eclipse Equinox等实现。Spring提供对于OSGI的支持,新版本3.0的组件都做成了OSGI bundle的形式。还有Sping DM项目对OSGI提供支持。

Component Manager的工作原理

Sakai的结构如下图所示:

Sakai结构图

在第一个web app启动时,Component Manager会随之启动。Component Manager为每一个component创建一个用来加载它的ClassLoader:

/**
 * Create the class loader for this component package
 *
 * @param dir
 *        The package's root directory.
 * @return A class loader, whose parent is this class's loader,
           which has the classes/ and jars for this component.
 */
protected ClassLoader newPackageClassLoader(File dir)
{
    // collect as a List, turn into an array after
    List urls = new Vector();

    File webinf = new File(dir, "WEB-INF");

    // put classes/ on the classpath
    File classes = new File(webinf, "classes");
    if ((classes != null) && (classes.isDirectory()))
    {
        try
        {
            URL url = new URL("file:" + classes.getCanonicalPath() + "/");
            urls.add(url);
        }
        catch (Throwable t)
        {
        }
    }

    // put each .jar file onto the classpath
    File lib = new File(webinf, "lib");
    if ((lib != null) && (lib.isDirectory()))
    {
        File[] jars = lib.listFiles(new FileFilter()
        {
            public boolean accept(File file)
            {
                return (file.isFile() && file.getName().endsWith(".jar"));
            }
        });

        if (jars != null)
        {
            for (int j = 0; j < jars.length; j++)
            {
                try
                {
                    URL url = new URL("file:" + jars[j].getCanonicalPath());
                    urls.add(url);
                }
                catch (Throwable t)
                {
                }
            }
        }
    }

    // make the array from the list
    URL[] urlArray = (URL[]) urls.toArray(new URL[urls.size()]);

    // make the classloader - my loader is parent
    URLClassLoader loader = new URLClassLoader(urlArray, getClass().getClassLoader());

    return loader;
}

Component的ClassLoader和每一个web应用有共同的父ClassLoader,这个父ClassLoader可以看见/WEB-INF/classes下的类,和所有/WEB-INF/lib/下的jar包。Component的ClassLoader和web应用的ClassLoader的行为很像,只是有一点不同,这个ClassLoader是标准的URLClassloader,它会首先委托自己的父ClassLoader来加载类,只有当父ClassLoader无法找到类时,才会自己的类路径下去寻找。而web应用的ClassLoader的行为和标准的ClassLoader的行为是不一样的,因为根据Servlet规范,web应用的ClassLoader需要首先寻找自己的类路径下的类和jar包,然后才会去寻找父ClassLoader的类路径。

做了一个向Google日历添加按年循环的农历生日的工具

Google日历不支持事件按农历年循环。要添加农历生日事件,就只能一年一年的添加,用起来很不方便,于是自己操刀,做了一个可以添加按年循环的农历生日事件的工具。

应用基于GAE,地址是http://spur.appspot.com,操作界面如下图:

screenshot

填写相关信息和需要重复的年份范围,导出ics文件,再将ics文件导入到Google日历里就可以了。

目前只能通过导ics文件的方式添加,后续会考虑通过gdata访问Google Calendar API的方式。

返回顶部