Module: VivlioStarter::CLI::HeadingSegmenter
- Defined in:
- lib/vivlio_starter/cli/heading_segmenter.rb
Overview
見出しを折返し可能な語へ分割するモジュール
Constant Summary collapse
- MAX_LENGTH =
見出しとして現実的な長さの上限(これを超えるものは解析しない)
200- GLUE_PATTERN =
行頭に単独で置きたくない語か。約物・閉じ括弧・1〜2 文字の仮名(助詞等)。
/\A[ぁ-んー、。,.・:;?!)]}」』〉》】〕”’]+\z/- OPEN_BRACKET_PATTERN =
行末に単独で置きたくない語(開き括弧)は次の語へ接着する
/\A[([{「『〈《【〔“‘]+\z/
Class Method Summary collapse
-
.collect_surfaces(text) ⇒ Object
MeCab の表層形を並べる(読みは使わないので feature は見ない)。 MeCab は空白を落とすので、原文を走査して落ちた分を直前の語の末尾へ戻す (語頭へ付けると行頭に空白が来る)。連結して原文に戻ることを呼び出し側が検証する。.
- .glue_open_brackets(words) ⇒ Object
- .glue_to_previous?(word) ⇒ Boolean
-
.inferrer ⇒ Object
YomiInferrer の MeCab インスタンス・可用性判定を共有する (MeCab の初期化と不在時の案内を 2 か所に持たない)。.
-
.merge_unbreakable(words) ⇒ Object
直前の語と離してはいけない語を接着する。 助詞・助動詞・記号だけの語や 1 文字の送り仮名を単独で行頭へ送ると、 かえって不自然に見える(「章の/管理」より「章の管理」)。.
-
.segment(text) ⇒ Array<String>
見出しを語の配列へ分割する。 分割できない(MeCab 不在・空文字・長すぎる)場合は [text] を返す。.
Class Method Details
.collect_surfaces(text) ⇒ Object
MeCab の表層形を並べる(読みは使わないので feature は見ない)。 MeCab は空白を落とすので、原文を走査して落ちた分を直前の語の末尾へ戻す (語頭へ付けると行頭に空白が来る)。連結して原文に戻ることを呼び出し側が検証する。
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/vivlio_starter/cli/heading_segmenter.rb', line 56 def collect_surfaces(text) surfaces = [] pos = 0 inferrer.send(:mecab).parse(text) do |node| next if node.is_eos? || node.surface.empty? index = text.index(node.surface, pos) or next skipped = text[pos...index] surfaces[-1] = surfaces[-1] + skipped if !skipped.empty? && !surfaces.empty? surfaces << (surfaces.empty? ? skipped + node.surface : node.surface) pos = index + node.surface.length end surfaces[-1] = surfaces[-1] + text[pos..] if pos < text.length && !surfaces.empty? surfaces end |
.glue_open_brackets(words) ⇒ Object
95 96 97 98 99 100 101 102 103 |
# File 'lib/vivlio_starter/cli/heading_segmenter.rb', line 95 def glue_open_brackets(words) words.reverse.each_with_object([]) do |word, acc| if acc.empty? || !word.match?(OPEN_BRACKET_PATTERN) acc << word.dup else acc[-1] = word + acc[-1] end end.reverse end |
.glue_to_previous?(word) ⇒ Boolean
91 92 93 |
# File 'lib/vivlio_starter/cli/heading_segmenter.rb', line 91 def glue_to_previous?(word) word.length <= 2 && word.match?(GLUE_PATTERN) end |
.inferrer ⇒ Object
YomiInferrer の MeCab インスタンス・可用性判定を共有する (MeCab の初期化と不在時の案内を 2 か所に持たない)。
107 108 109 110 |
# File 'lib/vivlio_starter/cli/heading_segmenter.rb', line 107 def inferrer require_relative 'index/yomi_inferrer' @inferrer ||= IndexCommands::YomiInferrer.new end |
.merge_unbreakable(words) ⇒ Object
直前の語と離してはいけない語を接着する。 助詞・助動詞・記号だけの語や 1 文字の送り仮名を単独で行頭へ送ると、 かえって不自然に見える(「章の/管理」より「章の管理」)。
75 76 77 78 79 80 81 82 83 84 |
# File 'lib/vivlio_starter/cli/heading_segmenter.rb', line 75 def merge_unbreakable(words) merged = words.each_with_object([]) do |word, acc| if acc.empty? || !glue_to_previous?(word) acc << word.dup else acc[-1] << word end end glue_open_brackets(merged) end |
.segment(text) ⇒ Array<String>
見出しを語の配列へ分割する。 分割できない(MeCab 不在・空文字・長すぎる)場合は [text] を返す。
41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/vivlio_starter/cli/heading_segmenter.rb', line 41 def segment(text) source = text.to_s return [source] if source.empty? || source.length > MAX_LENGTH return [source] unless inferrer.available? words = collect_surfaces(source) words.join == source ? merge_unbreakable(words) : [source] rescue StandardError => e Common.log_debug("見出しの語分割に失敗(そのまま扱います): #{e.}") [source] end |