丸数字をよける

Shift_JIS の文脈で丸数字をちょっとよけたかった

# coding: Windows-31J
(''..'').inject([ARGF.read.force_encoding('Windows-31J'), 1]) do |(input, i), n|
  [input.gsub(n, "(#{i})"), i + 1]
end.first.display

こんな感じか。

もう少し何とか、
丸数字一つずつじゃなくてまとめてとかどうかないかな、String#tr では変換先一文字だっけ

追記 (2019/11/17)

コメントにアドレスあげたようにスラックでいくつか助言をいただく

するとこんな感じ

gsub をハッシュで

# coding: Windows-31J
ms = (''..'').inject([{}, 1]){ |(h, i), m| h[m] = "(#{i})"; [h, i + 1] }.first
ARGF.set_encoding('Windows-31J').read.gsub(/[#{ms.keys.join}]/, ms).display

一連なりで書くとこんな

# coding: Windows-31J
ARGF.set_encoding('Windows-31J').read \
  .gsub(*((''..'').each_with_object([{}, 1]) do |m, h_i|
    h_i[0][m], h_i[1] = "(#{h_i[1]})", h_i[1] + 1
  end.first.each_with_object([//, {}]) do |(k, v), ks_h|
    ks_h[0], ks_h[1][k] = /[#{ks_h[0].source}#{k}]/, v
  end)).display

さらに unicode_normalize を使う

# coding: Windows-31J
ms = (''..'').each_with_object({}) \
  { |m, h| h[m] = "(#{m.encode('utf-8').unicode_normalize(:nfkc)})" }
ARGF.set_encoding('Windows-31J').read.gsub(/[#{ms.keys.join}]/, ms).display

一連なりで書くとこんな

# coding: Windows-31J
ARGF.set_encoding('Windows-31J').read \
  .gsub(*((''..'').each_with_object({}) do |m, h|
    h[m] = "(#{m.encode('utf-8').unicode_normalize(:nfkc)})"
  end.each_with_object([//, {}]) do |(k, v), ks_h|
    ks_h[0], ks_h[1][k] = /[#{ks_h[0].source}#{k}]/, v
  end)).display

メソッドチェインで一連なりに書くのは返って長くなるし分かり難いかな。

分かり難い所説明少し追記 (/11/23)

メソッドチェインで一連なりに書くの分かり難い辺りについて。

gsub の引数、正規表現と置換先ハッシュの配列をとって、配列の頭に「*」を付けて2引数としている。

その引数や正規表現と置換先ハッシュの配列を作るのも分かり難い。

まず、丸数字の並びからそれをキーに括弧数字を値にするハッシュを作る、#unicode_normalize を使うかどうかでちょっと分かれる、ハッシュへの値の代入がハッシュ自身を返すわけじゃないので #each_with_object が便利。

(''..'').each_with_object([{}, 1]) do |m, h_i|
    h_i[0][m], h_i[1] = "(#{h_i[1]})", h_i[1] + 1
  end.first
(''..'').each_with_object({}) do |m, h|
    h[m] = "(#{m.encode('utf-8').unicode_normalize(:nfkc)})"
  end

「m」は丸数字、「h」は結果のハッシュ、「i」は番号の整数、「h_i」はその二つ繋げたののつもり。

そうやって作った一つのハッシュから、そのキーを集めた正規表現と自身と同じハッシュの二つを要素にする配列を作る、自身とそこから派生するなにかと二つの要素を集めてくるのどうしたらいいのだろう、今回はハッシュ自身をもう一度作り直した。

.each_with_object([//, {}]) do |(k, v), ks_h|
    ks_h[0], ks_h[1][k] = /[#{ks_h[0].source}#{k}]/, v
 end

正規表現で文字クラスのネスト注意、そうすると全部フラットにまとめた文字クラスと同じ意味になる 正規表現 (Ruby 2.6.0)
「k」はキー、「v」は値、「ks」はキー複数、「h」は結果のハッシュ、「ks_h」はその二つ繋げたののつもり。

いずれも多重代入注意、ブロック引数受けるとき括弧でくくっておくと分解してくれるの便利、結果オブジェクト側では分解しすぎるとオブジェクトに反映しないので前記のように繋げた名前の変数で受ける。

さらに追記、gsub のブロック (/12/1)

#unicode_normalize があるので数字を数える必要ないなら、.gsub の置換先、わざわざハッシュにすることもなかった、ブロックで良かった。

# coding: Windows-31J
ARGF.set_encoding('Windows-31J').read \
 .gsub(/[①-⑳]/){ |m| "(#{m.encode('utf-8').unicode_normalize(:nfkc)})" }.display