Chienomi

Rubyのイテレータ変数itを喜ぶ記事

プログラミング::lang

Ruby 3.4がリリースされ、新しい機能としてイテレータ変数itが追加された。

これが意味するところは、これを使う人ならだいたい分かるし、解説しても「そんなん知ってるし、分かるし」になるだろう。

だが、これはかなり嬉しいので謎の解説をする記事を勢いで書いた。

itについて

Rubyの場合標準的なブロックの構造はこんな感じ。

[1, 2, 3].each do |i|
  puts i
end

iは一般的なイテレータ変数である。

イテレータ変数のような一時変数を使うメリットは、それがイテレータ内に閉じた使い捨ての変数であることが明瞭という、リーディングコストの低さだ。

for (const i of [1,2,3]) {
  console.log(i)
}

イテレータ変数は基本的に自分が所属する直近のブロックで定義されているものという前提で探すことができる。 2ブロックのネストループなどでは2レベル探すこともあるが、基本的には1つの文脈に閉じて定義すべきものだ。

そして、複数イテレータ変数を用意する場合はちょっと困る。i, jあたりがせいぜいで、数が多いときはイテレータ変数を使うべきでないように見える。

つまり、だいたいはイテレータ変数を使うときはiなのだ。 だったら、暗黙にiを使わせてくれても良いのではないかと思うことがある。

[1, 2, 3].each do
  puts i
end

そういう使い方ができるように追加されたのが、今回のitだ。

[1, 2, 3].each do
  puts it
end

従来も同じ用途で_1という変数を使うことができたが、「_1を使うと_2があるのではないかと思ってしまってreading costが高い」という理由でitが導入されたらしい。

私は_1にPerl的な汚さを感じるので使わない。別にブロック変数書けばええやん。

itの使いどころ

itは有効なケースというのが割と明確にある。

itというキーワードを使うものとして私が思い浮かべるのはPandocのテンプレート。 アップデートで追加された機能だけれど、これにより連想配列の配列が使えるようになった。

$for(links)$
  <li><a href="${it.url}">${it.title}
$endfor$

みたいなことができる。

とりあえず簡潔なイテレータでは結構効果が大きい。

ary.map { it[] }

連想配列の配列でもやはり便利。

ary.each do
  sum += it[]
end

ネストされたブロックからアクセスすると混乱を招くから、使えるケースはかなり短いブロックに限られてくる。 とはいえ、イテレータではないブロックでも便利なケースはあるだろう。

File.open path do
  data = it.read
end

もっとも、この場合は英語的には目的語に来るべきitが主語に来ているように見えるのが気になるから嫌だという人はいそう。

itは直近のブロックのみを対象にするため、ネストしたブロックの内側でitを使うことはできないし、そもそもブロックをネストさせるとitが意味するものが場所によって変わるため、そのようなブロックでは向いていない。

ary.each do
  # itは配列の要素 (連想配列)
  it.each do |k,v|
    it[k] #直近ブロックではk,vが定義されているのでエラーになる
    # `it` is not allowed when an ordinary parameter is defined
  end
end

だいぶ嬉しい機能

実際の使いどころはとても限られるけど、あればつい使っちゃうような機能が久しぶりに追加されてかなり嬉しい。

ただ、気軽に使いたくなるからこそ、気軽に使うと最低バージョンが3.4.0になるということに注意が必要だろう。