Array#eachのベンチマーク結果

概要

Ruby 3.4.0 リリース

Array#eachのパフォーマンスが良くなったとのことなので、ベンチマークを取ってみました。

v3.3.5とv3.4.1の比較

prelude: |
  size = 1_000
  arr = Array.new(size) { rand(1..10000) }

benchmark:
  each: |
    arr.each { |v| v }

loop_count: 10000

ips

> bundle exec benchmark-driver benchmark/own/array_each.yml --rbenv '3.3.5; 3.3.5 --yjit; 3.4.1; 3.4.1 --yjit' -v

3.3.5: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin23]
 3.3.5 --yjit: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) +YJIT [arm64-darwin23]
 3.4.1: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin23]
 3.4.1 --yjit: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin23]
Calculating -------------------------------------
                          3.3.5   3.3.5 --yjit       3.4.1   3.4.1 --yjit 
                each    51.265k        57.865k     52.145k        58.364k i/s -     10.000k times in 0.195064s 0.172817s 0.191774s 0.171339s

Comparison:
                             each
        3.4.1 --yjit:     58363.8 i/s 
        3.3.5 --yjit:     57864.7 i/s - 1.01x  slower
               3.4.1:     52144.7 i/s - 1.12x  slower
               3.3.5:     51265.2 i/s - 1.14x  slower


> bundle exec benchmark-driver benchmark/own/array_each.yml --rbenv '3.3.5; 3.3.5 --yjit; 3.4.1; 3.4.1 --yjit' -v
3.3.5: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin23]
 3.3.5 --yjit: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) +YJIT [arm64-darwin23]
 3.4.1: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin23]
 3.4.1 --yjit: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin23]
Calculating -------------------------------------
                          3.3.5   3.3.5 --yjit       3.4.1   3.4.1 --yjit 
                each    51.982k        56.486k     51.148k        58.827k i/s -     10.000k times in 0.192376s 0.177036s 0.195510s 0.169989s

Comparison:
                             each
        3.4.1 --yjit:     58827.3 i/s 
        3.3.5 --yjit:     56485.7 i/s - 1.04x  slower
               3.3.5:     51981.5 i/s - 1.13x  slower
               3.4.1:     51148.3 i/s - 1.15x  slower

memory

実行のたびに変わるようです。

> bundle exec benchmark-driver benchmark/own/array_each.yml --rbenv '3.3.5; 3.3.5 --yjit; 3.4.1; 3.4.1 --yjit' -v -r memory
3.3.5: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin23]
 3.3.5 --yjit: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) +YJIT [arm64-darwin23]
 3.4.1: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin23]
 3.4.1 --yjit: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin23]
Calculating -------------------------------------
                          3.3.5   3.3.5 --yjit       3.4.1   3.4.1 --yjit 
                each    13.517M        13.828M     13.369M        14.156M bytes -     10.000k times

Comparison:
                             each
               3.4.1:  13369344.0 bytes 
               3.3.5:  13516800.0 bytes - 1.01x  larger
        3.3.5 --yjit:  13828096.0 bytes - 1.03x  larger
        3.4.1 --yjit:  14155776.0 bytes - 1.06x  larger

> bundle exec benchmark-driver benchmark/own/array_each.yml --rbenv '3.3.5; 3.3.5 --yjit; 3.4.1; 3.4.1 --yjit' -v -r memory
3.3.5: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin23]
 3.3.5 --yjit: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) +YJIT [arm64-darwin23]
 3.4.1: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin23]
 3.4.1 --yjit: ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin23]
Calculating -------------------------------------
                          3.3.5   3.3.5 --yjit       3.4.1   3.4.1 --yjit 
                each    13.402M        13.861M     14.352M        14.811M bytes -     10.000k times

Comparison:
                             each
               3.3.5:  13402112.0 bytes 
        3.3.5 --yjit:  13860864.0 bytes - 1.03x  larger
               3.4.1:  14352384.0 bytes - 1.07x  larger
        3.4.1 --yjit:  14811136.0 bytes - 1.11x  larger

最近

近況

なんとか。

エンジニアとして興味のあること

CodewarsをRubyで解きながら、自分の解答とベストアンサーをベンチマークで比較することに面白さを感じています。

ChatGPTに解説をしてもらうことでより勉強になります。

課金するか迷っています・・・

ベンチマーク

# Gemfile
gem 'benchmark_driver'
# /array_first_take.yml
prelude: |
  size = 1_000_000
  arr = Array.new(size) { rand(1..10000) }
benchmark:
  use_first_only: |
    100.times { arr.first }
  use_bracket: |
    100.times { arr[0] }
  use_slice : |
    100.times { arr.slice(0) }
loop_count: 10000
# 実行
bundle exec benchmark-driver ./array_first_take.yml -v 
3.3.5: ruby 3.3.5 (2024-09-03 revision ef084cc8f4) [arm64-darwin23]
Calculating -------------------------------------
           use_first   181.419k i/s -     10.000k times in 0.055121s (5.51μs/i)
           use_slice   191.732k i/s -     10.000k times in 0.052156s (5.22μs/i)
            use_take   194.799k i/s -     10.000k times in 0.051335s (5.13μs/i)

Comparison:
            use_take:    194798.9 i/s 
           use_slice:    191732.5 i/s - 1.02x  slower
           use_first:    181419.1 i/s - 1.07x  slower

面白い・・・

その他興味のあること

2年ほど前にカメラを買いました。

写真を撮りたい、現像ツールも使いこなしたい、そんな最近です。

熱しやすく冷めやすい性格なのが難点のど飴。

なんとか熱があるときに叩き上げたい、そんな来年にしたいです。

Ruby_Array_62

この記事は、以下のドキュメントを改変(自分なりに整理)して利用しています。

class Array (Ruby 3.1 リファレンスマニュアル)

インスタンスメソッド

zip(*lists) -> [ [object] ]
zip(*lists) {|v1, v2, ...| ...} -> nil
  • 自身と引数に渡した配列の各要素からなる配列の配列を生成して返す
    • 生成される配列の要素数はselfの要素数と同じ
  • ブロック付きで呼び出した場合は、selfと引数に渡した配列の各要素を順番にブロックに渡す
  • listsに配列以外のオブジェクトを指定した場合、to_aryメソッドによる暗黙の型変換を試み、それに応答できない場合はeachメソッドんによる暗黙の型変換を試みる。配列以外の(暗黙の型変換が行えない)オブジェクトを指定するとTypeErrorが発生する
irb(main):001:0> [1, 2, 3].zip(['a', 'b', 'c'])
=> [[1, "a"], [2, "b"], [3, "c"]]

irb(main):002:0> [1, 2, 3].zip(['a', 'b', 'c'], [4, 5, 6])
=> [[1, "a", 4], [2, "b", 5], [3, "c", 6]]

irb(main):003:0> [1, 2, 3, 4, 5].zip(['a', 'b', 'c'], [4, 5, 6])
=> [[1, "a", 4], [2, "b", 5], [3, "c", 6], [4, nil, nil], [5, nil, nil]]
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> brr = %w[a b c]
=> ["a", "b", "c"]

irb(main):003:0> crr = arr.zip(brr)
=> [[1, "a"], [2, "b"], [3, "c"]]

irb(main):004:0> brr[0].capitalize!
=> "A"

irb(main):005:0> crr
=> [[1, "A"], [2, "b"], [3, "c"]]
irb(main):001:0> [1, 2, 3].zip
=> [[1], [2], [3]]

irb(main):002:0> [1, 2, 3].zip([])
=> [[1, nil], [2, nil], [3, nil]]

irb(main):003:0> [].zip([1, 2, 3])
=> []

# memo
# この挙動は覚えておきたい
irb(main):001:0> [1, 2, 3].zip([4, 5, 6], [7, 8, 9]) { |arr| pp arr }
[1, 4, 7]
[2, 5, 8]                                                                                                
[3, 6, 9]                                                                                                
=> nil                                                                                                   
irb(main):002:0> [1, 2, 3].zip([4, 5, 6], [7, 8, 9]) { |v1, v2, v3| pp v2 }
4
5                                                                                                        
6                                                                                                        
=> nil
irb(main):001:0> [1, 2, 3].zip('a')
(irb):1:in `zip': wrong argument type String (must respond to :each) (TypeError)
        from (irb):1:in `<main>'
        from /Users/mfham/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `load'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `<main>'
