SharpICTCLAS分词系统简介(3)DynamicArray

本文介绍了一种简化ICTCLAS分词系统中DynamicArray类的方法,通过将其拆分为抽象父类及两个子类来优化代码结构,提高了可读性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址:http://www.cnblogs.com/zhenyulu/articles/668695.html

从前文可以看出,ICTCLAS中DynamicArray类在初步分词过程中起到了至关重要的所用,而ICTCLAS中DynamicArray类的实现比较复杂,可以说是包罗万象,在一个GetElement方法就综合考虑了1)row优先排序的链表;2)col优先排序的链表;3)当nRow为-1时的行为;4)当nCol为-1时的行为;5)当nRow与nCol都不为-1时的行为 (可以参考本人的《天书般的ICTCLAS分词系统代码(一)》一文)。

为了简化编程接口,并将纠缠不清的代码逻辑剥离开来,我重新设计了DynamicArray类,利用三个类实现原有一个类的功能。具体改造包括:1) 将DynamicArray类做成一个抽象父类,实现一些公共功能;2)设计了RowFirstDynamicArray类与ColumnFirstDynaimcArray类作为DynamicArray的子类,分别实现row优先排序和col优先排序的DynamicArray。2) 在牺牲有限性能的同时力争大幅度简化代码的复杂度,提高可读性。

具体实现如下:

1、DynamicArray链表结点的定义

为了使得DynamicArray更具有通用性,使用了范型方式定义了链表的结点,代码如下:

DynamicArray链表结点的定义
public  class ChainItem<T> 

    public  int row; 
    public  int col; 
    public T Content; 
    public ChainItem<T> next; 
}

2、DynamicArray类

DynamicArray类是一个抽象类,主要为RowFirstDynamicArray类与ColumnFirstDynaimcArray类提供公共的基础功能,例如查找行、列值为nRow, nCol的结点等。同时,该类将插入一新结点的方法设计成抽象方法,需要具体类根据row优先排序还是col优先排序自行决定具体实现。DynamicArray类的代码实现如下(应当说非常简单):

DynamicArray.cs 程序
public  abstract  class DynamicArray<T> 

    protected ChainItem<T> pHead;   //The head pointer of array chain 
    public  int ColumnCount, RowCount;   //The row and col of the array 

    public DynamicArray() 
   { 
      pHead =  null
      RowCount = 0; 
      ColumnCount = 0; 
   } 

    public  int ItemCount 
   { 
       get 
      { 
         ChainItem<T> pCur = pHead; 
          int nCount = 0; 
          while (pCur !=  null
         { 
            nCount++; 
            pCur = pCur.next; 
         } 
          return nCount; 
      } 
   } 

    //==================================================================== 
    // 查找行、列值为nRow, nCol的结点 
    //==================================================================== 
    public ChainItem<T> GetElement( int nRow,  int nCol) 
   { 
      ChainItem<T> pCur = pHead; 

       while (pCur !=  null && !(pCur.col == nCol && pCur.row == nRow)) 
         pCur = pCur.next; 

       return pCur; 
   } 

    //==================================================================== 
    // 设置或插入一个新的结点 
    //==================================================================== 
    public  abstract  void SetElement( int nRow,  int nCol, T content); 

    //==================================================================== 
    // Return the head element of ArrayChain 
    //==================================================================== 
    public ChainItem<T> GetHead() 
   { 
       return pHead; 
   } 

    //==================================================================== 
    //Get the tail Element buffer and return the count of elements 
    //==================================================================== 
    public  int GetTail( out ChainItem<T> pTailRet) 
   { 
      ChainItem<T> pCur = pHead, pPrev =  null
       int nCount = 0; 
       while (pCur !=  null
      { 
         nCount++; 
         pPrev = pCur; 
         pCur = pCur.next; 
      } 
      pTailRet = pPrev; 
       return nCount; 
   } 

    //==================================================================== 
    // Set Empty 
    //==================================================================== 
    public  void SetEmpty() 
   { 
      pHead =  null
      ColumnCount = 0; 
      RowCount = 0; 
   } 

    public  override  string ToString() 
   { 
      StringBuilder sb =  new StringBuilder(); 

      ChainItem<T> pCur = pHead; 

       while (pCur !=  null
      { 
         sb.Append( string.Format( "row:{0,3},  col:{1,3},  ", pCur.row, pCur.col)); 
         sb.Append(pCur.Content.ToString()); 
         sb.Append( "\r\n"); 
         pCur = pCur.next; 
      } 

       return sb.ToString(); 
   } 
}

3、RowFirstDynamicArray类的实现

RowFirstDynamicArray类主要实现了row优先排序的DynamicArray,里面包含了两个方法:GetFirstElementOfRow(获取row为nRow的第一个元素)和SetElement方法。其中GetFirstElementOfRow有两个重载版本。

这等价于将原有ICTCLAS中GetElement方法拆分成了三个方法,如果算上重载版本的话共五个方法,它们分别是:1)获取行、列值为nRow, nCol的结点,此方法由DynamicArray类实现;2)对于Row优先排序的链表而言,单独提供了GetFirstElementOfRow方法。3)对于Column优先排序的链表而言,单独提供了GetFirstElementOfColumn方法。

