<< 目次へ
最終更新日: 2010.8.15
関数の定義
Rには演算子というものがあり、これによって数値計算を行うことができます。数値計算などというと大げさに聞こえるかもしれませんが、要するに四則演算(足し算、引き算、掛け算、割り算)のことです。これはそれぞれ+、-、*、/で行うことができます。また、演算子といってもこうした数値計算のための演算子もあれば、論理演算子といって==(等しい)、!=(等しくない)、>=(〜以上)、<=(〜以下)、あるいは>(より大きい)、<(より小さい)、さらに&(論理積:"かつ")や|(論理和:"または")といったものがあります。
|
|
ここでは四則演算を行う関数を定義してみましょう。関数には(a)関数名、(b)引数、(c)関数の中身(処理内容)によって構成されており、Rでは以下のようにして関数を定義します。まずは第1引数と第2引数に、それぞれ1つずつの数値を指定すると、その和を返す関数を定義してみます。
# ----- 関数の定義 ここから ----- plus <- function(a, b){ res <- a + b return(res) } # ----- 関数の定義 ここまで ----- > plus(3, 7) # 実行例:結果は3+7=10となる [1] 10
ここでplusというのは関数名であり、これは今までに数値や文字列をベクトル、行列、データフレームとして任意のオブジェクトに代入してきたように、関数を代入することもできます。ここではplusというオブジェクトにfunction(第1引数, 第2引数){ 関数の中身(処理内容) }を代入しているというわけです。中カッコ { } の中身を見てみると、1行目に res <- a + b とあります。これは引数に指定されたaとbを足して変数resに代入せよ、という命令です。その後、return()という関数で値を返しています。return()は引数にしたオブジェクトを返すための関数です。これは、単に省略して以下のようにしても構いません。
# ----- 関数の定義 ここから ----- plus <- function(a, b){ res <- a + b res } # ----- 関数の定義 ここまで -----
こうした関数は最も単純なものの1つですが、四則演算を行うために次のような関数を定義してみます。以下の関数において、第3引数にはmethod=""と書かれていますが、これはデフォルト(何も指定しなければ)method=""と指定したことと同じとみなされます。つまり割り算が行われるということです。
# ----- 関数の定義 ここから ----- shisoku <- function(num1, num2, method=""){ if(method == "plus"){ res <- num1 + num2 return(res) }else if(method == "minus"){ res <- num1 - num2 return(res) }else if(method == "multiple"){ res <- num1 * num2 return(res) }else{ res <- num1 / num2 return(res) } } # ----- 関数の定義 ここから ----- > shisoku(1, 2, method="plus") [1] 3 > shisoku(1, 2, method="minus") [1] -1 > shisoku(1, 2, method="multiple") [1] 2 > shisoku(1, 2) [1] 0.5
関数の中身の説明は一旦、置いておくとして、上記のコードは次のように書き直すことで、より可視性の高いコードとなります(内容は全く同じものです)。可視性の高いコードを書くことは関数の流用のために役立ちますし、バグの発見(コードの誤り)を探す際にも役立ちます。
shisoku2 <- function(num1, num2, method=""){ if(method=="plus") return(num1 + num2) else if(method=="minus") return(num1 - num2) else if(method=="multiple") return(num1 * num2) else{ return(num1 / num2)} }
それでは中身を説明していきましょう。まず1行目の if(method=="plus") という部分は「もしmethodがplusならば〜」という意味で、TRUE(真)ならばif文以降の命令: return(num1 + num2)が実行されます。続いて2行目の else if(method=="minus") return(num1 - num2) は「〜ではなく、もしmethodがminusならば〜」という意味です。同じようにこの条件がTRUEであれば、if文以降の命令: return(num1 - num2)が実行されることになります。3行目も2行目と同じですが、最後の else{ return(num1 / num2)} は「それ以外(上記のいずれとも違う)ならば〜」という意味で、else以下 { } でくくられた部分の命令が実行されます。
あえて日本語に訳してみると次のようになります。
shisoku2 <- function(num1, num2, method=""){ もし(method=="plus") ならば return(num1 + num2) それ以外でもし(method=="minus") ならば return(num1 - num2) それ以外でもし(method=="multiple") ならば return(num1 * num2) それ以外なら{ return(num1 / num2)} }
なお、if()以降にある命令文が1行である場合は { } でくくる必要はありませんが、2行以上ある場合は { } でくくらなければなりません。elseの後は必ず { } が必要となります。ただし、elseの後に来る命令を1行改行して書けば { } でくくる必要はありません。これはなぜかというと、見た目上はスペースが空いているように見えても、Rではスペースが無視されるので、実際には elsereturn(num1 / num2) という命令が下されていることになります。だから { } でくくっておかないと「関数 "elsereturn" を見つけることができませんでした」というエラーメッセージが出てしまうのです。
ところで、本来はプログラミングの入門書で最初に教えられる関数(プログラム)はHelloプログラムといって、単に画面上に「Hello!!」と表示されるだけのものを作ります。もちろんRでも作れますが、2つの方法があります。
# ----- cat()を用いた関数定義 ここから ----- Hello.prog <- function(){ cat("Hello!! This is my first program. \n") } # ----- ここまで ----- # ----- print()を用いた関数定義 ここから ----- Hello.prog2 <- function(){ print("Hello!! This is my first program.") } # ----- ここまで -----
これら2つを実行してみると以下のようになります。
> Hello.prog() # cat()を使っている Hello!! This is my first program. > Hello.prog2() # print()を使っている [1] "Hello!! This is my first program."
少し表示のされ方が違うようですが、cat()は引数に指定されたオブジェクトを表示するだけの関数であるのに対して、print()は指定されたオブジェクトをリストとして返す機能を持っているのです。リスト(list)はベクトル(vector)や行列(matrix)のようなデータ構造の1種です。例えば下の実行例を見ていると、Hello.prog()は内部でcat()が使われているので、変数aに代入しようとしても、cat()はただ画面に表示するだけの機能しかないので、中身を確認してもNULL(何もない、空のベクトルという意味)と表示されます。一方でHello.prog2()は内部でprint()を使っているため、リストとしてオブジェクトが返され、それがaにも代入されています。だからtypeof()で型を調べてみるとcharactorであることが確認できるわけです。
> a <- Hello.prog() # Hello.progはただ文字列が表示されるだけ Hello!! This is my first program. > a # だからaには何も代入されない NULL > a <- Hello.prog2() # Hello.prog2はリストとしてオブジェクトを返す [1] "Hello!! This is my first program." > a # だからaには文字列が返され、 [1] "Hello!! This is my first program." > typeof(a) # 型を調べてみると、確かにcharacter型である。 [1] "character"