• 基于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构造完成!

  • 相关阅读:
    dwSun带你选Python的编辑器/IDE
    ubuntu中文乱码解决
    解决matplotlib中文显示
    1506.01186-Cyclical Learning Rates for Training Neural Networks
    1503.02531-Distilling the Knowledge in a Neural Network.md
    1804.03235-Large scale distributed neural network training through online distillation.md
    mysql导入太慢解决方法
    已某个时间单位(日月周年)来分割时间段
    阿里云邮件推送
    阿里云短信推送服务
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3070287.html
Copyright © 2020-2023  润新知