Code Fix

中級

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")
Error in values[[i]] : subscript out of bounds

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")
Error in names_list[[i]] : subscript out of bounds

空のリストでも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")
Error in records[[i]] : subscript out of bounds

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")
Error in queue[[i]] : subscript out of bounds

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")
Error in buffer[[i]] : subscript out of bounds

空ベクトルでも安全にループしたい場合は、常に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)を使うほうが安全です。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)