Ruby

Ruby 2.3の新機能

More than 1 year has passed since last update.

本投稿はNithin Bekalさんのブログで紹介されているRuby 2.3の新機能を和訳したものである。また、動作確認を兼ねて内容を一部改版している。

Ruby 2.3.0が2015年のクリスマスにリリースされる。これに向け、つい数週間前にプレビュー版が利用可能になった。

そこでRuby 2.3の新機能について幾つか紹介したいと思う。

プレビュー版のインストール方法

RVMの場合

$ rvm install 2.3.0-preview1

rbenvの場合

$ rbenv install 2.3.0-preview1

Safe navigation operator

新しい演算子&.が追加になった。

これはオブジェクトがnilではないかどうかを判別し、演算子以降のメソッドを安全に呼ぶために利用できる。

Ruby <= 2.2.x

if user && user.admin?
  # do something
end

Ruby 2.3

if user&.admin?
  # do something
end

但し、気をつけるべき点が1つあり、オブジェクトがfalseの場合は後続のメソッドが実行されてしまう。そのため、false.admin?を実行し例外となってしまう。

Frozen string literals

Ruby 2.2では文字列はデフォルトだとミュータブル(値を変更することが可能)である。

str = 'foobar'
str[2] = 'z'
str #=> "fozbar"

もしイミュータブルな文字列にしたければString#freezeを実行する必要がある。

str = 'foobar'.freeze
str[2] = 'z'
RuntimeError: can't modify frozen String

文字列のfreeze(immutable)化はパフォーマンスの向上にもつながる。
というのも、そうすることでRubyでは管理するオブジェクトが少なくて済むからだ。
こういった事情もあり、Ruby 3.0ではデフォルトで文字列をimmutableにしようという計画がある。

その文字列のimmutable化の実現を加速するため、Ruby 2.3では文字列のimmutable化を適用するオプションが用意されている。

以下のようにファイルの先頭行にfrozen_string_literal: trueコメントを追加することで有効になる。
有効にしたら同ファイル上の全ての文字列オブジェクトはString#freezeを呼ばなくてもimmutableなオブジェクトとして機能するのである。

# frozen_string_literal: true

str = 'cat'
str[0] = 'b'

# frozen.rb:5:in `[]=': can't modify frozen String (RuntimeError)
#   from frozen.rb:5:in `<main>'

もっと詳しく知りたい方はこちらの記事を読むと言いだろう。

Array#dig and Hash#dig

次は、ArrayクラスとHashクラスに追加されたdigメソッドだ。
ネスト化された各要素に一度のメソッド呼び出しでアクセスできるAPIである。

Arrayの場合

list = [
  [2, 3],
  [5, 7, 9],
  [ [11, 13], [17, 19] ]
]

list.dig(1, 2)    #=> 9
list.dig(2, 1, 0) #=> 17
list.dig(0, 3)    #=> nil
list.dig(4, 0)    #=> nil

Hashの場合

dict = {
  a: { x: 23, y: 29 },
  b: { x: 31, z: 37 }
}

dict.dig(:a, :x) #=> 23
dict.dig(:b, :z) #=> 37
dict.dig(:b, :y) #=> nil
dict.dig(:c, :x) #=> nil

“Did you mean?”

メソッド名の打ち間違い(typo)でNoMethodErrorした時に、「ひょっとしてこれですか?」って正しいメソッドを提案してくれる機能。これはRuby初心者にとっては有り難い機能では?

2.3.0-preview1 :001 > "foo bar".uppcase
NoMethodError: undefined method `uppcase' for "foo bar":String
Did you mean?  upcase
               upcase!

Hash “comparison”

Ruby 2.3ではHashオブジェクト同士の比較ができるようになった。

下記の例をご覧いただくのが早いだろう。

{ x: 1, y: 2 } >= { x: 1 } #=> true
{ x: 1, y: 2 } >= { x: 2 } #=> false
{ x: 1 } >= { x: 1, y: 2 } #=> false

他にも比較演算子が用意されているがもっと知りたい方はこのブログを参考にすると良いだろう。

Hash#to_proc

HashオブジェクトをProcオブジェクトに変換するメソッドto_procが導入された。
Procオブジェクト化されたHashは元々のキーを使用し呼び、値を取得することができる。

h = { foo: 1, bar: 2, baz: 3}
p = h.to_proc

p.call(:foo)  #=> 1
p.call(:bar)  #=> 2
p.call(:quux) #=> nil

Hash#fetch_values

Hash#values_atに似たHash#fetch_valuesが導入された。
このメソッドは該当のキーが検出された場合のみ、キーに対する値の配列を返すというものだ。
値が無かった場合、#values_atnilを返すのに対して、 #fetch_valuesは例外KeyErrorを上げる。

h = { foo: 1, bar: 2, baz: 3}
h.fetch_values(:foo, :bar) #=> [1, 2]

h.values_at(:foo, :quux)    #=> [1, nil]
h.fetch_values(:foo, :quux) #=> raise KeyError

Enumerable#grep_v

Enumerable#grep_vメソッドはgrepコマンドの-vオプションと同等の役割を担う。
引数に与えた条件にマッチしない場合に要素を返す。

list = %w(foo bar baz)

list.grep_v(/ba/)
#=> ['foo']

list.grep(/ba/)
#=> ['bar', 'baz']

Numeric#positive? and #negative?

これは長らくRailsのコアにあったものだがRubyに移植されることになった。

> 1.positive?
# => true
> -1.negative?
# => true