Module: VivlioStarter::CLI::Metrics::SentenceEndings

Defined in:
lib/vivlio_starter/cli/metrics/sentence_endings.rb

Constant Summary collapse

MIN_RUN =

連続とみなす最小本数。です・ます中心の和文では 3〜4 連続は普通のため、 「気になるほどの単調さ」の目安として 5 連続以上を拾う。

5
POLITE =

長い順(最長一致)に判定する丁寧体・常体の語尾。

%w[ませんでした でしょう ましょう ですか ますか でした ました ません ます です].freeze
PLAIN =
%w[であった である だった ].freeze
CATEGORIES =

内訳で示す大分類(表示順)。

['です・ます', '体言止め', 'だ・である', 'その他'].freeze

Class Method Summary collapse

Class Method Details

.build_run(first, key, length) ⇒ Object



89
90
91
# File 'lib/vivlio_starter/cli/metrics/sentence_endings.rb', line 89

def build_run(first, key, length)
  SentenceRun.new(chapter_num: first.chapter_num, line: first.line, label: run_label(key), count: length)
end

.category(key) ⇒ Object

細かいキーを内訳の大分類へ畳む。



51
52
53
54
55
56
57
# File 'lib/vivlio_starter/cli/metrics/sentence_endings.rb', line 51

def category(key)
  return 'です・ます' if POLITE.include?(key)
  return 'だ・である' if PLAIN.include?(key)
  return '体言止め' if key == '体言止め'

  'その他'
end

.chapter_runs(sentences, min) ⇒ Object

--- 内部ヘルパー ---



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/vivlio_starter/cli/metrics/sentence_endings.rb', line 75

def chapter_runs(sentences, min)
  keys = sentences.map { classify(it.text) }
  runs = []
  start = 0
  while start < sentences.size
    stop = start
    stop += 1 while stop < sentences.size && keys[stop] == keys[start]
    length = stop - start
    runs << build_run(sentences[start], keys[start], length) if length >= min && keys[start] != 'その他'
    start = stop
  end
  runs
end

.classify(text) ⇒ Object

文末の細かいキー(連続判定・ラベル用)を返す。



39
40
41
42
43
44
45
46
47
48
# File 'lib/vivlio_starter/cli/metrics/sentence_endings.rb', line 39

def classify(text)
  body = text.sub(/[。.!?!?]+\z/, '').rstrip
  return 'その他' if body.empty?

  POLITE.each { |suffix| return suffix if body.end_with?(suffix) }
  PLAIN.each { |suffix| return suffix if body.end_with?(suffix) }
  return '体言止め' if taigen?(body[-1])

  'その他'
end

.distribution(sentences) ⇒ Object

大分類ごとの割合(%、四捨五入)を表示順のハッシュで返す。



60
61
62
63
64
65
66
# File 'lib/vivlio_starter/cli/metrics/sentence_endings.rb', line 60

def distribution(sentences)
  return CATEGORIES.to_h { [it, 0] } if sentences.empty?

  counts = sentences.each_with_object(Hash.new(0)) { |s, acc| acc[category(classify(s.text))] += 1 }
  total = sentences.size.to_f
  CATEGORIES.to_h { [it, (counts[it] / total * 100).round] }
end

.monotone_runs(sentences, min: MIN_RUN) ⇒ Object

同一文末が MIN_RUN 以上連続する箇所を、章ごとに検出する。



69
70
71
# File 'lib/vivlio_starter/cli/metrics/sentence_endings.rb', line 69

def monotone_runs(sentences, min: MIN_RUN)
  sentences.group_by(&:chapter_num).flat_map { |_num, group| chapter_runs(group, min) }
end

.run_label(key) ⇒ Object

語尾語は「です。」のように句点付き、体言止めはそのまま。



94
# File 'lib/vivlio_starter/cli/metrics/sentence_endings.rb', line 94

def run_label(key) = key == '体言止め' ? key : "#{key}"

.taigen?(char) ⇒ Boolean

文末の最後の 1 文字がひらがな以外(漢字・カタカナ・英数)なら名詞止めとみなす。

Returns:

  • (Boolean)


97
# File 'lib/vivlio_starter/cli/metrics/sentence_endings.rb', line 97

def taigen?(char) = char&.match?(/[\p{Han}\p{Katakana}A-Za-z0-9ー]/) || false