self | other -> Array
  • 集合の和演算。両方の配列いずれかに含まれる要素をすべて含む新しい配列を返す
    • 重複する要素は取り除かれる
    • 重複判定はObject#eql?とObject#hashにより行われる
    • 新しい配列における要素の順はselfにおける要素の順と同じ
  • otherには配列以外のオブジェクトを指定した場合は to_ary メソッドによる暗黙の型変換を試みる。配列以外の(暗黙の型変換が行えない)オブジェクトを指定した場合TypeErrorが発生する
irb(main):001:0> [1, 2, 3, 1] | [7, 5, 3]
=> [1, 2, 3, 7, 5]

irb(main):002:0> [1, 2, 3, 1, 1.0] | ['1']
=> [1, 2, 3, 1.0, "1"]

irb(main):003:0> [1, 2, 3] | 'a'
(irb):1:in `|': no implicit conversion of String into Array (TypeError)
        from (irb):1:in `<main>'
        from /Users/mfham/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `load'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `<main>'

Ruby_Array_61

この記事は、以下のドキュメントを改変(自分なりに整理)して利用しています。

class Array (Ruby 3.1 リファレンスマニュアル)

インスタンスメソッド

uniq -> Array
uniq! -> self | nil
uniq {|item| ... } -> Array
uniq! {|item| ... } -> self | nil
  • uniqは配列から重複した要素を取り除いた新しい配列を返す
  • uniq!は削除を破壊的に行い、削除が行われた場合はselfを、そうでなければnilを返す
    • この挙動は覚えておきたい
  • 要素の重複判定はObject#eql?によって行われる
  • ブロックが与えられた場合、ブロックが返した値が重複した要素を取り除いた配列を返す
  • 要素を先頭から巡っていき、最初に出現したものが残る
irb(main):001:0> arr = [1, 2, 1]
=> [1, 2, 1]

irb(main):002:0> brr = arr.uniq
=> [1, 2]

irb(main):003:0> arr
=> [1, 2, 1]

irb(main):004:0> brr
=> [1, 2]
irb(main):001:0> arr = [1, 2, 1]
=> [1, 2, 1]

irb(main):002:0> brr = arr.uniq!
=> [1, 2]

irb(main):003:0> arr
=> [1, 2]

irb(main):004:0> brr
=> [1, 2]
irb(main):001:0> [1, 2, 3].uniq
=> [1, 2, 3]

irb(main):002:0> [1, 2, 3].uniq!
=> nil
irb(main):001:0> [1, 1.0, '1'].uniq
=> [1, 1.0, "1"]

irb(main):002:0> [1, 1.0, '1'].uniq { |v| v.to_i }
=> [1]
irb(main):001:0> v1 = 'e'
=> "e"

irb(main):002:0> v2 = 'e'
=> "e"

irb(main):003:0> arr = [v1, v2]
=> ["e", "e"]

irb(main):004:0> brr = arr.uniq
=> ["e"]

irb(main):005:0> v2.capitalize!
=> "E"

irb(main):006:0> brr
=> ["e"]

irb(main):007:0> v1.capitalize!
=> "E"

irb(main):008:0> brr
=> ["E"]

# memo
# 覚えておきたい
values_at(*selectors) -> Array
  • 引数で指定されたインデックスに対応する要素を配列で返す
    • インデックスに対応する値がなければnilが要素になる
  • インデックスは整数もしくは整数のRangeで指定する
irb(main):001:0> arr = %w[a b c d e]
=> ["a", "b", "c", "d", "e"]

irb(main):002:0> arr.values_at(0, 2, 4)
=> ["a", "c", "e"]

irb(main):003:0> arr.values_at(0, 4, 2)
=> ["a", "e", "c"]

irb(main):004:0> arr.values_at(0, 0, 0)
=> ["a", "a", "a"]

irb(main):005:0> arr.values_at(0, 4, 10)
=> ["a", "e", nil]

irb(main):006:0> arr.values_at(-4, -1, -100)
=> ["b", "e", nil]

