程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 手把手教你做關鍵詞匹配項目(搜索引擎)---- 第二十天,教你做第二十天

手把手教你做關鍵詞匹配項目(搜索引擎)---- 第二十天,教你做第二十天

編輯:關於PHP編程

手把手教你做關鍵詞匹配項目(搜索引擎)---- 第二十天,教你做第二十天


客串:屌絲的坑人表單神器、數據庫那點事兒

面向對象升華:面向對象的認識----新生的初識、面向對象的番外----思想的夢游篇(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);

 

這樣就算你的算法怎麼改,它也能從容面對了。

 

小帥帥理解了這個,當你覺得類做的事情太多的時候,可以考慮下單一職責原則。

 

單一職責原則:一個類,只有一個引起它變化的原因。應該只有一個職責。每一個職責都是變化的一個軸線,如果一個類有一個以上的職責,這些職責就耦合在了一起。這會導致脆弱的設計。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響復用性。例如:要實現邏輯和界面的分離。【來自百度百科】

 

當於老大提到是不是有其他分詞算法的時候,我們能不能拿來用,小帥帥很高興,因為現在它的代碼是多麼美好。

小帥帥如何玩轉第三方分詞擴展,請繼續關注下回分解:手把手教你做關鍵詞匹配項目(搜索引擎)---- 第二十一天

 




  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved