Ruby 2.4 の変更内容 その2 - 組み込みライブラリ

Ruby 2.4の変更内容の組み込みライブラリ編です。



数値

Fixnum と Bignum が Integer に

Fixnum と Bignum は Ruby プログラムのレベルでは Integer として見えるようになりました。 Fixnum, Bignum を参照すると warning になります。

% irb
irb(main):001:0> Fixnum
(irb):1: warning: constant ::Fixnum is deprecated
=> Integer
irb(main):002:0> Bignum
(irb):2: warning: constant ::Bignum is deprecated
=> Integer

Float#ceil, Float#floor, Float#truncate にオプション引数で桁数を指定可

1234.5678.ceil(2)   #=> 1234.57
1234.5678.ceil(-2)  #=> 1300

Integer#ceil, Integer#floor, Integer#truncate にオプション引数で桁数を指定可

Float と同じです。

Integer#digits 追加

各桁の数値を配列で返します。何進法かを引数で指定できます。

12345.digits    #=> [5, 4, 3, 2, 1]
12345.digits(2) #=> [1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1]

Float#round, Integer#round, Rational#round にキーワード引数 half 追加

half は、中央値の丸め方向を :up, :even, :down で指定します。

1.5.round(half: :up)    #=> 2   :upは0から遠い方向
-1.5.round(half: :up)   #=> -2
1.5.round(half: :even)  #=> 2   :evenはもっとも近い偶数
-1.5.round(half: :even) #=> -2
1.5.round(half: :down)  #=> 1   :downは0に近い方向
-1.5.round(half: :down) #=> -1

Numeric#finite?, Numeric#infinite? 追加

finite? は絶対値が有限値の場合に true を返し、そうでなければ false を返します。 infinite? は絶対値が負の無限大の場合に -1, 正の無限大の場合に 1 を、有限値の場合に nil を返します。

123.finite?                #=> true
123.infinite?              #=> nil
Float::INFINITY.finite?    #=> false
Float::INFINITY.infinite?  #=> 1

文字列/シンボル/正規表現

MatchData#named_captures 追加

