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 }
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 }
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 }
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 }
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 }
空である可能性のある入力にReduceを使うときは、initを明示的に渡しておかないと後続のif判定がエラーで止まります。
直し方: (空) を , 0 にします。
よくある誤解
「Reduceはinitを省略しても、空の場合は0のような無害な初期値を補ってくれるはず」という思い込みは誤りです。実際にはNULLという値を静かに返すだけで、その場ではエラーになりません。後続の計算や条件分岐でNULLを数値として扱おうとした瞬間に、離れた場所でエラーが起きます。空の可能性がある入力にはinitを明示的に渡しておく必要があります。
実務での勘所
initに何を渡すべきかは、その演算の単位元(何と組み合わせても結果を変えない値)を考えると決めやすくなります。足し算ならinit = 0、掛け算ならinit = 1、文字列の連結ならinit = ""が単位元です。単位元を渡しておけば、xが空でも意味のある初期値からスタートでき、要素が1つ以上あるときの結果にも一切影響しません。Reduceを使うたびに「この演算の単位元は何か」を考える習慣をつけると、空の入力への対処を忘れにくくなります。