# -*- coding: utf-8 -*-
# 「入門 自然言語処理」
# 12章のプログラム

#注意: WindowsのOSでは日本語はShift_JISが主である
# それに対して Linuxでは 昔はEUC_JP (STドメインのコンピュータ)、
# 今は(例えばUbuntu 12.10やUbuntu 14.10) utf-8 (Unicode)となっている。
# 従って、Windowsでは utf-8 に「しない」方がスムーズに日本語入出力が
# できることに注意---近い将来、utf-8になるであろうが。

#--------------------------------------------------
# p.467

import sys
import codecs

# 以下は utf-8の文字列を標準入力(stdin)と標準出力(stdout)で扱う場合

sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
sys.stdin = codecs.getreader('utf_8')(sys.stdin)

f = codecs.open('sometext.txt', 'r', 'utf-8')


#--------------------------------------------------
# p.468

print "%s で %s" % (u"パイソン", u"自然言語処理")


import sys
sys.getdefaultencoding()
# 'ascii'


import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print "%s で %s" % (u"パイソン", u"自然言語処理")
# パイソン で 自然言語処理


import sys
sys.setdefaultencoding('utf-8')


#--------------------------------------------------
# p.469

import re, pprint

def pp(obj):
  pp = pprint.PrettyPrinter(indent=4, width=160)
  str = pp.pformat(obj)
  return re.sub(r"\\u([0-9a-f]{4})", lambda x: unichr(int("0x"+x.group(1), 16)), str)

data = {
  u"スクリプト言語":
    { u"Python": u"パイソン",
      u"Ruby": u"ルビー"},
  u"関数型言語":
    { u"Erlang": u"アーラング",
      u"Haskell": u"ハスケル",
      u"Lisp": u"リスプ"}
}

print data

"""
{u'\u95a2\u6570\u578b\u8a00\u8a9e': {u'Lisp': u'\u30ea\u30b9\u30d7', u'Erlang':
 u'\u30a2\u30fc\u30e9\u30f3\u30b0', u'Haskell': u'\u30cf\u30b9\u30b1\u30eb'}, 
 u'\u30b9\u30af\u30ea\u30d7\u30c8\u8a00\u8a9e': {u'Python':
 u'\u30d1\u30a4\u30bd\u30f3', u'Ruby': u'\u30eb\u30d3\u30fc',
 u'Perl': u'\u30d1\u30fc\u30eb'}}
"""

print pp(data)
"""
{   u'スクリプト言語': {u'Perl': u'パール', u'Python': u'パイソン', u'Ruby': 
 u'ルビー'}, u'関数型言語': {u'Erlang': u'アーラング', u'Haskell': u'ハスケル', 
 u'Lisp': u'リスプ'}}
"""

#--------------------------------------------------
# p.470

import nltk
from nltk.corpus.reader import *
from nltk.corpus.reader.util import *
from nltk.text import Text


jp_sent_tokenizer = nltk.RegexpTokenizer(u'[^ 「」!?。]*[!?。]')


#--------------------------------------------------
# p.471

jp_chartype_tokenizer = nltk.RegexpTokenizer(
   u'([ぁ-んー]+|[ァ-ンー]+|[\u4e00-\u9FFF]+|[^ぁ-んァ-ンー\u4e00-\u9FFF]+)')


# 注意: "H:\\NLP\\"のところを自分の環境に合わせて直すこと
# 以下では変数Homeに、ここで動かすpythonプログラムやデータがあるフォルダへのパスがセットされているものとする

# siraiの環境では: Home="c://Users/sirai/Documents/Classes/nlp/"

Home = "H:\\NLP\\"

# gingatetsudono_yoru.txt をダウンロードして、Homeフォルダにおくこと

ginga = PlaintextCorpusReader(Home, r'gingatetsudono_yoru.txt',
    encoding='utf-8',
    para_block_reader=read_line_block,
    sent_tokenizer=jp_sent_tokenizer,
    word_tokenizer=jp_chartype_tokenizer)



print ginga.raw()

"""
銀河鉄道の夜
宮沢賢治

   一、午后《ごご》の授業

「ではみなさんは、そういうふうに川だと云《い》われたり、乳の流れたあとだと云われたりしていた
このぼんやりと白いものがほんとうは何かご承知ですか。」先生は、黒板に吊《つる》した大きな黒い
星座の図の、上から下へ白くけ
...
"""

print '/'.join( ginga.words()[0:50] )

"""
銀河鉄道/の/夜/
/宮沢賢治/
/
/   /一/、/午后/《/ごご/》/の/授業/
/
/「/ではみなさんは/、/そういうふうに/川/だと/云/《/い/》/われたり/、/乳/の/流/れたあとだと/
云/われたりしていたこのぼんやりと/白/いものがほんとうは/何/かご/承知/ですか/。」/先生/は/、
/黒板/に/吊/《
"""

# ginga_t = Text( w.encode('utf-8') for w in ginga.words() )
ginga_t = Text( w for w in ginga.words() )


# 以下が動かない場合は、 ginga_t.concordance("川") を試す
ginga_t.concordance(u"川")

"""
Building index...
Displaying 25 of 66 matches:
・・さんは 、 そういうふうに 川 だと 云 《 い 》 われたり ・
・・しもこの 天 《 あま 》 の 川 《 がわ 》 がほんとうに 川
川 《 がわ 》 がほんとうに 川 だと 考 えるなら 、 その ・
の 小 さな 星 はみんなその 川 のそこの 砂 や 砂利 《 じ・
と 考 えるならもっと 天 の 川 とよく 似 ています 。 つま
です 。 そんなら 何 がその 川 の 水 にあたるかと 云 いま
・・ つまりは 私 どもも 天 の 川 の 水 のなかに 棲 《 す 》
けです 。 そしてその 天 の 川 の 水 のなかから 四方 を ・
・ く 見 えるように 、 天 の 川 の 底 の 深 く 遠 いところ・
...
"""

#--------------------------------------------------
# p.473


# 注意:
# Homeに適切なフォルダへのパスをセットしておくこと
# chasen.pyをHomeフォルダにおいておくこと
# jeita.zip を展開してHomeフォルダにおくこと
# もっとも、以下が失敗しても、Pythonを終了させずに何回でもやり直せる

from chasen import *

jeita = ChasenCorpusReader(Home+'jeita\\', '.*chasen', encoding='utf-8')

print '/'.join( jeita.words()[22100:22140] )
"""
たい/という/気持/が/、/この上なく/純粋/に/、/この上なく/強烈/で/あれ/ば/、/ついに/は/そのも
の/に/なれる/。/なれ/ない/の/は/、/まだ/その/気持/が/そこ/まで/至っ/て/い/ない/から/だ/。/
法
"""

print '\nEOS\n'.join(['\n'.join("%s/%s" % (w[0],w[1].split('\t')[2]) 
  for w in sent) for sent in jeita.tagged_sents()[2170:2173]])
"""
を/助詞-格助詞-一般
まくっ/動詞-自立
た/助動詞
とき/名詞-非自立-副詞可能
吹き/動詞-自立
こむ/動詞-非自立
粉雪/名詞-一般
の/助詞-連体化
ため/名詞-非自立-副詞可能
に/助詞-格助詞-一般
、/記号-読点
彼/名詞-代名詞-一般
の/助詞-連体化
姿/名詞-一般
は/助詞-係助詞
瞬間/名詞-副詞可能
に/助詞-格助詞-一般
みえ/動詞-自立
なく/助動詞
なっ/動詞-自立
た/助動詞
。/記号-句点
それなり/名詞-一般
だ/助動詞
。/記号-句点
...
"""

#--------------------------------------------------
# p.474


from knbc import *
from nltk.corpus.util import LazyCorpusLoader

# 注意:
# 変数Homeに適切なフォルダへのパスをセットしておくこと
# knbc.zip と knbc.py をダウンロードしてHomeフォルダにおくこと
# Homeフォルダに、knbc.zipを展開して、置いておくこと

root = nltk.data.find(Home+'knbc\\corpus1')
fileids = [f for f in find_corpus_fileids(FileSystemPathPointer(root), ".*")
  if re.search(r"\d\-\d\-[\d]+\-[\d]+", f)]



def _knbc_fileids_sort(x):
  cells = x.split('-')
  return (cells[0], int(cells[1]), int(cells[2]), int(cells[3]))

knbc = LazyCorpusLoader(Home+"knbc\\corpus1", KNBCorpusReader, sorted(fileids, key=_knbc_fileids_sort), encoding='euc-jp')


print knbc.fileids()
"""
['KN001_Keitai_1/KN001_Keitai_1-1-1-01', 'KN001_Keitai_1/KN001_Keitai_1-1-2-01',
'KN001_Keitai_1/KN001_Keitai_1-1-3-01', 'KN001_Keitai_1/KN001_Keitai_1-1-4-01',
'KN001_Keitai_1/KN001_Keitai_1-1-5-01', ...
"""


print ''.join( knbc.words()[:100] )
"""
[携帯電話]プリペイドカード携帯布教。
  もはや’今さら’だが、という接頭句で始めるしかないほど今さらだが、私はプリペイド携帯をずっと
  使っている。
  ...
"""

#--------------------------------------------------
# p.475

import codecs

reload(sys)
sys.stdout = codecs.getwriter('sjis')(sys.stdout)
sys.setdefaultencoding('utf-8')

# 以下が表示できたのは、 python 2.7.5 32-bit (他は16進数表記)

print '\n\n'.join( '%s' % tree for tree in knbc.parsed_sents()[0:2] )
"""
(布教/。
  (電話/] [/携帯)
  (携帯 (カード プリペイド)))

(使って/いる/。
  (今さら/だ/が/、
    (ほど
      (始める/しか/ない
       もはや
       (接頭句/で (いう ’/今さら/’/だ/が/、/と)))))
  私/は
  (携帯/を プリペイド)
  ずっと)
"""

print '\n'.join( ' '.join("%s/%s"%(w[0], w[1].split(' ')[2]) for w in sent)
   for sent in knbc.tagged_sents()[0:20] )

"""
[/特殊 携帯/名詞 電話/名詞 ]/特殊 プリペイド/名詞 カード/名詞 携帯/名詞 布教/名詞 。/特殊
もはや/副詞 ’/特殊 今さら/副詞 ’/特殊 だ/判定詞 が/助詞 、/特殊 と/助詞 いう/動詞 接頭句
/名詞 で/助詞 始める/動詞 しか/助詞 ない/形容詞 ほど/名詞 今さら/副詞 だ/判定詞 が/助詞 、
/特殊 私/名詞 は/助詞 プリペイド/名詞 携帯/名詞 を/助詞 ずっと/副詞 使って/動詞 いる/接尾辞
。/特殊
"""

#--------------------------------------------------
# p.476

# 注意:
# Homeに適切なフォルダへのパスをセットしておくこと
# jeita.zip を展開してHomeフォルダにおくこと


genpaku = ChasenCorpusReader(Home+'jeita', 'g.*chasen',
encoding='utf-8')

#注意: 以下2つはかなり時間がかかる

print len(genpaku.words())
#733016

print sum(len(w) for w in genpaku.words())
# 1247143


#注意: 次もかなり時間がかかる

genpaku_vocab = set(w for w in genpaku.words() if re.match(ur"^[ぁ-んーァ-ンー\u4e00-\u9FFF]+$", w))

print ' '.join( sorted(genpaku_vocab)[:10] )
"""
ぁ あ あぁ ああ あい あいさつ あいだ あいつ あいにく あいまい
"""

#注意: 単語数が多いので次もかなり時間がかかる

genpaku_t = Text(genpaku.words())
genpaku_wfd = nltk.FreqDist(genpaku_t)
genpaku_wfd.tabulate(10)
"""
、 の は 。 に を た て が で
40153 38046 26823 25088 24699 22836 18040 17770 17044 13144
"""

# genpaku_tfd = FreqDist(t[2] for (w, t) in genpaku.tagged_words())
genpaku_tfd = nltk.FreqDist(t.split('\t')[2] for (w, t) in genpaku.tagged_words())
genpaku_tfd.tabulate(10)
"""
名詞-一般 動詞-自立 助詞-格助詞-一般 助動詞 記号-読点 助詞-係助詞 名詞-サ変接続 助詞-連体化
記号-句点 助詞-接続助詞
88053 74235 71789 67201 40826 33452 31904 30419 26699 25760
"""

genpaku_wfd.plot(100)


print ' '.join( set(w for w,t in genpaku.tagged_words() if t.split('\t')[0] == u"コウショウ") )

# 高尚 公娼 工匠 交渉


#--------------------------------------------------
# p. 477

genpaku_t.collocations()
"""
Building collocations list
オープン ソース; コール バック; インター フェイス; Red Hat; Teddy bear; フリー ソフトウェア; science
fiction; Belle Epoque; 多かれ 少なかれ; シェーン ベルク; attribute name; ミドル ウェア;
package com; ソース コード; ミルキー ホワイト; GNU システム; import org; フリー ソフト;
GNU
プロジェクト; あちら こちら
"""


# NLTK2 のみ実行可能

genpaku_t.generate()
"""
Building ngram index...
ねんねん や   [ 直 示 定義 は しばしば 単一 ソース から の 手紙 は 、 役回り が 今 ここ
に いる の で ある と 。
が 、 いわゆる 実 無限 数 に 限定 し なかっ た 。 ふつふつ と わきあがる 怒り に 震え なが
ら 諦め て 死ぬ か を 。
だっ たら こんな 、 疲れる 上 に 醸成 さ れ た か と いう こと だ 、 僕 が どれ だけ 助か
る か 、 ぼく ら が
群がっ て 彼女 の 腕 に あ たま を のせ た 。   彼 の 身体 は 、 自己 や 自分
"""

genpaku_t.similar(u"ソフトウェア")
"""
# Building word-context index...
ソフト 人 彼 私 それ 彼女 僕 人間 労働 彼ら 男 これ ぼく わたし 仕事 目 自分 あなた 世界
今
"""


#--------------------------------------------------
# p.478-479

import nltk
from nltk.corpus.reader import *
from nltk.corpus.reader.util import *
from nltk.text import Text


###################### NLTK verion #############################

if (nltk.__version__[0] == '2'):
 class JapaneseWordNetCorpusReader(WordNetCorpusReader):
  def __init__(self, root, filename):
    WordNetCorpusReader.__init__(self, root)
    
    import codecs
    f=codecs.open(filename, encoding="utf-8")
    self._jword2offset = {}
    for line in f:
      _cells = line.strip().split('\t')
      _offset_pos = _cells[0]
      _word = _cells[1]
      if len(_cells)>2: _tag = _cells[2]
      _offset, _pos = _offset_pos.split('-')
      self._jword2offset[_word] = {'offset': int(_offset), 'pos': _pos}
  
  def synset(self, word):
    if word in self._jword2offset:
      return WordNetCorpusReader._synset_from_pos_and_offset(
        self, self._jword2offset[word]['pos'], self._jword2offset[word]['offset']
      ) 
    else  :
      return None
else:
 from nltk.corpus.reader.wordnet import WordNetCorpusReader
 class JapaneseWordNetCorpusReader(WordNetCorpusReader):
  def __init__(self, root, filename):
    WordNetCorpusReader.__init__(self, root, None)
    
    import codecs
    f=codecs.open(filename, encoding="utf-8")
    self._jword2offset = {}
    for line in f:
      _cells = line.strip().split('\t')
      _offset_pos = _cells[0]
      _word = _cells[1]
      if len(_cells)>2: _tag = _cells[2]
      _offset, _pos = _offset_pos.split('-')
      self._jword2offset[_word] = {'offset': int(_offset), 'pos': _pos}
  
  def synset(self, word):
    if word in self._jword2offset:
      return WordNetCorpusReader._synset_from_pos_and_offset(
        self, self._jword2offset[word]['pos'], self._jword2offset[word]['offset']
      ) 
    else  :
      return None

############################################################

# 注意:
# Homeに適切なフォルダへのパスをセットしておくこと
# wnjp-ok.tabをダウンロードして、Homeフォルダにおくこと

nltk.download()

jwn = JapaneseWordNetCorpusReader(nltk.data.find('corpora/wordnet'),
    Home+'wnjpn-ok.tab')

jsyn_whale = jwn.synset(u"鯨")
jsyn_apple = jwn.synset(u"りんご")
jsyn_orange = jwn.synset(u"ミカン")

jsyn_whale
# Synset('whale.n.02')

jsyn_apple
# Synset('apple.n.01')

jsyn_orange
# Synset('orange.n.01')

print jsyn_apple.path_similarity( jsyn_orange)
# 0.25

print jsyn_whale.path_similarity( jsyn_orange)
# 0.0526315789474

#--------------------------------------------------
# p.482

dict_entries = [
  [u"かれ", {'pos':'V-Y', 'lemma':u"枯れ"}],
  [u"かれ", {'pos':'V-Z', 'lemma':u"枯れ"}],
  [u"かれ", {'pos':'N', 'lemma':u"彼"}],
  [u"の", {'pos':'J-K', 'lemma':u"の"}],
  [u"く", {'pos':'N', 'lemma':u"区"}],
  [u"くる", {'pos':'V-S', 'lemma':u"来る"}],
  [u"くる", {'pos':'V-T', 'lemma':u"来る"}],
  [u"くるま", {'pos':'N', 'lemma':u"車"}],
  [u"ま", {'pos':'N', 'lemma':u"間"}],
  [u"まで", {'pos':'J-F', 'lemma':u"まで"}],
  [u"で", {'pos':'J-K', 'lemma':u"で"}],
  [u"でま", {'pos':'N', 'lemma':u"デマ"}],
  [u"まつ", {'pos':'N', 'lemma':u"松"}],
  [u"まつ", {'pos':'V-S', 'lemma':u"待つ"}],
  [u"まつ", {'pos':'V-T', 'lemma':u"待つ"}],
  [u"つ", {'pos':'N', 'lemma':u"津"}]
]

matrie = nltk.defaultdict(dict)

_BOS_ENTRY = {'length':1, 'pos':'BOS', 'lemma': u'BOS', 'cost': 0}
_EOS_ENTRY = {'length':1, 'pos':'EOS', 'lemma': u'EOS', 'cost': 0}


def insert(trie, key, value):
  if key:
    first, rest = key[0], key[1:]
    if first not in trie:
      trie[first] = {}
    insert(trie[first], rest, value)
  else:
    if not 'value' in trie:
      trie['value'] = []
    trie['value'].append(value)


for entry in dict_entries:
  entry[1]['length'] = len(entry[0])
  insert(matrie, entry[0].encode('utf-8'), entry[1])


#--------------------------------------------------
# p.483


def common_prefix_search(trie, key):
  ret = []
  if 'value' in trie:
    ret += trie['value']
  if key:
    first, rest = key[0], key[1:]
    if first in trie:
      ret += common_prefix_search(trie[first], rest)
  return ret


#--------------------------------------------------
# p.484

def is_connectable(bnode, cnode):
  ctable = set([
    ('BOS', 'N'), ('BOS', 'V'), ('BOS', 'T'),
    ('T', 'N'), ('N', 'J'), ('J', 'N'), ('J', 'V'),
    ('V-T', 'N'), ('V-T', 'J-F'), ('V-Y', 'A'), ('V-S', 'EOS'), ('A', 'EOS')
    ])
  bpos = bnode['entry']['pos']
  bpos_s = bpos.split('-')[0]
  cpos = cnode['entry']['pos']
  cpos_s = cpos.split('-')[0]
  
  return (((bpos, cpos) in ctable) or ((bpos_s, cpos) in ctable)
    or ((bpos, cpos_s) in ctable) or ((bpos_s, cpos_s) in ctable))



#--------------------------------------------------
# p.485

def analyze_simple(trie, sent, connect_func=lambda x, y: True):
  bos_node = {'next':[], 'entry': _BOS_ENTRY}
  end_node_list = nltk.defaultdict(list)
  end_node_list[0].append(bos_node)
  
  for i in range(0, len(sent)+1):
    if i < len(sent):
      cps_results = common_prefix_search(trie, sent[i:].encode('utf-8'))
    else:
      # EOS
      cps_results = [_EOS_ENTRY]
    
    for centry in cps_results:
      cnode = {'next':[], 'entry':centry}
      
      for bnode in end_node_list[i]:
        if connect_func(bnode, cnode):
          bnode['next'].append(cnode)
          
          end_nodes = end_node_list[i+centry['length']]
          if not cnode in end_nodes: end_nodes.append(cnode)
  
  return enum_solutions(bos_node)


#--------------------------------------------------
# p.486


def enum_solutions(node):
  results = []
  if node['entry']['lemma'] == u'EOS':
    return [[u'EOS']]
  for nnode in node['next']:
    for solution in enum_solutions(nnode):
      results.append([node['entry']['lemma']]+solution)
  return results



res = analyze_simple(matrie, u"かれのくるまでまつ", is_connectable)
print '\n'.join('/'.join(sent) for sent in res)
"""
BOS/彼/の/来る/間/で/待つ/EOS
BOS/彼/の/来る/まで/待つ/EOS
BOS/彼/の/車/で/待つ/EOS
"""


#--------------------------------------------------
# p.487

def analyze(trie, sent, connect_func=lambda x,y: True, cost_func=lambda x,y: 0):
  bos_node = {'begin':-1, 'next':[], 'entry': _BOS_ENTRY, 'cost': 0}
  end_node_list = nltk.defaultdict(list)
  end_node_list[0].append(bos_node)
  
  for i in range(0, len(sent)+1):
    if i < len(sent):
      cps_results = common_prefix_search(trie, sent[i:].encode('utf-8'))
    else:
      # EOS
      cps_results = [_EOS_ENTRY]
    
    for centry in cps_results:
      cnode = {'begin': i, 'next':[], 'entry':centry}
      min_cost = -1
      min_bnodes = []
      
      for bnode in end_node_list[i]:
        if connect_func(bnode, cnode):
           cost = bnode['cost']+cost_func(bnode, cnode)
           if min_cost < 0 or cost < min_cost:
              min_cost = cost
              min_bnodes = [bnode]
           elif cost == min_cost:
              min_bnodes.append(bnode)
      
      if len(min_bnodes) > 0:
        for bnode in min_bnodes:
          cnode['cost'] = min_cost
          bnode['next'].append(cnode)
        
        end_nodes = end_node_list[i+centry['length']]
        if not cnode in end_nodes: end_nodes.append(cnode)
     
  return enum_solutions(bos_node)

#--------------------------------------------------
# p.488


dict_entries2 = [
  [u"こ", {'pos':'SF', 'lemma':u"個", 'cost': 10}],
  [u"この", {'pos':'T', 'lemma':u"この", 'cost': 10}],
  [u"ひ", {'pos':'N', 'lemma':u"日", 'cost': 40}],
  [u"ひと", {'pos':'N', 'lemma':u"人", 'cost': 40}],
  [u"ひとこと", {'pos':'N', 'lemma':u"一言", 'cost': 40}],
  [u"と", {'pos':'J', 'lemma':u"と", 'cost': 10}],
  [u"こと", {'pos':'N', 'lemma':u"事", 'cost': 10}],
  [u"で", {'pos':'V-Z','lemma':u"出", 'cost': 40}],
  [u"で", {'pos':'V-Y','lemma':u"出", 'cost': 40}],
  [u"で", {'pos':'J', 'lemma':u"で", 'cost': 10}],
  [u"げんき", {'pos':'N', 'lemma':u"元気", 'cost': 40}],
  [u"に", {'pos':'J', 'lemma':u"に", 'cost': 10}],
  [u"になっ", {'pos':'V-Y','lemma':u"担っ", 'cost': 40}],
  [u"なっ", {'pos':'V-Y','lemma':u"なっ", 'cost': 40}],
  [u"た", {'pos':'A', 'lemma':u"た", 'cost': 10}]
]

def cost_minimum(bnode, cnode):
  ctable = {
    ('BOS', 'T'): 10,
    ('T', 'N'): 10,
    ('N', 'J'): 10,
    ('J', 'N'): 10,
    ('N', 'N'): 10,
    ('N', 'V-Z'): 40,
    ('N', 'V-Y'): 40,
    ('V-Z', 'N'): 40,
    ('V-Y', 'N'): 40,
    ('J', 'V-Z'): 10,
    ('J', 'V-Y'): 10,
    ('V-Y', 'A'): 10,
    ('A', 'EOS'): 10
  }
  pos_2gram = (bnode['entry']['pos'], cnode['entry']['pos'])
  return cnode['entry']['cost'] + (ctable[pos_2gram] if pos_2gram in ctable else 100)



#--------------------------------------------------
# p.489

matrie2 = nltk.defaultdict(dict)
for entry in dict_entries2:
  entry[1]['length'] = len(entry[0])
  insert(matrie2, entry[0].encode('utf-8'), entry[1])

res = analyze(matrie2, u"このひとことでげんきになった", lambda x,y: True, lambda bnode, cnode: 0)

print '\n'.join('/'.join(sent) for sent in res)

"""
BOS/この/日/と/個/と/出/元気/に/なっ/た/EOS
BOS/この/日/と/個/と/出/元気/担っ/た/EOS
BOS/この/日/と/個/と/出/元気/に/なっ/た/EOS
BOS/この/日/と/個/と/出/元気/担っ/た/EOS
BOS/この/日/と/個/と/で/元気/に/なっ/た/EOS
BOS/この/日/と/個/と/で/元気/担っ/た/EOS
BOS/この/日/と/事/出/元気/に/なっ/た/EOS
... 
"""
# 30個の出力

cost_morpheme_num = lambda bnode, cnode: 1

jiritsugo = set(['N', 'V'])

cost_bunsetsu_num = lambda bnode, cnode: 1 if cnode['entry']['pos'].split('-')[0] in jiritsugo else 0

res = analyze(matrie2, u"このひとことでげんきになった", lambda x,y: True, cost_morpheme_num)

print '\n'.join('/'.join(sent) for sent in res)

"""
BOS/この/一言/出/元気/担っ/た/EOS
BOS/この/一言/出/元気/担っ/た/EOS
BOS/この/一言/で/元気/担っ/た/EOS
…
"""
# 3個の出力


res = analyze(matrie2, u"このひとことでげんきになった", lambda x,y: True, cost_bunsetsu_num)
print '\n'.join('/'.join(sent) for sent in res)
"""
BOS/この/日/と/個/と/で/元気/に/なっ/た/EOS
BOS/この/日/と/個/と/で/元気/担っ/た/EOS
BOS/この/人/個/と/で/元気/に/なっ/た/EOS
BOS/この/人/個/と/で/元気/担っ/た/EOS
BOS/この/一言/で/元気/に/なっ/た/EOS
BOS/この/一言/で/元気/担っ/た/EOS
...
"""
# 6個の出力

res = analyze(matrie2, u"このひとことでげんきになった", is_connectable, cost_bunsetsu_num)
print '\n'.join('/'.join(sent) for sent in res)

"""
BOS/この/一言/で/元気/に/なっ/た/EOS
"""

#--------------------------------------------------
# p.490

# 注意:
# 変数Homeをセットしておくこと
# Homeフォルダにgingatetsudono_yoru.txtを置いておく
# カレントディレクトリに jptokenizer.py を置いておく
# カレントディレクトリの確認方法:
# import os
# os.getcwd()
# カレントディレクトリの変更方法:
# os.chdir(Home)

import jptokenizer

jp_sent_tokenizer = nltk.RegexpTokenizer(u'[^ 「」!?。]*[!?。]')

reader = PlaintextCorpusReader(Home, r'gingatetsudono_yoru.txt',
      encoding='utf-8',
      para_block_reader=read_line_block,
      sent_tokenizer=jp_sent_tokenizer,
      word_tokenizer=jptokenizer.JPSimpleTokenizer())

print ' '.join(reader.words()[20:80])
"""
「 で はみ なさん は 、 そういう ふう に 川だ と 云 《い 》わ れ たり 、 乳 の 流れ た あ
と だと 云わ れ たり し て い たこの ぼん やり と 白い もの が ほん とう は 何か ご 承知
です か 。」 先生 は 、 黒板 に 吊 《つる 》 し た 大き な 黒い 星座
"""


  
#--------------------------------------------------
# p.491

# 以下は、Linuxでのパッケージのインストール手順の例
# 一般に、 configure -> make -> make install でインストールできる
# sudo というのは命令を管理者権限で行うというコマンドで
# システムにパッケージを登録する場合に必要である

% ./configure --with-charset=utf8
% make
% su
# make install

#######################################################
# 注意:
# 以下は、MeCabモジュールがある場合である
# コンピュータ演習室ではできないので、
# (1) 講義用ウェブページから mecah-python.zipを展開し、その中の
#     MeCab.py を使う --- その場合は、以下のとおり、使える
# (2) もしくは、講義用ウェブページからmecab-cabocha.py (「コンピュータ演習室で
#    mecab, cabocha, juman, knpを使うためのプログラム」)を使う
#   その説明を読むこと(以下の手順とは異なる)
#
########################################################

import MeCab
mecab = MeCab.Tagger('-Ochasen')

# コンピュータ演習室の mecabは sjis の入出力なので、
# utf-8を前提とした教科書の記述と異なる方法で行う

#####
# sent = u"かれのくるまでまつ".encode('utf-8')
# print mecab.parse(sent)
#####

reload(sys)
import sys

sys.setdefaultencoding("sjis")
sent="かれがくるまでまつ"
print mecab.parse(sent)

"""
かれ    カレ    かれる  動詞-自立       一段    連用形
が      ガ      が      助詞-格助詞-一般
くる    クル    くる    動詞-自立       カ変・クル      基本形
まで    マデ    まで    助詞-副助詞
まつ    マツ    まつ    動詞-自立       五段・タ行      基本形
EOS"""

node = mecab.parseToNode(sent)
node = node.next
while node:
   print node.surface, node.feature
   node = node.next

"""
かれ 動詞,自立,*,*,一段,連用形,かれる,カレ,カレ
の 助詞,格助詞,一般,*,*,*,の,ノ,ノ
くる 動詞,自立,*,*,カ変・クル,基本形,くる,クル,クル
まで 助詞,副助詞,*,*,*,*,まで,マデ,マデ
まつ 動詞,自立,*,*,五段・タ行,基本形,まつ,マツ,マツ
BOS/EOS,*,*,*,*,*,*,*,*
"""

#--------------------------------------------------
# p.492


import nltk
from nltk.corpus.reader import *
from nltk.corpus.reader.util import *
from nltk.text import Text

reload(sys)
import sys

sys.setdefaultencoding("sjis")

# 注意:
# Homeに適切なフォルダへのパスをセットしておくこと
# gingatetsudono_yoru.txtをダウンロードして、Homeフォルダにおくこと
# カレントディレクトリに jptokenizer.py を置いておく

import jptokenizer

jp_sent_tokenizer = nltk.RegexpTokenizer(u'[^ 「」!?。]*[!?。]')

reader = PlaintextCorpusReader(Home, r'gingatetsudono_yoru.txt',
    encoding='utf-8',
    para_block_reader=read_line_block,
    sent_tokenizer=jp_sent_tokenizer,
    word_tokenizer=jptokenizer.JPMeCabTokenizer())

###############################
# 注意: コンピュータ演習室の環境で、次はエラーになる。。。
# 理由はよくわかっていない(utf-8ではこのコードが扱えない、というメッセージが出る
# ので、文字コード関係であることは分かる
################################

print ' '.join(reader.words()[20:80])
"""
は 、 そういう ふう に 川 だ と 云 《 い 》 われ たり 、 乳 の 流れ た あと だ と 云わ れ
たり し て い た この ぼんやり と 白い もの が ほんとう は 何 か ご 承知 です か 。 」 先
生 は 、 黒板 に 吊 《 つる 》 し た 大きな 黒い 星座 の
"""

#--------------------------------------------------
# p.493

###################################################################
# 注意:
# http://app-dist.khlog.net/software/python-cjuman/
#  がそのホームであるが
#  Linux 環境での実装が説明されている
#  ここでパスする
#  jumanを使いたい場合は、講義用ウェブページからmecab-cabocha.py 
#  (「コンピュータ演習室で mecab, cabocha, juman, knpを使うためのプログラム」)
#   を使う。そのの説明を読むこと
###############################################################	


import cJuman
cJuman.init(['-B', '-e2'])
S = [u'30年も前に言語と画像を研究していた。'.encode('euc-jp')]
print cJuman.parse_opt(S, cJuman.SKIP_NO_RESULT).decode('euc-jp')

#--------------------------------------------------
# p.496

jpcfg1 = nltk.parse_cfg("""
S -> PP VP
PP -> NP P
VP -> PP VP
VP -> V TENS
NP -> NP 'の' NP
NP -> NP 'と' NP
NP -> N
N -> '先生' | '自転車' | '学校' | '僕'
P -> 'は' | 'が' | 'を' | 'で' | 'に'
V -> '行k' | '殴r' | '見'
TENS -> 'ru' | 'ita'
""")

#--------------------------------------------------
# p.497


sent = "先生 は 自転車 で 学校 に 行k ita".split(' ')
parser = nltk.ChartParser(jpcfg1)
for tree in parser.nbest_parse(sent):
   print tree

"""
(S
(PP (NP (N 先生)) (P は))
(VP
(PP (NP (N 自転車)) (P で))
(VP (PP (NP (N 学校)) (P に)) (VP (V 行k) (TENS ita)))))
"""

sent = "学校 に 自転車 で 先生 は 行k ita".split(' ')
parser = nltk.ChartParser(jpcfg1)
for tree in parser.nbest_parse(sent):
   print tree
"""
(S
(PP (NP (N 学校)) (P に))
(VP
(PP (NP (N 自転車)) (P で))
(VP (PP (NP (N 先生)) (P は)) (VP (V 行k) (TENS ita)))))
"""

#--------------------------------------------------
# p.498-499

# Homeに適切なフォルダへのパスをセットしておくこと

genpaku = ChasenCorpusReader(Home+"jeita\\", 'g0013.chasen', encoding='utf-8')
print '/'.join( genpaku.words()[:94] )


grammar = u'''JIRITSU: {<形容詞-自立>|<名詞.*>|<未知語>|<動詞-自立>|<記号-一般>|<副詞.*>}
   FUZOKU: {<助動詞>|<助詞.*>|<動詞-接尾>|<動-非自立>|<名詞-非自立>}
   CHUNK: {<JIRITSU>+<FUZOKU>*}'''


"""
# HTMLでは<>の扱いに気をつけないといけないので...

grammar = u'''JIRITSU: {<形容詞-自立>|<名詞.*>|<未知語>|<動詞-自立>|<記号-一般>|<副詞.*>}
   FUZOKU: {<助動詞>|<助詞.*>|<動詞-接尾>|<動-非自立>|<名詞-非自立>}
   CHUNK: {+*}'''
"""

cp = nltk.RegexpParser(grammar)
print cp.parse([(x, y.split('\t')[2]) for x,y in genpaku.tagged_words()[:94]])
"""
(S
  (CHUNK (JIRITSU 呼び鈴/名詞-一般) (FUZOKU を/助詞-格助詞))
  (CHUNK
    (JIRITSU 押す/動詞-自立)
    (JIRITSU こと/名詞-非自立)
    (FUZOKU だろ/助動詞)
    (FUZOKU う/助動詞))
。/記号-句点
  (CHUNK (JIRITSU アスカム/未知語) (FUZOKU が/助詞-格助詞))
  (CHUNK (JIRITSU 時間/名詞-副詞可能) (FUZOKU に/助詞-格助詞))
  (CHUNK (JIRITSU 几帳面/名詞-形容動詞語幹) (FUZOKU で/助動詞))
  (CHUNK (JIRITSU 助かっ/動詞-自立) (FUZOKU た/助動詞))
。/記号-句点
  (CHUNK
    (JIRITSU どっち/名詞-代名詞)
    (JIRITSU つか/動詞-自立)
    (FUZOKU ず/助動詞)
    (FUZOKU の/助詞-連体化))
  (CHUNK (JIRITSU 状態/名詞-一般) (FUZOKU は/助詞-係助詞))
、/記号-読点
  (CHUNK (JIRITSU グラニス/未知語) (FUZOKU の/助詞-連体化))
  (CHUNK (JIRITSU 神経/名詞-一般) (FUZOKU にとって/助詞-格助詞))
、/記号-読点
  (CHUNK
    (JIRITSU 徐々に/副詞-一般)
    (JIRITSU 耐え難い/形容詞-自立)
    (JIRITSU もの/名詞-非自立)
    (FUZOKU に/助詞-格助詞))
  (CHUNK
    (JIRITSU なっ/動詞-自立)
    (FUZOKU て/助詞-接続助詞)
    (FUZOKU い/動詞-非自立)
    (FUZOKU た/助動詞))
  。/記号-句点
  (CHUNK (JIRITSU 呼び鈴/名詞-一般) (FUZOKU の/助詞-連体化))
  (CHUNK (JIRITSU 音/名詞-一般)))
"""

