R の dot-dot-dot オブジェクト


R を使ったことがある人は,おそらくすべての人が c 関数を使ったことがあるでしょう。 c 関数は,複数のベクトルやリストを連結する関数です。例えば c(1, 2, 3) は, 3 つの長さ 1 のベクトルを連結します。

c のヘルプを見ると,使用方法は次のようになっています[A]

c(..., recursive = FALSE)

引数の ... は「連結されるオブジェクト」であると説明があります。したがって,最初の例では 3 つのベクトルが ... に格納されていることになります。なぜ 1 つの ... という変数に, 3 つのベクトルが格納されるのでしょうか。

... は dot-dot-dot オブジェクトと呼ばれる特殊なオブジェクトです。これは関数の引数に利用され,他の引数以外のすべての引数が格納されます。 c 関数の例では, recursive という名前の引数以外が ... に格納されることになります。これの何が便利かというと,関数の内部で呼ぶ関数の引数を,すべて明示する必要がないということです。わかり易い例では read.csv 関数があります。ソースコード[B]を見ると read.csv は次の様に定義されています。

read.csv <-
function (file, header = TRUE, sep = ",", quote = "\"", dec = ".",
          fill = TRUE, comment.char = "", ...)
    read.table(file = file, header = header, sep = sep,
               quote = quote, dec = dec, fill = fill,
               comment.char = comment.char,  ...)

内部で呼ばれる read.table には stringAsFactorsblank.lines.skip のように他にも沢山引数があるのですが,これらすべてを ... という,見かけ上 1 つの引数に委譲することができています。他に plot をはじめとする描画系の関数でもよく用いられます。

このように dot-dot-dot オブジェクトを用いることで,その関数にとって本質でない引数を隠しつつも,実際には引数を受け取るというようなことが可能です。うまく dot-dot-dot オブジェクトを使い,柔軟かつ理解しやすい関数を作ると良いでしょう。

おまけ

基本的に引数として受けた ... はそのまま他の関数に渡せば良いのですが, ... の要素にアクセスしたい場合があるかもしれません。 ... は特殊なオブジェクトであるため, ... の要素へのアクセスは特殊な記法があります。要素の先頭から ..1..2,という具合に ..n という名前でアクセスすることができます。

f <- function(x, ...) ..1
f(1, 2, 3)  # 2
f(1, x=2, 3)  # 1

また, list 関数や as.list 関数に与えてやれば,リストになるので,通常のリストとして扱うこともできます。

g <- function(...) list(...)
g(x=1, y=2)$y  # 2

脚注

  1. R 2.15.3 の場合。 []
  2. readtable.R []