teratail header banner
teratail header banner
質問するログイン新規登録

質問編集履歴

27

2019/05/01 00:58

投稿

t-cool
t-cool

スコア71

title CHANGED
@@ -1,1 +1,1 @@
1
- Ruby製ライブラリlemmatizerのソースコード解読
1
+ Ruby製ライブラリlemmatizerのコード解読
body CHANGED
File without changes

26

2019/05/01 00:58

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -336,15 +336,15 @@
336
336
    ・@wordlistsと@exceptions、2つのハッシュを辞書として作成。
337
337
    ・それぞれ、次のような構造。これらにデータを登録する。
338
338
   {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
339
-   ・見出し語をwordlistsに登録する理由は、品詞を特定せずにlemmaを呼び出した際、どの品詞に属しているかを検索するため。
339
+   ・wordlistsに見出し語を登録する目的は、品詞を特定せずにlemmaを呼び出した際、どの品詞に属しているかを特定するため。
340
340
 
341
341
  2. lemmaメソッドの呼び出し
342
342
    <品詞を特定する場合>
343
343
     ・@exceptionsに単語があれば、そのデータを元に原形を返す
344
344
     ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
345
-   <品詞を特定しない場合場合>
345
+   <品詞を特定しない場合>
346
-    ・[:verb, :noun, :adj, :adv, :abbr]の順に、見出し語を元に、どの品詞なのか検索する。
346
+    ・動詞->名詞->形容詞->副詞の順で[:verb, :noun, :adj, :adv, :abbr]、見出し語から品詞を特定する。
347
-    ・@exceptionsに単語があれば、そのデータを元に原形を返す
347
+    ・@exceptionsに単語があれば、そこから原形を返す
348
348
     ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
349
349
 
350
350
  コードの解読を難しくしていた要因:

25

2019/05/01 00:57

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -339,8 +339,13 @@
339
339
    ・見出し語をwordlistsに登録する理由は、品詞を特定せずにlemmaを呼び出した際、どの品詞に属しているかを検索するため。
340
340
 
341
341
  2. lemmaメソッドの呼び出し
342
+   <品詞を特定する場合>
342
-   ・@exceptionsに単語があれば、そのデータを元に原形を返す
343
+    ・@exceptionsに単語があれば、そのデータを元に原形を返す
343
-   ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
344
+    ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
345
+   <品詞を特定しない場合場合>
346
+    ・[:verb, :noun, :adj, :adv, :abbr]の順に、見出し語を元に、どの品詞なのかを検索する。
347
+    ・@exceptionsに単語があれば、そのデータを元に原形を返す
348
+    ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
344
349
 
345
350
  コードの解読を難しくしていた要因:
346
351
    ・辞書データを作成するためのメソッドが多かったこと

24

2019/04/30 01:37

投稿

t-cool
t-cool

スコア71

title CHANGED
@@ -1,1 +1,1 @@
1
- Ruby製ライブラリlemmatizerのソースコード解読
1
+ Ruby製ライブラリlemmatizerのソースコード解読
body CHANGED
@@ -334,8 +334,10 @@
334
334
 
335
335
  1. 辞書の作成
336
336
    ・@wordlistsと@exceptions、2つのハッシュを辞書として作成。
337
-   ・次のような構造。これらにデータを登録する。
337
+   ・それぞれ、次のような構造。これらにデータを登録する。
338
338
   {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
339
+   ・見出し語をwordlistsに登録する理由は、品詞を特定せずにlemmaを呼び出した際、どの品詞に属しているかを検索するため。
340
+
339
341
  2. lemmaメソッドの呼び出し
340
342
    ・@exceptionsに単語があれば、そのデータを元に原形を返す
341
343
    ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。

23

a

2019/04/30 01:35

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
File without changes

22

2019/04/30 01:33

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -68,7 +68,7 @@
68
68
  module Lemmatizer
69
69
  class Lemmatizer
70
70
 
71
- #########################辞書データ作成#####################################
71
+ #########################辞書データ作成#####################################
72
72
  # 辞書データがディレクトリのPATH
73
73
  # 大文字で始まる場合は「定数」。各メソッドから参照可能。
74
74
  DATA_DIR = File.expand_path('..', File.dirname(__FILE__))
@@ -134,11 +134,10 @@
134
134
  ]
135
135
  }
136
136
 
137
- # @wordlistsと@exceptionsに辞書データを登録するためのメソッド
137
+ # @wordlistsと@exceptionsに辞書データを登録する
138
138
  def load_wordnet_files(pos, list, exc)
139
- # 実行前、@wordlistsと@exceptionsは次のような構造
139
+ # 実行前、@wordlistsと@exceptionsは次のような構造。これらにデータを登録する。
140
140
  # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
141
- # ここにデータを登録していく。
142
141
 
143
142
  # 見出し語の登録
144
143
  # "acculturation"での例
@@ -151,6 +150,7 @@
151
150
  @wordlists[pos][w] = w
152
151
  end
153
152
  end
153
+
154
154
     # 例外語の登録
155
155
     # 例外語の辞書の各行は、"活用形 原形"(went go)の形式
156
156
     # 活用形をwに、原形をsとして、ハッシュに追加していく
@@ -306,12 +306,12 @@
306
306
      # インスタンス変数
307
307
      # スコープ:クラス内で全メソッドで共通して使用することが出来る。
308
308
  # クラスから作成されるオブジェクト毎に固有のもの。
309
- # インスタンスごとに独立してもつ変数のため、インスタンス変数という。
310
309
 
311
310
  MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
312
311
  @wordlists[x] = {}
313
312
  @exceptions[x] = {}
314
313
  end
314
+
315
315
  # 実行後、@wordlistsと@exceptionsは次のデータになる
316
316
  # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
317
317
 
@@ -330,4 +330,16 @@
330
330
 
331
331
  `index.品詞`の辞書は、各行の見出しだけを読み込んでいるとasmさんから助言をうけました。
332
332
 
333
+ lemmaの動作についてまとめ:
334
+
335
+ 1. 辞書の作成
336
+   ・@wordlistsと@exceptions、2つのハッシュを辞書として作成。
337
+   ・次のような構造。これらにデータを登録する。
338
+  {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
339
+ 2. lemmaメソッドの呼び出し
340
+   ・@exceptionsに単語があれば、そのデータを元に原形を返す
333
- ぞれのメソッドを追っていますがようにファイル群から辞書を構築して、どようlemmaメソッドが動作しているのか、まだ解読でていません
341
+   ・なけmorphological substitution (形態論置き換え)ルールにしたがっ換える
342
+
343
+ コードの解読を難しくしていた要因:
344
+   ・辞書データを作成するためのメソッドが多かったこと
345
+    (NLTKの辞書を再利用していたため)

21

2019/04/30 01:31

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -68,6 +68,7 @@
68
68
  module Lemmatizer
69
69
  class Lemmatizer
70
70
 
71
+ #########################辞書データを作成#####################################
71
72
  # 辞書データがディレクトリのPATH
72
73
  # 大文字で始まる場合は「定数」。各メソッドから参照可能。
73
74
  DATA_DIR = File.expand_path('..', File.dirname(__FILE__))
@@ -133,74 +134,8 @@
133
134
  ]
134
135
  }
135
136
 
136
- # インスタンスの生成時に実行される
137
- # オプショナル変数。dictに値を渡さない場合はnilになる。
138
- def initialize(dict = nil)
139
-
140
- @wordlists = {}
141
- @exceptions = {}
142
-     # インスタンス変数
143
-     # スコープ:クラス内で全メソッドで共通して使用することが出来る。
144
- # クラスから作成されるオブジェクト毎に固有のもの。
145
- # インスタンスごとに独立してもつ変数のため、インスタンス変数という。
146
-
147
- MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
148
- @wordlists[x] = {}
149
- @exceptions[x] = {}
150
- end
151
- # 実行後、@wordlistsと@exceptionsは次のデータにな
137
+ # @wordlistsと@exceptionsに辞書データを登録すためのメソッド
152
- # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
153
-
154
- WN_FILES.each_pair do |pos, pair|
155
- load_wordnet_files(pos, pair[0], pair[1])
156
- end
157
-
158
- if dict
159
- [dict].flatten.each do |d|
160
- load_provided_dict(d)
161
- end
162
- end
163
- end
164
-
165
- def lemma(form, pos = nil)
166
- unless pos
167
- [:verb, :noun, :adj, :adv, :abbr].each do |p|
168
- result = lemma(form, p)
169
- return result unless result == form
170
- end
171
-
172
- return form
173
- end
174
-
175
- each_lemma(form, pos) do |x|
176
- return x
177
- end
178
-
179
- form
180
- end
181
-
182
- # Print object only on init
183
- def inspect
184
- "#{self}"
185
- end
186
-
187
- private
188
-
189
- # ファイルから見出し語を取り出す前処理?
190
- def open_file(*args) # *argsは可変長引数
191
- # args[0]がIOクラスかStringIOクラスなら、args[0]を返す
192
- if args[0].is_a? IO or args[0].is_a? StringIO
193
- yield args[0]
194
- else
195
- File.open(*args) do |io|
196
- yield io
197
- end
198
- end
199
- end
200
-
201
138
  def load_wordnet_files(pos, list, exc)
202
-
203
- # @wordlistsと@exceptionsに辞書データを登録するためのメソッド
204
139
  # 実行前、@wordlistsと@exceptionsは次のような構造
205
140
  # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
206
141
  # ここにデータを登録していく。
@@ -216,7 +151,6 @@
216
151
  @wordlists[pos][w] = w
217
152
  end
218
153
  end
219
-
220
154
     # 例外語の登録
221
155
     # 例外語の辞書の各行は、"活用形 原形"(went go)の形式
222
156
     # 活用形をwに、原形をsとして、ハッシュに追加していく
@@ -246,23 +180,47 @@
246
180
    # よって、pair[0]は見出し語、pair[1]は例外語を示す。
247
181
    # load_wordnet_files(pos, pair[0], pair[1])
248
182
 
183
+ def load_provided_dict(dict)
184
+ num_lex_added = 0
185
+ open_file(dict) do |io|
186
+ io.each_line do |line|
187
+ # pos must be either n|v|r|a or noun|verb|adverb|adjective
188
+ p, w, s = line.split(/\s+/, 3)
189
+ pos = str_to_pos(p)
190
+ word = w
249
- def each_substitutions(form, pos)
191
+ substitute = s.strip
192
+ if /\A\"(.*)\"\z/ =~ substitute
193
+ substitute = $1
194
+ end
195
+ if /\A\'(.*)\'\z/ =~ substitute
196
+ substitute = $1
197
+ end
198
+ next unless (pos && word && substitute)
250
- if lemma = @wordlists[pos][form]
199
+ if @wordlists[pos]
200
+ @wordlists[pos][word] = substitute
251
- yield lemma
201
+ num_lex_added += 1
202
+ end
203
+ end
252
204
  end
205
+ # puts "#{num_lex_added} items added from #{File.basename dict}"
206
+ end
253
207
 
208
+ #########################辞書データを検索#####################################
209
+ def lemma(form, pos = nil)
210
+ unless pos
254
- MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
211
+ [:verb, :noun, :adj, :adv, :abbr].each do |p|
212
+ result = lemma(form, p)
213
+ return result unless result == form
214
+ end
255
215
 
256
- # entryが展開されて、oldとnewに代入される
257
- old, new = *entry
216
+ return form
217
+ end
258
218
 