#--------------------------------------------------
# p.500

#############################################################################
#  注意:
#    Cabocha と KNP については、、講義用ウェブページからmecab-cabocha.py 
#  (「コンピュータ演習室で mecab, cabocha, juman, knpを使うためのプログラム」)
#   を使う。そのの説明を読むこと
###############################################################	

% ./configure --with-charset=UTF8
% make
% su
# make install


% python setup.py build
% su
# python setup.py install


import CaboCha
cabocha = CaboCha.Parser('--charset=UTF8')
sent = u"太郎はこの本を二郎を見た女性に渡した。".encode('utf-8')


print cabocha.parseToString(sent)
"""
太郎は-----------D
                      この-D      |
                         本を---D |
                         二郎を-D |
                           見た-D |
                           女性に-D
                           渡した。
EOS
"""

tree = cabocha.parse(sent)
print tree.toString(CaboCha.FORMAT_LATTICE)


#--------------------------------------------------
# p.504

def cabocha2depgraph(t):
  from nltk.parse import DependencyGraph
  import re
  dg = DependencyGraph()
  i = 0
  for line in t.splitlines():
    if line.startswith("*"):
      # start of bunsetsu
      
      cells = line.strip().split(" ", 3)
      m   = re.match(r"([\-0-9]*)([ADIP])", cells[2])
      
      node = dg.nodelist[i]
      node.update(
        {'address': i,
        'rel': m.group(2), # dep_type
        'word': [],
        'tag': []
        })
      dep_parent = int(m.group(1))
      
      while len(dg.nodelist) < i+1 or len(dg.nodelist) < dep_parent+1:
         dg.nodelist.append({'word':[], 'deps':[], 'tag': []}) # >
      
      if dep_parent == -1:
         dg.root = node
      else:
         dg.nodelist[dep_parent]['deps'].append(i)
      
      i += 1
    elif not line.startswith("EOS"):
      # normal morph
      cells = line.strip().split("\t")
      
      morph = (cells[0], tuple(cells[1].split(',')))
      dg.nodelist[i-1]['word'].append(morph[0])
      dg.nodelist[i-1]['tag'].append(morph[1])
  return dg

def reset_deps(dg):
  for node in dg.nodelist:
    node['deps'] = []
  dg.root = dg.nodelist[-1]



#--------------------------------------------------
# p.505


def set_head_form(dg):
  for node in dg.nodelist:
    tags = node['tag']
    num_morphs = len(tags)
    # extract bhead (主辞) and bform (語形)
    bhead = -1
    bform = -1
    for i in xrange(num_morphs-1, -1, -1):
      if tags[i][0] == u"記号":
        continue
      else:
        if bform == -1: bform = i
        if not (tags[i][0] == u"助詞"
             or (tags[i][0] == u"動詞" and tags[i][1] == u"非自立")
             or tags[i][0] == "助動詞"):
          if bhead == -1: bhead = i
    node['bhead'] = bhead
    node['bform'] = bform


# --------------------------------------------------
# p.506

NEXT_NODE = 1
NEXT_VERB_NODE = 2
NEXT_NOUN_NODE = 3
def get_dep_type(node):
  bform_tag = node['tag'][node['bform']]
  if bform_tag[0] == u"助詞" and bform_tag[1] == u"格助詞":
    return NEXT_VERB_NODE
  elif bform_tag[0] == u"助動詞" and bform_tag[-1] == u"タ":
    return NEXT_NOUN_NODE
  else:
    return NEXT_NODE


def analyze_dependency(dg):
  num_nodes = len(dg.nodelist)
  for i in xrange(num_nodes-1, 0, -1):
    node = dg.nodelist[i]
    if i == num_nodes - 1:
      # last node -> to_node = 0
      to_node = 0
    elif i == num_nodes - 2:
      # one from the last node -> to_node = num_nodes - 1
      to_node = num_nodes - 1
    else:
      # other nodes
      dep_type = get_dep_type(node)
      if dep_type == NEXT_NODE:
        to_node = i + 1
      elif (dep_type == NEXT_VERB_NODE) or (dep_type == NEXT_NOUN_NODE) :
        for j in xrange(i+1, num_nodes):
          node_j = dg.nodelist[j]
          node_j_headtag = node_j['tag'][node_j['bhead']]
          # if (node_j['closed'] == False and    # corrected by Sirai 2014-1225
          if (node_j.get('closed') == None and
            (dep_type == NEXT_VERB_NODE and node_j_headtag[0] == u"動詞") or
            (dep_type == NEXT_NOUN_NODE and node_j_headtag[0] == u"名詞" and
            node_j_headtag[1] != u"形容動詞語幹")):
            to_node = j
            break
    node['head'] = to_node
    dg.nodelist[to_node]['deps'].append(i)
    for j in xrange(i+1, to_node):
         dg.nodelist[j]['closed'] = True

# --------------------------------------------------
# p.507-508

