您的位置:首页 > 其它

SharpICTCLAS分词系统简介(3)DynamicArray

2010-02-06 08:48 639 查看
从前文可以看出,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更具有通用性,使用了范型方式定义了链表的结点,代码如下:


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


Copy Code
DynamicArray.cs 程序
public abstract class DynamicArray
{
protected ChainItem 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 pCur = pHead;
int nCount = 0;
while (pCur != null)
         {
            nCount++;
            pCur = pCur.next;
         }
return nCount;
      }
   }
//====================================================================
// 查找行、列值为nRow, nCol的结点
//====================================================================
public ChainItem GetElement(int nRow, int nCol)
   {
      ChainItem 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 GetHead()
   {
return pHead;
   }
//====================================================================
//Get the tail Element buffer and return the count of elements
//====================================================================
public int GetTail(out ChainItem pTailRet)
   {
      ChainItem 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 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类的实现如下:


Copy Code
RowFirstDynamicArray.cs 程序
public class RowFirstDynamicArray : DynamicArray
{
//====================================================================
// 查找行为 nRow 的第一个结点
//====================================================================
public ChainItem GetFirstElementOfRow(int nRow)
   {
      ChainItem pCur = pHead;
while (pCur != null && pCur.row != nRow)
         pCur = pCur.next;
return pCur;
   }
//====================================================================
// 从 startFrom 处向后查找行为 nRow 的第一个结点
//====================================================================
public ChainItem GetFirstElementOfRow(int nRow, ChainItem startFrom)
   {
      ChainItem pCur = startFrom;
while (pCur != null && pCur.row != nRow)
         pCur = pCur.next;
return pCur;
   }
//====================================================================
// 设置或插入一个新的结点
//====================================================================
public override void SetElement(int nRow, int nCol, T content)
   {
      ChainItem 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();//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的实现:


Copy Code
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代码做了逻辑上的简化):


Copy Code
程序
//====================================================================
// SharpICTCLAS 中的查找行为 nRow 的第一个结点
//====================================================================
public ChainItem GetFirstElementOfRow(int nRow)
{
   ChainItem 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类,力争简化原有设计中复杂的代码逻辑,应当说效果比较明显。即便有性能损失,那也是微不足道的,权衡利弊,我选择了走简单的代码这条路。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: