purrr 0.1.0を使ってみる(2) walk

walk()、地味に難しかったです。ヘルプを見ても使用例がひとつも載ってない…

RStudioブログの記事には、こう書いてあります。

walk() takes a vector, calls a function on piece, and returns its original input. It’s useful for functions called for their side-effects; it returns the input so you can use it in a pipe.

ここで言っている「side-effect」というのは何なんでしょうか。

Rの副作用? まさか...、Rに管理されたくなっちゃうという中毒症状のことでしょうか??

Advanced Rを読む

困ったときのAdvanced Rです。

Functions · Advanced R.

ここにはこんな例が書いてあります。これは「side-effect」ではないものの例です。

f <- function(x) {
  x$a <- 2
  x
}
x <- list(a = 1)
f(x)
#> $a
#> [1] 2
x$a
#> [1] 1

関数の中でリストの要素を書き換えても、元のオブジェクトには影響しません。このように、Rには意外と副作用がありません。

ただし、例外がいくつかある、とHadleyは続けます。そう、これこそが「side-effect」です。

(There are two important exceptions to the copy-on-modify rule: environments and reference classes. These can be modified in place, so extra care is needed when working with them.)

  • library() which loads a package, and hence modifies the search path.
  • setwd(), Sys.setenv(), Sys.setlocale() which change the working directory, environment variables, and the locale, respectively.
  • plot() and friends which produce graphical output.
  • write(), write.csv(), saveRDS(), etc. which save output to disk.
  • options() and par() which modify global settings.
  • S4 related functions which modify global tables of classes and methods.
  • Random number generators which produce different numbers each time you run them.

ざっくり言って、以下の3つと考えていいでしょう。(R中毒症状は副作用にカウントされていません)

  • 入出力
  • グローバルな環境や設定をいじる(上の例では出てませんでしたが、<<-とかを使って上位の環境に代入するとかも)
  • 特殊なオブジェクト(environmentとreference class)への代入

walk

で、walk()はこの「side-effect」をどう使うのでしょう。

walk()は、関数を適用した結果ではなく、元のオブジェクトを返します。(invisibleになってるので、print()しないと出力されません)

たとえば次の例だと、2が足された結果ではなく、元の値が返ってきます。

list(a = 1, b = 2) %>%
  walk(`+`, 2) %>%
  print
$a
[1] 1

$b
[1] 2

この挙動はたぶん、パイプの途中で入出力をしたいときとかに便利です。magrittrの%T>%と同じような使い方ができます(参考: magrittr 1.5を試してみる。 - Technically, technophobic.

list(a = 1:10, b = 2:22) %>%
  walk(str) %>%
  map(sum)
#>  int [1:10] 1 2 3 4 5 6 7 8 9 10
#>  int [1:21] 2 3 4 5 6 7 8 9 10 11 ...
#> $a
#> [1] 55
#> 
#> $b
#> [1] 252
#> 

他は...ちょっと使いどころが思いつきません。誰かいい使い方あれば教えてください。

walk2, walk3, walk_n

map2, map3と同じノリです。あるらしいです。説明はめんどくさいので割愛。

感想

ひとつの関数の使い方を調べるだけで無限に時間がつぶれていく...。なんて楽しいんだpurrr!(棒