Misoca開発チームのeitoballです。RubyKaigi2015 の開催を指折り数えて待っている今日この頃です。株式会社Misoca は、「Additional Sponsor」として、RubyKaigi2015 に協賛しています。
今日は、今年のクリスマスにリリースが予定されているRuby 2.3 の初めてのプレビューリリースが、11月11日に リリース されたので、NEWS を読みながら、新しく追加される予定の機能などを試していきたいと思います。
言語の変更
frozen string literal pragma(文字列リテラル凍結のプラグマ)の追加
文字列リテラル を記述すると、#freeze
を呼び出すと同じように凍結されるプラグマが追加されます。
有効にするには、次のようにファイルの先頭にプラグマを書きます。
# frozen-string-literal: true
文字列リテラルを変更しようとすると例外(RuntimeError
)が発生します。
$ cat a.rb # frozen-string-literal: true 'a'.tr!('a', 'b') $ ruby a.rb a.rb:1:in `tr!': can't modify frozen String (RuntimeError) from a.rb:1:in `<main>'
インタプリタを実行する時、--enable=frozen-string-literal
と書くことでも有効にすることができます。(無効にするには、--disable=frozen-string-literal
)
$ ruby --enable=frozen-string-literal -e '"a".tr!("b", "c")' -e:1:in `tr!': can't modify frozen String (RuntimeError) from -e:1:in `<main>'
safe navigation operator (&.
)の追加
ActiveSupport の Object#try
のように呼び出し元のオブジェクトが nil
の場合でも、NoMethodError
が発生することなく、メソッドを呼び出すことができる演算子です。
a = 'a' a&.upcase # => "A" a = nil a&.upcase # => nil
Swift とか他の言語では、同じような演算子で、?.
がありますが、Ruby では、メソッド名の最後に ?
を使うことができるため、 &.
になりました
ActiveSupport で、Object#try!
というこれと似たような拡張があります。違いは、次の3つの場合です。
&.
の後にはメソッド名が必要
obj.try! {} # ok obj&. {} # syntax error, unexpected {, expecting '('
引数は実際に呼び出しが行われる場合に評価される
obj.try!(:foo, bar()) # bar() は、obj が、nil でも評価される obj&.foo(bar()) # bar() は、obj が、nil ではなく、foo を呼び出す際に評価される
属性値への代入が可能
obj.try!(:attr) += 1 # SyntaxError: unexpected tOP_ASGN, expecting end-of-input obj&.attr += 1 # ok
did_you_mean gem の追加
did_you_mean gem が、bundled gems に追加されます。この gem は、NameError
や NoMethodError
が発生する場合において、本来、使おうとしていたと考えられる変数名やメソッド名を提案してくれます。
irb> obj = Object.new => #<Object:0x007ffa83827a00> irb> oba.to_s NameError: undefined local variable or method `oba' for main:Object Did you mean? obj from (irb):2 from .../bin/irb:11:in `<main>' irb> Class.to_t NoMethodError: undefined method `to_t' for Class:Class Did you mean? to_s from (irb):3 from .../bin/irb:11:in `<main>'
組み込みライブラリの変更
ARGF
ARGF.read_nonblock
での、exception: false
のサポート
IO#read_nonblock
のように読み込み時に Errno::EAGAIN
、 Errno::EWOULDBLOCK
が発生する代わりに :wait_readable
を返すかどうかを指定します。また、true
を指定した場合は既に EOF に達していれば EOFError
の代わりに nil
を返します。[Feature #11358]( 参考: instance method IO#read_nonblock
)
Array
Array#bsearch_index
の追加
ary#bsearch_index { |x| bool } -> int or nil
二分木探索(バイナリーサーチ)で、与えた条件に合致する要素の位置を返します。合致する要素がない場合、 nil
を返します。[Feature #10730]
[1, 2, 3, 4, 5].bsearch_index { |x| x > 2 } # => 2 [1, 2, 3, 4, 5].bsearch_index { |x| x > 6 } # => nil
Array#dig
の追加
ary#dig(idx, ...) -> object
ネストした配列から要素を取得します。a[1][2]
のように #[]
を使って、要素を取得する場合、a[1]
が nil
の場合、 NoMethodError
になってしまいますが、a.dig(1, 2)
では、例外が発生しないで、 nil
が返ります。[Feature #11643]
[[0, 1], [2, 3], [4, 5]].dig(1, 1) # => 2 [[0, 1], [2, 3], [4, 5]].dig(3, 1) # => nil
Enumerable
Enumerable#grep_v
の追加
enum.grep_v(pattern) -> array
enum.grep_v(pattern) { |obj| block } -> array
Enumerable#grep
の反対で、引数のパターンに合致しない要素を配列で返します。[Feature #11049]
['aa', 'bb', 'cc', 'dd', 'ee'].grep_v(/[bc]/) # => ["aa", "dd", "ee"] ['aa', 'bb', 'cc', 'dd', 'ee'].grep_v(/[bc]/) # => ["bb", "cc"] ['aa', 'bb', 'cc', 'dd', 'ee'].grep_v(/[bc]/) { |item| item.upcase } # => ["BB", "CC"] ['aa', 'bb', 'cc', 'dd', 'ee'].grep_v(/./) # => []
grep
というコマンドラインプログラムで、-v
(inVerse)というオプションは、パターンに合致しない行を出力しますが、これと同じ挙動です。
Enumerable#chunk_while
の追加
enum.chunk_while { |elt_before, elt_after| bool } -> an_enumerator
条件に合致する要素をグループに分けて配列にします。[Feature #10769]
例えば、昇順にソートされた整数の配列から、連続する整数で分ける場合は、
irb> [1, 2, 4, 9, 10, 11, 12, 15, 16, 19, 20, 21].chunk_while{|i, j| i + 1 == j}.each { |c| p c } [1, 2] [4] [9, 10, 11, 12] [15, 16] [19, 20, 21] => nil
File
File.mkfifo
の追加
File.mkfifo(file_name, mode) -> 0
名前付きパイプ ファイルを作成します。[Feature #11536]
file_name
で、作成するファイル名、mode
で、ファイルのパーミッションを指定します。
irb
で次のように名前付きパイプファイルに書き込むと
irb> File.mkfilo('fifo_test', 0600) => 0 irb> while true; File.write('fifo_test', Time.now.to_s); sleep 10 end
別の irb
にて、読み込むことができます。
irb> while true; p File.read('fifo_test'); sleep 10; end "2015-11-11 11:11:00 +0900" "2015-11-11 11:11:10 +0900" "2015-11-11 11:11:20 +0900" ...
File::TMPFILE
の追加
Linux 3.11 以上などで、O_TMPFILE
が定義されている場合、File.new
などで、このフラグを使うことで、指定したディレクトリに一時ファイルを作成することができます。作成されたファイルは、#close
を呼び出した時点で削除されます。
f = File.open('/tmp', File::TMPFILE | File::CREAT | File::RDWR) f.puts('Hello, world!') f.close # => ここで削除される
Hash
Hash#fetch_values
の追加
hsh#fetch(key, ...) -> array
hsh#fetch(key, ...) { |key| block } -> array
ハッシュから指定したキーに関連づけられた値の配列を取得します。#values_at
と異なり、該当するキーが登録されていない場合、例外KeyError
が発生します。ブロックが与えられていればそのブロックを評価した値を返します。[Feature #10017]
{a: 1, b: 2, c: 3}.fetch_values(:a, :c) # => [1, 3] {a: 1, b: 2, c: 3}.fetch_values(:d) # => KeyError: key not found: :d {a: 1, b: 2, c: 3}.fetch_values(:d) { |k| nil } # => nil
Hash#dig
の追加
Hash#dig(key, ...) -> object
Array#dig
と同じようにネストしたハッシュから要素を取得します。a[1][2]
のように #[]
を使って、要素を取得する場合、a[1]
が nil
の場合、 NoMethodError
になってしまいますが、a.dig(1, 2)
では、例外が発生しないで、 nil
が返ります。[Feature #11643]
配列とハッシュを混在させてアクセスすることも可能です。
{a: {b: 2, c: 3}, d: 4}.dig(:a, :b) # => 2 {a: {b: 2, c: 3}, d: 4}.dig(:e, :b) # => nil [1, {b: 2, c: 3}, 4, 5].dig(1, :c) # => 3
Hash#<=
Hash#<
Hash#>=
Hash#>
の追加
hash <= other -> true or false
自身(hash
)が other
と同じキーと関連づけられた値を含んでいるならば true
を返します。そうでない場合には false
を返します。
{a: 1} <= {a: 1, b: 2} #=> true {a: 1, b: 2} <= {a: 1, b: 2} #=> true {c: 3} <= {a: 1, b: 2} #=> false {a: 1, c: 3} <= {a: 1, b: 2} #=> false {a: 1, b: 2, c: 3} <= {a: 1, b: 2} #=> false
hash < other -> true or false
#<=
とほぼ同じですが、other
と全く同じキーと関連づけられた値を含んでいる場合はfalse
を返します。
{a: 1} < {a: 1, b: 2} #=> true {a: 1, b: 2} < {a: 1, b: 2} #=> false
hash >= other -> true or false
自身(hash
)に other
と同じキーと関連づけられた値を含んでいる場合には true
を返します。そうでない場合には false
を返します。
{a: 1, b: 2} >= {a: 1} #=> true {a: 1, b: 2} >= {a: 1, b: 2} #=> true {a: 1, b: 2} >= {c: 3} #=> false {a: 1, b: 2} >= {a: 1, c: 3} #=> false {a: 1, b: 2} >= {a: 1, b: 2, c: 3} #=> false
hash > other -> true or false
#>=
とほぼ同じですが、other
と全く同じキーと関連づけられた値を含んでいる場合はfalse
を返します。
{a: 1, b: 2} > {a: 1} #=> true {a: 1, b: 2} > {a: 1, b: 2} #=> false
Hash#to_proc
の追加
hsh#to_proc ->an_proc
self
に対応する Proc
オブジェクトを返します。Proc#call
の第一引数で指定されるキーに関連づけられた値を self
から取得します。[Feature #11653]
hsh = {a: 1, b: 2, c: 3} prc = hsh.to_proc prc.call(:a) # => 1 prc.call(:d) # => nil %i(a b).map(&hsh) # => [1, 2] %i(c d).map(&hsh) # => [3, nil]
IO
File::SHARE_DELETE
の追加
Windows において、誰かが開いているファイルのファイル名を変更したりする場合にFile.new
での 第2引数の mode
にこのフラグを指定します。Windows API の CreateFile
に FILE_SHARE_DELETE
フラグをつけると同等のようです。 [Feature #11218]
f = File.new('newfile', File::CREAT | File::RDWR | File::SHARE_DELETE) # => <File:newfile>
flags
オプションの追加
IO.new
や IO.open
の第3引数に指定できるオプション引数に File::EXCL
や File::SHARE_DELETE
など指定できる flags
オプションが追加されました。 [Feature #12253]
IO.open(filename, 'w', flags: File::EXCL) IO.open(filename, 'w', flags: File::SHARE_DELETE)
Kernel
Kernel#loop
が返す値の変更
Kernel#loop
のブロック内で、StopIteration
が発生する場合、その例外の値(StopIteration#result
)を返すようになりました。[Feature #11498]
enum = Enumerator.new do |yielder| yielder << 1 yielder << 2 :ok end loop do enum.next end # => :ok
Module
Module#deprecate_constant
の追加
定数(constant)が参照される場合、非推奨(deprecate)であることを警告されるようにします。[Feature #11398]
require 'timeout' TimeoutError # warning: constant ::TimeoutError is deprecated Foo = 1 Bar = Foo class Object deprecate_constant :Bar end Bar # warning: constant ::Bar is deprecated
NameError
NameError#receiver
の追加
NameError
が発生する場合、原因となる定数や変数の参照元(レシーバー)オブジェクトを返します。[Feature #10881]
obj = Object.new begin obj.no_such_name rescue NameError => e e.receiver # => #<Object:0x00000000> e.name # => :no_such_name end
Numeric
Numeric#positive?
Numeric#negative?
の追加
positive? -> bool
自身が、正の数の場合、true
を返します。そうでない場合、 false
を返します。
1.positive? # => true 2.0.positive? # => true (1/3r).positive? # => true -4.positive? # => false
negative? -> bool
自身が、負の数の場合、true
を返します。そうでない場合、 false
を返します。
-1.negative? # => true -2.0.negative? # => true (-1/3r).negative? # => true 4.negative? # => false
ActiveSupport でも導入される予定です。 Numeric#positive?
, Numeric#negative?
Proc
Proc#call
の最適化
Proc#call
が最適化され、#call
メソッド自身は、メソッドフレームに追加しないで、ブロックを直接呼び出すようになりました。Proc#[]
、Proc#===
、そして、Proc#yield
も同様に最適化されています。Kernel.set_trace_func
や TracePoint
でもトレースされないようになります。 [Feature #11569]
$ ruby -ve 'Proc.new { puts caller(0..2) }.call' ruby 2.3.0preview1 (2015-11-11 trunk 52539) [...] -e:1:in `block in <main>' -e:1:in `<main>'
現在(2.2.3)では、以下のようになり、2.3.0 では、Proc#call
の呼び出しが減っていることがわかります。
$ ruby -ve 'Proc.new { puts caller(0..2) }.call' ruby 2.2.3p173 (2015-08-18 revision 51636) [...] -e:1:in `block in <main>' -e:1:in `call' -e:1:in `<mail>'
Thread
Thread#name
Tread#name=
の追加
スレッド(thread)の名前を取得したり、付けたりします。#inspect
を呼び出す場合に付けた名前も表示されます。 [Feature #11251]
Thread#name -> string
Thread#name=(name) -> string
thr1 = Thread.new { puts 'with name' } thr1.name = 'with_name' thr2 = Thread.new { puts 'without name' } thr1.name # => "with_name" thr2.name # => nil p thr1 # => #<Thread:0x007f993c0878e8@_name_@a.rb:1 dead> p thr2 # => #<Thread:0x007f993c0878e8@a.rb:2 dead>
この preview1 の段階では、バージョン2.3で、追加や変更になる言語機能や組み込みライブラリは、少なくともバージョン2.2で動いているコードには、ほとんど全く影響を与えないという印象を受けます。
反響があれば、標準ライブラリの変更についての記事も書きたいと思います。
修正履歴
- [2015-11-20 22:00] "safe navigation operator" の説明で、「ActiveSupport で、
Object#try
...」を「ActiveSupport で、Object#try!
...」に修正しました。また、その後の1番目と3番目のコード例もtry!
を使うようにしました。( https://twitter.com/n0kada/status/667649792973996032 )