RowFirstDynamicArray类的实现如下:

RowFirstDynamicArray.cs 程序
public  class RowFirstDynamicArray<T> : DynamicArray<T> 

    //==================================================================== 
    // 查找行为 nRow 的第一个结点 
    //==================================================================== 
    public ChainItem<T> GetFirstElementOfRow( int nRow) 
   { 
      ChainItem<T> pCur = pHead; 

       while (pCur !=  null && pCur.row != nRow) 
         pCur = pCur.next; 

       return pCur; 
   } 

    //==================================================================== 
    // 从 startFrom 处向后查找行为 nRow 的第一个结点 
    //==================================================================== 
    public ChainItem<T> GetFirstElementOfRow( int nRow, ChainItem<T> startFrom) 
   { 
      ChainItem<T> pCur = startFrom; 

       while (pCur !=  null && pCur.row != nRow) 
         pCur = pCur.next; 

       return pCur; 
   } 

    //==================================================================== 
    // 设置或插入一个新的结点 
    //==================================================================== 
    public  override  void SetElement( int nRow,  int nCol, T content) 
   { 
      ChainItem<T> pCur = pHead, pPre =  null, pNew;   //The pointer of array chain 

       if (nRow > RowCount) //Set the array row 
         RowCount = nRow; 

       if (nCol > ColumnCount) //Set the array col 
         ColumnCount = nCol; 

       while (pCur !=  null && (pCur.row < nRow || (pCur.row == nRow && pCur.col < nCol))) 
      { 
         pPre = pCur; 
         pCur = pCur.next; 
      } 

       if (pCur !=  null && pCur.row == nRow && pCur.col == nCol) //Find the same position 
         pCur.Content = content; //Set the value 
       else 
      { 
         pNew =  new ChainItem<T>(); //malloc a new node 
         pNew.col = nCol; 
         pNew.row = nRow; 
         pNew.Content = content; 

         pNew.next = pCur; 

          if (pPre ==  null) //link pNew after the pPre 
            pHead = pNew; 
          else 
            pPre.next = pNew; 
      } 
   } 
}

有关ColumnFirstDynamicArray类的实现大同小异,这里就不再提供代码了。我们此时可以对比一下原有ICTCLAS中GetElement的实现:

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; 
}

可以看出,将原有GetElement方法拆分成3个方法后,代码得到大大简化,而且逻辑更为清晰了。

3、性能与代码可读性的权衡

DynamicArray类为了确保代码的清晰可读,在某些地方做了些调整,让我们对比一下SharpICTCLAS与ICTCLAS中在这方面的不同考虑。下面的代码演示了GetFirstElementOfRow方法在两者之间的不同之处(我特意对ICTCLAS代码做了逻辑上的简化):

程序
//==================================================================== 
// SharpICTCLAS 中的查找行为 nRow 的第一个结点 
//==================================================================== 
public ChainItem<T> GetFirstElementOfRow( int nRow) 

   ChainItem<T> pCur = pHead; 

    while (pCur !=  null && pCur.row != nRow) 
      pCur = pCur.next; 

    return pCur; 


//==================================================================== 
// ICTCLAS 中的查找行为 nRow 的第一个结点 
//==================================================================== 
... GetElement( int nRow,  int nCol, PARRAY_CHAIN pStart, PARRAY_CHAIN *pRet)  
{  
  PARRAY_CHAIN pCur = pStart;  

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

   if (pCur != NULL && pCur->row == nRow && pCur->col == nCol)  
  {  
     if (pRet != 0)  
      *pRet = pCur;  
     return pCur-> value;  
  }  
   //...... 
}

从上面代码中可以看出,原有ICTCLAS代码充分考虑到DynamicArray是一个排序链表,因此仅仅在pCur->row < nRow与pCur->col < nCol范围内检索,如果找到了“pCur->row == nRow && pCur->col == nCol”,那么再去做该做的事情。

而SharpICTCLAS中,判断条件仅为“pCur != null && pCur.row != nRow”,这意味着如果你要找的nRow不再该链表中,则会来个“完全遍历”,搜索范围似乎太大了。

不过出于以下几点考虑我还是采用了这种表示方式:

1)汉语中的一句话不会太长,这意味着链表长度不会很长,即使来个“完全遍历”也不会牺牲多少时间。

2)毕竟要找的nRow不在该链表中的可能性不大,出现“完全遍历”的机会也不多。

3)原有ICTCLAS虽然在搜索范围内下了翻功夫,但为了确保pRet变量得到赋值,循环体内部多次执行了“if (pRet != 0)”的判断,这从性能角度上说得不偿失。

4)原有ICTCLAS为了缩小搜索范围确增加了条件判断次数“pCur != NULL && (pCur->row < nRow || (pCur->row == nRow && pCur->col < nCol))”,而由此带来的性能损失不得不考虑一下。

正因为以上几点考虑,所以在SharpICTCLAS中采用了这种简单而且并不见得低效的方式取代原有的GetElement方法。

  • 小结

SharpICTCLAS重新设计了DynamicArray类,力争简化原有设计中复杂的代码逻辑,应当说效果比较明显。即便有性能损失,那也是微不足道的,权衡利弊,我选择了走简单的代码这条路。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值