Ruby_Array_48

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

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

インスタンスメソッド

replace(another) -> self
  • 配列の内容を配列anotherの内容で置き換える
  • 配列以外の(暗黙の型変換が行えない)オブジェクトを指定した場合、TypeErrorが発生する
irb(main):001:0> arr = ['a', 1]
=> ["a", 1]

irb(main):002:0> brr = ['b', 2]
=> ["b", 2]

irb(main):003:0> arr.replace(brr)
=> ["b", 2]

irb(main):004:0> arr
=> ["b", 2]

irb(main):005:0> brr[0].capitalize!
=> "B"

irb(main):006:0> brr << 'c'
=> ["B", 2, "c"]

irb(main):007:0> arr
=> ["B", 2]
irb(main):001:0> [].replace('a')
Traceback (most recent call last):
        5: from /Users/mfham/.rbenv/versions/3.0.0/bin/irb:23:in `<main>'
        4: from /Users/mfham/.rbenv/versions/3.0.0/bin/irb:23:in `load'
        3: from /Users/mfham/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
        2: from (irb):1:in `<main>'
        1: from (irb):1:in `replace'
TypeError (no implicit conversion of String into Array)

Ruby_Array_47

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

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

インスタンスメソッド

repeated_combination(n) { |c| ... } -> Array
repeated_combination(n) -> Enumerator
  • サイズnの重複組合せをすべて作成し、それを引数としてブロックを実行
    • 組み合わせの順序は保障されない
    • nは生成される配列のサイズ。整数で指定する。整数以外の(暗黙の型変換が行えない)オブジェクトを指定するとTypeError
  • ブロックなしで呼び出されると、組み合わせを生成するEnumeratorオブジェクトを返す
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> arr.repeated_combination(1).to_a
=> [[1], [2], [3]]

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

irb(main):004:0> arr.repeated_combination(3).to_a
=> [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 2], [1, 2, 3], [1, 3, 3], [2, 2, 2], [2, 2, 3], [2, 3, 3], [3, 3, 3]]

irb(main):005:0> arr.repeated_combination(4).to_a
=> [[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3], [1, 1, 2, 2], [1, 1, 2, 3], [1, 1, 3, 3], [1, 2, 2, 2], [1, 2, 2, 3], [1, 2, 3, 3], [1, 3, 3, 3], [2, 2, 2, 2], [2, 2, 2, 3], [2, 2, 3, 3], [2, 3, 3, 3], [3, 3, 3, 3]]

irb(main):00:0> arr.repeated_combination(0).to_a
=> [[]]

# memo
# nが0のときの挙動は覚えておきたい
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> arr.repeated_combination(1.0).to_a
=> [[1], [2], [3]]

irb(main):003:0> arr.repeated_combination('1').to_a
Traceback (most recent call last):
        5: from /Users/mfham/.rbenv/versions/3.0.0/bin/irb:23:in `<main>'
        4: from /Users/mfham/.rbenv/versions/3.0.0/bin/irb:23:in `load'
        3: from /Users/mfham/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
        2: from (irb):3:in `<main>'
        1: from (irb):3:in `repeated_combination'
TypeError (no implicit conversion of String into Integer)
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> brr = arr.repeated_combination(2)
=> #<Enumerator: [1, 2, 3]:repeated_combination(2)>

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

irb(main):004:0> arr[2] = 5
=> 5

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

irb(main):006:0> brr
=> #<Enumerator: [1, 2, 5]:repeated_combination(2)>

irb(main):007:0> brr.each { |v| pp v }
[1, 1]
[1, 2]
[1, 5]
[2, 2]
[2, 5]
[5, 5]
=> [1, 2, 5]
repeated_permutation(n) { |p| ... } -> Array
repeated_permutation(n) -> Enumerator
  • サイズnの重複順列をすべて作成し、それを引数としてブロックを実行
    • 順列の順序は保障されない
    • nは生成される配列のサイズ。整数で指定する。整数以外の(暗黙の型変換が行えない)オブジェクトを指定するとTypeError
  • ブロックなしで呼び出されると、組み合わせを生成するEnumeratorオブジェクトを返す
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> arr.repeated_permutation(1).to_a
=> [[1], [2], [3]]

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

