基于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算法的中文分词类库的详细内容...