LoginSignup
0
0

More than 1 year has passed since last update.

【Ruby のまずいコード】文字列を文字種で分割

Posted at

お題

文字列を文字種で分割するメソッドを書いてください。
つまり,

p divide_by_script("Rubyは楽しい") # => ["Ruby", "は", "楽", "しい"]

となるような。
漠然と「文字種」と書きましたが,Unicode の仕様でいう「スクリプト(script)」のことであるとしましょう。
script という単語は多義語ですが,ここでは「漢字」「平仮名」「片仮名」「ラテン文字」「アラビア文字」「チベット文字」といったもののことです1
Unicode では,各文字に script プロパティーという値が割り当てられていて,たとえば以下のようになっています:

  • 「亜」などの漢字:Han2
  • 「あ」などの平仮名:Hiragana
  • 「ア」などの片仮名:Katakana
  • 「a」などのラテン文字:Latin
  • 「α」などのギリシャ文字:Greek

「!」などの記号類は,文字学的には何のスクリプトにも含まれませんが,Unicode では script プロパティーを持っており,Common という値が割り当てられています。

文字のスクリプトは,unicode-scripts という gem で知ることができます。
まず

gem install unicode-scripts

で gem をインストールしておけば

require "unicode/scripts"

p Unicode::Scripts.script("あ") # => "Hiragana"

のようにして使えます。
もちろん Gemfile と Bundler を使っても構いません。

コード

require "unicode/scripts"

def divide_by_script(str)
  last_script = nil
  result = []
  str.each_char do |char|
    script = Unicode::Scripts.script(char)
    if last_script == script
      result.last << char
    else
      result << char
      last_script = script
    end
  end
  result
end

少しだけ説明を加えると,

result.last << char

は,配列の最後の要素である String オブジェクトの末尾に破壊的に文字列 char を結合しています。
一方,

result << char

は配列の末尾に文字列 char を追加しています。
同じ演算子 << を使っていますが,レシーバーのクラスが違うので,全く別のメソッドです。やっていることは違います3

改善

Ruby の Enumerable モジュールには,要素の並びを〈要素から算出される値が等しいものの連なり〉に分解する専用メソッドEnumerable#chunk があります4

これを使うと処理が 1 行で書けてしまいます:

require "unicode/scripts"

def divide_by_script(str)
  str.chars.chunk{ |char| Unicode::Scripts.script(char) }.map{ |script, chars| chars.join }
end

ここで,map のブロックパラメーター script は使っていないので,_ とかでいいのですが,読者に「スクリプトだよ」と分かっていただきやすいように書きました。

Ruby 2.7 以降なら番号指定ブロックパラメーターを使って

require "unicode/scripts"

def divide_by_script(str)
  str.chars.chunk{ Unicode::Scripts.script(_1) }.map{ _2.join }
end

とも書けますね。


  1. この意味の script には「用字系」とか「文字体系」という訳語もあります。 

  2. Kan じゃなくて Han なのは中国語だからでしょう。 

  3. とはいえ,「末尾にくっつける」という点が共通しているので同じ演算子にしているのでしょう。 

  4. 英単語の chunk は塊というような意味ですね。 

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0