Ruby Freaks Lounge

第5回Ruby 1.9 の新機能ひとめぐり(後編): 知っておくとお得な機能

前編では1.9の目玉機能を、中編では文法や意味論の改善について紹介しました。

後編である今回は、知っておくとお得な機能を取り上げたいと思います。きっとあなたのRubyライフを快適にするでしょう。

鬼車による正規表現の強化

1.9では正規表現エンジンに鬼車を採用したため、鬼車による拡張機能が利用可能になりました。

名前を使った参照

マッチした部分文字列を、$1や$2のようなインデックス番号ではなく、名前で参照する機能がつきました。

コード1 正規表現のマッチ部分を名前で参照する
# (?<foo>...) にマッチした部分を $~[:foo] で参照できる
if "Taro Yamada" =~ /^(?<first_name>\w+) (?<last_name>\w+)$/
  p $~[:first_name] #=> "Taro"
  p $~[:last_name]  #=> "Yamada"
end

これによって、正規表現を変更した時に$1や$2のインデックスを修正する煩わしさから解放されます。

また、/(?<foo>...)/ =~ stringという形でマッチングした場合のみ、fooという名前のローカル変数が定義されます。

コード2 正規表現のマッチ部分がローカル変数に代入される
if /(?<first_name>\w+)\s+(?<last_name>\w+)/ =~ "Taro Yamada"
  p first_name #=> "Taro"
  p last_name #=> "Yamada"
end

表現能力の向上