irb(main):007:0> arr.values_at(0..2)
=> ["a", "b", "c"]

irb(main):008:0> arr.values_at(0..10)
=> ["a", "b", "c", "d", "e", nil, nil, nil, nil, nil, nil]

irb(main):009:0> arr.values_at(5..6)
=> [nil, nil]

irb(main):010:0> arr.values_at(6..7)
=> [nil, nil]

irb(main):011:0> arr.values_at(0, 1..4, 2)
=> ["a", "b", "c", "d", "e", "c"]

# memo
# 引数を前から順に処理している感じ
irb(main):001:0> arr = %w[a b c d e]
=> ["a", "b", "c", "d", "e"]

irb(main):002:0> arr.values_at(1.0)
=> ["b"]

irb(main):003:0> arr.values_at(1.8)
=> ["b"]

irb(main):004:0> arr.values_at('1.0')
(irb):4:in `values_at': no implicit conversion of String into Integer (TypeError)
        from (irb):4:in `<main>'                                      
        from /Users/mfham/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `load'  
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `<main>'

Ruby_Array_60

この記事は、以下のドキュメントを改変(自分なりに整理)して利用しています。

class Array (Ruby 3.1 リファレンスマニュアル)

インスタンスメソッド

transpose -> Array
  • 自身を行列と見立てて、行列の転置を行い、転置した配列を生成して返す
    • 空の配列に対しては空の配列を生成して返す
  • 一次元の配列に対してはTypeErrorが、各要素のサイズが不揃いな配列に対してはIndexErrorが発生する
# 1 2
# 3 4
# 5 6
# ↓
# 1 3 5
# 2 4 6

irb(main):001:0> arr = [[1, 2], [3, 4], [5, 6]]
=> [[1, 2], [3, 4], [5, 6]]

irb(main):002:0> arr.transpose
=> [[1, 3, 5], [2, 4, 6]]

irb(main):003:0> arr
=> [[1, 2], [3, 4], [5, 6]]

irb(main):004:0> arr.transpose.transpose
=> [[1, 2], [3, 4], [5, 6]]

irb(main):005:0> [].transpose
=> []
irb(main):001:0> arr = [['a', 'b'], ['c', 'd']]
=> [["a", "b"], ["c", "d"]]

irb(main):002:0> brr = arr.transpose
=> [["a", "c"], ["b", "d"]]

irb(main):003:0> arr[0][0].capitalize!
=> "A"

irb(main):004:0> arr
=> [["A", "b"], ["c", "d"]]

irb(main):005:0> brr
=> [["A", "c"], ["b", "d"]]
irb(main):001:0> [1, 2, 3].transpose
(irb):1:in `transpose': no implicit conversion of Integer into Array (TypeError)
        from (irb):1:in `<main>'                                            
        from /Users/mfham/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `load'        
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `<main>'  

irb(main):002:0> [[1, 2], [3, 4, 5]].transpose
(irb):2:in `transpose': element size differs (3 should be 2) (IndexError)
        from (irb):2:in `<main>'                                                         
        from /Users/mfham/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `load'                     
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `<main>' 
union(*other_arrays) -> Array
  • 集合の和演算。重複する要素は取り除かれる
    • 重複判定はObject#eql?とObject#hashによって行われる
irb(main):001:0> [1, 'a', 'b'].union([1, 1.0, '1', 'a', 'A'])
=> [1, "a", "b", 1.0, "1", "A"]

irb(main):002:0> [1, 'a', 'b'].union([1, 1.0, '1', 'a', 'A'], ['c'])
=> [1, "a", "b", 1.0, "1", "A", "c"]

irb(main):003:0> [1, 'a', 'b'].union([])
=> [1, "a", "b"]

irb(main):004:0> [1, 'a', 'b'].union
=> [1, "a", "b"]
irb(main):001:0> v = 'a'
=> "a"

irb(main):002:0> [v].union([v])
=> ["a"]

irb(main):003:0> [v].union([v.capitalize!])
=> ["A"]

irb(main):004:0> ['a'].union(['a'.capitalize!])
=> ["a", "A"]
[7] pry(main)> show-source Array#union

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: union(*arg1)
Number of lines: 29