irb(main):004:0> arr.repeated_permutation(3).to_a
=> [[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 1], [1, 2, 2], [1, 2, 3], [1, 3, 1], [1, 3, 2], [1, 3, 3], [2, 1, 1], [2, 1, 2], [2, 1, 3], [2, 2, 1], [2, 2, 2], [2, 2, 3], [2, 3, 1], [2, 3, 2], [2, 3, 3], [3, 1, 1], [3, 1, 2], [3, 1, 3], [3, 2, 1], [3, 2, 2], [3, 2, 3], [3, 3, 1], [3, 3, 2], [3, 3, 3]]

irb(main):005:0> arr.repeated_permutation(4).to_a
=> [[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3], [1, 1, 2, 1], [1, 1, 2, 2], [1, 1, 2, 3], [1, 1, 3, 1], [1, 1, 3, 2], [1, 1, 3, 3], [1, 2, 1, 1], [1, 2, 1, 2], [1, 2, 1, 3], [1, 2, 2, 1], [1, 2, 2, 2], [1, 2, 2, 3], [1, 2, 3, 1], [1, 2, 3, 2], [1, 2, 3, 3], [1, 3, 1, 1], [1, 3, 1, 2], [1, 3, 1, 3], [1, 3, 2, 1], [1, 3, 2, 2], [1, 3, 2, 3], [1, 3, 3, 1], [1, 3, 3, 2], [1, 3, 3, 3], [2, 1, 1, 1], [2, 1, 1, 2], [2, 1, 1, 3], [2, 1, 2, 1], [2, 1, 2, 2], [2, 1, 2, 3], [2, 1, 3, 1], [2, 1, 3, 2], [2, 1, 3, 3], [2, 2, 1, 1], [2, 2, 1, 2], [2, 2, 1, 3], [2, 2, 2, 1], [2, 2, 2, 2], [2, 2, 2, 3], [2, 2, 3, 1], [2, 2, 3, 2], [2, 2, 3, 3], [2, 3, 1, 1], [2, 3, 1, 2], [2, 3, 1, 3], [2, 3, 2, 1], [2, 3, 2, 2], [2, 3, 2, 3], [2, 3, 3, 1], [2, 3, 3, 2], [2, 3, 3, 3], [3, 1, 1, 1], [3, 1, 1, 2], [3, 1, 1, 3], [3, 1, 2, 1], [3, 1, 2, 2], [3, 1, 2, 3], [3, 1, 3, 1], [3, 1, 3, 2], [3, 1, 3, 3], [3, 2, 1, 1], [3, 2, 1, 2], [3, 2, 1, 3], [3, 2, 2, 1], [3, 2, 2, 2], [3, 2, 2, 3], [3, 2, 3, 1], [3, 2, 3, 2], [3, 2, 3, 3], [3, 3, 1, 1], [3, 3, 1, 2], [3, 3, 1, 3], [3, 3, 2, 1], [3, 3, 2, 2], [3, 3, 2, 3], [3, 3, 3, 1], [3, 3, 3, 2], [3, 3, 3, 3]]

irb(main):006:0> arr.repeated_permutation(0).to_a
=> [[]]

# memo
# nが0のときの挙動は覚えておきたい
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> arr.repeated_permutation(1.0).to_a
=> [[1], [2], [3]]

irb(main):003:0> arr.repeated_permutation('1').to_a
Traceback (most recent call last):
        7: from /Users/mfham/.rbenv/versions/3.0.0/bin/irb:23:in `<main>'
        6: from /Users/mfham/.rbenv/versions/3.0.0/bin/irb:23:in `load'
        5: from /Users/mfham/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
        4: from (irb):3:in `<main>'
        3: from (irb):3:in `to_a'
        2: from (irb):3:in `each'
        1: from (irb):3:in `repeated_permutation'
TypeError (no implicit conversion of String into Integer)
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

