餡子付゛録゛

ソフトウェア開発ツールの便利な使い方を紹介。

Rのベクター処理にバグ? - ではありませんでした

FizzBuzz問題を解いていて気付いたのですが、Rにバグがあるかも知れません。と思ったら、無かったです。
ちょっと紛らわしいコードを書きます。ベクターに添字をすることで、ベクターの一部分の文字列を取得しているのですが、1から11まで一度に計算したときと、1から12まで一度に計算したときで、10番目の結果が変化します。

f <- function(i){
  l <- c("Fizz", "Buzz", "FizzBuzz")
  ii <- (0==i%%5)*2 + (0==i%%3)*1
  return(ifelse(0<ii, l[ii], i))
}

10番目に注目して、f(1:11)とf(1:12)を比較してみましょう。

> f(1:11)
 [1] "1"    "2"    "Fizz" "4"    "Buzz" "Fizz" "7"    "8"    "Fizz" "Buzz" "11"  
> f(1:12)
 [1] "1"    "2"    "Fizz" "4"    "Buzz" "Fizz" "7"    "8"    "Fizz" "Fizz" "11"   "Fizz"

場合によっては致命的ですよね、これ。でもバグでは無いみたいです。

ifelse()関数は第1項、第2項、第3項が独立に評価された上で、第1項と比較して第2項と第3項の長さが短い場合、自動的に繰り返して延長されます。

具体的には、l[ii]は、f(1:11)のときはc("Fizz","Buzz","Fizz","Fizz","Buzz")になり、f(1:11)のときはc("Fizz","Buzz","Fizz","Fizz","Buzz","Fizz")とります。ifelse()で短い系列は循環して結合されるため、以下のようになるわけですね。

1 2 3 4 5 6 7 8 9 10
正解     Fizz   Buzz Fizz     Fizz Buzz
f(1:11) Fizz Buzz Fizz Fizz Buzz Fizz Buzz Fizz Fizz Buzz
f(1:12) Fizz Buzz Fizz Fizz Buzz Fizz Fizz Buzz Fizz Fizz

だから、ベクターの添え字になる変数iiが必ず0以上の値になる場合は、問題は発生しません(
RでFizzBuzz問題を高速化)。