客串:屌絲的坑人表單神器、數據庫那點事兒
面向對象升華:面向對象的認識----新生的初識、面向對象的番外----思想的夢游篇(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);
這樣就算你的算法怎麼改,它也能從容面對了。
小帥帥理解了這個,當你覺得類做的事情太多的時候,可以考慮下單一職責原則。
單一職責原則:一個類,只有一個引起它變化的原因。應該只有一個職責。每一個職責都是變化的一個軸線,如果一個類有一個以上的職責,這些職責就耦合在了一起。這會導致脆弱的設計。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響復用性。例如:要實現邏輯和界面的分離。【來自百度百科】
當於老大提到是不是有其他分詞算法的時候,我們能不能拿來用,小帥帥很高興,因為現在它的代碼是多麼美好。
小帥帥如何玩轉第三方分詞擴展,請繼續關注下回分解:手把手教你做關鍵詞匹配項目(搜索引擎)---- 第二十一天