irb(main):002:0> brr = arr.repeated_permutation(1)
=> #<Enumerator: [1, 2, 3]:repeated_permutation(1)>

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

irb(main):004:0> arr[2] = 5
=> 5

irb(main):005:0> brr
=> #<Enumerator: [1, 2, 5]:repeated_permutation(1)>
irb(main):006:0> brr.each { |v| pp v }
[1]
[2]
[5]
=> [1, 2, 5]

メモ

Ruby_Array_46

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

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

インスタンスメソッド

reject -> Enumerator
reject {|item| ... } -> [object]
  • 各要素に対してブロックを評価し、その要素が偽であった要素を集めた新しい配列を返す
  • ブロックを省略した場合はEnumeratorを返す
irb(main):001:0> [1, 2, 3, 4, 5, 6].reject { |i| i % 2 == 0 }
=> [1, 3, 5]
irb(main):001:1> enum = [1, 2, 3, 4, 5, 6].reject
=> #<Enumerator: [1, 2, 3, 4, 5, 6]:reject>

irb(main):002:0> enum.each { |v| v % 2 != 0 }
=> [2, 4, 6]
irb(main):001:0> arr = %w[a b c]
=> ["a", "b", "c"]

irb(main):002:0> enum = arr.reject
=> #<Enumerator: ["a", "b", "c"]:reject>

irb(main):003:0> arr[1].upcase!
=> "B"

irb(main):004:0> enum
=> #<Enumerator: ["a", "B", "c"]:reject>

irb(main):005:0> enum.each { |v| v == 'B' }
=> ["a", "c"]
[2] pry(main)> show-source Array#select

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: select()
Number of lines: 15

static VALUE
rb_ary_select(VALUE ary)
{
    VALUE result;
    long i;

    RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
    result = rb_ary_new2(RARRAY_LEN(ary));
    for (i = 0; i < RARRAY_LEN(ary); i++) {
        if (RTEST(rb_yield(RARRAY_AREF(ary, i)))) {
            rb_ary_push(result, rb_ary_elt(ary, i));
        }
    }
    return result;
}
[3] pry(main)> show-source Array#reject

From: array.c (C Method):
Owner: Array
Visibility: public
Signature: reject()
Number of lines: 10

static VALUE
rb_ary_reject(VALUE ary)
{
    VALUE rejected_ary;

    RETURN_SIZED_ENUMERATOR(ary, 0, 0, ary_enum_length);
    rejected_ary = rb_ary_new();
    ary_reject(ary, rejected_ary);
    return rejected_ary;
}

# memo
# 条件を反転させたselectと書かれていたが、内部コードはだいぶ違う

メモ

Ruby_Array_45

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

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

インスタンスメソッド

rassoc(obj) -> Array | nil
  • 自身が配列の配列であると仮定して、要素の配列でインデックス1の要素がobjに等しいものを検索し、見つかった最初の要素を返す。
    • 見つからなければnilを返す
    • 比較は==演算子を使って行われる
irb(main):001:0> arr = [['a', 1], ['b', 2], ['c', 2]]
=> [["a", 1], ["b", 2], ["c", 2]]

irb(main):002:0> arr.rassoc(2)
=> ["b", 2]

irb(main):003:0> arr.rassoc(3)
=> nil

# memo
# 一番最初に見つかったものを返すので、['c', 2]は返されない
irb(main):001:0> arr = [['a', 1], ['b', 2], ['c', 2]]
=> [["a", 1], ["b", 2], ["c", 2]]

irb(main):002:0> obj = 2
=> 2

# rassocを使わないとこうなるのかな?
irb(main):003:0> arr.select { |v| v[1] == obj }[0]
=> ["b", 2]

# 使うとすっきりする。パッと見てrassocってどういうメソッドだっけっていう感覚ではある
irb(main):004:0> arr.rassoc(obj)
=> ["b", 2]

メモ

  • latestドキュメントへのリンクにしてたらいつの間にかver3.1になってた。バージョン指定のドキュメントリンクにしておけばよかった・・・けどまあいっか。
  • rassocを見てぱっとどういう挙動かわかるRubyistはどのくらいいるんやろうか
  • assocはassociateの意味?rassocはreverse associateの意味?

