「え!? ifもforも使わずにライフゲームの実装を!?」
「できらR!!」

Tokyo.R 86

Atusy

2020-6-27

Atusy

  • R Markdown関係のコミッタ
  • felp、ftExtra、minidownなどパッケージを作ってはTokyoRで紹介している
  • Pythonでデータ分析してる
  • blog.atusy.net
  • @Atsushi776

今日の話

sionnさんのPythonによる

「え!? ifもforも使わずにライフゲームの実装を!? 」「できらぁ!!」

のR版です。

lifegameとは?

生命の誕生、進化、淘汰などをモデル化したシミュレーションゲーム (Conway, 1970)

ルール

誕生

死亡セルに隣接する生存セルが3つなら次世代が誕生。

生存

生存セルに隣接する生存セルが2つか3つならば、次世代も生存。

過疎・過密

誕生も生存もしないセルは過疎・過密として死滅。

参考: https://ja.wikipedia.org/wiki/ライフゲーム

追加ルール

  • 可視化を除いてRレベルでのif文、for文、apply族禁止
  • 可視化を除いてbaseに限定
  • パッケージ化

https://github.com/atusy/lifegamer

lifegame

Diehard実行例

library(lifegamer)
diehard_result <- lifegame(diehard, 131)
animate(diehard_result, ncol = 19)

注: 時間の都合でアニメーションGIF化できませんでした。

Diehard実行結果

色んなループで階乗

お約束

  • prod(seq(n))
  • factorial(n)
  • gamma(n + 1L)

とかで解けるとか言わない

for

フツー

factorial_for <- function(n) {
  result <- 1L
  for (i in seq(n)) {
    result <- i * result
  }
  result
}
factorial_for(10L)
#> [1] 3628800

lappy

前回値の利用は面倒

factorial_lapply <- function(n) {
  result <- 1L
  lapply(seq(n), function(i) {
    result <<- i * result
  })
  result
}
factorial_lapply(10L)
#> [1] 3628800

再帰

普通はifを使わないと終了できない

factorial_recall <- function(n) {
  if (n <= 0L) return(1L)
  Recall(n - 1L) * n
}
factorial_recall(10L)
#> [1] 3628800

S3再帰

if不要

factorial_s3 <- 
  function(n, ...) UseMethod("factorial_s3")
factorial_s3.default <- function(n, ...) {
  n * factorial_s3(structure(
    n - 1L, class = as.character(n - 1L)))[[1L]]
}
factorial_s3.0 <- function(n) 1L
factorial_s3(10L)
#> [1] 3628800

S3再帰は劇的に遅い

bench_mark <- bench::mark(
  factorial_for(10L),
  factorial_lapply(10L),
  factorial_recall(10L),
  factorial_s3(10L)
)
ggplot2::autoplot(bench_mark)

ベンチマーク可視化

Tip: Rのforは遅いと誰が言った?

S3再帰の使い道

遅いのにあるの?

dplyr::mutate ≦ 0.8.5

  1. 入力がdata.frameならtibbleに変換
  2. 再帰的mutate
  3. 出力をdata.frameに戻す
mutate.data.frame <- function(.data, ...) {
  as.data.frame(mutate(tbl_df(.data), ...))
}

ifを使わない工夫

トリッキーなif else

nが1以下なら"end"、違えば"continue"を返す

n <- 2L
c("end", "continue")[c(n <= 1L, TRUE)][1L]
#> [1] "continue"

名前付きベクトル

x = c(A = 1, B = 2, C = 3)
x[c("C", "A", "B", "B")]
#> C A B B 
#> 3 1 2 2

Enjoy