客串:屌絲的坑人表單神器、數據庫那點事兒
面向對象升華:面向對象的認識----新生的初識、面向對象的番外----思想的夢游篇(1)、面向對象的認識---如何找出類
負載均衡:負載均衡----概念認識篇、負載均衡----實現配置篇(Nginx)
吐槽:有人反饋了這樣的一個信息,說該文章越到最後越難看懂,跟不上節奏,也有的人說小帥帥的能力怎麼飙的那麼快,是不是我比較蠢。也有的直接看文字,不看代碼,代碼太難懂了。
其實我這幾天也一直在思考這個問題,所以沒辦法就去開展了一些面向對象的課程,希望對那些跟不上的有些幫助。其實說真的,讀者不反饋的話,我只好按照我認為的小帥帥去開展課程了。
第二十天
起點:手把手教你做關鍵詞匹配項目(搜索引擎)---- 第一天
回顧:手把手教你做關鍵詞匹配項目(搜索引擎)---- 第十九天
話說小帥帥為了解決那個分詞算法寫出了初版,他拿給於老大看的時候,被要求重寫了。
原因有以下幾點:
1. 如何測試,測試數據呢?
2. Splitter是不是做了太多事情?
3. 連衣裙xxl裙連衣裙這種 有重復詞組怎麼辦?
小帥帥拿著這些問題,開始重構。
首先他發現了這點,中文、英文和中英文的判斷,以及長度的計算,他把這個寫成了類:
<?php class UTF8 { /** * 檢測是否utf8 * @param $char * @return bool */ public static function is($char){ return (preg_match("/^([".chr(228)."-".chr(233)."]{1}[".chr(128)."-".chr(191)."]{1}[".chr(128)."-".chr(191)."]{1}){1}/",$char) || preg_match("/([".chr(228)."-".chr(233)."]{1}[".chr(128)."-".chr(191)."]{1}[".chr(128)."-".chr(191)."]{1}){1}$/",$char) || preg_match("/([".chr(228)."-".chr(233)."]{1}[".chr(128)."-".chr(191)."]{1}[".chr(128)."-".chr(191)."]{1}){2,}/",$char)); } /** * 計算utf8字的個數 * @param $char * @return float|int */ public static function length($char) { if(self::is($char)) return ceil(strlen($char)/3); return strlen($char); } /** * 檢測是否為詞組 * @param $word * @return bool */ public static function isPhrase($word){ if(self::length($word)<=1) return false; return true; } }
小帥帥又考慮到詞典的來源有可能來自多個地方,比如我給的測試數據,這樣不就是可以解決於老大說到無法測試的問題了,小帥帥把詞典的來源抽成了個類,類如下:
<?php class DBSegmentation { public $cid; /** * 獲取類目下分詞的詞組數據 * @return array */ public function transferDictionary(){ $ret = array(); $sql = "select word from category_linklist where cid='$this->cid'"; $words = DB::makeArray($sql); foreach($words as $strWords){ $words = explode(",",$strWords); foreach($words as $word){ if(UTF8::isPhrase($word)){ $ret[] = $word; } } } return $ret; } } class TestSegmentation { public function transferDictionary(){ $words = array( "連衣裙,連衣", "XXL,xxl,加大,加大碼", "X碼,中碼", "外套,衣,衣服,外衣,上衣", "女款,女士,女生,女性" ); $ret = array(); foreach($words as $strWords){ $words = explode(",",$strWords); foreach($words as $word){ if(UTF8::isPhrase($word)){ $ret[] = $word; } } } return $ret; } }
那麼Splitter 就專心分詞把,代碼如下:
class Splitter { public $keyword; private $dictionary = array(); public function setDictionary($dictionary = array()){ usort($dictionary,function($a,$b){ return (UTF8::length($a)>UTF8::length($b))?1:-1; }); $this->dictionary = $dictionary; } public function getDictionary(){ return $this->dictionary; } /** * 把關鍵詞拆分成詞組或者單詞 * @return KeywordEntity $keywordEntity */ public function split(){ $remainKeyword = $this->keyword; $keywordEntity = new KeywordEntity($this->keyword); foreach($this->dictionary as $phrase){ $matchTimes = preg_match_all("/$phrase/",$remainKeyword,$matches); if($matchTimes>0){ $keywordEntity->addElement($phrase,$matchTimes); $remainKeyword = str_replace($phrase,"::",$remainKeyword); } } $remainKeywords = explode("::",$remainKeyword); foreach($remainKeywords as $splitWord){ if(!empty($splitWord)){ $keywordEntity->addElement($splitWord); } } return $keywordEntity; } } class KeywordEntity { public $keyword; public $elements = array(); public function __construct($keyword){ $this->keyword = $keyword; } public function addElement($word,$times=1){ if(isset($this->elements[$word])){ $this->elements[$word]->times += $times; }else $this->elements[] = new KeywordElement($word,$times); } /** * @desc 計算UTF8字符串權重 * @param string $word * @return float */ public function calculateWeight($word) { $element = $this->elements[$word]; return ROUND(strlen($element->word)*$element->times / strlen($this->keyword), 3); } } class KeywordElement { public $word; public $times; public function __construct($word,$times){ $this->word = $word; $this->times = $times; } }
他把算權重的也丟給了一個類專門去處理。
小帥帥寫完之後,也順手寫了測試實例:
<?php $segmentation = new TestSegmentation(); $splitter = new Splitter(); $splitter->setDictionary($segmentation->transferDictionary()); $splitter->keyword = "連衣裙xxl裙連衣裙"; $keywordEntity = $splitter->split(); var_dump($keywordEntity);
這樣就算你的算法怎麼改,它也能從容面對了。
小帥帥理解了這個,當你覺得類做的事情太多的時候,可以考慮下單一職責原則。
單一職責原則:一個類,只有一個引起它變化的原因。應該只有一個職責。每一個職責都是變化的一個軸線,如果一個類有一個以上的職責,這些職責就耦合在了一起。這會導致脆弱的設計。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響復用性。例如:要實現邏輯和界面的分離。【來自百度百科】
當於老大提到是不是有其他分詞算法的時候,我們能不能拿來用,小帥帥很高興,因為現在它的代碼是多麼美好。
小帥帥如何玩轉第三方分詞擴展,請繼續關注下回分解:手把手教你做關鍵詞匹配項目(搜索引擎)---- 第二十一天