T

不自然言語処理にはPHPをどうぞ

自然言語処理はPythonがいちばんという記事を読んで思い出したのだが、自分でもWordnetのPHP版フロントエンドをさくっと(Ruby版をコピーして)作っていた。ほんとまるコピーなのでほったらかしにしていたのだが、イテレータのところの醜さに悶絶してもいいなら責任は取らないが読むのは許可するので公開しておく。都市の空気は自由だ。

<?php
class Wordnet{

  public $db;

  function __construct($dsn){
    $this->db =& $this->_connect($dsn);
  }

  function __destruct(){
  }

  public function singleton($dsn){
    static $obj = null;
    if(is_null($obj)){
      $name = __CLASS__;
      $obj =& new $name($dsn);
    }
    return $obj;
  }

  private function _connect($dsn){
    static $dbh = null;
    if(is_null($dbh)){
      try {
        $dbh = new PDO($dsn);
      } catch(Exception $e) {
        echo $e->getMessage();
      }
    }
    return $dbh;
  }

  private function _exception(&$e){
    echo $e->getMessage();
  }


  private function _select($sql, $param=array()){
    $statement = $this->db->prepare($sql);
    $statement->setFetchMode(PDO::FETCH_ASSOC);
    if($statement->execute($param)){
      return $statement->fetchAll();
    }else{
      $info = $statement->errorInfo();
      foreach($info as $k => $v){
        $message .= $k . ":" . $v . "\n";
      }
      throw new Exception($message);
    }
  }

  private function _get($sql, $param){
    try {
      $array = $this->_select($sql, $param);
    }catch(Exception $e){
      $this->_exception($e);
      return null;
    }
    return $array;
  }

  public function get_words($lemma){
    $sql = "SELECT * FROM word WHERE lemma=?";
    $array = $this->_get($sql, array($lemma));
    return $array;
  }

  public function get_word($wordid){
    $sql = "SELECT * FROM word WHERE wordid=?";
    $array = $this->_get($sql, array($wordid));
    return $array;
  }

  public function get_senses($wordid){
    $sql = "SELECT * FROM sense WHERE wordid=?";
    $array = $this->_get($sql, array($wordid));
    return $array;
  }

  public function get_sense($synset, $lang='jpn'){
    $sql = "SELECT * FROM sense WHERE synset=? AND lang=?";
    $array = $this->_get($sql, array($synset, $lang));
    return $array;
  }

  public function get_synset($synset){
    $sql = "SELECT * FROM synset WHERE synset=?";
    $array = $this->_get($sql, array($synset));
    return $array;
  }

  public function get_syn_links($synset1, $link){
    $sql = "SELECT * FROM synlink WHERE synset1=? AND link=?";
    $array = $this->_get($sql, array($synset1, $link));
    return $array;
  }

  public function get_sys_links_recursive($senses, $link, $lang='jpn', $depth=0){
    if(is_array($senses)){
      foreach($senses as $sense){
        $syn_links = $this->get_syn_links($sense['synset'], $link);
        if(count($syn_links) > 0){
          $get_word = $this->get_word($sense['wordid']);
          $synset = $this->get_synset($sense['synset']);
          $space = str_repeat(' ', $depth);
          print($space . $get_word[0]['lemma'] . ' ' . $synset[0]['name'] . "\n");
          $tmp_links = array();
          foreach($syn_links as $k => $syn_link){
            $tmp_sense = $this->get_sense($syn_link['synset2'], $lang);
            if($tmp_sense != ''){
              $tmp_links[] = $tmp_sense[0];
            }
          }
          $this->get_sys_links_recursive($tmp_links, $link, $lang, $depth + 1);
        }
      }
    }
  }

  public function main($word, $link, $lang='jpn'){
    if($words = $this->get_words($word)){
      $senses = $this->get_senses($words[0]['wordid']);
      $this->get_sys_links_recursive($senses, $link, $lang);
    }else{
      return "nothing found";
    }
  }

  public function usage(){

      $usage =  >>>EOS
usage: wordnet.php word link [lang]
  word  word to investigate

  link
  syns - Synonyms
  hype - Hypernyms
  inst - Instances
  hypo - Hyponym
  hasi - Has Instances
  mero - Meronyms
  mmem - Meronyms --- Member
  msub - Meronyms --- Substance
  mprt - Meronyms --- Part
  holo - Holonyms
  hmem - Holonyms --- Member
  hsub - Holonyms --- Substance
  hprt - Holonyms -- Part
  attr - Attributes
  sim - Similar to
  entag - Entails
  causg - Causes
  dmncg - Domain --- Category
  dmnug - Domain --- usage
  dmnrg - Domain --- Region
  dmtcg - In Domain --- Category
  dmtug - In Domain --- usage
  dmtrg - In Domain --- Region
  antsg - Antonyms

  lang (default: jpn)
  jpn - Japanese
  eng - English

EOS;
    print($usage);
  }

}
$dsn = 'sqlite:./wnjpn-0.9.db';
$obj = Wordnet::singleton($dsn);
$word = $_SERVER['argv'][1];
$link = $_SERVER['argv'][2];
$lang = $_SERVER['argv'][3] ? $_SERVER['argv'][3] : 'jpn';
if($word == '' || $link == ''){
  $obj->usage();
  exit;
}

$obj->main($word, $link, $lang);

?>

ではテスト。

$ ruby wordnet.rb  'うんこ' hype
うんこ dung
  大便 stool
うんこ turd
  大便 stool
$ php -q wordnet.php 'うんこ' hype
うんこ dung
 大便 stool
うんこ turd
 大便 stool

そもそもlinkとか意味がわかってないのでこれ以上テストする気がないのだが、動いていそうな雰囲気ではある。うんこしかテストしていないので、不自然な言語処理にぜひともおすすめ。

テストしてない上にローカルで使ってたのでセキュリティとか一切考慮してないから、これを使って何をしようとも愚挙としかいいようがないので念のため。

Posted by on 4月 6, 2009 in PHP

Comments

  • k1 より:

    PHP版を探してここにたどり着きました。
    ありがとうございます。

    手元の環境(Debian GNU/Linux 6.0)で動かすには以下の変更が必要でした。

    if($tmp_sense != ”){
    ↓↓
    if($tmp_sense != NULL){

    $lang = $_SERVER[‘argv’][3] ? $_SERVER[‘argv’][3] : ‘jpn’;
    ↓↓
    if (isset($_SERVER[‘argv’][3])) $lang = $_SERVER[‘argv’][3]; else $lang = ‘jpn’;

    $usage = >>>EOS
    ↓↓
    $usage = <<

  • k1 より:

    すみません、訂正です。一部、文字が抜けていました。

    $usage = >>>EOS
    ↓↓
    $usage = <<EOS

    が正解です。

  • コメントを残す