static VALUE
rb_ary_union_multi(int argc, VALUE *argv, VALUE ary)
{
    int i;
    long sum;
    VALUE hash, ary_union;

    sum = RARRAY_LEN(ary);
    for (i = 0; i < argc; i++) {
        argv[i] = to_ary(argv[i]);
        sum += RARRAY_LEN(argv[i]);
    }

    if (sum <= SMALL_ARRAY_LEN) {
        ary_union = rb_ary_new();

        rb_ary_union(ary_union, ary);
        for (i = 0; i < argc; i++) rb_ary_union(ary_union, argv[i]);

        return ary_union;
    }

    hash = ary_make_hash(ary);
    for (i = 0; i < argc; i++) rb_ary_union_hash(hash, argv[i]);

    ary_union = rb_hash_values(hash);
    ary_recycle_hash(hash);
    return ary_union;
}

[8] pry(main)> show-source Array#|

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: |(arg1)
Number of lines: 20

static VALUE
rb_ary_or(VALUE ary1, VALUE ary2)
{
    VALUE hash, ary3;

    ary2 = to_ary(ary2);
    if (RARRAY_LEN(ary1) + RARRAY_LEN(ary2) <= SMALL_ARRAY_LEN) {
        ary3 = rb_ary_new();
        rb_ary_union(ary3, ary1);
        rb_ary_union(ary3, ary2);
        return ary3;
    }

    hash = ary_make_hash(ary1);
    rb_ary_union_hash(hash, ary2);

    ary3 = rb_hash_values(hash);
    ary_recycle_hash(hash);
    return ary3;
}

# memo
# Array#unionとArray#|の内部実装が若干違う
# Array#unionは配列を複数受け取るからそこの対応の差なのかな?内部実装はまだ読み解いていない

Ruby_Array_59

この記事は、以下のドキュメントを改変(自分なりに整理)して利用しています。

class Array (Ruby 3.1 リファレンスマニュアル)

インスタンスメソッド

to_a -> Array
  • selfを返す
    • ただしArray のサブクラスのインスタンスに対して呼ばれたときは自身をArrayに変換したものを返す
irb(main):001:0> arr = []
=> []

irb(main):002:0> arr.to_a.class
=> Array
irb(main):001:0> class Foo < Array
irb(main):002:0> end
=> nil

irb(main):003:0> foo = Foo.new
=> []

irb(main):004:0> foo.to_a.class
=> Array
to_ary -> self
  • selfをそのまま返す
irb(main):001:0> arr = []
=> []

irb(main):002:0> arr.to_a.class
=> Array
irb(main):001:0> class Foo < Array
irb(main):002:0> end
=> nil

irb(main):003:0> foo = Foo.new
=> []

irb(main):004:0> foo.to_ary.class
=> Foo
to_h -> Hash
to_h { block } -> Hash
  • selfを[key, value]のペアの配列として解析し、結果をHashにして返す
    • [[k1, v1], [k2, v2], ...]の形式
  • ブロックを指定すると、配列の各要素でブロックを呼び出しその結果をペアとして利用する
irb(main):001:0> [['a', 1]].to_h
=> {"a"=>1}

irb(main):002:0> [['a', 1], ['b', 2]].to_h
=> {"a"=>1, "b"=>2}

irb(main):003:0> [['a', 1], ['b', 2], ['c', nil]].to_h
=> {"a"=>1, "b"=>2, "c"=>nil}
irb(main):002:0> ['a', 1].to_h
(irb):2:in `to_h': wrong element type String at 0 (expected array) (TypeError)
        from (irb):2:in `<main>'          
        from /Users/mfham/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `load'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `<main>'

irb(main):001:0> [['a', 1], ['b']].to_h
(irb):1:in `to_h': wrong array length at 1 (expected 2, was 1) (ArgumentError)
        from (irb):1:in `<main>'                                                
        from /Users/mfham/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `load'            
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `<main>'
irb(main):001:0> [['a', 1], ['b', 2]].to_h { |k, v| [k, v] }
=> {"a"=>1, "b"=>2}

irb(main):002:0> [['a', 1], ['b', 2]].to_h { |k, v| [k.upcase, v + 100] }
=> {"A"=>101, "B"=>102}

