1:length(x) は x が空ベクトルのとき 1 と 0 の2回ループしてしまう
1:length(x)はxが空ベクトル(長さ0)のとき1:0、つまりc(1, 0)という降順の数列になります。空のはずのループが2回実行され、存在しない添字xでのアクセスが発生します。
エラーメッセージの読み方
Error in values[[i]] : subscript out of bounds
Error- エラー種別 — 実行中に発生しました
values[[i]]- 発生箇所 — エラーの原因になった呼び出し
subscript out of bounds- 詳細メッセージ — 何が問題だったか
このエラーが出る典型パターン
パターン1
1 values <- list() 2 for (i in 1:length (values)) { ^ 3 cat(values[[i]], "\n") 4 } 5 cat("done\n")
valuesが空のとき1:length(values)は1:0でc(1,0)になり、存在しない1番目の要素にアクセスしてエラーになります。
直し方: 1:length を seq_along にします。
パターン2
1 names_list <- list() 2 for (i in 1:length (names_list)) { ^ 3 cat(names_list[[i]], "\n") 4 } 5 cat("end\n")
空のリストでも1:length(x)は逆順の2回ループになってしまい、範囲外アクセスのエラーが起きます。
直し方: 1:length を seq_along にします。
パターン3
1 records <- list() 2 for (i in 1:length (records)) { ^ 3 cat(records[[i]], "\n") 4 } 5 cat("finished\n")
seq_along(x)ならxが空のとき何もループしませんが、1:length(x)は空を想定していません。
直し方: 1:length を seq_along にします。
パターン4
1 queue <- list() 2 for (i in 1:length (queue)) { ^ 3 cat(queue[[i]], "\n") 4 } 5 cat("complete\n")
queueが空のときlength(queue)は0になり、1:0はc(1, 0)という降順の数列として扱われます。
直し方: 1:length を seq_along にします。
パターン5
1 buffer <- list() 2 for (i in 1:length (buffer)) { ^ 3 cat(buffer[[i]], "\n") 4 } 5 cat("stop\n")
空ベクトルでも安全にループしたい場合は、常にseq_along()を使うのが安全です。
直し方: 1:length を seq_along にします。
よくある誤解
「xが空ならlength(x)は0だから、1:length(x)は0回のループになるはず」という思い込みは誤りです。Rの:演算子は1:0をc(1, 0)という降順の数列として扱うため、空を意図したループが逆に2回実行されてしまいます。空ベクトルでも安全にループしたい場合はseq_along(x)を使う必要があります。
実務での勘所
この事故は:演算子のバグではなく、:が「最初の数から2番目の数まで、必要なら降順ででも律儀に数え上げる」という、一貫した規則通りに動いた結果です。1:10が1から10への上昇列を作るのと同じ規則性で、1:0は1から0への下降する列c(1, 0)を作ります。:はループの境界を作るためだけの道具ではなく汎用的な数列生成の演算子なので、「開始点のほうが大きければ空にする」という特別扱いは行いません。ループの回数を作るという目的に限っては、seq_along(x)やseq_len(n)を使うほうが安全です。