Ruby_Array_44

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

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

インスタンスメソッド

product(*lists) -> Array
product(*lists) { |e| ... } -> self
  • レシーバの配列と引数で与えられた配列のそれぞれから要素を1個ずつとって配列とし、それらのすべての配列を要素とする配列を返す。つまり、返される配列の長さはレシーバと引数で与えられた配列の長さの積になる
    • 引数の配列は複数指定可能
  • ブロックが与えられた場合、作成した配列の各要素を引数としてブロックを実行してselfを返す
    • どういうときに使うのだろうか?
irb(main):001:0> arr = %w[a b c]
=> ["a", "b", "c"]

irb(main):002:0> brr =arr.product([1, 2])
=> [["a", 1], ["a", 2], ["b", 1], ["b", 2], ["c", 1], ["c", 2]]

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

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

irb(main):005:0> brr
=> [["A", 1], ["A", 2], ["b", 1], ["b", 2], ["c", 1], ["c", 2]]

# memo
# selfに破壊的変更を加えると影響が出る
irb(main):001:0> [1, 2, 3].product(['a', 'c'], ['X', 'Y', 'Z'])
=> [[1, "a", "X"], [1, "a", "Y"], [1, "a", "Z"], [1, "c", "X"], [1, "c", "Y"], [1, "c", "Z"], [2, "a", "X"], [2, "a", "Y"], [2, "a", "Z"], [2, "c", "X"], [2, "c", "Y"], [2, "c", "Z"], [3, "a", "X"], [3, "a", "Y"], [3, "a", "Z"], [3, "c", "X"], [3, "c", "Y"], [3, "c", "Z"]]
irb(main):001:0> [1, 2, 3].product
=> [[1], [2], [3]]

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

# memo
# これは覚えておきたい
irb(main):001:0> arr = %w[a b c]
=> ["a", "b", "c"]

irb(main):002:0> brr = []
=> []

irb(main):003:0> arr.product([1, 2]) { |elem| brr << elem }
=> ["a", "b", "c"]

irb(main):004:0> brr
=> [["a", 1], ["a", 2], ["b", 1], ["b", 2], ["c", 1], ["c", 2]]

irb(main):005:0> arr[0].upcase!
=> "A"

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

irb(main):007:0> brr
=> [["A", 1], ["A", 2], ["b", 1], ["b", 2], ["c", 1], ["c", 2]]
arr = [1, 2, 3]
brr = [7, 8]

# 例1
sum = 0
arr.each do |i|
  brr.each do |j|
    sum += i * j
  end
end

# 例2
arr
  .product(brr)
  .inject(0) { |result, item| result + item[0] * item[1] }

# memo
# こういう感じで書けるのか
# Rubyに詳しくない人にとっては例1のほうが可読性高そう・・・

メモ

【Advent Calendar 2021】点字メーカープログラム

概要

本記事は Calendar for Rubyプログラミング問題にチャレンジ! -改訂版・チェリー本発売記念- | Advent Calendar 2021 - Qiitaの15日目です。
入力されたローマ字に対応する点字をテキストで出力するプログラムを作成するという企画です。

詳細な内容についてはREADMEをご覧ください。

Pull Request

https://github.com/JunichiIto/tenji-maker-challenge/pull/24

ロジックの解説

方針

企画の内容を見て、しばらく考えて頭に浮かんだ方針が次の3つです。

  1. YAMLで変換対象となるローマ字の点字をすべて定義して利用する
  2. Arrayで点字をうまく表現してなんとかする
  3. Matrixで点字をうまく表現してなんとかする

メリット・デメリットを整理してみました。

メリット デメリット
YAML シンプルといえばシンプル 将来もし点字パターンが変わることがあるならそのときの修正にコストがかかりそう
点字を理解している人にとって、YAMLを見たときに正しさがパッと見でわかる
Array Ruby実装経験者は扱いに慣れている 点字のしくみ・概念に沿った実装ができない?
Matrix 点字のしくみ・概念に沿った実装ができる Ruby実装経験者でも扱ったことのあることが少なそうで、Matrix自体の理解にかかるコストがある

