Code Fix

上級

Reduce に init を渡さず空ベクトルを渡すと NULL が返り後続処理でエラーになる

Reduce(f, x)はinitを省略した状態でxが空ベクトルだと、畳み込みを始める最初の値が無いためエラーにはならずNULLを返します。そのNULLをif()の条件などにそのまま使うと、そこで初めて「argument is of length zero」というエラーになります。

エラーメッセージの読み方

Error in if (total > 5) { : argument is of length zero
Error
エラー種別 — 実行中に発生しました
if (total > 5) {
発生箇所 — エラーの原因になった呼び出し
argument is of length zero
詳細メッセージ — 何が問題だったか

このエラーが出る典型パターン

パターン1

 1  values <- numeric(0)
 2  total <- Reduce(function(a, b) a + b, values   )
                                                 ^
 3  if (total > 5) {
 4    cat("big\n")
 5  } else {
 6    cat("small\n")
 7  }
Error in if (total > 5) { : argument is of length zero

initを省略した状態でvaluesが空だとReduceはNULLを返し、if(total > 5)がNULLを判定できずエラーになります。initに0を渡しておけば空の場合でも数値として扱えます。

直し方: (空) を , 0 にします。

この問題を解いてみる →

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

パターン2

 1  scores <- numeric(0)
 2  total <- Reduce(function(a, b) a + b, scores   )
                                                 ^
 3  if (total > 0) {
 4    cat("positive\n")
 5  } else {
 6    cat("zero\n")
 7  }
Error in if (total > 0) { : argument is of length zero

scoresが空のときReduceはNULLを返すため、その後のif判定がargument is of length zeroで止まります。

直し方: (空) を , 0 にします。

この問題を解いてみる →

パターン3

 1  items <- numeric(0)
 2  count <- Reduce(function(a, b) a + b, items   )
                                                ^
 3  if (count >= 1) {
 4    cat("has items\n")
 5  } else {
 6    cat("empty\n")
 7  }
Error in if (count >= 1) { : argument is of length zero

itemsが空ベクトルの場合、initを渡さないReduceはNULLを返し、それをif条件に使うとエラーになります。

直し方: (空) を , 0 にします。

この問題を解いてみる →

パターン4

 1  fees <- numeric(0)
 2  total <- Reduce(function(a, b) a + b, fees   )
                                               ^
 3  if (total != 0) {
 4    cat("charged\n")
 5  } else {
 6    cat("free\n")
 7  }
Error in if (total != 0) { : argument is of length zero

feesが空の可能性を考慮していないと、initを省略したReduceがNULLを返しif文でエラーになります。

直し方: (空) を , 0 にします。

この問題を解いてみる →

パターン5

 1  weights <- numeric(0)
 2  total <- Reduce(function(a, b) a + b, weights   )
                                                  ^
 3  if (total > 100) {
 4    cat("heavy\n")
 5  } else {
 6    cat("light\n")
 7  }
Error in if (total > 100) { : argument is of length zero

空である可能性のある入力にReduceを使うときは、initを明示的に渡しておかないと後続のif判定がエラーで止まります。

直し方: (空) を , 0 にします。

この問題を解いてみる →

よくある誤解

「Reduceはinitを省略しても、空の場合は0のような無害な初期値を補ってくれるはず」という思い込みは誤りです。実際にはNULLという値を静かに返すだけで、その場ではエラーになりません。後続の計算や条件分岐でNULLを数値として扱おうとした瞬間に、離れた場所でエラーが起きます。空の可能性がある入力にはinitを明示的に渡しておく必要があります。

実務での勘所

initに何を渡すべきかは、その演算の単位元(何と組み合わせても結果を変えない値)を考えると決めやすくなります。足し算ならinit = 0、掛け算ならinit = 1、文字列の連結ならinit = ""が単位元です。単位元を渡しておけば、xが空でも意味のある初期値からスタートでき、要素が1つ以上あるときの結果にも一切影響しません。Reduceを使うたびに「この演算の単位元は何か」を考える習慣をつけると、空の入力への対処を忘れにくくなります。

演習をはじめる

関連するエラー

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