'''
cabocha_result = u'''* 0 7D 0/1 0.000000
太郎\t名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー B-PERSON
は\t助詞,係助詞,*,*,*,*,は,ハ,ワ O
* 1 2D 0/0 1.468291
この\t連体詞,*,*,*,*,*,この,コノ,コノ O
* 2 4D 0/1 0.742535
本\t名詞,一般,*,*,*,*,本,ホン,ホン O
を\t助詞,格助詞,一般,*,*,*,を,ヲ,ヲ O
* 3 4D 1/2 1.892480
二\t名詞,数,*,*,*,*,二,ニ,ニ O
郎\t名詞,一般,*,*,*,*,郎,ロウ,ロー O
を\t助詞,格助詞,一般,*,*,*,を,ヲ,ヲ O
* 4 6D 0/1 0.702689
見\t動詞,自立,*,*,一段,連用形,見る,ミ,ミ O
た\t助動詞,*,*,*,特殊・タ,基本形,た,タ,タ O
* 5 6D 0/1 1.193842
きれい\t名詞,形容動詞語幹,*,*,*,*,きれい,キレイ,キレイ O
な\t助動詞,*,*,*,特殊・ダ,体言接続,だ,ナ,ナ O
* 6 7D 0/1 0.000000
女性\t名詞,一般,*,*,*,*,女性,ジョセイ,ジョセイ O
に\t助詞,格助詞,一般,*,*,*,に,ニ,ニ O
* 7 -1D 0/1 0.000000
渡し\t動詞,自立,*,*,五段・サ行,連用形,渡す,ワタシ,ワタシ O
た\t助動詞,*,*,*,特殊・タ,基本形,た,タ,タ O
。\t記号,句点,*,*,*,*,。,。,。 O
EOS
'''

dg = cabocha2depgraph(cabocha_result)
reset_deps(dg)
set_head_form(dg)

import sys

###############################################################
# The following depends on encoding
#
if sys.getdefaultencoding() == 'utf-8':
  def _node_map(node):
     node['word'] = '/'.join(node['word']).encode('utf-8')
     return node
else:
  def _node_map(node):
     node['word'] = '/'.join(node['word'])    # .encode('utf-8')
     return node

###############################################################
dg.nodelist = [_node_map(n) for n in dg.nodelist]
analyze_dependency(dg)	 	  # corrected by Sirai 2014-12-25

###############################################################
# The following depends on encoding
#
if sys.getdefaultencoding() == 'utf-8':
   print str(dg.tree()).decode('utf-8')
else:
    print str(dg.tree())              #  .decode('utf-8')

##############################################################
"""
(渡し/た/。
 (女性/に きれい/な (見/た 二/郎/を (本/を この))))
"""

--------------------------------------------------
# p.511

def extract_case_frame(dg):
  res = []
  for node in dg.nodelist:
    if is_yogen(node):
      yogen = node			# corrected by Sirai, 2014-12-25
      for case_cand in [dg.nodelist[i] for i in node['deps']]:
        if is_case(case_cand):
           res.append([yogen, case_cand])
  return res


--------------------------------------------------
# p.512


def is_yogen(node):
  bhead_tag = node['tag'][node['bhead']]
  bform_tag = node['tag'][node['bform']]
  if bhead_tag[0] == u"動詞":
    return True
  elif bhead_tag[0] == u"形容詞":
    return True
  elif bhead_tag[0] == u"名詞" and bform_tag[0] == u"助動詞":
    return True
  else:
    return False

def is_case(node):
  bhead_tag = node['tag'][node['bhead']]
  bform_tag = node['tag'][node['bform']]
  bform_surface = bform_tag[-1]
  if (bform_tag[0] == u"助詞" and bform_tag[1] == u"格助詞"
    and (bform_surface in set([u"ガ", u"ヲ", u"ニ", u"ト", u"デ", u"カラ", u"ヨリ",
    u"ヘ", u"マデ"]))):
    return True
  elif bhead_tag[0] == u"名詞" and bform_tag[0:2] == [u"名詞", u"接尾"]:
    return True
  else:
    return False

# 係り受け解析+CaboCha=>格解析のためのデータ

def case_process(sent):
  import subprocess
  # mecab-cabocha.pyを使う場合
  cabocha_result = unicode(cabocha(sent,1),encoding="sjis")
  # そうでなければ
  # cabocha_result = CaboCha.FORMAT_LATTICE(sent)
  dg = cabocha2depgraph(cabocha_result)
  reset_deps(dg)
  set_head_form(dg)
  dg.nodelist = [_node_map(n) for n in dg.nodelist]
  analyze_dependency(dg)
  return extract_case_frame(dg)

def case_verb(node):
   return (node[0]['word'], node[0]['tag'][0][-3],
       node[1]['tag'][node[1]['bform']][-1],
       node[1]['tag'][node[1]['bhead']][-3])


# 実行例

sent="太郎が荷物をトラックに積んだ"
for x in case_process(sent): print pp(case_verb(x))

"""
(u'積ん/だ', u'積む', u'ニ', u'トラック')
(u'積ん/だ', u'積む', u'ヲ', u'荷物')
"""

######################################################################
# cabochaの結果をそのまま使う
#####################################################################

def cabocha2depgraph_rev(t):
  from nltk.parse import DependencyGraph
  import re
  dg = DependencyGraph()
  i = 0
  for line in t.splitlines():
    if line.startswith("*"):
      # start of bunsetsu
      
      cells = line.strip().split(" ", 3)
      m   = re.match(r"([\-0-9]*)([ADIP])", cells[2])
      f_b = re.match(r"(\d+)/(\d+)", cells[3])
      
      node = dg.nodelist[i]
      node.update(
        {'address': i,
        'rel': m.group(2), # dep_type
        'bform': int(f_b.group(2)),   # bform --- function word
        'bhead': int(f_b.group(1)),   # bhead --- head
        'word': [],
        'tag': []
        })
      dep_parent = int(m.group(1))
      
      while len(dg.nodelist) < i+1 or len(dg.nodelist) < dep_parent+1:
         dg.nodelist.append({'word':[], 'deps':[], 'tag': []}) # >
      
      if dep_parent == -1:
         dg.root = node
      else:
         dg.nodelist[dep_parent]['deps'].append(i)
      
      i += 1
    elif not line.startswith("EOS"):
      # normal morph
      cells = line.strip().split("\t")
      
      morph = (cells[0], tuple(cells[1].split(',')))
      dg.nodelist[i-1]['word'].append(morph[0])
      dg.nodelist[i-1]['tag'].append(morph[1])
  return dg

def case_process_rev(sent):
  import subprocess
  # mecab-cabocha.pyを使う場合
  cabocha_result = unicode(cabocha(sent,1),encoding="sjis")
  # そうでなければ
  # cabocha_result = CaboCha.FORMAT_LATTICE(sent)
  dg = cabocha2depgraph_rev(cabocha_result)
  dg.nodelist = [_node_map(n) for n in dg.nodelist]
  return extract_case_frame(dg)

# 実行例

sent="太郎が荷物をトラックに積んだ"
for x in case_process_rev(sent): print pp(case_verb(x))

"""
(u'積ん/だ', u'積む', u'ガ', u'太郎')
(u'積ん/だ', u'積む', u'ヲ', u'荷物')
(u'積ん/だ', u'積む', u'ニ', u'トラック')
"""