精华内容
下载资源
问答
  • ICTCLAS

    2014-11-10 15:06:22
    import ICTCLAS.I3S.AC.ICTCLAS50; public class TestMain { public static void main(String[] args){ try{ ICTCLAS50 testICTCLAS50 = new ICTCLAS50(); String argu = ".";
    import ICTCLAS.I3S.AC.ICTCLAS50;
    public class TestMain {
        public static void main(String[] args){
            try{
                ICTCLAS50 testICTCLAS50 = new ICTCLAS50();
                String argu = ".";
                if(testICTCLAS50.ICTCLAS_Init(argu.getBytes("GB2312")) == false){
                    System.out.println("Init Fail");
                }else{
                    System.out.println("Init Succeed!");
                }
                
                String sInput = "香港MSEK 2014夏装新款 男式短袖T恤  618爆品抢先逛 TX4002藏青 L-偏小一码";
                //未导入用户词典
                byte nativeBytes[] = testICTCLAS50.ICTCLAS_ParagraphProcess(sInput.getBytes("GB2312"), 0, 1);
                System.out.println(nativeBytes.length);            
                String nativeStr = new String(nativeBytes,0,nativeBytes.length,"GB2312");
                System.out.println("未导入用户词典分词结果:"+nativeStr);
                //导入用户词典
                int nCount = 0;
                String usrdir = "d:/userdict1.txt";
                byte[] usrdirb = usrdir.getBytes();
     //第一个参数为用户字典路径,第二个参数为用户字典的编码类型(0:type unknown;1:ASCII码;2:GB2312,GBK,GB10380;3:UTF-8;4:BIG5)
                nCount = testICTCLAS50.ICTCLAS_ImportUserDictFile(usrdirb, 2);        
                System.out.println("导入用户词个数:"+nCount);
                nCount = 0;            
                //导入用户词典之后再分词
    //  public native byte[] ICTCLAS_ParagraphProcess(byte[] sSrc, int eCodeType, int bPOSTagged);第一个参数就是待处理文本,
    //            注意要将String转换为bytes数组,第二个参数是字符集类型【我的源码中有介绍】,第三个参数0:无标注,1:有标注
                byte[] nativeBytes1 = testICTCLAS50.ICTCLAS_ParagraphProcess(sInput.getBytes("GB2312"), 0, 1);
                System.out.println(nativeBytes1.length);
                String nativeStr1 = new String(nativeBytes1,0,nativeBytes1.length,"GB2312");
                System.out.println("导入用户词典分词结果:"+nativeStr1);
                
                /*
                 * ICT_POS_MAP_SECOND  计算所二级标注集   0
                 * ICT_POS_MAP_FIRST  计算所一级标注集    1
                 * PKU_POS_MAP_SECOND   北大二级标注集    2
                 * PKU_POS_MAP_FIRST       北大一级标注集   3
                 * */
                //使用计算所二级标注集
                testICTCLAS50.ICTCLAS_SetPOSmap(0);
                byte[] nativeBytes2 = testICTCLAS50.ICTCLAS_ParagraphProcess(sInput.getBytes("GB2312"), 0, 1);
                System.out.println(nativeBytes2.length);
                String nativeStr2 = new String(nativeBytes2,0,nativeBytes2.length,"GB2312");
                System.out.println("计算所二级标注集:"+nativeStr2);
                //使用北大二级标注集
                testICTCLAS50.ICTCLAS_SetPOSmap(2);
                byte[] nativeBytes3 = testICTCLAS50.ICTCLAS_ParagraphProcess(sInput.getBytes("GB2312"), 0, 1);
                System.out.println(nativeBytes3.length);
                String nativeStr3 = new String(nativeBytes3,0,nativeBytes3.length,"GB2312");
                System.out.println("北大二级标注集:"+nativeStr3);    
                //释放分词组件资源            
                testICTCLAS50.ICTCLAS_Exit();
                
            }catch(Exception ex){
                
            }
        }
    }

    展开全文
  • ICTClAS

    2014-06-16 19:51:47
    ICTClAS分词系统是由中科院计算所的张华平、刘群所开发的一套获得广泛好评的分词系统,难能可贵的是该版的Free版开放了源代码,为我们很多初学者提供了宝贵的学习材料。  但有一点不完美的是,该源代码没有配套...

      ICTClAS分词系统是由中科院计算所的张华平、刘群所开发的一套获得广泛好评的分词系统,难能可贵的是该版的Free版开放了源代码,为我们很多初学者提供了宝贵的学习材料。

          但有一点不完美的是,该源代码没有配套的文档,阅读起来可能有一定的障碍,尤其是对C/C++不熟的人来说.本人就一直用Java/VB作为主要的开发语言,C/C++上大学时倒是学过,不过工作之后一直没有再使用过,语法什么的忘的几乎一干二净了.但语言这东西,基本的东西都相通的,况且Java也是在C/C++的基础上形成的,有一定的相似处.阅读一遍源代码,主要的语法都应该不成问题了.

        虽然在ICTCLAS的系统中没有完整的文档说明,但是我们可以通过查阅张华平和刘群发表的一些相关论文资料,还是可以窥探出主要的思路.

       该分词系统的主要是思想是先通过CHMM(层叠形马尔可夫模型)进行分词,通过分层,既增加了分词的准确性,又保证了分词的效率.共分五层,如下图一所示:

    基本思路:先进行原子切分,然后在此基础上进行N-最短路径粗切分,找出前N个最符合的切分结果,生成二元分词表,然后生成分词结果,接着进行词性标注并完成主要分词步骤.

    下面是对源代码的主要内容的研究:

    1.首先,ICTCLAS分词程序首先调用CICTCLAS_WinDlg::OnBtnRun()开始程序的执行.并且可以从看出它的处理方法是把源字符串分段处理。并且在分词前,完成词典的加载过程,即生成m_ICTCLAS对象时调用构造函数完成词典库的加载。关于词典结构的分析,请参加分词系统研究(二)。

    void CICTCLAS_WinDlg::OnBtnRun() 
    {

       ......

     //在此处进行分词和词性标记

      if(!m_ICTCLAS.ParagraphProcessing((char *)(LPCTSTR)m_sSource,sResult))
             m_sResult.Format("错误:程序初始化异常!");
       else
            m_sResult.Format("%s",sResult);//输出最终分词结果

        ......

    }

    2.在OnBtnRun()方法里面调用分段分词处理方法bool CResult::ParagraphProcessing(char *sParagraph,char *sResult)完成分词的整个处理过程,包括分词的词性标注.其中第一个参数为源字符串,第二个参数为分词后的字符串.在这两个方法中即完成了整个分词处理过程,下面需要了解的是在此方法中,如何调用其它方法一步步按照上图所示的分析框架完成分词过程.为了简单起见,我们先不做未登录词的分析。

    //Paragraph Segment and POS Tagging
    bool CResult::ParagraphProcessing(char *sParagraph,char *sResult)
    {

       ........

       Processing(sSentence,1); //Processing and output the result of current sentence.
      Output(m_pResult[0],sSentenceResult,bFirstIgnore); //Output to the imediate result

      .......

    }

    3.主要的分词处理是在Processing()方法里面发生的,下面我们对它进行进一步的分析.

    bool CResult::Processing(char *sSentence,unsigned int nCount)
    {

    ......

     //进行二叉分词

    m_Seg.BiSegment(sSentence, m_dSmoothingPara,m_dictCore,m_dictBigram,nCount);

    ......

     //在此处进行词性标注

    m_POSTagger.POSTagging(m_Seg.m_pWordSeg[nIndex],m_dictCore,m_dictCore);

    ......

    }

    4.现在我们先不管词性标注,把注意力集中在二叉分词上,因为这个是分词的两大关键步骤的第一步.

    参考文章:

    1.<<基于层叠隐马模型的汉语词法分析>>,刘群 张华平等

    2.<<基于N-最短路径的中文词语粗分模型>>,张华平 刘群

    展开全文
  • ictclas:ictclas的开源代码
  • ICTCLAS2013

    热门讨论 2013-01-15 11:00:32
    简介: 应各位ICTCLAS用户的要求,张华平博士提前发布ICTCLAS2013 版本,为了与以前工作进行大的区隔,并推广NLPIR自然语言处理与信息检索共享平台,从本版本开始,系统名称调整为NLPIR汉语分词系统。张博士先后倾力...
  • ICTCLAS2010

    2010-04-28 16:12:23
    ICTCLAS2010. ICTCLAS2010 ICTCLAS2010 ICTCLAS2010 ICTCLAS2010
  • ICTCLAS50 ICTCLAS50综合包

    2011-08-13 15:41:01
    ICTCLAS50综合包ICTCLAS50综合包ICTCLAS50综合包
  • #ICTCLAS Python Wrapper Python wrapper for ICTCLAS 2015 (or NLPIR 2015). NLPIR(ICTCLAS)中文分词系统Python接口。 ICTCLAS Python 接口 本项目提供了一个利用Python调用ICTCLAS(NLPIR)的便捷的接口,可以...
  • ICTCLAS源码解析

    2015-05-14 14:29:55
    ICTCLAS源码解析
  • vs2012 ictclas

    2015-06-03 20:58:12
    vs2012编译的ictclas库,和测试程序
  • ICTCLAS_api及使用方法

    2021-03-16 06:05:49
    摘要:Java源码,网络相关,ICTCLAS_api,使用方法 ICTCLAS_api及使用方法,有详细教程 运行环境:ASP+Access
  • 中科院分词ictclas

    2019-10-06 01:43:21
    中科院分词ictclas http://ictclas.org/index.html posted on 2011-09-29 19:29lexus 阅读(....
    http://ictclas.org/index.html

    posted on 2011-09-29 19:29 lexus 阅读(...) 评论(...) 编辑 收藏

    转载于:https://www.cnblogs.com/lexus/archive/2011/09/29/2195847.html

    展开全文
  • ICTCLAS2008

    2008-12-19 14:36:52
    ICTCLAS2008是最新版的分词系统,在以前的基础上增加了分词效果!
  • ICTCLAS2016分词系统2016

    2018-12-05 11:21:21
    中科院ICTCLAS2014分词系统下载包,文本分析工具,方便使用。
  • ICTCLAS解析

    2014-06-16 18:49:46
    ICTCLAS解析 ICTCLAS分词系统是由中科院计算所的张华平、刘群所开发的一套获得广泛好评的分词系统,该版的Free版开放了源代码,为初学者提供了宝贵的学习材料。我们可以在“http://sewm.pku.edu.cn/QA/”找到...

    ICTCLAS解析

    ICTCLAS分词系统是由中科院计算所的张华平、刘群所开发的一套获得广泛好评的分词系统,该版的Free版开放了源代码,为初学者提供了宝贵的学习材料。我们可以在“http://sewm.pku.edu.cn/QA/”找到FreeICTCLASLinux.tar的C++代码。

    可是目前该版本的ICTCLAS并没有提供完善的文档,所以阅读起来有一定的难度,所幸网上可以找到一些对ICTCLAS进行代码分析的文章,对理解分词系统的内部运行机制提供了很大的帮助。这些文章包括:

    1)http://blog.csdn.net/group/ictclas4j/;《ICTCLAS分词系统研究(一)~(六)》作者:sinboy。

    2)http://qxred.yculblog.com/post.1204714.html;《ICTCLAS 中科院分词系统 代码 注释 中文分词 词性标注》作者:风暴红QxRed 。

    按照上面这些文章的思路去读ICTCLAS的代码,可以比较容易的理顺思路。然而在我阅读代码的过程中,越来越对ICTCLAS天书般的代码感到厌烦。我不得不佩服中科院计算所的人思维缜密,头脑清晰,能写出滴水不漏而又让那些“头脑简单”的人百思不得其解的代码。将一件本来很简单的事情做得无比复杂...

    ICTCLAS中有一个名为CDynamicArray的类,存放在DynamicArray.cpp与DynamicArray.h两个文件中,这个DynamicArray是干什么用的?经过一番研究后终于明白是一个经过排序的链表。为了表达的更明白些,我们不妨看下面这张图:

    (图一)

    上面这张图是一个按照index值进行了排序的链表,当插入新结点时必须确保index值的有序性。DynamicArray类完成的功能基本上与上面这个链表差不多,只是排序规则不是index,而是row和col两个数据,如下图:

    (图二)

    大家可以看到,这个有序链表的排序规则是先按row排序,row相同的按照col排序。当然排序规则是可以改变的,如果先按col排,再按row排,则上面的链表必须表述成:

    (图三)

    在了解了这些内容的基础上,不妨让我们看看ICTCLAS中DynamicArray.cpp中的代码实现(这里我们只看GetElement方法的实现,其基本功能为给出row与col,然后将对应的元素取出来)。

    DynamicArray.cpp
    ELEMENT_TYPE CDynamicArray::GetElement(int nRow, int nCol, PARRAY_CHAIN pStart, 
      PARRAY_CHAIN *pRet) 

      PARRAY_CHAIN pCur = pStart; 
      if (pStart == 0) 
        pCur = m_pHead; 
      if (pRet != 0) 
        *pRet = NULL; 
      if (nRow > (int)m_nRow || nCol > (int)m_nCol) 
      //Judge if the row and col is overflow 
        return INFINITE_VALUE; 
      if (m_bRowFirst) 
      { 
        while (pCur != NULL && (nRow !=  - 1 && (int)pCur->row < nRow || (nCol !=   
          - 1 && (int)pCur->row == nRow && (int)pCur->col < nCol))) 
        { 
          if (pRet != 0) 
            *pRet = pCur; 
          pCur = pCur->next; 
        } 
      } 
      else 
      { 
        while (pCur != NULL && (nCol !=  - 1 && (int)pCur->col < nCol || ((int)pCur 
          ->col == nCol && nRow !=  - 1 && (int)pCur->row < nRow))) 
        { 
          if (pRet != 0) 
            *pRet = pCur; 
          pCur = pCur->next; 
        } 
      } 
      if (pCur != NULL && ((int)pCur->row == nRow || nRow ==  - 1) && ((int)pCur 
        ->col == nCol || nCol ==  - 1)) 
      //Find the same position 
      { 
        //Find it and return the value 
        if (pRet != 0) 
          *pRet = pCur; 
        return pCur->value; 
      } 
      return INFINITE_VALUE; 
    }

    这里我先要说明的是程序中的m_bRowFirst变量,它表示是先按row大小排列还是先按col大小排列。如果m_bRowFirst为逻辑真值,那么链表就如上面图二所示,如果为假,则如图三所示。

    除了这个外,看到上面长长的条件表达式,你一定会吓坏了吧!更让人吓坏的是调用这段程序的代码:

    对GetElement方法的调用

    //来自NShortPath.cpp中ShortPath方法 
    eWeight = m_apCost->GetElement( -1, nCurNode, 0, &pEdgeList); 
     
    //来自Segment.cpp中BiGraphGenerate方法 
    aWord.GetElement(pCur->col, -1, pCur, &pNextWords);//Get next words which begin with pCur->col
     
    • 先分析第一个调用

    第一个调用给GetElement方法的nRow传递了-1,他想干什么呢?

    假设这时候变量m_bRowFirst为true,并且传递过去的nCol!=-1,那么while (pCur != NULL && (nRow !=  - 1 && (int)pCur->row < nRow || (nCol != -1 && (int)pCur->row == nRow && (int)pCur->col < nCol))) 等价于while (pCur != NULL && ( (int)pCur->row == -1 && (int)pCur->col < nCol))) ,注意红色部分在程序运行时永远为false(因为根本就不存在row为-1的结点),因此,上面的表达式等价于while(false)!这对于该段程序没有任何意义!

    因此我们可以得到这样一个结论:如果GetElement方法的nRow参数取-1,当且仅当m_bRowFirst为false时才有意义。这时候,代码中第二个while得到执行,让我们分析一下:

    while (pCur != NULL && (nCol !=  - 1 && (int)pCur->col < nCol || ((int)pCur->col == nCol && nRow !=  - 1 && (int)pCur->row < nRow))) 在nRow为-1时等价于while (pCur != NULL && ((int)pCur->col < nCol ) ,这就容易解释的多了:在如图三所示的链表中查找col=nCol 的第一个结点。

    My God!

    • 再分析第二个调用

    上面的第二个调用就更让人摸不着头脑了:将pCur->col传递给GetElement的nRow参数,并将-1传递给nCol参数,这想干什么呢?要想分析清楚这个问题,没有个把钟头恐怕不行(再次佩服这些中科院的牛人们)。

    按照“分析第一个调用”中的结论可知,如果GetElement方法的nCol参数取-1,当且仅当m_bRowFirst为true时才有意义。因此链表排序一定是先按照行排(如图二),此时对DynamicArray的GetElement方法的调用可以简化成:

    对方法调用进行剥离和简化
    //来自Segment.cpp中BiGraphGenerate方法  
    aWord.GetElement(pCur->col, -1, pCur, &pNextWords); 

    //====================================================================== 

    ELEMENT_TYPE CDynamicArray::GetElement(int nRow, int nCol, PARRAY_CHAIN pStart, PARRAY_CHAIN *pRet)  
    // 经过调用后,上面的形参对应的值分别是:nRow:pStart->col, nCol:-1, pStart, &pNextWords 
    // 注意,为了和下面代码中的pCur以示区分,这里用了pStart这个变量名。 
    {  
      ...... 

      while (pCur != NULL && ((int)pCur->row < pStart->col))  
      {  
        if (pRet != 0)  
          *pRet = pCur;  
        pCur = pCur->next;  
      }  

      if (pCur != NULL && ((int)pCur->row == pStart->col)  
      //Find the same position  
      {  
        //Find it and return the value  
        if (pRet != 0)  
          *pRet = pCur;  
        return pCur->value;  
      }  
      return INFINITE_VALUE;  
    } 

    此时的意义就比较明显了,其实就是找pCur->row == pStart->col的那个结点。

    可有人会问,干吗把row和col扯到一起呢?这又是一个非常复杂的问题。具体内容可以参考sinboy的《ICTCLAS分词系统研究(四)--初次切分》一文。这里简单解释如下:

    如图四,这是row优先排列的一个链表:

    图四 进行初步分词后的链表结构(TagArrayChain)实例

    用二维表来表示图四中的链表结构如下图五所示:

    图五 TagArrayChain实例的二维表表示形式

    然后找出相邻两个词的平滑值。例如“他@说”、“的@确”、“的@确实”、“的确@实”、“的确@实在”等。如果仔细观察的话,可以注意到以下特点:例如“的确”这个词,它的col = 5,需要和它计算平滑值的有两个,分别是“实”和“实在”,你会发现这两个词的row = 5。同样道理,“确”的col = 5,它也需要和“实”与“实在”(row = 5)分别计算平滑值。

    其实,这就是为什么上面分析的找pCur->row == pStart->col的那个结点的原因了。最终得到的平滑值图可以表述成图六:

    图六 进行初次分词后生成的二叉图表的二维图表表示形式

    到此为止才明白代码作者的真正用意:

    将该调用放到上下文中再次查看
    //========= 来自Segment.cpp中BiGraphGenerate方法 =========== 
    ......  
    //取得和当前结点列值(col)相同的下个结点 
    aWord.GetElement(pCur->col, -1, pCur, &pNextWords); 
    while(pNextWords&&pNextWords->row==pCur->col)//Next words 
    {  
      //前后两个词用@分隔符连接起来 
      strcpy(sTwoWords,pCur->sWord); 
      strcat(sTwoWords,WORD_SEGMENTER); 
      strcat(sTwoWords,pNextWords->sWord); 
      ...... 

    • 小结

    想不到短短一个GetElement方法中竟然综合考虑了1)row优先排序的链表;2)col优先排序的链表;3)当nRow为-1时的行为(只有m_bRowFirst为false时才能这么做,代码中没有指,所以非常容易出错!);4)当nCol为-1时的行为;5)当nRow与nCol都不为-1时的行为。

    这也难怪我们会看到诸如while (pCur != NULL && (nRow !=  - 1 && (int)pCur->row < nRow || (nCol != -1 && (int)pCur->row == nRow && (int)pCur->col < nCol))) 这样的逻辑表达式了!我们也不得不佩服代码书写者复杂的逻辑思维能力(离散数学的谓词逻辑一定学得超级好)和给代码阅读者制造障碍的能力!类似代码在ICTCLAS中比比皆是,看来我只能恨自己脑筋太简单了!






    天书般的ICTCLAS分词系统代码(一)》 说了说ICTCLAS分词系统有些代码让人无所适从,需要好一番努力才能弄明白究竟是怎么回事。尽管有很多人支持应当写简单、清晰的代码,但也有人持不同意见。主要集中在(1)如果效率高,代码复杂点也行; (2)只要注释写得好就行;(3)软件关键在思路(这我同意),就好像买了一台电脑,不管包装箱内的电脑本身怎么,一群人偏在死扣那个外面透明胶带帖歪了(这我坚决不同意,因为只有好思路出不来好电脑,好电脑还要性能稳定,即插即用的好硬件;另外天书般的代码不仅仅是透明胶带 贴歪的问题,他甚至可能意味着电脑中的绝缘胶带失效了...)。

    这两天在抓紧学习ICTCLAS分词系统的思路的同时,也在消化学习它的代码实现,然而我看到的代码已经不仅仅是为了效率牺牲代码清晰度的问题了,我看到的是连作者都不知道自己真正想要做什么了,尽管程序的执行结果是正确的!

    为了说明这种情况的严重性,我们需要从CQueue.cpp这个文件着手。我对CQueue这个类颇有些微辞,明明是个Queue,里面确用的是Push、Pop方法(让人感觉是个Stack而不是Queue),而且Pop方法纯粹是个大杂烩,不过这些都不是原则性问题,毕竟每个人有每个人写代码的习惯。CQueue完成的工作是制造一个排序队列(按照eWeight从小到大排序),如图一:

    (图一)

    在了解了这些内容的基础上,让我们看看ICTCLAS中NShortPath.cpp中的代码实现(这里我们只看ShortPath方法的实现) ,为了让问题暴露得更清晰一些,我简化了代码中一些不相关的内容。

    来自NShortPath.cpp中的ShortPath方法
    int CNShortPath::ShortPath() 

      ...... 
      for (; nCurNode < m_nVertex; nCurNode++) 
      { 
        CQueue queWork; 
         
        //此处省略的代码主要负责将一些结点按照eWeight从 
        //小到大的顺序放入队列queWork 
        ...... 

        //初始化权重 
        for (i = 0; i < m_nValueKind; i++) 
          m_pWeight[nCurNode - 1][i] = INFINITE_VALUE; 

        i = 0; 
        while (i < m_nValueKind && queWork.Pop(&nPreNode, &nIndex, &eWeight) !=  -1) 
        { 
          //Set the current node weight and parent 
          if (m_pWeight[nCurNode - 1][i] == INFINITE_VALUE) 
            m_pWeight[nCurNode - 1][i] = eWeight; 
          else if (m_pWeight[nCurNode - 1][i] < eWeight) 
          //Next queue 
          { 
            i++; //Go next queue and record next weight 
            if (i == m_nValueKind) 
            //Get the last position 
              break; 
            m_pWeight[nCurNode - 1][i] = eWeight; 
          } 
          m_pParent[nCurNode - 1][i].Push(nPreNode, nIndex); 
        } 
      } 
      ...... 
    }

    上面的代码作者想干什么?让我们来分析一番:

    变量queWork中存放的是一个按照eWeight从小到大排列的队列, 我们不妨假设里面有4个元素,其eWeight值分别是5、6、7、8。另外我们假设变量m_nValueKind的值为2,即查找最短的两条路径(注意:这种说法不完全正确,后面会解释为什么)。在此假设基础上,我们看看程序是如何运行的:

    1)将所有m_pWeight[nCurNode - 1][i]初始化为INFINITE_VALUE。

    2)在第一轮循环中,我们从queWork中取出第一个元素,其eWeight为5,注意表达式“if (m_pWeight[nCurNode - 1][i] == INFINITE_VALUE) ”没有任何作用,因为我们在第一步将所有m_pWeight[nCurNode - 1][i] 均初始化成了INFINITE_VALUE,所以第一轮循环该条件一定为true。

    3)在第二轮循环中,我们从queWork中取出第二个元素,其eWeight为6,此时表达式“else if (m_pWeight[nCurNode - 1][i] < eWeight) ”似乎就没有什么作用了,因为queWork是经过排序的,第二个元素的eWeight不会小于第一个eWeight,对于我们这个例子来说, 该表达式一定为true,于是就让 i++。

    4)紧接着你会发现程序重新进入了步骤2)的循环。

    程序执行结果如图二:

    (图二)

    如果真是这样的话,上面的代码似乎可以简化成:

    简化后的程序
    int CNShortPath::ShortPath()  
    {  
      ......  
      for (; nCurNode < m_nVertex; nCurNode++)  
      {  
        CQueue queWork;  
          
        //此处省略的代码主要负责将一些结点按照eWeight从  
        //小到大的顺序放入队列queWork  
        ......  

        //初始化权重  
        for (i = 0; i < m_nValueKind; i++)  
          m_pWeight[nCurNode - 1][i] = INFINITE_VALUE;  

        i = 0;  
        while (i < m_nValueKind && queWork.Pop(&nPreNode, &nIndex, &eWeight) !=  -1)  
        {  
          m_pWeight[nCurNode - 1][i] = eWeight;  
          m_pParent[nCurNode - 1][i].Push(nPreNode, nIndex);  
          i++; 
        }  
      }  
      ......  
    }

    对于上面这个案例,简化后的程序与ICTCLAS中的程序执行结果完全相同。可作者写出如此复杂的代码应当是有理由的,难道我们对代码的分析有什么问题吗?

    是的!作者将一个最为重要的内容作为隐含条件放入了代码之中,我们只能通过 if 条件以及 else if 条件中的内容推断出这个隐含条件究竟是什么,而这个隐含的条件恰恰应当是这段代码中最关键的内容。如果没能将最关键的内容展现在代码当中,而是需要读者去推断的话,我只能说连作者自己都不清楚究竟什么是最关键的东西,仅仅是让程序执行没有错误而已。

    那么究竟隐藏了什么关键的内容呢?那就是“m_pWeight[nCurNode - 1][i] = eWeight”这个条件。在ShortPath方法代码中,作者用了 if 条件、 else if 条件,但都没有提及等于eWeight时程序的执行行为,他将这个留给了读者去推敲,看出来这个隐含条件就看出来了,看不出来就只能怪你自己笨了。

    我们更换一组数据来看看:假设queWork里面有4个元素,其eWeight值分别是5、6、6、7,还假设变量m_nValueKind的值为2,那么ICTCLAS中ShortPath程序执行结果是什么呢?读者可以根据代码自己推敲一下,然后再看看下面的结果,与你预期的一样不一样。如图三。

    (图三)

    这里m_Parent[nCurNode - 1][2]是一个CQueue,里面存入了eWeight为6的两个结点。这也是为什么我前文说,NShortPath中 N 如果取2,并不意味着只有两条路径。

    如果那位有耐心看到这里,对ICTCLAS中的NShortPath.cpp代码有什么感觉呢?其实要想写出一个比较清晰的代码并不复杂,只要你真正了解究竟什么是最重要的东西,对于NShortPath.cpp中的代码,只要我们稍加修改,就可以让这天书般的代码改善不少。经过调整后的代码如下:

    重新改造后的代码
    int CNShortPath::ShortPath()  
    {  
      ......  
      for (; nCurNode < m_nVertex; nCurNode++)  
      {  
        CQueue queWork;  
          
        //此处省略的代码主要负责将一些结点按照eWeight从  
        //小到大的顺序放入队列queWork  
        ......  

        //初始化权重  
        for (i = 0; i < m_nValueKind; i++)  
          m_pWeight[nCurNode - 1][i] = INFINITE_VALUE;  

        if(queWork.Pop(&nPreNode, &nIndex, &eWeight) != -1) 
        { 
          for(i=0; i < m_nValueKind ; i++) 
          { 
            m_pWeight[nCurNode - 1][i] = eWeight;  
            do 
            { 
              m_pParent[nCurNode - 1][i].Push(nPreNode, nIndex);         
              if(queWork.Pop(&nPreNode, &nIndex, &new_eWeight) == -1) 
                goto finish; 
            }while(new_eWeight == eWeight) 
             
            eWeight = new_eWeight; 
          } 
        } 
      }  
      finish: 
      ......  
    }

    经过改造的代码使用了一个do...while循环,并利用了goto命令简化代码结构,我想这样的代码读起来应当清晰多了吧。

    • 小结

    (1)软件关键在思路,只有真正了解思路的人才能写出清晰的代码。如果代码不清晰,说明思路根本不清晰。

    (2)注释写得好不如代码结构清晰。

    (3)除非经过测试,否则不要为了一点效率提升而损失代码的可读性。

    展开全文
  • ICTCLAS 中科院分词

    2012-11-30 13:58:25
    ICTCLAS 中科院分词 里面有demo和相关使用文档,最好的中文分词工具
  • ictclas去掉词性仅分词

    2016-04-06 22:09:49
    ictclas去掉词性仅分词
  • ICTCLAS50综合包

    2011-08-13 15:40:20
    ICTCLAS50综合包ICTCLAS50综合包ICTCLAS50综合包
  • 中科院ICTCLAS分词出错

    2017-04-11 03:06:28
    package Base; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException;... at ICTCLAS.I3S.AC.ICTCLAS50.ICTCLAS_Init(Native Method)是为什么
  • ictclas4j

    2008-07-10 14:11:41
    ictclas中科院分词
  • ictclas免费代码

    2012-10-21 16:49:02
    计算所汉语词法分析系统ICTCLAS(Institute of Computing Technology, Chinese Lexical Analysis System), // 功能有:中文分词;词性标注;未登录词识别。 // 分词正确率高达97.58%(973专家评测结果), // 未登录词...
  • ICTCLAS 2009

    千次阅读 热门讨论 2009-06-19 18:52:00
    由于工作原因,又一次接触到ICTCLAS,记得第一次使用ICTCLAS是我做硕士论文的时候,那时ICTCLAS才刚出来,非常感谢作者的无私共享,让我顺利完成了课题。一晃许多年过去了,ICTCLAS也推出了2009 Build0421最新版,...
  • ICTCLAS学习笔记

    2008-11-08 00:18:26
    ICTCLAS学习笔记,详细的解说ICTCLAS开发的整个过程及函数
  • 中国科学院计算技术研究所在多年研究工作积累的基础上,研制出了汉语词法分析系统ICTCLAS(Institute of Computing Technology, Chinese Lexical Analysis System),主要功能包括中文分词;词性标注;命名实体识别;...
  • ICTCLAS分词系统研究

    2010-08-20 17:13:24
    ICTCLAS 中文分词 中科院 ICTCLAS 中文分词 中科院
  • ICTCLAS 免费共享版

    2014-06-14 16:01:23
    为http://ictclas.org/所发布软件的免费共享版,此版为JNI版,没有使用时间限制,
  • ICTCLAS5.0与ictclas4j分词工具

    千次阅读 2013-04-22 18:40:06
    从一开始用的分词工具一直是中科院的ICTCLAS中文词法分析系统,被称为是世界上最好的中文词法分析器,对于ICTCLAS中文词法分析器的其他信息可以Google或者百度百科寻找答案。 之前用的是ictclas4j版本,该版本...
  • 中科院ICTCLAS50_Windows分词,内含32位与64位,能智能地将一段文字分解成词语,可以用于翻译,比如要翻译一段话,机器如何知道那几个字是一个词呢?就需要先通过分词,再翻译,当然现在智能的翻译肯定不是逐词翻译...
  • ictclas50_windows64

    2015-01-30 17:16:22
    中科院分词器ictclas50 本人亲测windows64位可以使用

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,311
精华内容 524
关键字:

ictclas