31. 関数の定義


新しい関数の定義

例えば引数の 2 倍を計算する新しい関数 mydouble() を定義する例を挙げる.

 
 mydouble <- function(x) 2*x

 mydouble <- function(x) {
   return( 2*x )              # 上と同じ関数定義
 }
 mydouble(2)

[1] 4

新しい演算子の定義

新しい演算子を定義することも出来る.以下では R には用意されていないインクリメント演算子 %+=% ,%-=% を定義している.以下に示した通り,新しく作成した演算子を定義する場合は演算子を ”” で囲まなければならない.

 
 "%+=%" <- function(x, y) x <<- x + y 
 "%-=%" <- function(x, y) x <<- x - y 
 x <- 1;
 (x %+=% 2)

[1] 3

 (x %-=% 3)

[1] 0

関数の返す値(返り値)

R では手続きも関数もそれぞれ「関数」と呼ぶのだが,本来の関数として用いるのならば返す値を明示的に指定すべきである.返す値を明らかにするには関数 return() を使えばよい.動作は return() が実行された時点で関数の処理が終了し, return() の引数が関数の返す値になる.

【返り値が一つの場合】
以下は,引数の 2 倍を計算する前述の関数 mydouble() だが,引数が数値ベクトルでなければ NA を返すように改造してみる.return() が実行されると関数は終了して後の文は実行されないのだが,この働きを関数の途中で抜け出すために用いることも出来る.また,return() を書かなくても最後の文は複合文全体の返り値になるので,単に値だけを書くことでも値を返すことが出来る.

 
 mydouble <- function(x) {
   if (!is.numeric(x)) return(NA)
   return(2*x)
 }

 mydouble <- function(x) {
   if (!is.numeric(x)) return(NA)
   2*x                           
 }

【返り値が複数の場合】
関数 return() に複数の値を指定すると警告が出る.警告を出さないようにするには,関数 return() の引数にリストを与えればよく,複数の返り値を返すことが出来る.このとき,リストの各成分には元の変数名が名前タグとして自動的に付加される.

 
 mydouble <- function(x) {
   y <- 2*x
   return(x, y)
 }
 mydouble(2)

$x
[1] 2

$y
[1] 4

警告メッセージ:...
 
 mydouble <- function(x) {
   y <- 2*x
   return( list(x, y) )    # 警告は出ない
 }
 mydouble(2)

[[1]]
[1] 2

[[2]]
[1] 4

名前タグを自前で指定するには,明示的に名前付きリストを返り値にすれば良い.

 
 mydouble <- function(x) return( list(input=x, output=2*x) )

【返り値が関数の場合】
R の関数はオブジェクトを返すので,返り値として関数オブジェクトを返すことも出来る.

 
 mymultiply <- function(x) {
   return( function (y){ x*y } )
 }
 result <- mymultiply(2)       # result(y) は関数 2*x
 result(3)

[1] 6

画面に返り値を表示しない

関数 invisible() を使うことで,関数の実行結果を自動的に表示しないようにする.

 
 mydouble <- function(x) {
   return( invisible(2*x) )
 }
 mydouble(3)                   # 何も表示されない
 y <- mydouble(3)              # 変数に代入して初めて表示できる
 y

[1] 6

エラー・警告を表示

関数 stop() によって意図的にエラーを発生させることが出来る.以下では引数が整数でない場合にエラーを発生させている.

 
 mydouble <- function(x) {
   if (!is.numeric(x)) {
     stop("not numeric !")
   }
   return(2*x)
 }
 mydouble("a")

以下にエラー mydouble("a") : not numeric !

エラーではなく,関数 warning() によって警告を発生させることも出来る.

 
 mydouble <- function(x) {
   if (!is.numeric(x)) {
     warning("not numeric !")
     return(NA)
   }
   return(2*x)
 }
 mydouble("a")

[1] NA
警告メッセージ:not numeric ! in: mydouble("a")

エラーが起きても作業を続行する

同様の処理を繰り返すようなシミュレーションを行う際,ある段階でエラーが起きても最後まで処理を続けたい場合は関数 try() を用いる.以下の関数 mydouble() は引数が偶数の場合はエラーを出力するが,関数 try() を用いているので,途中でエラーが起きても関数 mydouble() は最後まで実行されている.

 
 mydouble <- function(x) {
   if (x%%2 == 0) stop("error !")   # 偶数ならばエラーを
   return(2*x)                      # 奇数ならば 2*x を返す
 }
 result <- lapply(1:3, function(x){ try(mydouble(x), TRUE) } )
 result                             # 途中でエラーが出るが最後まで実行される

関数内での関数定義

関数内でも別の関数を定義することも出来る.

 
 myfunc <- function(x) {
   y <- 2
   mydouble <- function(a, b) {
     a*b
   }
   mydouble(x, y)
 }
 myfunc(3)

[1] 6

関数についての情報を見る

関数がどのような式で定義されているかを見る場合は,自分で定義した関数を名前のみ(括弧なしで)入力すればよい.また,関数の形式引数を得る場合や形式引数を設定する場合は関数 formals() ,関数 alist() を用いればよい.関数定義を見る方法は こちら

 
 f <- function(x) 2*x                      # 関数の定義
 f                                         # 関数の定義式を確認

function(x) 2*x                            # 関数 body(f) でも良い

 formals(f)                                # 関数 f の形式的引数を調べる

$x

 formals(f) <- alist(x=, y=3)              # 形式的引数 x, y を設定(y は既定値 3)
 f                                         # f の定義を確認

function (x, y = 3) 
2*x

 formals(f) <- alist(x = , y = 3, ... = )  # 「その他」の引数 ... の設定
 f

function (x, y = 3, ...) 
2*x

関数終了時の処理

関数の実行終了時は関数の実行前の状態に戻すことが望ましい.例えば,関数中でグラフィックスパラメータを変更する場合,関数の一番最初にパラメータ値を保存し,関数終了時に値を元に戻すことが望ましい.ただ,以下の一番目の関数定義では,もしも関数が途中でエラーが出て異常終了した場合でも,パラメータ値が元に戻らない.この問題は関数 on.exit() を用いて「関数が終了する時の処理」を指定することで解決する.

 
 myfunc <- function(x) {
   oldpar <- par()        # 保存
   par(col="red")
   plot(x)
   par(oldpar)            # 復帰
 } 
 myfunc(1:10)


 myfunc <- function(x) {
   oldpar <- par()        # 保存
   on.exit(par(oldpar))   # 関数終了時に復帰
   par(col="red")
   plot(x)
 }
 myfunc(1:10)

関数 on.exit() の処理は,関数が正常終了・異常終了に関わらず,関数終了時に必ず実行される.

on.exit() で指定した関数終了時の処理を初期化する場合は on.exit() を引数無しで実行すればよい.また,on.exit() の引数 add によって終了処理を追加する(add=T)のか置換する(add=F)のかを指定することが出来る.

 
on.exit(par(oldpar), add=T)    # 終了処理:par(oldpar) を追加する
on.exit(par(oldpar), add=F)    # 終了処理:par(oldpar) のみを実行する
on.exit()                      # 関数終了時の処理を無効にする