259
- # formがoldで終わっている場合
260
- if form.endwith(old)
261
- each_substitutions(form[0, form.length - old.length] + new, pos) do |x|
219
+ each_lemma(form, pos) do |x|
262
- yield x
220
+ return x
263
- end
264
- end
265
221
  end
222
+
223
+ form
266
224
  end
267
225
 
268
226
  def each_lemma(form, pos)
@@ -282,6 +240,45 @@
282
240
  end
283
241
  end
284
242
 
243
+ # Print object only on init
244
+ def inspect
245
+ "#{self}"
246
+ end
247
+
248
+ private
249
+
250
+ # ファイルから見出し語を取り出す前処理?
251
+ def open_file(*args) # *argsは可変長引数
252
+ # args[0]がIOクラスかStringIOクラスなら、args[0]を返す
253
+ if args[0].is_a? IO or args[0].is_a? StringIO
254
+ yield args[0]
255
+ else
256
+ File.open(*args) do |io|
257
+ yield io
258
+ end
259
+ end
260
+ end
261
+
262
+
263
+ def each_substitutions(form, pos)
264
+ if lemma = @wordlists[pos][form]
265
+ yield lemma
266
+ end
267
+
268
+ MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
269
+
270
+ # entryが展開されて、oldとnewに代入される
271
+ old, new = *entry
272
+
273
+ # formがoldで終わっている場合
274
+ if form.endwith(old)
275
+ each_substitutions(form[0, form.length - old.length] + new, pos) do |x|
276
+ yield x
277
+ end
278
+ end
279
+ end
280
+ end
281
+
285
282
  def str_to_pos(str)
286
283
  case str
287
284
  when "n", "noun"
@@ -298,32 +295,36 @@
298
295
  return :unknown
299
296
  end
300
297
  end
298
+ end
301
299
 
302
- def load_provided_dict(dict)
300
+ ##################辞書を利用するための初期化##############################
303
- num_lex_added = 0
301
+ # インスタンスの生成時に実行される
302
+ # オプショナル変数。dictに値を渡さない場合はnilになる。
304
- open_file(dict) do |io|
303
+ def initialize(dict = nil)
305
- io.each_line do |line|
306
- # pos must be either n|v|r|a or noun|verb|adverb|adjective
307
- p, w, s = line.split(/\s+/, 3)
308
- pos = str_to_pos(p)
309
- word = w
304
+ @wordlists = {}
310
- substitute = s.strip
311
- if /\A\"(.*)\"\z/ =~ substitute
312
- substitute = $1
305
+ @exceptions = {}
306
+     # インスタンス変数
307
+     # スコープ:クラス内で全メソッドで共通して使用することが出来る。
308
+ # クラスから作成されるオブジェクト毎に固有のもの。
309
+ # インスタンスごとに独立してもつ変数のため、インスタンス変数という。
310
+
311
+ MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
312
+ @wordlists[x] = {}
313
+ @exceptions[x] = {}
313
- end
314
+ end
315
+ # 実行後、@wordlistsと@exceptionsは次のデータになる
316
+ # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
317
+
318
+ WN_FILES.each_pair do |pos, pair|
314
- if /\A\'(.*)\'\z/ =~ substitute
319
+ load_wordnet_files(pos, pair[0], pair[1])
315
- substitute = $1
316
- end
320
+ end
317
- next unless (pos && word && substitute)
321
+
318
- if @wordlists[pos]
322
+ if dict
319
- @wordlists[pos][word] = substitute
323
+ [dict].flatten.each do |d|
320
- num_lex_added += 1
324
+ load_provided_dict(d)
321
- end
322
325
  end
323
326
  end
324
- # puts "#{num_lex_added} items added from #{File.basename dict}"
325
327
  end
326
- end
327
328
  end
328
329
  ```
329
330
 

20

2019/04/30 00:27

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -7,6 +7,8 @@
7
7
 
8
8
  読み込まれる辞書データは次のような形式になっています。
9
9
 
10
+ 不規則活用の辞書では、”不規則活用形 原形”で各行が並んでいます。
11
+
10
12
  ```
11
13
  # noun.exc
12
14
  aardwolves aardwolf
@@ -16,6 +18,8 @@
16
18
  abscissae abscissa
17
19
  ```
18
20
 
21
+ index.品詞のファイルでは、規則変化、不規則変化に関わらず、単語のリストが並んでいます。この辞書で活用されるのは、最初の1単語のみです。
22
+
19
23
  ```
20
24
  # index.noun
21
25
  acculturation n 3 3 @ ~ + 3 1 01128984 05984936 05757049

19

だいぶOK

2019/04/30 00:14

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -144,6 +144,8 @@
144
144
  @wordlists[x] = {}
145
145
  @exceptions[x] = {}
146
146
  end
147
+ # 実行後、@wordlistsと@exceptionsは次のデータになる
148
+ # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
147
149
 
148
150
  WN_FILES.each_pair do |pos, pair|
149
151
  load_wordnet_files(pos, pair[0], pair[1])
@@ -180,7 +182,9 @@
180
182
 
181
183
  private
182
184
 
185
+ # ファイルから見出し語を取り出す前処理?
183
186
  def open_file(*args) # *argsは可変長引数
187
+ # args[0]がIOクラスかStringIOクラスなら、args[0]を返す
184
188
  if args[0].is_a? IO or args[0].is_a? StringIO
185
189
  yield args[0]
186
190
  else
@@ -192,9 +196,16 @@
192
196
 
193
197
  def load_wordnet_files(pos, list, exc)
194
198
 