私の心情も整理してみました。

ポジティブ ネガティブ
YAML なんとなく伊藤さんが求めるものではない気がする
Array AtCoderでグリッド問題を配列で解こうとしたときに混乱した経験があるので不安
Matrix イケている感じがする Matrixを利用したコードを書いた経験がほぼなく不安

採用案

Matrix案を採用しました。
採用理由は次の通りです。

  • 点字のしくみ・概念に沿った実装ができる
    • 実装当初は確信を持つことができていません
  • (イケている感じがする)

処理の流れ

メイン処理の流れは次の図の通りです。

メイン処理の流れ
メイン処理の流れ

  • ローマ字を点字と同等の3行2列のMatrixに変換します
    • 各要素は点があれば1、なければ0
      • 最終的に1'o'へ、0'-'へ変換します
  • 空白(セパレーター)も3行1列のMatrixに変換します
    • 各要素は-1
      • 最終的に-1' 'へ変換します
12/19 追記

よくよく考えると空白を-1で表現しないほうが 心残りなこと に記載した疎結合も解消できていいなと思ったのでリファクタしてみました。

https://github.com/mfham/tenji-maker-challenge/pull/2 https://github.com/mfham/tenji-maker-challenge/pull/3

※企画が終わるまではフォーク元のPRに反映されないようにしたいので、上記PRはRevertしてあります。

コードのアピールポイント

点字のしくみ・概念に沿った実装

全視情協:点字とは - 点字のしくみに記載の通り、点字は①②④の点を組み合わせて母音を表し③⑤⑥の点を組み合わせて子音(行)を表します。
その概念をそのまま実装で表現しました。
もし将来他の開発者が追加実装をすることになったとしても、すんなり実装に入れるのではないかと思っています。

変換処理の切り出し

心残りなこと

縦3点、横2点での表現が縦N点、横M点へ変更になるときの考慮

点字は縦3点、横2点の6点で表現されています。
おそらく近い将来も変わることはない気がするのですが、もしかしたら点字の世界にも絵文字・顔文字のような表現方法が生まれるかもしれません。

その場合、6点では足りなくなるのではないか?
そのときのことも考え、縦N点、横M点に変更されることになっても柔軟に対応できるような実装にしたいという思いがありました。

しかし今回そのことを考慮した実装ができませんでした。

密結合の解消

今回、空白(セパレーター)を表すMatrixをMatrix[[-1], [-1], [-1]]としました。
たとえば、各要素の値を-1から-2に変更しようとした場合、空白を表すMatrixの定義だけでなく最終的に変換するロジックの定義も合わせて修正する必要があります。
この関係性を知らずに片方だけを-2へ変更するとバグが起きます。

ここら辺をうまく実装したかったのですが、力及ばずでした。

Converterの設計

実装の考え方については コードのアピールポイント に記載しています。
実装中にデザインパターンとして知られるStrategyパターンが頭に浮かびましたが、深く理解しておらず理解する時間もとることができず、なんちゃって実装になっている気がしています。

オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方 | Sandi Metz, 髙山 泰基 |本 | 通販 | Amazonを購入したので、設計全般やデザインパターンについて学びなおします。

プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plus) | 伊藤 淳一 |本 | 通販 | Amazonも購入したので、合わせて勉強しなおします。

新規作成した部分のテストコード

もともと用意されているテストコードがパスすればよいとはいえ、新規作成した部分のテストコードは作成すべきでした。

参加した感想

初めてのAdvent Calendarへの参加

初めてAdvent Calendarに参加しました。
先輩が「こういうのあるよー」とリンクを共有してくださり、数時間悩んだ結果、えいやでぽちっと参加を決めました。
1ヵ月近く時間があったにもかかわらず、当日23:20現在、この本文を書いています。 パーキンソンの法則 - Wikipediaですね・・・。

