3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

is_a? や Check_Type で Array を要求されたときに to_a 可能なオブジェクトを to_a せずに渡したい(できない)(*できる)

Last updated at Posted at 2015-02-05

Ruby で is_a?=== などで Array を要求するインターフェースを見ると、to_a できるオブジェクトを渡したら暗黙的に to_a してくれ~~~~という気持ちになることがよくあります(あるいは拡張ライブラリで Check_Type してるときとか)。

class Color

  def initialize(color)
    @arr = color
  end

  # 文字列とか数値から色配列を作ったり
  # 色配列から文字列とか数値を吐き出したりするスゴイ処理

  def to_a
    @arr
  end

end

色配列をクラスで管理して、Array を要求されるシーンでは to_a で Array を取り出すようにしました。これ Array を要求されるたびに毎回 to_a しないといけなくてつらみがあります。暗黙的に to_a するインターフェースにしてくれ~~~~と思うことがよくあります。

でも実際には to_a 可能なオブジェクトと Array はべつものなので何でもかんでも暗黙的に to_a するわけにはいかなかったりします。つらみ。
でもでもいちいち明示的に to_a 書くのはうつくしくないので何とかしたいです。

color = Color.from_int(0xff00ff)
nanka_array_hoshigaru_method(color.to_a)

この to_a がにくい。
to_a 書かずに to_a する方法があれば to_a 書かなくてもいいですね?

color = *Color.from_int(0xff00ff) #=> [255, 0, 255]
nanka_array_hoshigaru_method(color)

かいせつ

代入のときに右辺値に * プレフィックスをつけると

  • Array -> そのまま
  • Range -> Array
  • String -> [String]

みたいな感じで Array に変換されます。Array や Range を見ると to_a してるのかな?(Array#to_a は自身を返す)って思うんですが to_a を持たないはずの String が要素数 1 の Array になっているあたりめっちゃ謎です。

見た感じ、

  • to_a できるオブジェクト -> to_a
  • to_a できないオブジェクト -> [to_a できないオブジェクト]

こんな動きをしているっぽい。

この推測が正しいのか確かめるため、われわれは南米アマゾンの奥地へと向かった。

Ruby のうちがわではどんなふうに書かれているのか

アマゾンの奥地なんか行かなくても Ruby のソースを見に行けば分かりますね……。

insns.def にそれらしい記述があります。insns.def というのはなんぞやというと VM の命令シーケンスの一覧が書かれたファイルです。VM の命令シーケンスはこんなふうな処理してますよということが書いてあるっぽい(たぶん……)。

insns.def

/**
 @c put
 @e splat array
 @j 配列 ary に対して to_a を呼び出す。
*/
DEFINE_INSN
splatarray
(VALUE flag)
(VALUE ary)
(VALUE obj)
{
    VALUE tmp = rb_check_convert_type(ary, T_ARRAY, "Array", "to_a");
    if (NIL_P(tmp)) {
      tmp = rb_ary_new3(1, ary);
    }
    else if (RTEST(flag)) {
      tmp = rb_ary_dup(tmp);
    }
    obj = tmp;
}

to_a できなかったら rb_ary_new して to_a できたら to_a したものを返すよと書いてあります。

なので、代入時の splat 展開は

  • to_a できるオブジェクト -> to_a
  • to_a できないオブジェクト -> [to_a できないオブジェクト]

という実装になっていると思ってよさそうです。

あとがき

変数に格納するなら to_a したほうが明示的に配列作ってることが分かっていいので変なことしたくてエビデンス求めてアマゾンの奥地目指すの本末転倒なので素直に to_a しましょう。

余談

変数に格納しないでそのまま引数に渡したいときとかあると思います。

nanka_array_hoshigaru_method([*Color.from_int(0xff00ff)])

こういう書き方ができます。「to_a するよりタイプ数が少なくて有利だゾ!」という甘言に惑わされることなく、読みやすいコードを書きましょう……。

3
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?