メモ

  • to_aryを今知った

Ruby_Array_58

この記事は、以下のドキュメントを改変(自分なりに整理)して利用しています。

class Array (Ruby 3.1 リファレンスマニュアル)

インスタンスメソッド

take(n) -> Array
  • 配列の先頭からn要素を配列として返す
    • 自身を破壊的には変更しない
irb(main):001:0> arr = %w[a b c d e]
=> ["a", "b", "c", "d", "e"]

irb(main):002:0> arr.take(3)
=> ["a", "b", "c"]

irb(main):003:0> arr
=> ["a", "b", "c", "d", "e"]

irb(main):004:0> arr.take(0)
=> []

irb(main):005:0> arr.take(10)
=> ["a", "b", "c", "d", "e"]
irb(main):001:0> arr = %w[a b c d e]
=> ["a", "b", "c", "d", "e"]

irb(main):002:0> arr.take(1.0)
=> ["a"]

irb(main):003:0> arr.take(1.5)
=> ["a"]

irb(main):004:0> arr.take('1')
(irb):3:in `take': no implicit conversion of String into Integer (TypeError)
        from (irb):3:in `<main>'                            
        from /Users/mfham/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `load'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `<main>'

irb(main):005:0> arr.take(-1)
(irb):4:in `take': attempt to take negative size (ArgumentError)
        from (irb):4:in `<main>'                            
        from /Users/mfham/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `load'
        from /Users/mfham/.rbenv/versions/3.1.0/bin/irb:25:in `<main>'
pry(main)> show-source Array#take

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: take(arg1)
Number of lines: 9

static VALUE
rb_ary_take(VALUE obj, VALUE n)
{
    long len = NUM2LONG(n);
    if (len < 0) {
        rb_raise(rb_eArgError, "attempt to take negative size");
    }
    return rb_ary_subseq(obj, 0, len);
}


pry(main)> show-source Array#slice

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: slice(*arg1)
Number of lines: 9

VALUE
rb_ary_aref(int argc, const VALUE *argv, VALUE ary)
{
    rb_check_arity(argc, 1, 2);
    if (argc == 2) {
        return rb_ary_aref2(ary, argv[0], argv[1]);
    }
    return rb_ary_aref1(ary, argv[0]);
}


# memo
# 内部ではslice(0, n)と同じことをしていそうだ
# https://github.com/ruby/ruby/blob/a51f30c6712798fc07e57f692d0d0e5ccc59acf1/array.c#L1885-L1893
take_while -> Enumerator
take_while {|element| ... } -> Array
  • 配列の要素を順に偽になるまでブロックで評価し、最初に偽になった要素の手前の要素までを配列として返す
    • 自身を破壊的に変更しない
  • ブロックを省略した場合はEnumeratorを返す
irb(main):001:0> arr = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]

irb(main):002:0> arr.take_while { |v| v < 3 }
=> [1, 2]

irb(main):003:0> arr
=> [1, 2, 3, 4, 5]

irb(main):004:0> arr.take_while { |v| v < 1 }
=> []

irb(main):005:0> arr.take_while { |v| v < 10 }
=> [1, 2, 3, 4, 5]
irb(main):001:0> arr = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]

irb(main):002:0> enum = arr.take_while
=> #<Enumerator: ...>

irb(main):003:0> enum.each { |v| v < 3 }
=> [1, 2]

irb(main):004:0> arr.unshift(0)
=> [0, 1, 2, 3, 4, 5]

irb(main):005:0> enum
=> #<Enumerator: ...>

irb(main):006:0> enum.each { |v| v < 3 }
=> [0, 1, 2]

メモ

irbの表示が変わっている。要素が少ないときは3.0.0のような表示のほうが個人的にはわかりやすくてよかったかも。

$ ruby -v
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin18]

$ irb

irb(main):001:0> [1, 2, 3].keep_if
=> #<Enumerator: [1, 2, 3]:keep_if>

$ rbenv local 3.1.0

$ ruby -v
ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin18]

$ irb

irb(main):001:0> [1, 2, 3].keep_if
=> #<Enumerator: ...>