\g<n>によって、n番目のグループを参照できます。これによって、従来では表現できなかった括弧の対応などが表現できるようになっています(機能名:田中哲スペシャル⁠⁠。

コード3 正規表現でかぎ括弧の対応を判定する
# かぎ括弧の対応がとれた文字列にマッチする正規表現
re = /^(\[\g<1>+\]|x)$/

p "[[x][x]]"[re]  #=> "[[x][x]]"
p "[[x][x]"[re]   #=> nil
p "[[x][x]]]"[re] #=> nil

また、16進表記で使われる文字[0-9A-Fa-f]のための省略記法\hが用意されたのも便利です。

順序付きHash

1.8ではHash#eachが要素を列挙する順序は不定でしたが、1.9では挿入順に列挙されることが保証されるようになりました。

コード4 ハッシュの順序が保存される
h = { "dog" => 1, "cat" => 2 }
h["panda"] = 3
h.each {|k| p k }
図1 コード4の実行結果
# 1.8の場合:順序は不定
$ ruby18 hash-order.rb
["cat", 2]
["panda", 3]
["dog", 1]

# 1.9の場合:挿入した順序
$ ruby19 hash-order.rb
["dog", 1]
["cat", 2]
["panda", 3]

これによって、キーの順序を配列として別途管理する煩わしさから解放されます。ただし、Hashのイテレート中に新たなキーを挿入をし続けると無限ループになりますので注意しましょう。ちなみに、この機能はPHPの連想配列に由来します。

プロセスの起動

system関数が拡張され、子プロセスの起動を細かく制御できるようになりました。また、子プロセスの終了を待たないspawn関数も導入されました。

コード5 柔軟なプロセス起動
# 環境変数ENV_VAR=childを指定して起動する
system({ "ENV_VAR" => "child" }, "echo $ENV_VAR")

# 標準出力をファイルにリダイレクトして起動する
open("log", "w") do |f|
  system("echo child", out: f)  # ファイルlogに"child"が書き込まれる
end

# 子プロセスを起動するだけ(子プロセスの終了を待たない)
spawn("echo child") #=> pid

環境変数や標準入出力やエラー出力以外にも、リソース制限やumaskなどの指定ができます。詳しくはri Kernel#spawnをご覧ください。

また、プロセスをデーモン化するためのProcess.daemonメソッドも追加されました(daemon(3)のラッパ⁠⁠。UNIXでサービス型のプログラムを作る際に便利です。

有理数と複素数が組み込みに

従来rational.rb(有理数ライブラリ)やcomplex.rb(複素数ライブラリ)は添付ライブラリでしたが、1.9では組み込みになりました。

コード6 有理数と複素数
# 1/3+1/6=1/2
p Rational(1,3) + Rational(1,6) #=> (1/2)

# 虚数i*i=-1
p Complex(0,1) * Complex(0,1) #=> (-1+0i)

著者は数学クイズの解答ツールに使ったことがあります。

Kernel#pが引数を返すように

1.8ではpの戻り値は常にnilでしたが、1.9では引数をそのまま返すようになりました。この変更理由は、デバッグをしやすくすることです。

コード7 メソッドの戻り値をpで表示するための変更
# オリジナルの定義
def remove_tags(str)
  str.gsub(/<[^<]+>/, "")
end

# 1.8で戻り値をデバッグ出力させる場合の変更
def remove_tags(str)
  r = str.gsub(/<[^<]+>/, "")
  p r
  r
end

# 1.9の場合はこれだけ
def remove_tags(str)
  p str.gsub(/<[^<]+>/, "")
end

1.8ではタイプ数5+カーソル移動+改行が必要でしたが、1.9ではタイプ数2で済んでいます。取るに足らないことと思うかも知れませんが、デバッグ時のストレスが確実に減ります。

名前つきprintfフォーマット

printfのフォーマット文字列に名前を書き、引数にシンボルをキーとしたハッシュを与えられるようになりました。%sの代わりに%<foo>sなどと書きます。

コード8 名前で値を参照するprinfフォーマット
h = { first_name: "Taro", last_name: "Yamada", age: 30 }
p "%<last_name>s, %<first_name>s (%<age>d)" % h
図2 コード8の実行結果
"Yamada, Taro (30)"

ソースコードの可読性を上げたり、多言語化のためのメッセージを生成したりする場合に使えると思います。Python由来の機能です。

改行の入らないbase64のエンコーディング

[string].pack("m")はstringをbase64変換する機能ですが、メールに特化した古い仕様(RFC2045)に準拠しており、60桁ごとに勝手に改行を挿入していました。1.9で導入されたm0は、RFC4648に準拠したbase64変換を行うため、余計な改行が入りません。

コード9 改行の入らない base64 エンコーディング
puts "m:" ; puts ["a"*46].pack("m")
puts "m0:"; puts ["a"*46].pack("m0")
図3 コード9の実行結果
m:
YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh
YQ==
m0:
YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ==

Proc関係の増強

最後に、Procに追加された2つのメソッドを紹介します。

Proc#===

Proc#===はProc#callのaliasです。case文のマッチが===メソッドで判定されることと組み合わせることで、以下のようなコードが書けます。

コード10 whenの判定をProcで行う
even = proc {|x| x % 2 == 0 }  # 偶数なら真を返す 
odd  = proc {|x| x % 2 == 1 }  # 奇数なら真を返す

5.times do |i|
  case i
  when even then puts "even!"  # iが偶数の場合
  when odd  then puts "odd!"   # iが奇数の場合
  end
end
図4 コード10の実行結果
even!
odd!
even!
odd!
even!

いちいちproc呼び出しするので実行効率は悪いですが、記述性はあがります。使いどころを選べば使えるでしょう。

Proc#curry

Procをカリー化するProc#curryというメソッドが新設されました。カリー化とは、引数を一度に受け取るのではなく、1個ずつ受け取るようにすることです。

コード 11 Proc のカリー化
# 2つの引数をとって和を返すProc
plus = proc {|x, y| x + y }

# Procをカリー化した Procを返す
plus = plus.curry

# 第1引数だけ適用したProcを返す
add1 = plus[1]

# 得られたProcをcallすると第2引数となり、和が計算される
p add1[42]  #=> 43

まとめ

以上3回にわたって、Ruby1.9の新機能をひとめぐりしました。他にも紹介しきれなかった新機能がありますので、興味のある方はNEWSなどをご参照ください。この記事を読んで、1.9を使ってみたい!と思っていただけたら幸いです。

おすすめ記事

記事・ニュース一覧