199
+ # @wordlistsと@exceptionsに辞書データを登録するためのメソッド
200
+ # 実行前、@wordlistsと@exceptionsは次のような構造
201
+ # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
202
+ # ここにデータを登録していく。
203
+
195
-     # 見出し語処理
204
+ # 見出し語の登録
196
-     # 辞書データ各行(line)を文字列に分解して、見出し語[0]をwに入れる
205
+ # "acculturation"で
197
-    # @ハッシュのwordlistsに、{品詞 => 単語}の形式で見出し語を追加していく
206
+ # w = "acculturation n 3 3 @~省略~".split(/\s+/)[0]
207
+ # w は "acculturation"
208
+ # wordlists[:noun]["acculturation"] = "acculturation"
198
209
  open_file(list) do |io|
199
210
  io.each_line do |line|
200
211
  w = line.split(/\s+/)[0]
@@ -202,10 +213,10 @@
202
213
  end
203
214
  end
204
215
 
205
-    # 例外語処理
216
+    # 例外語の登録
206
-    # 例外語の辞書の各行は、"活用形 原形"(例、aardwolves aardwolf)の形式
217
+    # 例外語の辞書の各行は、"活用形 原形"(went go)の形式
207
218
     # 活用形をwに、原形をsとして、ハッシュに追加していく
208
- # @exceptions[pos][w]が未定義ならば[]を代入する
219
+ # @exceptions[pos][w]がならば[]を代入する
209
220
  # @exceptions[pos][w]に、原形をpush << する。
210
221
  open_file(exc) do |io|
211
222
  io.each_line do |line|
@@ -214,7 +225,6 @@
214
225
  @exceptions[pos][w] << s
215
226
  end
216
227
  end
217
-
218
228
  end
219
229
 
220
230
    # インスタンスの初期化の際、次のように呼び出される

18

助言

2019/04/30 00:09

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -48,9 +48,9 @@
48
48
  require 'lemmatizer/core_ext'
49
49
  require 'lemmatizer/lemmatizer'
50
50
 
51
+ # asmさんからの助言
52
+ #  lem = Lemmatizer::Lemmatizer.new と書くのがめんどくさいから
51
- # ?? モジュールで self.new を定義する、モジュールしてnewが呼べ
53
+ #  lem = Lemmatizer.new 書けるようにして
52
- # ?? `lemmatizer/lib/lemmatizer.rb`で、モジュール読み込み時に、Lemmatizer.new(dict)を実行?
53
- # ?? このコードは何のため?
54
54
  module Lemmatizer
55
55
  def self.new(dict = nil)
56
56
  Lemmatizer.new(dict)
@@ -58,7 +58,7 @@
58
58
  end
59
59
  ```
60
60
 
61
- 次は、'lemmatizer/lemmatizer.rb'のコード内を読みます。
61
+ 次は、'lemmatizer/lemmatizer.rb'のコードす。
62
62
 
63
63
  ```
64
64
  module Lemmatizer
@@ -90,7 +90,7 @@
90
90
  ]
91
91
  }
92
92
 
93
- # morphological(形態論の)substitution(置き換え)
93
+ # morphological substitution (形態論の置き換え)
94
94
  # 規則的に置き換え可能な場合のルール
95
95
  # 重複するものは、ing, es, ed, er, est。
