foreachパッケージで、並列化する/しないを綺麗に書き分ける

で、「これからRで並列化するならforeachパッケージ使うだろ」という話を書いたが、その続き的な?もの。
並列化する/しないを、どう美しく書き分けるにはどうしたらよいかというお話。

たとえば、自作の関数かなんかで、並列化する/しないを書き分けようとすると、引数かなんかにparallel引数を用意しておいて、以下のように書いてしまうんじゃかろうか。

parallel=TRUE
if(parallel){
  foreach(i=1:3) %dopar% {
    rnorm(i)
  }
}else{
  foreach(i=1:3) %do% {
    rnorm(i)
  }  
}

これはdo/dopar部分だけが違うだけであって、冗長なコードだ。Rだと、最近流行りのパイプ一族系ポスト

を見てもわかるように、%で挟まれた奴らは実はこれ関数なのです。なので、以下のように書くことで、上のコードは

parallel=TRUE
doop <- ifelse(parallel, `%do%`, `%dopar%`)
doop(foreach(i=1:3), {rnorm(i)})

とまで簡略化される。こっちのほうがスマートに(私には)見えるので、これでいく。ここのコードではワーカーの立ち上げ・停止は書いていないことに注意。立ち上げたワーカー(スレッド、というかRScript.exeのプロセス)を正しく停止させるためには以下を見ておくとよい。

これを受けて、pipeR三銃士

である俺はpipeRパッケージを活用して以下のように書いておくことにした。

library(pipeR)
parallel <- TRUE
doop <- if(parallel){detectCores() %>>% (cl = makeCluster(.)) %>>% registerDoParallel; `%dopar%`}else{`%do%`}
doop(foreach(i=1:3), {rnorm(i)})
if(parallel){stopCluster(cl)}

さようなら可読性。そしてさらにコメント欄で貰ったアドバイスをもとにすると

library(pipeR)
parallel <- TRUE
"%doop%" <- if(parallel){detectCores() %>>% (cl = makeCluster(.)) %>>% registerDoParallel; `%dopar%`}else{`%do%`}
foreach(i=1:3) %doop% {
  rnorm(i)
}
if(parallel){stopCluster(cl)}

とできる。これがスマートかなぁ。