他の誰かがやりたいであろう枠を自分がとっているので、そのプレッシャーを感じながら毎日過ごしていました。
他人から見られるというほどよいプレッシャーのおかげで、頭を悩ませながらなんとか設計・実装できてよかったです。

他の方の実装・記事もこれからゆっくり読みたいと思います。

点字

今回参加するまで、点字のしくみなど点字のことについてほぼ知らない状態でした。
設計・実装を進める中で、参考リンクとして紹介されていたページを何度も読みなおし、点字の歴史などを調べるくらいまで興味が湧いていることに気付きました。

  • 将来9点や12点で顔文字に相当するような表現は生まれるだろうか?
  • 世界の点字はどうなっているのだろうか?文章を右から読む国ではどういう点字が使われているのだろうか?
  • 左から読んでいくので、のような濁音は最初に濁点があるとお知らせする(始めにを表す点があってその後ろに清音(五十音)がある)と読みやすい。なるほど、おもしろい
  • 表面に点字がついているキーボードは販売されていないのかな?

この後も引き続き点字のことを理解していく予定です。

伊藤さんへのメッセージ

企画していただきありがとうございました。
緊張しながらも楽しみながら取り組むことができました。

もし機会があれば、伊藤さんが今回の仕様を実装する様子を見てみたい(ライブコーディング、動画)です。

  • どのようなことを考えて設計するのか
  • どのような情報(点字Rubyドキュメント、自分用メモ、その他)を参照するのか
  • どういう順番で実装するのか
  • どんな拡張機能やツールを使っているのか
  • 設計から実装までどのくらい時間がかかるのか

素敵な企画、ありがとうございました!

Ruby_Array_43

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

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

インスタンスメソッド

unshift(*obj) -> self
prepend(*obj) -> self
  • 指定されたobjを引数の最後から順番に配列に先頭に挿入する
    • "最後から順番に"っていうのが混乱したけど、そのまままるっと先頭に挿入されると思えばよさそう
  • 引数を指定しない場合、何もしない
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

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

irb(main):003:0> arr.unshift(7, 9)
=> [7, 9, 5, 1, 2, 3]

irb(main):004:0> arr.unshift('a', [1, 2], :foo)
=> ["a", [1, 2], :foo, 7, 9, 5, 1, 2, 3]

irb(main):005:0> arr
=> ["a", [1, 2], :foo, 7, 9, 5, 1, 2, 3]
irb(main):001:0> arr = [1, 2, 3]
=> [1, 2, 3]

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

irb(main):003:0> arr
=> [1, 2, 3]
[1] pry(main)> require 'pry-doc'
=> true
[2] pry(main)> show-source Array#unshift

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

static VALUE
rb_ary_unshift_m(int argc, VALUE *argv, VALUE ary)
{
    long len = RARRAY_LEN(ary);
    VALUE target_ary;

    if (argc == 0) {
        rb_ary_modify_check(ary);
        return ary;
    }

    target_ary = ary_ensure_room_for_unshift(ary, argc);
    ary_memcpy0(ary, 0, argc, argv, target_ary);
    ARY_SET_LEN(ary, len + argc);
    return ary;
}
irb(main):001:0> arr = [1, 2, 3].freeze
=> [1, 2, 3]
irb(main):002:0> arr.unshift
Traceback (most recent call last):
        5: from /Users/mfham/.rbenv/versions/3.0.0/bin/irb:23:in `<main>'
        4: from /Users/mfham/.rbenv/versions/3.0.0/bin/irb:23:in `load'
        3: from /Users/mfham/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
        2: from (irb):2:in `<main>'
        1: from (irb):2:in `unshift'
FrozenError (can't modify frozen Array: [1, 2, 3])

# memo
# 引数を指定しなければ何もしないとあるが、rb_ary_modify_checkのチェックが行われている。
# レシーバをfrozenするとエラーが起こる。何もしないというのは違う感じ。

メモ

  • ドキュメントのunshift, prependのサンプルの配列の命名aryじゃなくてarrだ。ドキュメント全体で揃っていない。メソッド呼び出し時にカッコをつけていない。