質問編集履歴

27

2019/05/01 00:58

投稿

t-cool
t-cool

スコア71

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

26

2019/05/01 00:58

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -674,7 +674,7 @@
674
674
 
675
675
   {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
676
676
 
677
-   ・見出し語をwordlistsに登録する理由は、品詞を特定せずにlemmaを呼び出した際、どの品詞に属しているかを検索するため。
677
+   ・wordlistsに見出し語を登録する目的は、品詞を特定せずにlemmaを呼び出した際、どの品詞に属しているかを特定するため。
678
678
 
679
679
 
680
680
 
@@ -686,11 +686,11 @@
686
686
 
687
687
     ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
688
688
 
689
-   <品詞を特定しない場合場合>
689
+   <品詞を特定しない場合>
690
-
690
+
691
-    ・[:verb, :noun, :adj, :adv, :abbr]の順に、見出し語を元に、どの品詞なのか検索する。
691
+    ・動詞->名詞->形容詞->副詞の順で[:verb, :noun, :adj, :adv, :abbr]、見出し語から品詞を特定する。
692
-
692
+
693
-    ・@exceptionsに単語があれば、そのデータを元に原形を返す
693
+    ・@exceptionsに単語があれば、そこから原形を返す
694
694
 
695
695
     ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
696
696
 

25

2019/05/01 00:57

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -680,9 +680,19 @@
680
680
 
681
681
  2. lemmaメソッドの呼び出し
682
682
 
683
+   <品詞を特定する場合>
684
+
683
-   ・@exceptionsに単語があれば、そのデータを元に原形を返す
685
+    ・@exceptionsに単語があれば、そのデータを元に原形を返す
684
-
686
+
685
-   ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
687
+    ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
688
+
689
+   <品詞を特定しない場合場合>
690
+
691
+    ・[:verb, :noun, :adj, :adv, :abbr]の順に、見出し語を元に、どの品詞なのかを検索する。
692
+
693
+    ・@exceptionsに単語があれば、そのデータを元に原形を返す
694
+
695
+    ・なければ、morphological substitution (形態論の置き換え)のルールにしたがって置き換える。
686
696
 
687
697
 
688
698
 

24

2019/04/30 01:37

投稿

t-cool
t-cool

スコア71

test CHANGED
@@ -1 +1 @@
1
- Ruby製ライブラリlemmatizerのソースコード解読
1
+ Ruby製ライブラリlemmatizerのソースコード解読
test CHANGED
@@ -670,10 +670,14 @@
670
670
 
671
671
    ・@wordlistsと@exceptions、2つのハッシュを辞書として作成。
672
672
 
673
-   ・次のような構造。これらにデータを登録する。
673
+   ・それぞれ、次のような構造。これらにデータを登録する。
674
674
 
675
675
   {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
676
676
 
677
+   ・見出し語をwordlistsに登録する理由は、品詞を特定せずにlemmaを呼び出した際、どの品詞に属しているかを検索するため。
678
+
679
+
680
+
677
681
  2. lemmaメソッドの呼び出し
678
682
 
679
683
    ・@exceptionsに単語があれば、そのデータを元に原形を返す

23

a

2019/04/30 01:35

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
File without changes

22

2019/04/30 01:33

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -138,7 +138,7 @@
138
138
 
139
139
 
140
140
 
141
- #########################辞書データ作成#####################################
141
+ #########################辞書データ作成#####################################
142
142
 
143
143
  # 辞書データがディレクトリのPATH
144
144
 
@@ -270,16 +270,14 @@
270
270
 
271
271
 
272
272
 
273
- # @wordlistsと@exceptionsに辞書データを登録するためのメソッド
273
+ # @wordlistsと@exceptionsに辞書データを登録する
274
274
 
275
275
  def load_wordnet_files(pos, list, exc)
276
276
 
277
- # 実行前、@wordlistsと@exceptionsは次のような構造
277
+ # 実行前、@wordlistsと@exceptionsは次のような構造。これらにデータを登録する。
278
278
 
279
279
  # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
280
280
 
281
- # ここにデータを登録していく。
282
-
283
281
 
284
282
 
285
283
  # 見出し語の登録
@@ -304,6 +302,8 @@
304
302
 
305
303
  end
306
304
 
305
+
306
+
307
307
     # 例外語の登録
308
308
 
309
309
     # 例外語の辞書の各行は、"活用形 原形"(went go)の形式
@@ -614,8 +614,6 @@
614
614
 
615
615
  # クラスから作成されるオブジェクト毎に固有のもの。
616
616
 
617
- # インスタンスごとに独立してもつ変数のため、インスタンス変数という。
618
-
619
617
 
620
618
 
621
619
  MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
@@ -626,6 +624,8 @@
626
624
 
627
625
  end
628
626
 
627
+
628
+
629
629
  # 実行後、@wordlistsと@exceptionsは次のデータになる
630
630
 
631
631
  # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
@@ -662,4 +662,28 @@
662
662
 
663
663
 
664
664
 
665
+ lemmaの動作についてまとめ:
666
+
667
+
668
+
669
+ 1. 辞書の作成
670
+
671
+   ・@wordlistsと@exceptions、2つのハッシュを辞書として作成。
672
+
673
+   ・次のような構造。これらにデータを登録する。
674
+
675
+  {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
676
+
677
+ 2. lemmaメソッドの呼び出し
678
+
679
+   ・@exceptionsに単語があれば、そのデータを元に原形を返す
680
+
665
- ぞれのメソッドを追っていますがようファイル群から辞書を構築て、どのようにlemmaメソッド動作しのか、まだ解読できていません
681
+   ・なけmorphological substitution (形態論置き換え)のルールにし置き換える。
682
+
683
+
684
+
685
+ コードの解読を難しくしていた要因:
686
+
687
+   ・辞書データを作成するためのメソッドが多かったこと
688
+
689
+    (NLTKの辞書を再利用していたため)

21

2019/04/30 01:31

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -138,6 +138,8 @@
138
138
 
139
139
 
140
140
 
141
+ #########################辞書データを作成#####################################
142
+
141
143
  # 辞書データがディレクトリのPATH
142
144
 
143
145
  # 大文字で始まる場合は「定数」。各メソッドから参照可能。
@@ -268,14 +270,340 @@
268
270
 
269
271
 
270
272
 
273
+ # @wordlistsと@exceptionsに辞書データを登録するためのメソッド
274
+
275
+ def load_wordnet_files(pos, list, exc)
276
+
277
+ # 実行前、@wordlistsと@exceptionsは次のような構造
278
+
279
+ # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
280
+
281
+ # ここにデータを登録していく。
282
+
283
+
284
+
285
+ # 見出し語の登録
286
+
287
+ # "acculturation"での例
288
+
289
+ # w = "acculturation n 3 3 @~省略~".split(/\s+/)[0]
290
+
291
+ # w は "acculturation"
292
+
293
+ # wordlists[:noun]["acculturation"] = "acculturation"
294
+
295
+ open_file(list) do |io|
296
+
297
+ io.each_line do |line|
298
+
299
+ w = line.split(/\s+/)[0]
300
+
301
+ @wordlists[pos][w] = w
302
+
303
+ end
304
+
305
+ end
306
+
307
+    # 例外語の登録
308
+
309
+    # 例外語の辞書の各行は、"活用形 原形"(went go)の形式
310
+
311
+    # 活用形をwに、原形をsとして、ハッシュに追加していく
312
+
313
+ # @exceptions[pos][w]が空ならば[]を代入する
314
+
315
+ # @exceptions[pos][w]に、原形をpush << する。
316
+
317
+ open_file(exc) do |io|
318
+
319
+ io.each_line do |line|
320
+
321
+ w, s = line.split(/\s+/)
322
+
323
+ @exceptions[pos][w] ||= []
324
+
325
+ @exceptions[pos][w] << s
326
+
327
+ end
328
+
329
+ end
330
+
331
+ end
332
+
333
+
334
+
335
+   # インスタンスの初期化の際、次のように呼び出される
336
+
337
+   # WN_FILES.each_pair do |pos, pair|
338
+
339
+   # load_wordnet_files(pos, pair[0], pair[1])
340
+
341
+   # end
342
+
343
+   #
344
+
345
+   # WN_FILESは、{品詞 => [index.品詞, 品詞.例外]}を持つハッシュ
346
+
347
+   # WN_FILES = {
348
+
349
+   # :noun => [
350
+
351
+   # DATA_DIR + '/dict/index.noun',
352
+
353
+   # DATA_DIR + '/dict/noun.exc'
354
+
355
+   # ],
356
+
357
+   #
358
+
359
+   # よって、pair[0]は見出し語、pair[1]は例外語を示す。
360
+
361
+   # load_wordnet_files(pos, pair[0], pair[1])
362
+
363
+
364
+
365
+ def load_provided_dict(dict)
366
+
367
+ num_lex_added = 0
368
+
369
+ open_file(dict) do |io|
370
+
371
+ io.each_line do |line|
372
+
373
+ # pos must be either n|v|r|a or noun|verb|adverb|adjective
374
+
375
+ p, w, s = line.split(/\s+/, 3)
376
+
377
+ pos = str_to_pos(p)
378
+
379
+ word = w
380
+
381
+ substitute = s.strip
382
+
383
+ if /\A\"(.*)\"\z/ =~ substitute
384
+
385
+ substitute = $1
386
+
387
+ end
388
+
389
+ if /\A\'(.*)\'\z/ =~ substitute
390
+
391
+ substitute = $1
392
+
393
+ end
394
+
395
+ next unless (pos && word && substitute)
396
+
397
+ if @wordlists[pos]
398
+
399
+ @wordlists[pos][word] = substitute
400
+
401
+ num_lex_added += 1
402
+
403
+ end
404
+
405
+ end
406
+
407
+ end
408
+
409
+ # puts "#{num_lex_added} items added from #{File.basename dict}"
410
+
411
+ end
412
+
413
+
414
+
415
+ #########################辞書データを検索#####################################
416
+
417
+ def lemma(form, pos = nil)
418
+
419
+ unless pos
420
+
421
+ [:verb, :noun, :adj, :adv, :abbr].each do |p|
422
+
423
+ result = lemma(form, p)
424
+
425
+ return result unless result == form
426
+
427
+ end
428
+
429
+
430
+
431
+ return form
432
+
433
+ end
434
+
435
+
436
+
437
+ each_lemma(form, pos) do |x|
438
+
439
+ return x
440
+
441
+ end
442
+
443
+
444
+
445
+ form
446
+
447
+ end
448
+
449
+
450
+
451
+ def each_lemma(form, pos)
452
+
453
+ if lemma = @exceptions[pos][form]
454
+
455
+ lemma.each { |x| yield x }
456
+
457
+ end
458
+
459
+
460
+
461
+ if pos == :noun && form.endwith('ful')
462
+
463
+ each_lemma(form[0, form.length-3], pos) do |x|
464
+
465
+ yield x + 'ful'
466
+
467
+ end
468
+
469
+ else
470
+
471
+
472
+
473
+ each_substitutions(form, pos) do|x|
474
+
475
+ yield x
476
+
477
+ end
478
+
479
+ end
480
+
481
+ end
482
+
483
+
484
+
485
+ # Print object only on init
486
+
487
+ def inspect
488
+
489
+ "#{self}"
490
+
491
+ end
492
+
493
+
494
+
495
+ private
496
+
497
+
498
+
499
+ # ファイルから見出し語を取り出す前処理?
500
+
501
+ def open_file(*args) # *argsは可変長引数
502
+
503
+ # args[0]がIOクラスかStringIOクラスなら、args[0]を返す
504
+
505
+ if args[0].is_a? IO or args[0].is_a? StringIO
506
+
507
+ yield args[0]
508
+
509
+ else
510
+
511
+ File.open(*args) do |io|
512
+
513
+ yield io
514
+
515
+ end
516
+
517
+ end
518
+
519
+ end
520
+
521
+
522
+
523
+
524
+
525
+ def each_substitutions(form, pos)
526
+
527
+ if lemma = @wordlists[pos][form]
528
+
529
+ yield lemma
530
+
531
+ end
532
+
533
+
534
+
535
+ MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
536
+
537
+
538
+
539
+ # entryが展開されて、oldとnewに代入される
540
+
541
+ old, new = *entry
542
+
543
+
544
+
545
+ # formがoldで終わっている場合
546
+
547
+ if form.endwith(old)
548
+
549
+ each_substitutions(form[0, form.length - old.length] + new, pos) do |x|
550
+
551
+ yield x
552
+
553
+ end
554
+
555
+ end
556
+
557
+ end
558
+
559
+ end
560
+
561
+
562
+
563
+ def str_to_pos(str)
564
+
565
+ case str
566
+
567
+ when "n", "noun"
568
+
569
+ return :noun
570
+
571
+ when "v", "verb"
572
+
573
+ return :noun
574
+
575
+ when "a", "j", "adjective", "adj"
576
+
577
+ return :adj
578
+
579
+ when "r", "adverb", "adv"
580
+
581
+ return :adv
582
+
583
+ when "b", "abbrev", "abbr", "abr"
584
+
585
+ return :abbr
586
+
587
+ else
588
+
589
+ return :unknown
590
+
591
+ end
592
+
593
+ end
594
+
595
+ end
596
+
597
+
598
+
599
+ ##################辞書を利用するための初期化##############################
600
+
271
601
  # インスタンスの生成時に実行される
272
602
 
273
603
  # オプショナル変数。dictに値を渡さない場合はnilになる。
274
604
 
275
605
  def initialize(dict = nil)
276
606
 
277
-
278
-
279
607
  @wordlists = {}
280
608
 
281
609
  @exceptions = {}
@@ -324,332 +652,6 @@
324
652
 
325
653
  end
326
654
 
327
-
328
-
329
- def lemma(form, pos = nil)
330
-
331
- unless pos
332
-
333
- [:verb, :noun, :adj, :adv, :abbr].each do |p|
334
-
335
- result = lemma(form, p)
336
-
337
- return result unless result == form
338
-
339
- end
340
-
341
-
342
-
343
- return form
344
-
345
- end
346
-
347
-
348
-
349
- each_lemma(form, pos) do |x|
350
-
351
- return x
352
-
353
- end
354
-
355
-
356
-
357
- form
358
-
359
- end
360
-
361
-
362
-
363
- # Print object only on init
364
-
365
- def inspect
366
-
367
- "#{self}"
368
-
369
- end
370
-
371
-
372
-
373
- private
374
-
375
-
376
-
377
- # ファイルから見出し語を取り出す前処理?
378
-
379
- def open_file(*args) # *argsは可変長引数
380
-
381
- # args[0]がIOクラスかStringIOクラスなら、args[0]を返す
382
-
383
- if args[0].is_a? IO or args[0].is_a? StringIO
384
-
385
- yield args[0]
386
-
387
- else
388
-
389
- File.open(*args) do |io|
390
-
391
- yield io
392
-
393
- end
394
-
395
- end
396
-
397
- end
398
-
399
-
400
-
401
- def load_wordnet_files(pos, list, exc)
402
-
403
-
404
-
405
- # @wordlistsと@exceptionsに辞書データを登録するためのメソッド
406
-
407
- # 実行前、@wordlistsと@exceptionsは次のような構造
408
-
409
- # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
410
-
411
- # ここにデータを登録していく。
412
-
413
-
414
-
415
- # 見出し語の登録
416
-
417
- # "acculturation"での例
418
-
419
- # w = "acculturation n 3 3 @~省略~".split(/\s+/)[0]
420
-
421
- # w は "acculturation"
422
-
423
- # wordlists[:noun]["acculturation"] = "acculturation"
424
-
425
- open_file(list) do |io|
426
-
427
- io.each_line do |line|
428
-
429
- w = line.split(/\s+/)[0]
430
-
431
- @wordlists[pos][w] = w
432
-
433
- end
434
-
435
- end
436
-
437
-
438
-
439
-    # 例外語の登録
440
-
441
-    # 例外語の辞書の各行は、"活用形 原形"(went go)の形式
442
-
443
-    # 活用形をwに、原形をsとして、ハッシュに追加していく
444
-
445
- # @exceptions[pos][w]が空ならば[]を代入する
446
-
447
- # @exceptions[pos][w]に、原形をpush << する。
448
-
449
- open_file(exc) do |io|
450
-
451
- io.each_line do |line|
452
-
453
- w, s = line.split(/\s+/)
454
-
455
- @exceptions[pos][w] ||= []
456
-
457
- @exceptions[pos][w] << s
458
-
459
- end
460
-
461
- end
462
-
463
- end
464
-
465
-
466
-
467
-   # インスタンスの初期化の際、次のように呼び出される
468
-
469
-   # WN_FILES.each_pair do |pos, pair|
470
-
471
-   # load_wordnet_files(pos, pair[0], pair[1])
472
-
473
-   # end
474
-
475
-   #
476
-
477
-   # WN_FILESは、{品詞 => [index.品詞, 品詞.例外]}を持つハッシュ
478
-
479
-   # WN_FILES = {
480
-
481
-   # :noun => [
482
-
483
-   # DATA_DIR + '/dict/index.noun',
484
-
485
-   # DATA_DIR + '/dict/noun.exc'
486
-
487
-   # ],
488
-
489
-   #
490
-
491
-   # よって、pair[0]は見出し語、pair[1]は例外語を示す。
492
-
493
-   # load_wordnet_files(pos, pair[0], pair[1])
494
-
495
-
496
-
497
- def each_substitutions(form, pos)
498
-
499
- if lemma = @wordlists[pos][form]
500
-
501
- yield lemma
502
-
503
- end
504
-
505
-
506
-
507
- MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
508
-
509
-
510
-
511
- # entryが展開されて、oldとnewに代入される
512
-
513
- old, new = *entry
514
-
515
-
516
-
517
- # formがoldで終わっている場合
518
-
519
- if form.endwith(old)
520
-
521
- each_substitutions(form[0, form.length - old.length] + new, pos) do |x|
522
-
523
- yield x
524
-
525
- end
526
-
527
- end
528
-
529
- end
530
-
531
- end
532
-
533
-
534
-
535
- def each_lemma(form, pos)
536
-
537
- if lemma = @exceptions[pos][form]
538
-
539
- lemma.each { |x| yield x }
540
-
541
- end
542
-
543
-
544
-
545
- if pos == :noun && form.endwith('ful')
546
-
547
- each_lemma(form[0, form.length-3], pos) do |x|
548
-
549
- yield x + 'ful'
550
-
551
- end
552
-
553
- else
554
-
555
-
556
-
557
- each_substitutions(form, pos) do|x|
558
-
559
- yield x
560
-
561
- end
562
-
563
- end
564
-
565
- end
566
-
567
-
568
-
569
- def str_to_pos(str)
570
-
571
- case str
572
-
573
- when "n", "noun"
574
-
575
- return :noun
576
-
577
- when "v", "verb"
578
-
579
- return :noun
580
-
581
- when "a", "j", "adjective", "adj"
582
-
583
- return :adj
584
-
585
- when "r", "adverb", "adv"
586
-
587
- return :adv
588
-
589
- when "b", "abbrev", "abbr", "abr"
590
-
591
- return :abbr
592
-
593
- else
594
-
595
- return :unknown
596
-
597
- end
598
-
599
- end
600
-
601
-
602
-
603
- def load_provided_dict(dict)
604
-
605
- num_lex_added = 0
606
-
607
- open_file(dict) do |io|
608
-
609
- io.each_line do |line|
610
-
611
- # pos must be either n|v|r|a or noun|verb|adverb|adjective
612
-
613
- p, w, s = line.split(/\s+/, 3)
614
-
615
- pos = str_to_pos(p)
616
-
617
- word = w
618
-
619
- substitute = s.strip
620
-
621
- if /\A\"(.*)\"\z/ =~ substitute
622
-
623
- substitute = $1
624
-
625
- end
626
-
627
- if /\A\'(.*)\'\z/ =~ substitute
628
-
629
- substitute = $1
630
-
631
- end
632
-
633
- next unless (pos && word && substitute)
634
-
635
- if @wordlists[pos]
636
-
637
- @wordlists[pos][word] = substitute
638
-
639
- num_lex_added += 1
640
-
641
- end
642
-
643
- end
644
-
645
- end
646
-
647
- # puts "#{num_lex_added} items added from #{File.basename dict}"
648
-
649
- end
650
-
651
- end
652
-
653
655
  end
654
656
 
655
657
  ```

20

2019/04/30 00:27

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -16,6 +16,10 @@
16
16
 
17
17
 
18
18
 
19
+ 不規則活用の辞書では、”不規則活用形 原形”で各行が並んでいます。
20
+
21
+
22
+
19
23
  ```
20
24
 
21
25
  # noun.exc
@@ -34,6 +38,10 @@
34
38
 
35
39
 
36
40
 
41
+ index.品詞のファイルでは、規則変化、不規則変化に関わらず、単語のリストが並んでいます。この辞書で活用されるのは、最初の1単語のみです。
42
+
43
+
44
+
37
45
  ```
38
46
 
39
47
  # index.noun

19

だいぶOK

2019/04/30 00:14

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -290,6 +290,10 @@
290
290
 
291
291
  end
292
292
 
293
+ # 実行後、@wordlistsと@exceptionsは次のデータになる
294
+
295
+ # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
296
+
293
297
 
294
298
 
295
299
  WN_FILES.each_pair do |pos, pair|
@@ -362,8 +366,12 @@
362
366
 
363
367
 
364
368
 
369
+ # ファイルから見出し語を取り出す前処理?
370
+
365
371
  def open_file(*args) # *argsは可変長引数
366
372
 
373
+ # args[0]がIOクラスかStringIOクラスなら、args[0]を返す
374
+
367
375
  if args[0].is_a? IO or args[0].is_a? StringIO
368
376
 
369
377
  yield args[0]
@@ -386,11 +394,25 @@
386
394
 
387
395
 
388
396
 
397
+ # @wordlistsと@exceptionsに辞書データを登録するためのメソッド
398
+
399
+ # 実行前、@wordlistsと@exceptionsは次のような構造
400
+
401
+ # {:noun=>{}, :verb=>{}, :adj=>{}, :adv=>{}, :abbr=>{}, :unknown=>{}}
402
+
403
+ # ここにデータを登録していく。
404
+
405
+
406
+
389
-     # 見出し語処理
407
+ # 見出し語の登録
390
-
408
+
391
-     # 辞書データの各行(line)を文字列に分解して、見出し語[0]をwに入れる
409
+ # "acculturation"での例
392
-
410
+
393
-    # @ハッシュのwordlistsに、{品詞 => 単語}の形式で見出し語を追加していく
411
+ # w = "acculturation n 3 3 @~省略~".split(/\s+/)[0]
412
+
413
+ # w は "acculturation"
414
+
415
+ # wordlists[:noun]["acculturation"] = "acculturation"
394
416
 
395
417
  open_file(list) do |io|
396
418
 
@@ -406,13 +428,13 @@
406
428
 
407
429
 
408
430
 
409
-    # 例外語処理
431
+    # 例外語の登録
410
-
432
+
411
-    # 例外語の辞書の各行は、"活用形 原形"(例、aardwolves aardwolf)の形式
433
+    # 例外語の辞書の各行は、"活用形 原形"(went go)の形式
412
434
 
413
435
     # 活用形をwに、原形をsとして、ハッシュに追加していく
414
436
 
415
- # @exceptions[pos][w]が未定義ならば[]を代入する
437
+ # @exceptions[pos][w]がならば[]を代入する
416
438
 
417
439
  # @exceptions[pos][w]に、原形をpush << する。
418
440
 
@@ -430,8 +452,6 @@
430
452
 
431
453
  end
432
454
 
433
-
434
-
435
455
  end
436
456
 
437
457
 

18

助言

2019/04/30 00:09

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -98,11 +98,11 @@
98
98
 
99
99
 
100
100
 
101
+ # asmさんからの助言
102
+
103
+ #  lem = Lemmatizer::Lemmatizer.new と書くのがめんどくさいから
104
+
101
- # ?? モジュールで self.new を定義すと、モジュールしてnewが呼べ
105
+ #  lem = Lemmatizer.new と書けようにして
102
-
103
- # ?? `lemmatizer/lib/lemmatizer.rb`で、モジュール読み込み時に、Lemmatizer.new(dict)を実行?
104
-
105
- # ?? このコードは何のため?
106
106
 
107
107
  module Lemmatizer
108
108
 
@@ -118,7 +118,7 @@
118
118
 
119
119
 
120
120
 
121
- 次は、'lemmatizer/lemmatizer.rb'のコード内を読みます。
121
+ 次は、'lemmatizer/lemmatizer.rb'のコードす。
122
122
 
123
123
 
124
124
 
@@ -182,7 +182,7 @@
182
182
 
183
183
 
184
184
 
185
- # morphological(形態論の)substitution(置き換え)
185
+ # morphological substitution (形態論の置き換え)
186
186
 
187
187
  # 規則的に置き換え可能な場合のルール
188
188
 

17

nnn

2019/04/29 23:13

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -60,18 +60,72 @@
60
60
 
61
61
 
62
62
 
63
- [lemmatizer/lib/lemmatizer/lemmatizer.rb](https://github.com/yohasebe/lemmatizer/blob/master/lib/lemmatizer/lemmatizer.rb)
64
-
65
- が、どのように辞書データを参照して、原形に変換しているかをご助言いただけないでか?
63
+ 以下、ソースコードを読み解きな、どのようにlemmaメソッドが動作しているかを解読と試みます。
64
+
65
+
66
+
66
-
67
+ まずは、ライブラリを管理する`lemmatizer.gemspec`です。以下の箇所では、Gemをrequireしたとき、実際にロードするファイルのパスが指定されています。libフォルダ以下にあるファイル群が読み込まれます。
67
-
68
-
68
+
69
+
70
+
69
- ```
71
+ ```
72
+
70
-
73
+ Gem::Specification.new do |gem|
74
+
71
-
75
+ (省略)
76
+
77
+ gem.require_paths = ['lib']
78
+
79
+ end
80
+
81
+ ```
82
+
83
+
84
+
85
+ `lib/lemmatizer.rb`では、次の順でモジュールが読み込まれます。
86
+
87
+
88
+
89
+ ```
90
+
91
+ require 'stringio'
92
+
93
+ require 'lemmatizer/version'
94
+
95
+ require 'lemmatizer/core_ext'
96
+
97
+ require 'lemmatizer/lemmatizer'
98
+
99
+
100
+
101
+ # ?? モジュールで self.new を定義すると、モジュールに対してnewが呼べる。
102
+
103
+ # ?? `lemmatizer/lib/lemmatizer.rb`で、モジュール読み込み時に、Lemmatizer.new(dict)を実行?
104
+
105
+ # ?? このコードは何のため?
72
106
 
73
107
  module Lemmatizer
74
108
 
109
+ def self.new(dict = nil)
110
+
111
+ Lemmatizer.new(dict)
112
+
113
+ end
114
+
115
+ end
116
+
117
+ ```
118
+
119
+
120
+
121
+ 次は、'lemmatizer/lemmatizer.rb'のコード内を読みます。
122
+
123
+
124
+
125
+ ```
126
+
127
+ module Lemmatizer
128
+
75
129
  class Lemmatizer
76
130
 
77
131
 
@@ -208,10 +262,10 @@
208
262
 
209
263
  # インスタンスの生成時に実行される
210
264
 
265
+ # オプショナル変数。dictに値を渡さない場合はnilになる。
266
+
211
267
  def initialize(dict = nil)
212
268
 
213
- # オプショナル変数。dictに値を渡さない場合はnilになる。
214
-
215
269
 
216
270
 
217
271
  @wordlists = {}

16

a

2019/04/28 12:35

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -575,3 +575,7 @@
575
575
 
576
576
 
577
577
  `index.品詞`の辞書は、各行の見出しだけを読み込んでいるとasmさんから助言をうけました。
578
+
579
+
580
+
581
+ それぞれのメソッドを追っていますが、どのようにファイル群から辞書を構築して、どのようにlemmaメソッドが動作しているのか、まだ解読できていません。

15

a

2019/04/27 09:25

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -424,7 +424,15 @@
424
424
 
425
425
  MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
426
426
 
427
+
428
+
429
+ # entryが展開されて、oldとnewに代入される
430
+
427
- old, new = *entry
431
+ old, new = *entry
432
+
433
+
434
+
435
+ # formがoldで終わっている場合
428
436
 
429
437
  if form.endwith(old)
430
438
 

14

a

2019/04/27 09:19

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -68,536 +68,502 @@
68
68
 
69
69
  ```
70
70
 
71
- WN_FILES
72
-
73
- ->
74
-
75
-
76
-
77
- MORPHOLOGICAL_SUBSTITUTIONS
78
-
79
- -> 形態素に基づく変換規則。
80
-
81
-
82
-
83
- # メソッド
84
-
85
- lemma(form, pos = nil)
86
-
87
- -> 単語(form)と品詞(pos)を受け取り、原形を返す。
88
-
89
-
90
-
91
- initialize(dict = nil)
92
-
93
- -> インスタンスが生成されたときに実行される。
94
-
95
- -> wordlistsとexceptionsの空のハッシュを宣言後、単語を追加していく。
96
-
97
-
98
-
99
- each_lemma(form, pos)
100
-
101
- -> メインのメソッド?
102
-
103
- -> 単語(form)と品詞(pos)を受け取り、
104
-
105
-    1. 例外の単語ペアに合致すれば返す
106
-
107
-    2. `ful`で終わっている場合の対応
108
-
109
-    3. 原則どおりの場合の対応
110
-
111
-      each_substitutions(form, pos)が呼び出される。
112
-
113
-
114
-
115
- each_substitutions(form, pos)
116
-
117
- -> 形態素を元に原則どおり変換する場合の変換。
118
-
119
-
120
-
121
- inspect
122
-
123
- -> インスタンス生成時に、オブジェクトを印字する。
124
-
125
-
126
-
127
- open_file(*args)
128
-
129
- -> ファイルを開く。
130
-
131
-
132
-
133
- load_wordnet_files(pos, list, exc)
134
-
135
- -> wordnet_filesを読み込む
136
-
137
-
138
-
139
- str_to_pos(str)
140
-
141
- -> 文字列をシンボルに変換して返す。
142
-
143
-
144
-
145
- load_provided_dict(dict)
146
-
147
- -> 辞書を読み込む
71
+
72
+
73
+ module Lemmatizer
74
+
75
+ class Lemmatizer
76
+
77
+
78
+
79
+ # 辞書データがディレクトリのPATH
80
+
81
+ # 大文字で始まる場合は「定数」。各メソッドから参照可能。
82
+
83
+ DATA_DIR = File.expand_path('..', File.dirname(__FILE__))
84
+
85
+
86
+
87
+ # 辞書データのPATH
88
+
89
+ # noun(名詞)、verb(動詞)、adj(形容詞)、adv(副詞)
90
+
91
+ # index.品詞は見出し語。品詞.excは不規則活用。excはexception(例外)
92
+
93
+ WN_FILES = {
94
+
95
+ :noun => [
96
+
97
+ DATA_DIR + '/dict/index.noun',
98
+
99
+ DATA_DIR + '/dict/noun.exc'
100
+
101
+ ],
102
+
103
+ :verb => [
104
+
105
+ DATA_DIR + '/dict/index.verb',
106
+
107
+ DATA_DIR + '/dict/verb.exc'
108
+
109
+ ],
110
+
111
+ :adj => [
112
+
113
+ DATA_DIR + '/dict/index.adj',
114
+
115
+ DATA_DIR + '/dict/adj.exc'
116
+
117
+ ],
118
+
119
+ :adv => [
120
+
121
+ DATA_DIR + '/dict/index.adv',
122
+
123
+ DATA_DIR + '/dict/adv.exc'
124
+
125
+ ]
126
+
127
+ }
128
+
129
+
130
+
131
+ # morphological(形態論の)substitution(置き換え)
132
+
133
+ # 規則的に置き換え可能な場合のルール
134
+
135
+ # 重複するものは、ing, es, ed, er, est
136
+
137
+ MORPHOLOGICAL_SUBSTITUTIONS = {
138
+
139
+ :noun => [
140
+
141
+ ['s', '' ],
142
+
143
+ ['ses', 's' ],
144
+
145
+ ['ves', 'f' ],
146
+
147
+ ['xes', 'x' ],
148
+
149
+ ['zes', 'z' ],
150
+
151
+ ['ches', 'ch' ],
152
+
153
+ ['shes', 'sh' ],
154
+
155
+ ['men', 'man'],
156
+
157
+ ['ies', 'y' ]
158
+
159
+ ],
160
+
161
+ :verb => [
162
+
163
+ ['s', '' ],
164
+
165
+ ['ies', 'y'],
166
+
167
+ ['es', 'e'],
168
+
169
+ ['es', '' ],
170
+
171
+ ['ed', 'e'],
172
+
173
+ ['ed', '' ],
174
+
175
+ ['ing', 'e'],
176
+
177
+ ['ing', '' ]
178
+
179
+ ],
180
+
181
+ :adj => [
182
+
183
+ ['er', '' ],
184
+
185
+ ['est', '' ],
186
+
187
+ ['er', 'e'],
188
+
189
+ ['est', 'e']
190
+
191
+ ],
192
+
193
+ :adv => [
194
+
195
+ ],
196
+
197
+ :abbr => [
198
+
199
+ ],
200
+
201
+ :unknown => [
202
+
203
+ ]
204
+
205
+ }
206
+
207
+
208
+
209
+ # インスタンスの生成時に実行される
210
+
211
+ def initialize(dict = nil)
212
+
213
+ # オプショナル変数。dictに値を渡さない場合はnilになる。
214
+
215
+
216
+
217
+ @wordlists = {}
218
+
219
+ @exceptions = {}
220
+
221
+     # インスタンス変数
222
+
223
+     # スコープ:クラス内で全メソッドで共通して使用することが出来る。
224
+
225
+ # クラスから作成されるオブジェクト毎に固有のもの。
226
+
227
+ # インスタンスごとに独立してもつ変数のため、インスタンス変数という。
228
+
229
+
230
+
231
+ MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
232
+
233
+ @wordlists[x] = {}
234
+
235
+ @exceptions[x] = {}
236
+
237
+ end
238
+
239
+
240
+
241
+ WN_FILES.each_pair do |pos, pair|
242
+
243
+ load_wordnet_files(pos, pair[0], pair[1])
244
+
245
+ end
246
+
247
+
248
+
249
+ if dict
250
+
251
+ [dict].flatten.each do |d|
252
+
253
+ load_provided_dict(d)
254
+
255
+ end
256
+
257
+ end
258
+
259
+ end
260
+
261
+
262
+
263
+ def lemma(form, pos = nil)
264
+
265
+ unless pos
266
+
267
+ [:verb, :noun, :adj, :adv, :abbr].each do |p|
268
+
269
+ result = lemma(form, p)
270
+
271
+ return result unless result == form
272
+
273
+ end
274
+
275
+
276
+
277
+ return form
278
+
279
+ end
280
+
281
+
282
+
283
+ each_lemma(form, pos) do |x|
284
+
285
+ return x
286
+
287
+ end
288
+
289
+
290
+
291
+ form
292
+
293
+ end
294
+
295
+
296
+
297
+ # Print object only on init
298
+
299
+ def inspect
300
+
301
+ "#{self}"
302
+
303
+ end
304
+
305
+
306
+
307
+ private
308
+
309
+
310
+
311
+ def open_file(*args) # *argsは可変長引数
312
+
313
+ if args[0].is_a? IO or args[0].is_a? StringIO
314
+
315
+ yield args[0]
316
+
317
+ else
318
+
319
+ File.open(*args) do |io|
320
+
321
+ yield io
322
+
323
+ end
324
+
325
+ end
326
+
327
+ end
328
+
329
+
330
+
331
+ def load_wordnet_files(pos, list, exc)
332
+
333
+
334
+
335
+     # 見出し語への処理
336
+
337
+     # 辞書データの各行(line)を文字列に分解して、見出し語[0]をwに入れる
338
+
339
+    # @ハッシュのwordlistsに、{品詞 => 単語}の形式で見出し語を追加していく
340
+
341
+ open_file(list) do |io|
342
+
343
+ io.each_line do |line|
344
+
345
+ w = line.split(/\s+/)[0]
346
+
347
+ @wordlists[pos][w] = w
348
+
349
+ end
350
+
351
+ end
352
+
353
+
354
+
355
+    # 例外語への処理
356
+
357
+    # 例外語の辞書の各行は、"活用形 原形"(例、aardwolves aardwolf)の形式
358
+
359
+    # 活用形をwに、原形をsとして、ハッシュに追加していく
360
+
361
+ # @exceptions[pos][w]が未定義ならば[]を代入する
362
+
363
+ # @exceptions[pos][w]に、原形をpush << する。
364
+
365
+ open_file(exc) do |io|
366
+
367
+ io.each_line do |line|
368
+
369
+ w, s = line.split(/\s+/)
370
+
371
+ @exceptions[pos][w] ||= []
372
+
373
+ @exceptions[pos][w] << s
374
+
375
+ end
376
+
377
+ end
378
+
379
+
380
+
381
+ end
382
+
383
+
384
+
385
+   # インスタンスの初期化の際、次のように呼び出される
386
+
387
+   # WN_FILES.each_pair do |pos, pair|
388
+
389
+   # load_wordnet_files(pos, pair[0], pair[1])
390
+
391
+   # end
392
+
393
+   #
394
+
395
+   # WN_FILESは、{品詞 => [index.品詞, 品詞.例外]}を持つハッシュ
396
+
397
+   # WN_FILES = {
398
+
399
+   # :noun => [
400
+
401
+   # DATA_DIR + '/dict/index.noun',
402
+
403
+   # DATA_DIR + '/dict/noun.exc'
404
+
405
+   # ],
406
+
407
+   #
408
+
409
+   # よって、pair[0]は見出し語、pair[1]は例外語を示す。
410
+
411
+   # load_wordnet_files(pos, pair[0], pair[1])
412
+
413
+
414
+
415
+ def each_substitutions(form, pos)
416
+
417
+ if lemma = @wordlists[pos][form]
418
+
419
+ yield lemma
420
+
421
+ end
422
+
423
+
424
+
425
+ MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
426
+
427
+ old, new = *entry
428
+
429
+ if form.endwith(old)
430
+
431
+ each_substitutions(form[0, form.length - old.length] + new, pos) do |x|
432
+
433
+ yield x
434
+
435
+ end
436
+
437
+ end
438
+
439
+ end
440
+
441
+ end
442
+
443
+
444
+
445
+ def each_lemma(form, pos)
446
+
447
+ if lemma = @exceptions[pos][form]
448
+
449
+ lemma.each { |x| yield x }
450
+
451
+ end
452
+
453
+
454
+
455
+ if pos == :noun && form.endwith('ful')
456
+
457
+ each_lemma(form[0, form.length-3], pos) do |x|
458
+
459
+ yield x + 'ful'
460
+
461
+ end
462
+
463
+ else
464
+
465
+
466
+
467
+ each_substitutions(form, pos) do|x|
468
+
469
+ yield x
470
+
471
+ end
472
+
473
+ end
474
+
475
+ end
476
+
477
+
478
+
479
+ def str_to_pos(str)
480
+
481
+ case str
482
+
483
+ when "n", "noun"
484
+
485
+ return :noun
486
+
487
+ when "v", "verb"
488
+
489
+ return :noun
490
+
491
+ when "a", "j", "adjective", "adj"
492
+
493
+ return :adj
494
+
495
+ when "r", "adverb", "adv"
496
+
497
+ return :adv
498
+
499
+ when "b", "abbrev", "abbr", "abr"
500
+
501
+ return :abbr
502
+
503
+ else
504
+
505
+ return :unknown
506
+
507
+ end
508
+
509
+ end
510
+
511
+
512
+
513
+ def load_provided_dict(dict)
514
+
515
+ num_lex_added = 0
516
+
517
+ open_file(dict) do |io|
518
+
519
+ io.each_line do |line|
520
+
521
+ # pos must be either n|v|r|a or noun|verb|adverb|adjective
522
+
523
+ p, w, s = line.split(/\s+/, 3)
524
+
525
+ pos = str_to_pos(p)
526
+
527
+ word = w
528
+
529
+ substitute = s.strip
530
+
531
+ if /\A\"(.*)\"\z/ =~ substitute
532
+
533
+ substitute = $1
534
+
535
+ end
536
+
537
+ if /\A\'(.*)\'\z/ =~ substitute
538
+
539
+ substitute = $1
540
+
541
+ end
542
+
543
+ next unless (pos && word && substitute)
544
+
545
+ if @wordlists[pos]
546
+
547
+ @wordlists[pos][word] = substitute
548
+
549
+ num_lex_added += 1
550
+
551
+ end
552
+
553
+ end
554
+
555
+ end
556
+
557
+ # puts "#{num_lex_added} items added from #{File.basename dict}"
558
+
559
+ end
560
+
561
+ end
562
+
563
+ end
148
564
 
149
565
  ```
150
566
 
151
567
 
152
568
 
153
- ```
154
-
155
-
156
-
157
- module Lemmatizer
158
-
159
- class Lemmatizer
160
-
161
-
162
-
163
- # 辞書データがディレクトリのPATH
164
-
165
- # 大文字で始まる場合は「定数」。各メソッドから参照可能。
166
-
167
- DATA_DIR = File.expand_path('..', File.dirname(__FILE__))
168
-
169
-
170
-
171
- # 辞書データのPATH
172
-
173
- # noun(名詞)、verb(動詞)、adj(形容詞)、adv(副詞)
174
-
175
- # index.品詞は見出し語。品詞.excは不規則活用。
176
-
177
- WN_FILES = {
178
-
179
- :noun => [
180
-
181
- DATA_DIR + '/dict/index.noun',
182
-
183
- DATA_DIR + '/dict/noun.exc'
184
-
185
- ],
186
-
187
- :verb => [
188
-
189
- DATA_DIR + '/dict/index.verb',
190
-
191
- DATA_DIR + '/dict/verb.exc'
192
-
193
- ],
194
-
195
- :adj => [
196
-
197
- DATA_DIR + '/dict/index.adj',
198
-
199
- DATA_DIR + '/dict/adj.exc'
200
-
201
- ],
202
-
203
- :adv => [
204
-
205
- DATA_DIR + '/dict/index.adv',
206
-
207
- DATA_DIR + '/dict/adv.exc'
208
-
209
- ]
210
-
211
- }
212
-
213
-
214
-
215
- # morphological(形態論の)substitution(置き換え)
216
-
217
- # 規則的に置き換え可能な場合のルール
218
-
219
- # 重複するものは、ing, es, ed, er, est。
220
-
221
- MORPHOLOGICAL_SUBSTITUTIONS = {
222
-
223
- :noun => [
224
-
225
- ['s', '' ],
226
-
227
- ['ses', 's' ],
228
-
229
- ['ves', 'f' ],
230
-
231
- ['xes', 'x' ],
232
-
233
- ['zes', 'z' ],
234
-
235
- ['ches', 'ch' ],
236
-
237
- ['shes', 'sh' ],
238
-
239
- ['men', 'man'],
240
-
241
- ['ies', 'y' ]
242
-
243
- ],
244
-
245
- :verb => [
246
-
247
- ['s', '' ],
248
-
249
- ['ies', 'y'],
250
-
251
- ['es', 'e'],
252
-
253
- ['es', '' ],
254
-
255
- ['ed', 'e'],
256
-
257
- ['ed', '' ],
258
-
259
- ['ing', 'e'],
260
-
261
- ['ing', '' ]
262
-
263
- ],
264
-
265
- :adj => [
266
-
267
- ['er', '' ],
268
-
269
- ['est', '' ],
270
-
271
- ['er', 'e'],
272
-
273
- ['est', 'e']
274
-
275
- ],
276
-
277
- :adv => [
278
-
279
- ],
280
-
281
- :abbr => [
282
-
283
- ],
284
-
285
- :unknown => [
286
-
287
- ]
288
-
289
- }
290
-
291
-
292
-
293
- # インスタンスの生成時に実行される
294
-
295
- def initialize(dict = nil)
296
-
297
- # オプショナル変数。dictに値を渡さない場合はnilになる。
298
-
299
-
300
-
301
- @wordlists = {}
302
-
303
- @exceptions = {}
304
-
305
-     # インスタンス変数
306
-
307
-     # スコープ:クラス内で全メソッドで共通して使用することが出来る。
308
-
309
- # クラスから作成されるオブジェクト毎に固有のもの。
310
-
311
- # インスタンスごとに独立してもつ変数のため、インスタンス変数という。
312
-
313
-
314
-
315
- MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
316
-
317
- @wordlists[x] = {}
318
-
319
- @exceptions[x] = {}
320
-
321
- end
322
-
323
-
324
-
325
- WN_FILES.each_pair do |pos, pair|
326
-
327
- load_wordnet_files(pos, pair[0], pair[1])
328
-
329
- end
330
-
331
-
332
-
333
- if dict
334
-
335
- [dict].flatten.each do |d|
336
-
337
- load_provided_dict(d)
338
-
339
- end
340
-
341
- end
342
-
343
- end
344
-
345
-
346
-
347
- def lemma(form, pos = nil)
348
-
349
- unless pos
350
-
351
- [:verb, :noun, :adj, :adv, :abbr].each do |p|
352
-
353
- result = lemma(form, p)
354
-
355
- return result unless result == form
356
-
357
- end
358
-
359
-
360
-
361
- return form
362
-
363
- end
364
-
365
-
366
-
367
- each_lemma(form, pos) do |x|
368
-
369
- return x
370
-
371
- end
372
-
373
-
374
-
375
- form
376
-
377
- end
378
-
379
-
380
-
381
- # Print object only on init
382
-
383
- def inspect
384
-
385
- "#{self}"
386
-
387
- end
388
-
389
-
390
-
391
- private
392
-
393
-
394
-
395
- def open_file(*args) # *argsは可変長引数
396
-
397
- if args[0].is_a? IO or args[0].is_a? StringIO
398
-
399
- yield args[0]
400
-
401
- else
402
-
403
- File.open(*args) do |io|
404
-
405
- yield io
406
-
407
- end
408
-
409
- end
410
-
411
- end
412
-
413
-
414
-
415
- def load_wordnet_files(pos, list, exc)
416
-
417
- open_file(list) do |io|
418
-
419
- io.each_line do |line|
420
-
421
- w = line.split(/\s+/)[0]
422
-
423
- @wordlists[pos][w] = w
424
-
425
- end
426
-
427
- end
428
-
429
-
430
-
431
- open_file(exc) do |io|
432
-
433
- io.each_line do |line|
434
-
435
- w, s = line.split(/\s+/)
436
-
437
- @exceptions[pos][w] ||= []
438
-
439
- @exceptions[pos][w] << s
440
-
441
- end
442
-
443
- end
444
-
445
- end
446
-
447
-
448
-
449
- def each_substitutions(form, pos)
450
-
451
- if lemma = @wordlists[pos][form]
452
-
453
- yield lemma
454
-
455
- end
456
-
457
-
458
-
459
- MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
460
-
461
- old, new = *entry
462
-
463
- if form.endwith(old)
464
-
465
- each_substitutions(form[0, form.length - old.length] + new, pos) do |x|
466
-
467
- yield x
468
-
469
- end
470
-
471
- end
472
-
473
- end
474
-
475
- end
476
-
477
-
478
-
479
- def each_lemma(form, pos)
480
-
481
- if lemma = @exceptions[pos][form]
482
-
483
- lemma.each { |x| yield x }
484
-
485
- end
486
-
487
-
488
-
489
- if pos == :noun && form.endwith('ful')
490
-
491
- each_lemma(form[0, form.length-3], pos) do |x|
492
-
493
- yield x + 'ful'
494
-
495
- end
496
-
497
- else
498
-
499
-
500
-
501
- each_substitutions(form, pos) do|x|
502
-
503
- yield x
504
-
505
- end
506
-
507
- end
508
-
509
- end
510
-
511
-
512
-
513
- def str_to_pos(str)
514
-
515
- case str
516
-
517
- when "n", "noun"
518
-
519
- return :noun
520
-
521
- when "v", "verb"
522
-
523
- return :noun
524
-
525
- when "a", "j", "adjective", "adj"
526
-
527
- return :adj
528
-
529
- when "r", "adverb", "adv"
530
-
531
- return :adv
532
-
533
- when "b", "abbrev", "abbr", "abr"
534
-
535
- return :abbr
536
-
537
- else
538
-
539
- return :unknown
540
-
541
- end
542
-
543
- end
544
-
545
-
546
-
547
- def load_provided_dict(dict)
548
-
549
- num_lex_added = 0
550
-
551
- open_file(dict) do |io|
552
-
553
- io.each_line do |line|
554
-
555
- # pos must be either n|v|r|a or noun|verb|adverb|adjective
556
-
557
- p, w, s = line.split(/\s+/, 3)
558
-
559
- pos = str_to_pos(p)
560
-
561
- word = w
562
-
563
- substitute = s.strip
564
-
565
- if /\A\"(.*)\"\z/ =~ substitute
566
-
567
- substitute = $1
568
-
569
- end
570
-
571
- if /\A\'(.*)\'\z/ =~ substitute
572
-
573
- substitute = $1
574
-
575
- end
576
-
577
- next unless (pos && word && substitute)
578
-
579
- if @wordlists[pos]
580
-
581
- @wordlists[pos][word] = substitute
582
-
583
- num_lex_added += 1
584
-
585
- end
586
-
587
- end
588
-
589
- end
590
-
591
- # puts "#{num_lex_added} items added from #{File.basename dict}"
592
-
593
- end
594
-
595
- end
596
-
597
- end
598
-
599
- ```
600
-
601
-
602
-
603
569
  `index.品詞`の辞書は、各行の見出しだけを読み込んでいるとasmさんから助言をうけました。

13

ok

2019/04/27 08:52

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -68,17 +68,9 @@
68
68
 
69
69
  ```
70
70
 
71
- # 変数
72
-
73
- DATA_DIR
74
-
75
- -> 辞書があるディレクトリの指定。
76
-
77
-
78
-
79
71
  WN_FILES
80
72
 
81
- -> 辞書データの指定
73
+ -> 。
82
74
 
83
75
 
84
76
 
@@ -166,9 +158,21 @@
166
158
 
167
159
  class Lemmatizer
168
160
 
161
+
162
+
163
+ # 辞書データがディレクトリのPATH
164
+
165
+ # 大文字で始まる場合は「定数」。各メソッドから参照可能。
166
+
169
167
  DATA_DIR = File.expand_path('..', File.dirname(__FILE__))
170
168
 
169
+
170
+
171
-
171
+ # 辞書データのPATH
172
+
173
+ # noun(名詞)、verb(動詞)、adj(形容詞)、adv(副詞)
174
+
175
+ # index.品詞は見出し語。品詞.excは不規則活用。
172
176
 
173
177
  WN_FILES = {
174
178
 
@@ -206,7 +210,13 @@
206
210
 
207
211
  }
208
212
 
213
+
214
+
209
-
215
+ # morphological(形態論の)substitution(置き換え)
216
+
217
+ # 規則的に置き換え可能な場合のルール
218
+
219
+ # 重複するものは、ing, es, ed, er, est。
210
220
 
211
221
  MORPHOLOGICAL_SUBSTITUTIONS = {
212
222
 
@@ -280,12 +290,26 @@
280
290
 
281
291
 
282
292
 
293
+ # インスタンスの生成時に実行される
294
+
283
295
  def initialize(dict = nil)
284
296
 
297
+ # オプショナル変数。dictに値を渡さない場合はnilになる。
298
+
299
+
300
+
285
301
  @wordlists = {}
286
302
 
287
303
  @exceptions = {}
288
304
 
305
+     # インスタンス変数
306
+
307
+     # スコープ:クラス内で全メソッドで共通して使用することが出来る。
308
+
309
+ # クラスから作成されるオブジェクト毎に固有のもの。
310
+
311
+ # インスタンスごとに独立してもつ変数のため、インスタンス変数という。
312
+
289
313
 
290
314
 
291
315
  MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
@@ -368,7 +392,7 @@
368
392
 
369
393
 
370
394
 
371
- def open_file(*args)
395
+ def open_file(*args) # *argsは可変長引数
372
396
 
373
397
  if args[0].is_a? IO or args[0].is_a? StringIO
374
398
 

12

a

2019/04/25 21:11

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -158,4 +158,422 @@
158
158
 
159
159
 
160
160
 
161
+ ```
162
+
163
+
164
+
165
+ module Lemmatizer
166
+
167
+ class Lemmatizer
168
+
169
+ DATA_DIR = File.expand_path('..', File.dirname(__FILE__))
170
+
171
+
172
+
173
+ WN_FILES = {
174
+
175
+ :noun => [
176
+
177
+ DATA_DIR + '/dict/index.noun',
178
+
179
+ DATA_DIR + '/dict/noun.exc'
180
+
181
+ ],
182
+
183
+ :verb => [
184
+
185
+ DATA_DIR + '/dict/index.verb',
186
+
187
+ DATA_DIR + '/dict/verb.exc'
188
+
189
+ ],
190
+
191
+ :adj => [
192
+
193
+ DATA_DIR + '/dict/index.adj',
194
+
195
+ DATA_DIR + '/dict/adj.exc'
196
+
197
+ ],
198
+
199
+ :adv => [
200
+
201
+ DATA_DIR + '/dict/index.adv',
202
+
203
+ DATA_DIR + '/dict/adv.exc'
204
+
205
+ ]
206
+
207
+ }
208
+
209
+
210
+
211
+ MORPHOLOGICAL_SUBSTITUTIONS = {
212
+
213
+ :noun => [
214
+
215
+ ['s', '' ],
216
+
217
+ ['ses', 's' ],
218
+
219
+ ['ves', 'f' ],
220
+
221
+ ['xes', 'x' ],
222
+
223
+ ['zes', 'z' ],
224
+
225
+ ['ches', 'ch' ],
226
+
227
+ ['shes', 'sh' ],
228
+
229
+ ['men', 'man'],
230
+
231
+ ['ies', 'y' ]
232
+
233
+ ],
234
+
235
+ :verb => [
236
+
237
+ ['s', '' ],
238
+
239
+ ['ies', 'y'],
240
+
241
+ ['es', 'e'],
242
+
243
+ ['es', '' ],
244
+
245
+ ['ed', 'e'],
246
+
247
+ ['ed', '' ],
248
+
249
+ ['ing', 'e'],
250
+
251
+ ['ing', '' ]
252
+
253
+ ],
254
+
255
+ :adj => [
256
+
257
+ ['er', '' ],
258
+
259
+ ['est', '' ],
260
+
261
+ ['er', 'e'],
262
+
263
+ ['est', 'e']
264
+
265
+ ],
266
+
267
+ :adv => [
268
+
269
+ ],
270
+
271
+ :abbr => [
272
+
273
+ ],
274
+
275
+ :unknown => [
276
+
277
+ ]
278
+
279
+ }
280
+
281
+
282
+
283
+ def initialize(dict = nil)
284
+
285
+ @wordlists = {}
286
+
287
+ @exceptions = {}
288
+
289
+
290
+
291
+ MORPHOLOGICAL_SUBSTITUTIONS.keys.each do |x|
292
+
293
+ @wordlists[x] = {}
294
+
295
+ @exceptions[x] = {}
296
+
297
+ end
298
+
299
+
300
+
301
+ WN_FILES.each_pair do |pos, pair|
302
+
303
+ load_wordnet_files(pos, pair[0], pair[1])
304
+
305
+ end
306
+
307
+
308
+
309
+ if dict
310
+
311
+ [dict].flatten.each do |d|
312
+
313
+ load_provided_dict(d)
314
+
315
+ end
316
+
317
+ end
318
+
319
+ end
320
+
321
+
322
+
323
+ def lemma(form, pos = nil)
324
+
325
+ unless pos
326
+
327
+ [:verb, :noun, :adj, :adv, :abbr].each do |p|
328
+
329
+ result = lemma(form, p)
330
+
331
+ return result unless result == form
332
+
333
+ end
334
+
335
+
336
+
337
+ return form
338
+
339
+ end
340
+
341
+
342
+
343
+ each_lemma(form, pos) do |x|
344
+
345
+ return x
346
+
347
+ end
348
+
349
+
350
+
351
+ form
352
+
353
+ end
354
+
355
+
356
+
357
+ # Print object only on init
358
+
359
+ def inspect
360
+
361
+ "#{self}"
362
+
363
+ end
364
+
365
+
366
+
367
+ private
368
+
369
+
370
+
371
+ def open_file(*args)
372
+
373
+ if args[0].is_a? IO or args[0].is_a? StringIO
374
+
375
+ yield args[0]
376
+
377
+ else
378
+
379
+ File.open(*args) do |io|
380
+
381
+ yield io
382
+
383
+ end
384
+
385
+ end
386
+
387
+ end
388
+
389
+
390
+
391
+ def load_wordnet_files(pos, list, exc)
392
+
393
+ open_file(list) do |io|
394
+
395
+ io.each_line do |line|
396
+
397
+ w = line.split(/\s+/)[0]
398
+
399
+ @wordlists[pos][w] = w
400
+
401
+ end
402
+
403
+ end
404
+
405
+
406
+
407
+ open_file(exc) do |io|
408
+
409
+ io.each_line do |line|
410
+
411
+ w, s = line.split(/\s+/)
412
+
413
+ @exceptions[pos][w] ||= []
414
+
415
+ @exceptions[pos][w] << s
416
+
417
+ end
418
+
419
+ end
420
+
421
+ end
422
+
423
+
424
+
425
+ def each_substitutions(form, pos)
426
+
427
+ if lemma = @wordlists[pos][form]
428
+
429
+ yield lemma
430
+
431
+ end
432
+
433
+
434
+
435
+ MORPHOLOGICAL_SUBSTITUTIONS[pos].each do |entry|
436
+
437
+ old, new = *entry
438
+
439
+ if form.endwith(old)
440
+
441
+ each_substitutions(form[0, form.length - old.length] + new, pos) do |x|
442
+
443
+ yield x
444
+
445
+ end
446
+
447
+ end
448
+
449
+ end
450
+
451
+ end
452
+
453
+
454
+
455
+ def each_lemma(form, pos)
456
+
457
+ if lemma = @exceptions[pos][form]
458
+
459
+ lemma.each { |x| yield x }
460
+
461
+ end
462
+
463
+
464
+
465
+ if pos == :noun && form.endwith('ful')
466
+
467
+ each_lemma(form[0, form.length-3], pos) do |x|
468
+
469
+ yield x + 'ful'
470
+
471
+ end
472
+
473
+ else
474
+
475
+
476
+
477
+ each_substitutions(form, pos) do|x|
478
+
479
+ yield x
480
+
481
+ end
482
+
483
+ end
484
+
485
+ end
486
+
487
+
488
+
489
+ def str_to_pos(str)
490
+
491
+ case str
492
+
493
+ when "n", "noun"
494
+
495
+ return :noun
496
+
497
+ when "v", "verb"
498
+
499
+ return :noun
500
+
501
+ when "a", "j", "adjective", "adj"
502
+
503
+ return :adj
504
+
505
+ when "r", "adverb", "adv"
506
+
507
+ return :adv
508
+
509
+ when "b", "abbrev", "abbr", "abr"
510
+
511
+ return :abbr
512
+
513
+ else
514
+
515
+ return :unknown
516
+
517
+ end
518
+
519
+ end
520
+
521
+
522
+
523
+ def load_provided_dict(dict)
524
+
525
+ num_lex_added = 0
526
+
527
+ open_file(dict) do |io|
528
+
529
+ io.each_line do |line|
530
+
531
+ # pos must be either n|v|r|a or noun|verb|adverb|adjective
532
+
533
+ p, w, s = line.split(/\s+/, 3)
534
+
535
+ pos = str_to_pos(p)
536
+
537
+ word = w
538
+
539
+ substitute = s.strip
540
+
541
+ if /\A\"(.*)\"\z/ =~ substitute
542
+
543
+ substitute = $1
544
+
545
+ end
546
+
547
+ if /\A\'(.*)\'\z/ =~ substitute
548
+
549
+ substitute = $1
550
+
551
+ end
552
+
553
+ next unless (pos && word && substitute)
554
+
555
+ if @wordlists[pos]
556
+
557
+ @wordlists[pos][word] = substitute
558
+
559
+ num_lex_added += 1
560
+
561
+ end
562
+
563
+ end
564
+
565
+ end
566
+
567
+ # puts "#{num_lex_added} items added from #{File.basename dict}"
568
+
569
+ end
570
+
571
+ end
572
+
573
+ end
574
+
575
+ ```
576
+
577
+
578
+
161
579
  `index.品詞`の辞書は、各行の見出しだけを読み込んでいるとasmさんから助言をうけました。

11

a

2019/04/25 11:57

投稿

t-cool
t-cool

スコア71

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

10

a

2019/04/25 11:48

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -49,20 +49,6 @@
49
49
  accuracy n 2 5 ! @ ~ = ; 2 2 04802907 04803209
50
50
 
51
51
  ```
52
-
53
-
54
-
55
- 次の点に気が付きました。
56
-
57
-  ・単語の後にpos(品詞)の省略形が続き、その後に、2つ数字があること。
58
-
59
-  ・@の後には、 ~ + ; %pなどが続くこと。
60
-
61
-  ・@の後に続く数字は2つであること。
62
-
63
-  ・@の後に続く数字と、そのあとに続く数字(8桁)の数が一致していること。
64
-
65
-  ・pos直後の数字2つと、@に続く数字2つの最初の数字は同じであること。
66
52
 
67
53
 
68
54
 

9

good

2019/04/25 04:17

投稿

t-cool
t-cool

スコア71

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

8

fix

2019/04/25 04:16

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -17,6 +17,26 @@
17
17
 
18
18
 
19
19
  ```
20
+
21
+ # noun.exc
22
+
23
+ aardwolves aardwolf
24
+
25
+ abaci abacus
26
+
27
+ aboideaux aboideau
28
+
29
+ aboiteaux aboiteau
30
+
31
+ abscissae abscissa
32
+
33
+ ```
34
+
35
+
36
+
37
+ ```
38
+
39
+ # index.noun
20
40
 
21
41
  acculturation n 3 3 @ ~ + 3 1 01128984 05984936 05757049
22
42
 

7

分析

2019/04/25 03:41

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -54,7 +54,101 @@
54
54
 
55
55
 
56
56
 
57
+ [lemmatizer/lib/lemmatizer/lemmatizer.rb](https://github.com/yohasebe/lemmatizer/blob/master/lib/lemmatizer/lemmatizer.rb)
58
+
57
- 辞書のどこのデータを参照して原形を特定しているのかをご助言いただけないでしょうか?
59
+ が、どのように辞書データを参照して原形に変換しているのかをご助言いただけないでしょうか?
60
+
61
+
62
+
63
+ ```
64
+
65
+ # 変数
66
+
67
+ DATA_DIR
68
+
69
+ -> 辞書があるディレクトリの指定。
70
+
71
+
72
+
73
+ WN_FILES
74
+
75
+ -> 辞書データの指定。
76
+
77
+
78
+
79
+ MORPHOLOGICAL_SUBSTITUTIONS
80
+
81
+ -> 形態素に基づく変換規則。
82
+
83
+
84
+
85
+ # メソッド
86
+
87
+ lemma(form, pos = nil)
88
+
89
+ -> 単語(form)と品詞(pos)を受け取り、原形を返す。
90
+
91
+
92
+
93
+ initialize(dict = nil)
94
+
95
+ -> インスタンスが生成されたときに実行される。
96
+
97
+ -> wordlistsとexceptionsの空のハッシュを宣言後、単語を追加していく。
98
+
99
+
100
+
101
+ each_lemma(form, pos)
102
+
103
+ -> メインのメソッド?
104
+
105
+ -> 単語(form)と品詞(pos)を受け取り、
106
+
107
+    1. 例外の単語ペアに合致すれば返す
108
+
109
+    2. `ful`で終わっている場合の対応
110
+
111
+    3. 原則どおりの場合の対応
112
+
113
+      each_substitutions(form, pos)が呼び出される。
114
+
115
+
116
+
117
+ each_substitutions(form, pos)
118
+
119
+ -> 形態素を元に原則どおり変換する場合の変換。
120
+
121
+
122
+
123
+ inspect
124
+
125
+ -> インスタンス生成時に、オブジェクトを印字する。
126
+
127
+
128
+
129
+ open_file(*args)
130
+
131
+ -> ファイルを開く。
132
+
133
+
134
+
135
+ load_wordnet_files(pos, list, exc)
136
+
137
+ -> wordnet_filesを読み込む。
138
+
139
+
140
+
141
+ str_to_pos(str)
142
+
143
+ -> 文字列をシンボルに変換して返す。
144
+
145
+
146
+
147
+ load_provided_dict(dict)
148
+
149
+ -> 辞書を読み込む
150
+
151
+ ```
58
152
 
59
153
 
60
154
 

6

ふぃx

2019/04/25 03:39

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -50,7 +50,7 @@
50
50
 
51
51
  Wordnetの他の相関データがそのまま入っていて、
52
52
 
53
- @以降数字は原形特定すために不要なのかも?と推測しています。
53
+ ここ辞書データの全て利用してい訳ではなでは?と推測しています。
54
54
 
55
55
 
56
56
 

5

fix

2019/04/24 13:51

投稿

t-cool
t-cool

スコア71

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

4

tweak

2019/04/24 13:48

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -42,6 +42,8 @@
42
42
 
43
43
   ・@の後に続く数字と、そのあとに続く数字(8桁)の数が一致していること。
44
44
 
45
+  ・pos直後の数字2つと、@に続く数字2つの最初の数字は同じであること。
46
+
45
47
 
46
48
 
47
49
  この辞書は、PythonのNLTKライブラリのWordnetの辞書から借用されているようなので、

3

改善

2019/04/24 13:47

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -32,10 +32,6 @@
32
32
 
33
33
 
34
34
 
35
- ここでの`@`や`+`、またそのあとに続く数字の羅列が何を意味しているかご助言いただけないでしょうか?
36
-
37
-
38
-
39
35
  次の点に気が付きました。
40
36
 
41
37
   ・単語の後にpos(品詞)の省略形が続き、その後に、2つ数字があること。
@@ -48,4 +44,16 @@
48
44
 
49
45
 
50
46
 
47
+ この辞書は、PythonのNLTKライブラリのWordnetの辞書から借用されているようなので、
48
+
49
+ Wordnetの他の相関データがそのまま入っていて、
50
+
51
+ @以降の数字は原形を特定するためには不要なのかも?と推測しています。
52
+
53
+
54
+
55
+ 辞書のどこのデータを参照して原形を特定しているのかをご助言いただけないでしょうか?
56
+
57
+
58
+
51
- ご助言をよろしくお願いいたします。
59
+ よろしくお願いいたします。

2

tweak

2019/04/24 13:42

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -40,7 +40,11 @@
40
40
 
41
41
   ・単語の後にpos(品詞)の省略形が続き、その後に、2つ数字があること。
42
42
 
43
+  ・@の後には、 ~ + ; %pなどが続くこと。
44
+
45
+  ・@の後に続く数字は2つであること。
46
+
43
-  ・@数字と、そのあとに続く数字の数が一致していること。
47
+  ・@に続く数字と、そのあとに続く数字(8桁)の数が一致していること。
44
48
 
45
49
 
46
50
 

1

tweak

2019/04/24 13:37

投稿

t-cool
t-cool

スコア71

test CHANGED
File without changes
test CHANGED
@@ -36,4 +36,12 @@
36
36
 
37
37
 
38
38
 
39
+ 次の点に気が付きました。
40
+
41
+  ・単語の後にpos(品詞)の省略形が続き、その後に、2つ数字があること。
42
+
43
+  ・@直後の数字と、そのあとに続く数字の数が一致していること。
44
+
45
+
46
+
39
- よろしくお願いいたします。
47
+ ご助言をよろしくお願いいたします。