96
96
  MORPHOLOGICAL_SUBSTITUTIONS = {

17

nnn

2019/04/29 23:13

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -29,12 +29,39 @@
29
29
  Wordnetの他の相関データがそのまま入っていて、
30
30
  ここの辞書データの全てを利用している訳ではないのでは?と推測しています。
31
31
 
32
- [lemmatizer/lib/lemmatizer/lemmatizer.rb](https://github.com/yohasebe/lemmatizer/blob/master/lib/lemmatizer/lemmatizer.rb)
33
- が、どのように辞書データを参照して、原形に変換しているかをご助言いただけないでか?
32
+ 以下、ソースコードを読み解きな、どのようにlemmaメソッドが動作しているかを解読と試みます。
34
33
 
34
+ まずは、ライブラリを管理する`lemmatizer.gemspec`です。以下の箇所では、Gemをrequireしたとき、実際にロードするファイルのパスが指定されています。libフォルダ以下にあるファイル群が読み込まれます。
35
+
35
36
  ```
37
+ Gem::Specification.new do |gem|
38
+ (省略)
39
+ gem.require_paths = ['lib']
40
+ end
41
+ ```
36
42
 
43
+ `lib/lemmatizer.rb`では、次の順でモジュールが読み込まれます。
44
+
45
+ ```
46
+ require 'stringio'
47
+ require 'lemmatizer/version'
48
+ require 'lemmatizer/core_ext'
49
+ require 'lemmatizer/lemmatizer'
50
+
51
+ # ?? モジュールで self.new を定義すると、モジュールに対してnewが呼べる。
52
+ # ?? `lemmatizer/lib/lemmatizer.rb`で、モジュール読み込み時に、Lemmatizer.new(dict)を実行?
53
+ # ?? このコードは何のため?
37
54
  module Lemmatizer
55
+ def self.new(dict = nil)
56
+ Lemmatizer.new(dict)
57
+ end
58
+ end
59
+ ```
60
+
61
+ 次は、'lemmatizer/lemmatizer.rb'のコード内を読みます。
62
+
63
+ ```
64
+ module Lemmatizer
38
65
  class Lemmatizer
39
66
 
40
67
  # 辞書データがディレクトリのPATH
@@ -103,8 +130,8 @@
103
130
  }
104
131
 
105
132
  # インスタンスの生成時に実行される
133
+ # オプショナル変数。dictに値を渡さない場合はnilになる。
106
134
  def initialize(dict = nil)
107
- # オプショナル変数。dictに値を渡さない場合はnilになる。
108
135
 
109
136
  @wordlists = {}
110
137
  @exceptions = {}

16

a

2019/04/28 12:35

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -286,4 +286,6 @@
286
286
  end
287
287
  ```
288
288
 
289
- `index.品詞`の辞書は、各行の見出しだけを読み込んでいるとasmさんから助言をうけました。
289
+ `index.品詞`の辞書は、各行の見出しだけを読み込んでいるとasmさんから助言をうけました。
290
+
291
+ それぞれのメソッドを追っていますが、どのようにファイル群から辞書を構築して、どのようにlemmaメソッドが動作しているのか、まだ解読できていません。

15

a

2019/04/27 09:25

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -211,7 +211,11 @@
211
211
  end
212
212
 
213
213
  MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
214
+
215
+ # entryが展開されて、oldとnewに代入される
214
- old, new = *entry
216
+ old, new = *entry
217
+
218
+ # formがoldで終わっている場合
215
219
  if form.endwith(old)
216
220
  each_substitutions(form[0, form.length - old.length] + new, pos) do |x|
217
221
  yield x

14

a

2019/04/27 09:19

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -33,49 +33,7 @@
33
33
  が、どのように辞書データを参照して、原形に変換しているのかをご助言いただけないでしょうか?
34
34
 
35
35
  ```
36
- WN_FILES
37
- -> 。
38
36
 
39
- MORPHOLOGICAL_SUBSTITUTIONS
40
- -> 形態素に基づく変換規則。
41
-
42
- # メソッド
43
- lemma(form, pos = nil)
44
- -> 単語(form)と品詞(pos)を受け取り、原形を返す。
45
-
46
- initialize(dict = nil)
47
- -> インスタンスが生成されたときに実行される。
48
- -> wordlistsとexceptionsの空のハッシュを宣言後、単語を追加していく。
49
-
50
- each_lemma(form, pos)
51
- -> メインのメソッド?
52
- -> 単語(form)と品詞(pos)を受け取り、
53
-    1. 例外の単語ペアに合致すれば返す
54
-    2. `ful`で終わっている場合の対応
55
-    3. 原則どおりの場合の対応
56
-      each_substitutions(form, pos)が呼び出される。
57
-
58
- each_substitutions(form, pos)
59
- -> 形態素を元に原則どおり変換する場合の変換。
60
-
61
- inspect
62
- -> インスタンス生成時に、オブジェクトを印字する。
63
-
64
- open_file(*args)
65
- -> ファイルを開く。
66
-
67
- load_wordnet_files(pos, list, exc)
68
- -> wordnet_filesを読み込む。
69
-
70
- str_to_pos(str)
71
- -> 文字列をシンボルに変換して返す。
72
-
73
- load_provided_dict(dict)
74
- -> 辞書を読み込む
75
- ```
76
-
77
- ```
78
-
79
37
  module Lemmatizer
80
38
  class Lemmatizer
81
39
 
@@ -85,7 +43,7 @@
85
43
 
86
44
  # 辞書データのPATH
87
45
  # noun(名詞)、verb(動詞)、adj(形容詞)、adv(副詞)
88
- # index.品詞は見出し語。品詞.excは不規則活用。
46
+ # index.品詞は見出し語。品詞.excは不規則活用。excはexception(例外)。
89
47
  WN_FILES = {
90
48
  :noun => [
91
49
  DATA_DIR + '/dict/index.noun',
@@ -206,6 +164,10 @@
206
164
  end
207
165
 
208
166
  def load_wordnet_files(pos, list, exc)
167
+
168
+     # 見出し語への処理
169
+     # 辞書データの各行(line)を文字列に分解して、見出し語[0]をwに入れる
170
+    # @ハッシュのwordlistsに、{品詞 => 単語}の形式で見出し語を追加していく
209
171
  open_file(list) do |io|
210
172
  io.each_line do |line|
211
173
  w = line.split(/\s+/)[0]
@@ -213,15 +175,36 @@
213
175
  end
214
176
  end
215
177
 
178
+    # 例外語への処理
179
+    # 例外語の辞書の各行は、"活用形 原形"(例、aardwolves aardwolf)の形式
180
+    # 活用形をwに、原形をsとして、ハッシュに追加していく
181
+ # @exceptions[pos][w]が未定義ならば[]を代入する
182
+ # @exceptions[pos][w]に、原形をpush << する。
216
183
  open_file(exc) do |io|
217
184
  io.each_line do |line|
218
185
  w, s = line.split(/\s+/)
219
- @exceptions[pos][w] ||= []
186
+ @exceptions[pos][w] ||= []
220
187
  @exceptions[pos][w] << s
221
188
  end
222
189
  end
190
+
223
191
  end
224
192
 
193
+   # インスタンスの初期化の際、次のように呼び出される
194
+   # WN_FILES.each_pair do |pos, pair|
195
+   # load_wordnet_files(pos, pair[0], pair[1])
196
+   # end
197
+   #
198
+   # WN_FILESは、{品詞 => [index.品詞, 品詞.例外]}を持つハッシュ
199
+   # WN_FILES = {
200
+   # :noun => [
201
+   # DATA_DIR + '/dict/index.noun',
202
+   # DATA_DIR + '/dict/noun.exc'
203
+   # ],
204
+   #
205
+   # よって、pair[0]は見出し語、pair[1]は例外語を示す。
206
+   # load_wordnet_files(pos, pair[0], pair[1])
207
+
225
208
  def each_substitutions(form, pos)
226
209
  if lemma = @wordlists[pos][form]
227
210
  yield lemma

13

ok

2019/04/27 08:52

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -33,12 +33,8 @@
33
33
  が、どのように辞書データを参照して、原形に変換しているのかをご助言いただけないでしょうか?
34
34
 
35
35
  ```
36
- # 変数
37
- DATA_DIR
38
- -> 辞書があるディレクトリの指定。
39
-
40
36
  WN_FILES
41
- -> 辞書データの指定
37
+ -> 。
42
38
 
43
39
  MORPHOLOGICAL_SUBSTITUTIONS
44
40
  -> 形態素に基づく変換規則。
@@ -82,8 +78,14 @@
82
78
 
83
79
  module Lemmatizer
84
80
  class Lemmatizer
81
+
82
+ # 辞書データがディレクトリのPATH
83
+ # 大文字で始まる場合は「定数」。各メソッドから参照可能。
85
84
  DATA_DIR = File.expand_path('..', File.dirname(__FILE__))
85
+
86
-
86
+ # 辞書データのPATH
87
+ # noun(名詞)、verb(動詞)、adj(形容詞)、adv(副詞)
88
+ # index.品詞は見出し語。品詞.excは不規則活用。
87
89
  WN_FILES = {
88
90
  :noun => [
89
91
  DATA_DIR + '/dict/index.noun',
@@ -102,7 +104,10 @@
102
104
  DATA_DIR + '/dict/adv.exc'
103
105
  ]
104
106
  }
107
+
105
-
108
+ # morphological(形態論の)substitution(置き換え)
109
+ # 規則的に置き換え可能な場合のルール
110
+ # 重複するものは、ing, es, ed, er, est。
106
111
  MORPHOLOGICAL_SUBSTITUTIONS = {
107
112
  :noun => [
108
113
  ['s', '' ],
@@ -139,9 +144,16 @@
139
144
  ]
140
145
  }
141
146
 
147
+ # インスタンスの生成時に実行される
142
148
  def initialize(dict = nil)
149
+ # オプショナル変数。dictに値を渡さない場合はnilになる。
150
+
143
151
  @wordlists = {}
144
152
  @exceptions = {}
153
+     # インスタンス変数
154
+     # スコープ:クラス内で全メソッドで共通して使用することが出来る。
155
+ # クラスから作成されるオブジェクト毎に固有のもの。
156
+ # インスタンスごとに独立してもつ変数のため、インスタンス変数という。
145
157
 
146
158
  MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
147
159
  @wordlists[x] = {}
@@ -183,7 +195,7 @@
183
195
 
184
196
  private
185
197
 
186
- def open_file(*args)
198
+ def open_file(*args) # *argsは可変長引数
187
199
  if args[0].is_a? IO or args[0].is_a? StringIO
188
200
  yield args[0]
189
201
  else

12

a

2019/04/25 21:11

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -78,4 +78,213 @@
78
78
  -> 辞書を読み込む
79
79
  ```
80
80
 
81
+ ```
82
+
83
+ module Lemmatizer
84
+ class Lemmatizer
85
+ DATA_DIR = File.expand_path('..', File.dirname(__FILE__))
86
+
87
+ WN_FILES = {
88
+ :noun => [
89
+ DATA_DIR + '/dict/index.noun',
90
+ DATA_DIR + '/dict/noun.exc'
91
+ ],
92
+ :verb => [
93
+ DATA_DIR + '/dict/index.verb',
94
+ DATA_DIR + '/dict/verb.exc'
95
+ ],
96
+ :adj => [
97
+ DATA_DIR + '/dict/index.adj',
98
+ DATA_DIR + '/dict/adj.exc'
99
+ ],
100
+ :adv => [
101
+ DATA_DIR + '/dict/index.adv',
102
+ DATA_DIR + '/dict/adv.exc'
103
+ ]
104
+ }
105
+
106
+ MORPHOLOGICAL_SUBSTITUTIONS = {
107
+ :noun => [
108
+ ['s', '' ],
109
+ ['ses', 's' ],
110
+ ['ves', 'f' ],
111
+ ['xes', 'x' ],
112
+ ['zes', 'z' ],
113
+ ['ches', 'ch' ],
114
+ ['shes', 'sh' ],
115
+ ['men', 'man'],
116
+ ['ies', 'y' ]
117
+ ],
118
+ :verb => [
119
+ ['s', '' ],
120
+ ['ies', 'y'],
121
+ ['es', 'e'],
122
+ ['es', '' ],
123
+ ['ed', 'e'],
124
+ ['ed', '' ],
125
+ ['ing', 'e'],
126
+ ['ing', '' ]
127
+ ],
128
+ :adj => [
129
+ ['er', '' ],
130
+ ['est', '' ],
131
+ ['er', 'e'],
132
+ ['est', 'e']
133
+ ],
134
+ :adv => [
135
+ ],
136
+ :abbr => [
137
+ ],
138
+ :unknown => [
139
+ ]
140
+ }
141
+
142
+ def initialize(dict = nil)
143
+ @wordlists = {}
144
+ @exceptions = {}
145
+
146
+ MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
147
+ @wordlists[x] = {}
148
+ @exceptions[x] = {}
149
+ end
150
+
151
+ WN_FILES.each_pair do |pos, pair|
152
+ load_wordnet_files(pos, pair[0], pair[1])
153
+ end
154
+
155
+ if dict
156
+ [dict].flatten.each do |d|
157
+ load_provided_dict(d)
158
+ end
159
+ end
160
+ end
161
+
162
+ def lemma(form, pos = nil)
163
+ unless pos
164
+ [:verb, :noun, :adj, :adv, :abbr].each do |p|
165
+ result = lemma(form, p)
166
+ return result unless result == form
167
+ end
168
+
169
+ return form
170
+ end
171
+
172
+ each_lemma(form, pos) do |x|
173
+ return x
174
+ end
175
+
176
+ form
177
+ end
178
+
179
+ # Print object only on init
180
+ def inspect
181
+ "#{self}"
182
+ end
183
+
184
+ private
185
+
186
+ def open_file(*args)
187
+ if args[0].is_a? IO or args[0].is_a? StringIO
188
+ yield args[0]
189
+ else
190
+ File.open(*args) do |io|
191
+ yield io
192
+ end
193
+ end
194
+ end
195
+
196
+ def load_wordnet_files(pos, list, exc)
197
+ open_file(list) do |io|
198
+ io.each_line do |line|
199
+ w = line.split(/\s+/)[0]
200
+ @wordlists[pos][w] = w
201
+ end
202
+ end
203
+
204
+ open_file(exc) do |io|
205
+ io.each_line do |line|
206
+ w, s = line.split(/\s+/)
207
+ @exceptions[pos][w] ||= []
208
+ @exceptions[pos][w] << s
209
+ end
210
+ end
211
+ end
212
+
213
+ def each_substitutions(form, pos)
214
+ if lemma = @wordlists[pos][form]
215
+ yield lemma
216
+ end
217
+
218
+ MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
219
+ old, new = *entry
220
+ if form.endwith(old)
221
+ each_substitutions(form[0, form.length - old.length] + new, pos) do |x|
222
+ yield x
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ def each_lemma(form, pos)
229
+ if lemma = @exceptions[pos][form]
230
+ lemma.each { |x| yield x }
231
+ end
232
+
233
+ if pos == :noun && form.endwith('ful')
234
+ each_lemma(form[0, form.length-3], pos) do |x|
235
+ yield x + 'ful'
236
+ end
237
+ else
238
+
239
+ each_substitutions(form, pos) do|x|
240
+ yield x
241
+ end
242
+ end
243
+ end
244
+
245
+ def str_to_pos(str)
246
+ case str
247
+ when "n", "noun"
248
+ return :noun
249
+ when "v", "verb"
250
+ return :noun
251
+ when "a", "j", "adjective", "adj"
252
+ return :adj
253
+ when "r", "adverb", "adv"
254
+ return :adv
255
+ when "b", "abbrev", "abbr", "abr"
256
+ return :abbr
257
+ else
258
+ return :unknown
259
+ end
260
+ end
261
+
262
+ def load_provided_dict(dict)
263
+ num_lex_added = 0
264
+ open_file(dict) do |io|
265
+ io.each_line do |line|
266
+ # pos must be either n|v|r|a or noun|verb|adverb|adjective
267
+ p, w, s = line.split(/\s+/, 3)
268
+ pos = str_to_pos(p)
269
+ word = w
270
+ substitute = s.strip
271
+ if /\A\"(.*)\"\z/ =~ substitute
272
+ substitute = $1
273
+ end
274
+ if /\A\'(.*)\'\z/ =~ substitute
275
+ substitute = $1
276
+ end
277
+ next unless (pos && word && substitute)
278
+ if @wordlists[pos]
279
+ @wordlists[pos][word] = substitute
280
+ num_lex_added += 1
281
+ end
282
+ end
283
+ end
284
+ # puts "#{num_lex_added} items added from #{File.basename dict}"
285
+ end
286
+ end
287
+ end
288
+ ```
289
+
81
290
  `index.品詞`の辞書は、各行の見出しだけを読み込んでいるとasmさんから助言をうけました。

11

a

2019/04/25 11:57

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -78,4 +78,4 @@
78
78
  -> 辞書を読み込む
79
79
  ```
80
80
 
81
- `index.品詞`の辞書は、各行の見出しだけを読み込んでいるとasmさんから助言をうけました。変形、活用しない単語であり、そのまま値を返しているようです。
81
+ `index.品詞`の辞書は、各行の見出しだけを読み込んでいるとasmさんから助言をうけました。

10

a

2019/04/25 11:48

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -25,13 +25,6 @@
25
25
  accuracy n 2 5 ! @ ~ = ; 2 2 04802907 04803209
26
26
  ```
27
27
 
28
- 次の点に気が付きました。
29
-  ・単語の後にpos(品詞)の省略形が続き、その後に、2つ数字があること。
30
-  ・@の後には、 ~ + ; %pなどが続くこと。
31
-  ・@の後に続く数字は2つであること。
32
-  ・@の後に続く数字と、そのあとに続く数字(8桁)の数が一致していること。
33
-  ・pos直後の数字2つと、@に続く数字2つの最初の数字は同じであること。
34
-
35
28
  この辞書は、PythonのNLTKライブラリのWordnetの辞書から借用されているようなので、
36
29
  Wordnetの他の相関データがそのまま入っていて、
37
30
  ここの辞書データの全てを利用している訳ではないのでは?と推測しています。

9

good

2019/04/25 04:17

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -85,4 +85,4 @@
85
85
  -> 辞書を読み込む
86
86
  ```
87
87
 
88
- よろくお願たします。
88
+ `index.品詞`の辞書は、各行の見出だけを読み込んでるとasmさんから助言をうけまし。変形、活用ない単語であり、そのま値を返しているようです。

8

fix

2019/04/25 04:16

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -8,6 +8,16 @@
8
8
  読み込まれる辞書データは次のような形式になっています。
9
9
 
10
10
  ```
11
+ # noun.exc
12
+ aardwolves aardwolf
13
+ abaci abacus
14
+ aboideaux aboideau
15
+ aboiteaux aboiteau
16
+ abscissae abscissa
17
+ ```
18
+
19
+ ```
20
+ # index.noun
11
21
  acculturation n 3 3 @ ~ + 3 1 01128984 05984936 05757049
12
22
  accumulation n 4 4 @ ~ + ; 4 3 13424865 07951464 00372013 13366693
13
23
  accumulator n 3 4 @ ~ %p ; 3 0 09936362 04328329 02673078

7

分析

2019/04/25 03:41

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -26,6 +26,53 @@
26
26
  Wordnetの他の相関データがそのまま入っていて、
27
27
  ここの辞書データの全てを利用している訳ではないのでは?と推測しています。
28
28
 
29
+ [lemmatizer/lib/lemmatizer/lemmatizer.rb](https://github.com/yohasebe/lemmatizer/blob/master/lib/lemmatizer/lemmatizer.rb)
29
- 辞書ののデータを参照して原形を特定しているのかをご助言いただけないでしょうか?
30
+ が、どのように辞書データを参照して原形に変換しているのかをご助言いただけないでしょうか?
30
31
 
32
+ ```
33
+ # 変数
34
+ DATA_DIR
35
+ -> 辞書があるディレクトリの指定。
36
+
37
+ WN_FILES
38
+ -> 辞書データの指定。
39
+
40
+ MORPHOLOGICAL_SUBSTITUTIONS
41
+ -> 形態素に基づく変換規則。
42
+
43
+ # メソッド
44
+ lemma(form, pos = nil)
45
+ -> 単語(form)と品詞(pos)を受け取り、原形を返す。
46
+
47
+ initialize(dict = nil)
48
+ -> インスタンスが生成されたときに実行される。
49
+ -> wordlistsとexceptionsの空のハッシュを宣言後、単語を追加していく。
50
+
51
+ each_lemma(form, pos)
52
+ -> メインのメソッド?
53
+ -> 単語(form)と品詞(pos)を受け取り、
54
+    1. 例外の単語ペアに合致すれば返す
55
+    2. `ful`で終わっている場合の対応
56
+    3. 原則どおりの場合の対応
57
+      each_substitutions(form, pos)が呼び出される。
58
+
59
+ each_substitutions(form, pos)
60
+ -> 形態素を元に原則どおり変換する場合の変換。
61
+
62
+ inspect
63
+ -> インスタンス生成時に、オブジェクトを印字する。
64
+
65
+ open_file(*args)
66
+ -> ファイルを開く。
67
+
68
+ load_wordnet_files(pos, list, exc)
69
+ -> wordnet_filesを読み込む。
70
+
71
+ str_to_pos(str)
72
+ -> 文字列をシンボルに変換して返す。
73
+
74
+ load_provided_dict(dict)
75
+ -> 辞書を読み込む
76
+ ```
77
+
31
78
  よろしくお願いいたします。

6

ふぃx

2019/04/25 03:39

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -24,7 +24,7 @@
24
24
 
25
25
  この辞書は、PythonのNLTKライブラリのWordnetの辞書から借用されているようなので、
26
26
  Wordnetの他の相関データがそのまま入っていて、
27
- @以降数字は原形特定すために不要なのかも?と推測しています。
27
+ ここ辞書データの全て利用してい訳ではなでは?と推測しています。
28
28
 
29
29
  辞書のどこのデータを参照して原形を特定しているのかをご助言いただけないでしょうか?
30
30
 

5

fix

2019/04/24 13:51

投稿

t-cool
t-cool

スコア71

title CHANGED
@@ -1,1 +1,1 @@
1
- Rubyのソースコード解読
1
+ Ruby製ライブラリlemmatizerのソースコード解読
body CHANGED
File without changes

4

tweak

2019/04/24 13:48

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -20,6 +20,7 @@
20
20
   ・@の後には、 ~ + ; %pなどが続くこと。
21
21
   ・@の後に続く数字は2つであること。
22
22
   ・@の後に続く数字と、そのあとに続く数字(8桁)の数が一致していること。
23
+  ・pos直後の数字2つと、@に続く数字2つの最初の数字は同じであること。
23
24
 
24
25
  この辞書は、PythonのNLTKライブラリのWordnetの辞書から借用されているようなので、
25
26
  Wordnetの他の相関データがそのまま入っていて、

3

改善

2019/04/24 13:47

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -15,12 +15,16 @@
15
15
  accuracy n 2 5 ! @ ~ = ; 2 2 04802907 04803209
16
16
  ```
17
17
 
18
- ここでの`@`や`+`、またそのあとに続く数字の羅列が何を意味しているかご助言いただけないでしょうか?
19
-
20
18
  次の点に気が付きました。
21
19
   ・単語の後にpos(品詞)の省略形が続き、その後に、2つ数字があること。
22
20
   ・@の後には、 ~ + ; %pなどが続くこと。
23
21
   ・@の後に続く数字は2つであること。
24
22
   ・@の後に続く数字と、そのあとに続く数字(8桁)の数が一致していること。
25
23
 
24
+ この辞書は、PythonのNLTKライブラリのWordnetの辞書から借用されているようなので、
25
+ Wordnetの他の相関データがそのまま入っていて、
26
+ @以降の数字は原形を特定するためには不要なのかも?と推測しています。
27
+
28
+ 辞書のどこのデータを参照して原形を特定しているのかをご助言いただけないでしょうか?
29
+
26
- ご助言をよろしくお願いいたします。
30
+ よろしくお願いいたします。

2

tweak

2019/04/24 13:42

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -19,6 +19,8 @@
19
19
 
20
20
  次の点に気が付きました。
21
21
   ・単語の後にpos(品詞)の省略形が続き、その後に、2つ数字があること。
22
+  ・@の後には、 ~ + ; %pなどが続くこと。
23
+  ・@の後に続く数字は2つであること。
22
-  ・@直後の数字と、そのあとに続く数字の数が一致していること。
24
+  ・@の後に続く数字と、そのあとに続く数字(8桁)の数が一致していること。
23
25
 
24
26
  ご助言をよろしくお願いいたします。

1

tweak

2019/04/24 13:37

投稿

t-cool
t-cool

スコア71

title CHANGED
File without changes
body CHANGED
@@ -17,4 +17,8 @@
17
17
 
18
18
  ここでの`@`や`+`、またそのあとに続く数字の羅列が何を意味しているかご助言いただけないでしょうか?
19
19
 
20
+ 次の点に気が付きました。
21
+  ・単語の後にpos(品詞)の省略形が続き、その後に、2つ数字があること。
22
+  ・@直後の数字と、そのあとに続く数字の数が一致していること。
23
+
20
- よろしくお願いいたします。
24
+ ご助言をよろしくお願いいたします。