"2018-01-21".match(/(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/).named_captures
#=> {"year"=>"2018", "month"=>"01", "day"=>"21"}

MatchData#values_at が名前付きキャプチャをサポート

"2018-01-21".match(/(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/).values_at(:year, :month, :day)'
#=> ["2018", "01", "21"]

Regexp#match?, String#match? 追加

true /false だけを返します。=~ と異なり、MatchData や括弧に適合した部分文字列は生成されません。

/(b)/.match? 'abc' #=> true
$~                 #=> nil
$1                 #=> nil
/(b)/ =~ 'abc'     #=> 1
$~                 #=> #<MatchData "b" 1:"b">
$1                 #=> "b"

String#casecmp?, Symbol#casecmp? 追加

文字列の大文字小文字を無視して比較し、真偽値を返します。

casecmp と似ていますが、casecmp と異なり、ASCII以外の文字も大文小文字の違いを無視します。

"aBcD".casecmp("abcd")   #=> 0
"aBcD".casecmp?("abcd")  #=> true

"".casecmp("")       #=> -1
"".casecmp?("")      #=> true

String#concat, String#prepend が複数引数可能

"a".concat("b", "c", "d")   #=> "abcd"
"a".prepend("b", "c", "d")  #=> "bcda"

String#each_line, String#lines にキーワード引数 chomp 追加

真を指定すると行末の "\n", "\r\n" を取り除きます。

"abc\nxyz\r\n".lines               #=> ["abc\n", "xyz\r\n"]
"abc\nxyz\r\n".lines(chomp: true)  #=> ["abc", "xyz"]

String#unpack1 追加

String#unpack の結果の最初の要素を返します。

"YWJj".unpack("m")   #=> ["abc"]
"YWJj".unpack1("m")  #=> "abc"

大文字小文字変換メソッドがASCII以外の文字も変換する

対象のメソッドは String#upcase, String#downcase, String#capitalize, String#swapcase, String#upcase!, String#downcase!, String#capitalize!, String#swapcase!, Symbol#upcase, Symbol#downcase, Symbol#capitalize, Symbol#swapcase です。

:ascii オプションを指定するとASCII文字だけが対象になります。

"abcabc".upcase          #=> "ABCABC"
"abcabc".upcase(:ascii)  #=> "ABCabc"

String.new にキーワード引数 capacity 追加

初期内部バッファの大きさを指定します。文字列がある程度以上大きくなることが最初からわかっている場合は、大きめの値を指定してStringを作成しておくことで、内部的なメモリの再割り当ての繰り返しを避けることができます。

% strace -e mremap ruby -e 's=String.new; 1000000.times{s.concat("a")}'
mremap(0x7fcf9b0d7000, 397312, 790528, MREMAP_MAYMOVE) = 0x7fcf992da000
mremap(0x7fcf992da000, 790528, 1576960, MREMAP_MAYMOVE) = 0x7fcf99159000
+++ exited with 0 +++

% strace -e mremap ruby -e 's=String.new(capacity: 1000000); 1000000.times{s.concat("a")}'
+++ exited with 0 +++

Symbol#match が MatchData を返す

String#match に合わせたようです。

配列/Hash/Enumerable/Enumerator

Array#concat が複数引数可能

# Ruby 2.3
[1].concat([2], [3])
#=> `concat': wrong number of arguments (given 2, expected 1) (ArgumentError)

# Ruby 2.4
[1].concat([2], [3]) #=> [1, 2, 3]

Array#max, Array#min 追加

今までは Array に組み込まれているモジュールの Enumerable のメソッドが使われていましたが、Array クラスに実装されました。

普通に使ってる分には影響ないですが、Enumerable#max, #min を再定義していたりする場合は、それが効かなくなります。

module Enumerable
  def max
    999
  end
end

# Ruby 2.3
[1, 2, 3].max  #=> 999

# Ruby 2.4
[1, 2, 3].max  #=> 3

Array#pack にキーワード引数 buffer 追加

Array#pack が :buffer で指定された文字列オブジェクトを使用します。

buf = "0123456789"
["hoge"].pack("m", buffer: buf)
buf  #=> "aG9nZQ==\n"

Array#sum 追加

[1, 2, 3].sum  #=> 6

Hash#compact, #compact! 追加

値がnilの要素を取り除いたHashを返します。

{a: 0, b: false, c: nil}.compact  #=> {a: 0, b: false}

Hash#transform_values, #transform_values! 追加

Hashの値を変換します。

{a: "abc", b: "xyz"}.transform_values(&:upcase)  #=> {a: "ABC", b: "XYZ"}

ブロックなしの Enumerable#chunk が Enumerator を返す

# Ruby 2.3
[1, 2, 3].chunk  #=> in `chunk': no block given (ArgumentError)

# Ruby 2.4
[1, 2, 3].chunk  #=> #<Enumerator: [1, 2, 3]:chunk>

Enumerable#sum 追加

{a: 1, b: 2, c: 3}.sum{|k, v| v}  #=> 6

Enumerable#uniq 追加

{a: 1, b: 2, c: 1, d: 2}.uniq{|k, v| v}  #=> [[:a, 1], [:b, 2]]

Enumerator::Lazy#chunk_while, #uniq 追加

Enumerable#chunk_while, #uniq の遅延評価版です。

IO/ファイル

Dir.empty? 追加

ディレクトリが空の場合に真を返します。

Dir.empty?('/')  #=> false

require 'tmpdir'
Dir.mktmpdir{|d| Dir.empty?(d)}  #=> true

File.empty? 追加

ファイルが0バイトであれば真を返します。File.zero? と同じです。

IO#gets, IO#readline, IO#each_line, IO#readlines, IO.foreach にキーワード引数 chomp 追加

真を指定すると行末の "\n", "\r\n" を取り除きます。

File.write("text", "abc\nxyz\r\n")
IO.readlines("text")               #=> ["abc\n", "xyz\r\n"]
IO.readlines("text", chomp: true)  #=> ["abc", "xyz"]

StringIO#gets, StringIO#readline, StringIO#each_line, StringIO#readlines にキーワード引数 chomp 追加

IO と同様です。

その他

Comparable#clamp 追加

範囲内の近い値を返します。

5.clamp(1, 10)  #=> 5
0.clamp(1, 10)  #=> 1
99.clamp(1, 10) #=> 10

Kernel#clone にキーワード引数 freeze 追加

偽を指定すると freeze されているオブジェクトの複製時に freeze されていないオブジェクトを返します。

s = "abc".freeze
s2 = s.clone
s3 = s.clone(freeze: false)
s.frozen?   #=> true
s2.frozen?  #=> true
s3.frozen?  #=> false

123.clone(freeze: false)  #=> in `clone': can't unfreeze Integer (ArgumentError)

Process::CLOCK_MONOTONIC_RAW_APPROX, Process::CLOCK_UPTIME_RAW, Process::CLOCK_UPTIME_RAW_APPROX をサポート (macOS 10.12 用)

詳しくないのでパス。

RubyVM::Env 削除

詳しくないのでパス。

Thread#report_on_exception, Thread.report_on_exception 追加

真に設定すると、スレッドが例外によって終了した場合にバックトレースを標準エラー出力に出力します。 デフォルトは false (2.5 でデフォルトが true に変更されました)。

TracePoint#callee_id 追加

詳しくないのでパス。

Warning 追加

Ruby が警告を出力する際に、Warning.warn を使用するようになりました。 Warning.warn を上書きすることで、警告出力処理を制御できるようになります。