好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

基于MMSeg算法的中文分词类库

基于MMSeg算法的中文分词类库

基于MMSeg算法的中文分词类库

最近在实现基于lucene.net的搜索方案,涉及中文分词,找了很多,最终选择了MMSeg4j,但MMSeg4j只有Java版,在博客园上找到了*王员外*( http://www.cnblogs.com/land/archive/2011/07/19/mmseg4j.html  )基于Java版的翻译代码,但它不支持最新的Lucene.Net 3.0.3,于是基于它的代码升级升级到了最新版Lucene.Net (≥ 3.0.3),同时将其中大部分Java风格代码修改为.Net风格,并修正了其中几个小错误。

为了方便大家使用,我把修改后代码放到Github上了,并包含简单示例代码。另外,为了方便使用,制作了NuGet安装包,上传到了NuGet上,使用时,直接NuGet搜索Lucene.Net.Analysis.MMSeg即可。

Git地址

https://github.com/JimLiu/Lucene.Net.Analysis.MMSeg

NuGet地址

https://nuget.org/packages/Lucene.Net.Analysis.MMSeg/

PM> Install-Package Lucene.Net.Analysis.MMSeg

使用

一共三种搜索模式供选择:

SimpleAnalyzer

 Analyzer   analyzer   =   new   SimpleAnalyzer  (); 

MaxWordAnalyzer

 Analyzer   analyzer   =   new   MaxWordAnalyzer  (); 

ComplexAnalyzer

 Analyzer   analyzer   =   new   ComplexAnalyzer  (); 

具体使用方法,请参考代码中的示例和lucene.net的文档

Scala中的语言特性是如何实现的(1)

2013-05-09 22:38 by 崔鹏飞, 55 阅读,  0  评论,  收藏 ,  编辑

Scala可以编译为Java bytecode和CIL,从而在JVM和CLI之上运行。Scala有很多在Java和C#的世界中显得陌生的语言特性,本文将分析这些语言特性是如何实现的。

object

Scala中可以像这样创建object:

 1 
 2 
 3 
 4 
 5 
   object   HowIsObjectImplementedInScala   { 
     def   printSomething  ()   { 
       println  (  "printSomething"  ) 
     } 
   } 
  

然后在代码的其他地方调用printSomething,一个object究竟是什么东西呢? 我们将这段Scala编译为Java bytecode,然后反编译为Java,会发现编译器为HowIsObjectImplementedInScala这个object生成了两个类:

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
   public   final   class   HowIsObjectImplementedInScala 
   { 
     public   static   void   printSomething  () 
     { 
       HowIsObjectImplementedInScala  ..  MODULE  $  .  printSomething  (); 
     } 
   } 
   public   final   class   HowIsObjectImplementedInScala  $ 
   { 
     public   static   final    MODULE$  ; 
     static 
     { 
       new   (); 
     } 
     public   void   printSomething  () 
     { 
       Predef  ..  MODULE  $  .  println  (  "printSomething"  ); 
     } 
     private   HowIsObjectImplementedInScala$  () 
     { 
       MODULE$   =   this  ; 
     } 
   } 
  

第一个类只包含一个静态方法,其实现依赖于第二个叫做HowIsObjectImplementedInScala$的类。

HowIsObjectImplementedInScala$是一个单例,其静态块实例化自己并把this赋值给MODULE$这个public static的成员,从而可以被外界访问。

同样,我们可以把这段代码编译为CIL,然后反编译为C#:

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
   public   sealed   class   HowIsObjectImplementedInScala 
   { 
     public   static   void   printSomething  () 
     { 
       HowIsObjectImplementedInScala  $  .  MODULE  $  .  printSomething  (); 
     } 
   } 
   public   sealed   class   HowIsObjectImplementedInScala  $   :   ScalaObject 
   { 
     public   static   HowIsObjectImplementedInScala  $   MODULE  $  ; 
     public   override   void   printSomething  () 
     { 
       Predef  $  .  MODULE  $  .  println  (  "printSomething"  ); 
     } 
     private   HowIsObjectImplementedInScala  $  () 
     { 
       HowIsObjectImplementedInScala  $  .  MODULE  $   =   this  ; 
     } 
     static   HowIsObjectImplementedInScala  $  () 
     { 
       new   HowIsObjectImplementedInScala  $  (); 
     } 
   } 
  

和Java代码大同小异,除了静态构造和某几个关键字外,基本一样。一个object就是一个Scala编译器帮我们实现的singleton。

var和val

var:可变。val:不可变。关于这两个关键字何时该使用哪一个,这里不做讨论,我们只是观察这二者在编译后是如何被表示的。

这段Scala代码:

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
   class   HowAreVarAndValImplementedInScala   { 
     var   v1   =   123 
     val   v2   =   456 
     def   method1  ()   =   { 
       var   v3   =   123 
       val   v4   =   456 
       println  (  v3   +   v4  ) 
     } 
   } 
  

定义了两个字段一个var,一个val,方法中定义了两个局部变量,一个var,一个val。

编译为Java bytecode并反编译之后:

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
   public   class   HowAreVarAndValImplementedInScala 
   { 
     private   int   v1   =   123  ; 
     private   final   int   v2   =   456  ; 
     public   int   v1  () 
     { 
       return   this  .  v1  ; 
     } 
     public   void   v1_$eq  (  int   x$1  )   {   this  .  v1   =   x$1  ;   } 
     public   int   v2  ()   {   return   this  .  v2  ;   } 
     public   void   method1  ()   { 
       int   v3   =   123  ; 
       int   v4   =   456  ; 
       Predef  ..  MODULE  $  .  println  (  BoxesRunTime  .  boxToInteger  (  v3   +   v4  )); 
     } 
   } 
  

声明为字段的v1和v2,一个是普通字段,另一个则被标记为final。编译器为v1生成了getter和setter,为v2则只有getter,因为v2作为immutable的字段是不可以被重新赋值的。

有趣的是方法中的局部变量都是普通的变量,没有被final修饰。

再来看这段Scala编译为CIL再反编译为C#之后的样子:

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
   public   class   HowAreVarAndValImplementedInScala   :   ScalaObject 
   { 
     private   int   v1  ; 
     private   int   v2  ; 
     public   override   int   v1  () 
     { 
       return   this  .  v1  ; 
     } 
     public   override   void   v1_  $  eq  (  int   x  $  1  ) 
     { 
       this  .  v1   =   x  $  1  ; 
     } 
     public   override   int   v2  () 
     { 
       return   this  .  v2  ; 
     } 
     public   override   void   method1  () 
     { 
       int   v3   =   123  ; 
       int   v4   =   456  ; 
       Predef  $  .  MODULE  $  .  println  (  v3   +   v4  ); 
     } 
     public   HowAreVarAndValImplementedInScala  () 
     { 
       this  .  v1   =   123  ; 
       this  .  v2   =   456  ; 
     } 
   } 
  

有一个明显的问题,v2没有标为readonly(C#世界中用于声明变量不可以重新赋值的关键字),这是compiler的bug吗?

除此之外,和Java代码一致。但是有趣的是代码中的所有public方法(包括上一段演示object的代码)都被标为了override,原因不明。

小结

本来以为研究这么简单的两个语言特性不会有啥收获,仅仅是反编译一下,看看compiler都做了啥,满足下好奇心罢了。

结果还是有意外收获,我在反编译后的代码中发现了三个有趣的问题:

在Scala中被声明为val的v4为什么在反编译的Java中不是final的呢? 在Scala中被声明为val的v2为什么在反编译的C#中不是readonly的呢? 为什么反编译出来的C#代码中的实例级公开方法都是标有override的呢?

为什么呢?为什么呢?为什么呢?答案下期揭晓。

如何一步一步推导出Y Combinator

2013-04-13 22:24 by 崔鹏飞, 585 阅读,  1  评论,  收藏 ,  编辑

粘贴过来的代码高亮有问题,可以到我的另一个博客阅读: http://cuipengfei.me/blog/2013/04/09/make-y/  

本文讲什么?

本文用Scheme(Racket)代码为例,一步一步的推出Y Combinator的实现。

本文不讲什么?

Y Combinator是什么,干什么用的,它为什么能够work,它的数学含义以及实际应用场景,这些话题由于篇幅所限(咳咳,楼主的无知)不在本文论述范围之内。

如果有兴趣,请参考维基:  http://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator

鸣谢

感谢Jojo同学的  这段JavaScript代码 的启发,我写了 对应的Scheme实现 。然后才有了本文。

正文开始

我们知道Y Combinator可以帮匿名函数实现递归。那就从一个广为人知的递归函数-阶乘开始吧。

 1 
 2 
 3 
   (  define   (  fac1   n  ) 
     (  if   (  <   n   2  )   1 
         (  *   n   (  fac1   (  -   n   1  ))))) 
  

如果n小于2,则返回1,否则开始递归,简单明了。如果像这样调用它一下:

 1 
   (  fac1   5  ) 
  

会返回120,结果无误。

上面是阶乘的递归实现,它有一个名字叫做fac1,但是如果用匿名函数实现阶乘呢?

 1 
 2 
 3 
 4 
   (  lambda   (  f  ) 
     (  lambda   (  n  ) 
       (  if   (  <   n   2  )   1 
           (  *   n   (  f   (  -   n   1  )))))) 
  

这个匿名函数“梦想着”其调用者会把该函数自己的实现作为参数传递进去。

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
   (((  lambda   (  f  ) 
       (  lambda   (  n  ) 
         (  if   (  <   n   2  )   1 
             (  *   n   (  f   (  -   n   1  )))))) 
     (  lambda   (  f  ) 
       (  lambda   (  n  ) 
         (  if   (  <   n   2  )   1 
             (  *   n   (  f   (  -   n   1  )))))))   1  ) 
  

我们把匿名函数重复写一遍,就可以计算1或者是0的阶乘,但是要计算3的阶乘呢?那就得这么写:

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
   (((  lambda   (  f  ) 
       (  lambda   (  n  ) 
         (  if   (  <   n   2  )   1 
             (  *   n   (  f   (  -   n   1  )))))) 
     ((  lambda   (  f  ) 
        (  lambda   (  n  ) 
          (  if   (  <   n   2  )   1 
              (  *   n   (  f   (  -   n   1  )))))) 
      ((  lambda   (  f  ) 
         (  lambda   (  n  ) 
           (  if   (  <   n   2  )   1 
               (  *   n   (  f   (  -   n   1  )))))) 
       (  lambda   (  f  ) 
         (  lambda   (  n  ) 
           (  if   (  <   n   2  )   1 
               (  *   n   (  f   (  -   n   1  )))))))))   3  ) 
  

想要计算一个大于2的n的阶乘,就得把这个匿名函数重复写n+1次。这么多的重复代码,这么多的括号。。。

所以我们需要一个神奇的函数,Y,它可以接受一个匿名的伪递归函数作为参数,产出一个真递归的函数。 这个神奇的Y作用在上面的匿名函数上之后产出的结果就可以用来计算任何n的阶乘。下面的代码会输出120(如果Y已经实现了的话)。

 1 
 2 
 3 
 4 
   ((  Y   (  lambda   (  f  ) 
          (  lambda   (  n  ) 
            (  if   (  <   n   2  )   1 
                (  *   n   (  f   (  -   n   1  )))))))   5  ) 
  

下面就开始一步步的构造这个神奇的Y吧。

为了便于推导,暂时给这个匿名函数一个名字,叫做fake_fac。

 1 
 2 
 3 
 4 
 5 
   (  define   fake_fac 
     (  lambda   (  f  ) 
       (  lambda   (  n  ) 
         (  if   (  <   n   2  )   1 
             (  *   n   (  f   (  -   n   1  ))))))) 
  

有了这个名字之后,再要计算3的阶乘就容易了一些。

 1 
   ((  fake_fac   (  fake_fac   (  fake_fac   fake_fac  )))   3  ) 
  

观察上面的代码,我们把fake_fac传递给它自己,得到一个返回值,把这个返回的值再次传递给fake_fac,再得到一个新的返回值,又把新的返回值传递给fake_fac,得到最终的返回值,最后把3传递给这个返回值。

可以看到,我们在不停的把fake_rec传给它自己,所以定义一个helper吧:

 1 
   (  define   (  callself   f  )   (  f   f  )) 
  

这个helper一会儿会派上用场。

现在看看fake_fac中的f是什么呢?对于((fake_fac (fake_fac (fake_fac fake_fac))) 3)这行代码中的最右侧的fake_fac来说,f没有用,因为这个fake_fac自己都没有被调到,它只是起个占位符的作用,实际上这行代码((fake_fac (fake_fac (fake_fac 1))) 3)和上面的那行是等价的。

对于右侧第二个fake_fac来说,f就是fake_fac。对于左侧第二个fake_fac来说,f是(fake_fac fake_fac)的返回值。

对于左侧第一个fake_fac来说,f是(fake_fac (fake_fac fake_fac))的返回值。

由此可见,f是fake_fac对自己反复调用的返回值。而且从fake_fac的定义可见,我们总是给f传递一个数字n,这样的话,我们再写一个helper:

 1 
   (  lambda   (  n  )   ((  f   f  )   n  )) 
  

再把这个helper传递给fake_fac。

 1 
   (  fake_fac   (  lambda   (  n  )   ((  f   f  )   n  ))) 
  

但是上面这两段代码是有问题的,因为f的值无法确定。

有句话说的好: if you don’t know exactly what you want to put somewhere in a piece of code, just abstract it out and make it a parameter of a function. 所以我们就把f抽成参数吧。

 1 
 2 
   (  define   (  callselfWithN   f  ) 
     (  fake_fac   (  lambda   (  n  )   ((  f   f  )   n  )))) 
  

我们希望这个helper可以帮fake_fac无限次的调用自己。

现在,我们该怎么调用callselfWithN呢?不能把fake_fac传给它,因为那样的话(f f)就只是fake_fac对自己的调用,它只能计算0或者1的阶乘。所以要把callselfWithN这个我们希望可以帮fake_fac实现无限次自调用的函数传给callselfWithN它自己。

 1 
   ((  callselfWithN   callselfWithN  )   5  ) 
  

这行代码可以返回120,结果正确了!

记得前面定义的第一个helper吗?现在用的上了:

 1 
   ((  callself   callselfWithN  )   5  ) 
  

现在把callselfWithN带入:

 1 
 2 
   ((  callself    (  lambda   (  f  ) 
     (  fake_fac   (  lambda   (  n  )   ((  f   f  )   n  )))))   5  ) 
  

可以看出,这段代码和fake_fac是紧耦合的,把它抽到参数上去:

 1 
 2 
 3 
   (  define   (  Y3   fake_recur  ) 
     (  callself    (  lambda   (  f  ) 
                  (  fake_recur   (  lambda   (  n  )   ((  f   f  )   n  )))))) 
  

然后再把callself也带入:

 1 
 2 
 3 
 4 
 5 
   (  define   Y   (  lambda   (  fake_recur  ) 
               ((  lambda   (  f  )   (  f   f  )) 
                (  lambda   (  f  ) 
                  (  fake_recur 
                   (  lambda   (  n  )   ((  f   f  )   n  ))))))) 
  

现在Y不依赖于任何其他函数了,测试一下Y,把前面的计算阶乘的匿名函数传给它:

 1 
 2 
 3 
 4 
   ((  Y   (  lambda   (  f  ) 
         (  lambda   (  n  ) 
           (  if   (  <   n   2  )   1 
               (  *   n   (  f   (  -   n   1  )))))))   5  ) 
  

能够返回120,正确!Y Combinator构造完成!

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于基于MMSeg算法的中文分词类库的详细内容...

  阅读:41次