Rによる1命令だけのプログラミング

今月の「情報処理」という学会誌に奥村晴彦先生の「Rによる1行プログラミング」という記事が載っていた.記事は情報処理学会員でなくても読めるようである:

Rを好んで使っている身としては読まざるを得ないタイトルである.しかも1行プログラミングということで,いわゆるワンライナーの奇怪な短いコードで問題を解くという内容かと思ったが,実際には1行ずつ実行できるプログラミングというような内容であった.

ワンライナーを期待して読んでしまったので消化不良な気分になり,しばらく1行でなるべく短く書く方法を自分で考えた.解くべき問題は1から100を出力することであるが,3の倍数のときは数字の代わりに"Fizz"と,5の倍数のときは"Buzz",両方の倍数ならば"FizzBuzz"と出力することである.つまり,いわゆる「アホになる」やつである.

今回はこれをRの一行のなるべく短いコードで実行することを目指す.実際には,Rではセミコロン「;」で区切ることによって複数の命令が書けてしまう.Rのよいところは,豊富な関数がすでに用意されており,命令の数を少なくできることにあると思っているので,セミコロンの使用も今回は禁止して一行と言うより一命令文でこの問題を解くことにしよう.

実は,Rにはifelse関数があるのでこれを知っていれば割と簡単に次のように書ける:

ifelse(1:100%%3,ifelse(1:100%%5,1:100,"Buzz"),ifelse(1:100%%5,"Fizz","FizzBuzz"))

これで81文字である.1:100が何回も現れるので,これを工夫すればもっと短くできる(73文字):

ifelse((a=1:100)%%3,ifelse(a%%5,a,"Buzz"),ifelse(a%%5,"Fizz","FizzBuzz"))

ifelseがあまりに強力なので,これ以上短くするのは容易ではないだろう.実際私にはこれ以上短いものは思いつかなかった.

だが,結果を出力せずに「1 2 Fizz 4 Buzz ...」を変数に入れた状態にするだけであれば,より短いコードがあることに気付いた.そのやや奇怪なコードがこれである:

(a=b=1:100)[a[!b%%3]<-"Fizz"][a[!b%%5]<-"Buzz"][a[!b%%15]<-"FizzBuzz"]

70文字のこのコードは,それ自身だけでは結果としてNAしか出力しない.だが,aにはちゃんと問題の解が入っている:

> (a=b=1:100)[a[!b%%3]<-"Fizz"][a[!b%%5]<-"Buzz"][a[!b%%15]<-"FizzBuzz"]
[1] NA
> a
  [1] "1"        "2"        "Fizz"     "4"        "Buzz"     "Fizz"    
  [7] "7"        "8"        "Fizz"     "Buzz"     "11"       "Fizz"    
 [13] "13"       "14"       "FizzBuzz" "16"       "17"       "Fizz"    
 [19] "19"       "Buzz"     "Fizz"     "22"       "23"       "Fizz"    
 [25] "Buzz"     "26"       "Fizz"     "28"       "29"       "FizzBuzz"
 [31] "31"       "32"       "Fizz"     "34"       "Buzz"     "Fizz"    
 [37] "37"       "38"       "Fizz"     "Buzz"     "41"       "Fizz"    
 [43] "43"       "44"       "FizzBuzz" "46"       "47"       "Fizz"    
 [49] "49"       "Buzz"     "Fizz"     "52"       "53"       "Fizz"    
 [55] "Buzz"     "56"       "Fizz"     "58"       "59"       "FizzBuzz"
 [61] "61"       "62"       "Fizz"     "64"       "Buzz"     "Fizz"    
 [67] "67"       "68"       "Fizz"     "Buzz"     "71"       "Fizz"    
 [73] "73"       "74"       "FizzBuzz" "76"       "77"       "Fizz"    
 [79] "79"       "Buzz"     "Fizz"     "82"       "83"       "Fizz"    
 [85] "Buzz"     "86"       "Fizz"     "88"       "89"       "FizzBuzz"
 [91] "91"       "92"       "Fizz"     "94"       "Buzz"     "Fizz"    
 [97] "97"       "98"       "Fizz"     "Buzz"

ポイントは[]で代入を囲むことである.Rでは代入した結果にインデックスを表す[]をそのままつけることができる:

> (a <- 1:10)[2:5]
[1] 2 3 4 5

これを利用し,[]の中身に代入文を書いてしまうのである.そして,[]は[]の後にさらにつけることができるので,[]を使えばいくらでも代入文を書くことができる.そんなことをしたらセミコロンを使うのと変わらないではないかと思うかもしれない.その通りである.