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 には stringAsFactors や blank.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
脚注
- R 2.15.3 の場合。 [↩]
- readtable.R [↩]