Tech Racho エンジニアの「?」を「!」に。
  • 開発

Ruby 2.1.0リリース!注目の新機能を見てみましょう

2013年12月25日。おめでとうございます、Ruby 2.1.0がリリースされました
日本時間で26日という細かい突っ込みをしている場合ではありません。
まだ使ったことがない方、簡単に変更点を見てみましょう。

文法の変更

デフォルト値を伴わないキーワード引数

Ruby 2.0が出たときから誰もが思っていた疑問、「なんでキーワード引数はデフォルト値必須なんだ?」がついに解消されました。以下の記法で、デフォルト値をつけないキーワード引数を宣言できます。この場合、呼び出し時に対応する実引数を渡さないとArgumentErrorになります。

def hello(name: )
  puts name
end

hello(name: "taro")
# => taro

hello
# => ArgumentError: missing keyword: name

キーワード引数同士ではデフォルト引数の有無による順番の制約を受けないので、以下のようなコードも正当です。

def hello(a:, b:1, c:, d:2)
end

def world(a, b=1, *c, d:, e:2, f:, **g, &h)
end

新しい整数リテラル

"r"サフィックスをつけることで、Rational型が作れるようになりました。
42rRational(42, 1)と同義で、3.14r3.14.rationalizeと同義です。
Rational型は有理数を表す型で、循環小数も分数形式で表現することができます。たとえば、1/3に3をかけるとちゃんと1になります。

1/3r
# => (1/3)

(1/3r).to_f
# => 0.3333333333333333

(1/3r) * 3
# => (1/1)

((1/3r) * 3).to_i
# => 1

また、"i"サフィックスをつけることで、Complex型が作れるようになりました。
42iComplex(0, 42)と同義です。

42i
# => (0+42i)

42i.real
# => 0

42i.imaginary
# => 42

42i.to_f
# => RangeError: can't convert 0+42i into Float

42i * 2i
# => (-84+0i)

(42i * 2i).to_f
# => -84.0

さらに、"ri"サフィックスをつけることで、Complex型の虚数部に有理数を指定することもできるようになりました。
ちなみに"ir"ではダメです。

3ri
# => (0+(3/1)*i)

使わない人は全く使わないかもしれませんが、一部の人には非常に便利な機能ですね。

ついでなので、他の整数リテラルも復習しておきましょう。

# 10進数
123
0d123

# 浮動小数点数(Float型)
123.45
1.2345e+2

# "."で始まる記法Syntax Error
.45 # => syntax error

# 16進数
0xabcd

# 8進数
0666
0o666

# 2進数
0b1010

defがメソッド名のsymbolを返すようになった

従来def式はnilを返していましたが、Ruby 2.1ではメソッド名のsymbolを返すようになりました。
きっと、メタプログラミングがしやすくなるはずです。

高速化

RGenGCとメソッドキャッシュ、その他の最適化により、Ruby 2.0やそれ以前のバージョンと比較して高速化されています。
手元の環境で、何も考えずに適当なRailsプロジェクトのrspecを回してみたところ、以下のように15%以上高速化されました。

Ruby 2.0.0 Ruby 2.1.0
1回目 57.8 53.9
2回目 62.9 54.0
3回目 61.4 46.7
平均 60.7 51.5

Ruby 2.1の一番の目玉はここだと言っても過言ではないですね!
そのうち、より詳細なベンチマークを取ってみましょう。

その他注目の新機能

String#scrub

String#scrubにより、不正なバイト列を文字列から除去できるようになりました。

Web APIなどで日本語を取得すると、文字列が壊れていることが割と頻繁にあります。Twitter APIでもよくありました。
壊れた文字列は都合が悪いため、Ruby 1.9の頃は以下のようなハックがよく使われていました。

# (A) 同じエンコーディングに変換
str.encode('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: '?')

# (B) いったん別のエンコーディングに変換
str.encode('UTF-16', invalid: :replace, undef: :replace, replace: '?').encode('UTF-8')

しかし、(A)は本来何も起きないのが正常(バグが使われていただけ)ですし、(B)はめんどくさい上にCSIっぽくありません。

そこで、専用メソッドscrubが用意されました。破壊的メソッドscrub!もあります。
scrubでは引数を指定しないとU+FFFDに置換されます。ひし形の中にクエスチョンマークが入ったあれです。これが気にくわないときはscrubの引数に文字列を指定できますが、空文字を指定するときは、バリデーションをすりぬける脆弱性を作らないように注意してください。

ちなみにscrubという名前はZFSのコマンドzpool scrubから取ったそうです。

# ファイルやネットワークを経由して、こんな感じの不正バイト列が作られる
str = "あいうえお"
str.force_encoding('ASCII-8BIT')
str.slice!(0, 14)
str.force_encoding('UTF-8')
# => "あいうえ\xE3\x81"

str.scrub
# => "あいうえ�"

str.scrub("")
# => "あいうえ"

str.scrub("[不正]")
# => "あいうえ[不正]"

str.scrub {|x| x.force_encoding('ASCII-8BIT'); x.ord.to_s(16) }
# => "あいうえe3"

あまりに便利すぎてRuby 2.0でも使いたい!という人はstring-scrub gemを使いましょう。

Array#to_h

やっと、Arrayにto_hメソッドがつきました。これで、Hash#to_aと対称性が保たれることになります。あの醜いflattenごにょごにょをしなくてよくなります。

ary = { a: 1, b: 2 }.to_a
# => [[:a, 1], [:b, 2]]

# 元に戻すには、Ruby 2.0ではこうしていた
Hash[*ary.flatten]
# => { a: 1, b: 2 }

# ↑と思ったらこれでOKでしたすみません
Hash[ary]
# => { a: 1, b: 2}

# Ruby 2.1ならこれでOK
ary.to_h
# => { a: 1, b: 2 }

# Ruby 2.1なら気持ちいい
{ a: 1, b: 2 }.to_a.to_h.to_a.to_h
# => { a: 1, b: 2 }

追記

Hash[[[:a, 1], [:b, 2]]]形式ならflattenなんかせずOKというご指摘を頂き、追記しました。
こんな初歩的なことをRubyコミッタ様にコメント頂いてしまった...

Socket#getifaddrs

なんかとても便利そうですが、まだ試していません。

まとめ

以上、ざっくりと遊んでみた結果を書いてみました。
他にもたくさんの新機能や改善があります。試せていないので書いていませんが、中でもRefinementsがパワーアップしてno longer experimentalとなったのは注目ポイントです。特殊用途のgemとは相性が良さそうなので、普及するかどうか楽しみです。

Ruby 2.0に移行済の環境なら、ほとんど互換性問題は起きないと思います。1.9からも、デフォルトエンコーディング以外は比較的容易にアップグレードできるでしょう。

Ruby 2.1にアップグレードして、快適な年末年始を過ごしたいですね。

関連記事

Ruby 2.0.0リリース! – キーワード引数を使ってみよう


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。