<< 目次へ
最終更新日: 2010.8.15
繰り返し処理
繰り返し処理はコンピュータの得意とする作業であり、どのプログラミング言語にもfor構文やwhile構文と呼ばれるものがあります。Rにはこれに加えてrepeat構文がありますが、これらの使い方を学んでいきましょう。データ解析の日常的な場面でこの繰り返し処理を行う機会は多く、簡単なプログラミングをする(関数を定義する)ことで作業が効率的になります。
まずは最も単純な繰り返し処理として、ベクトルの要素を第1要素から第n要素まで順番に表示させていくという関数を作ってみます。
myprog <- function(x){ n <- length(x) # ベクトルの長さを取得しnに代入 for(i in 1:n){ # n回の繰り返し処理 cat("i = ", i, "回目の繰り返しです。\n") # 何回目の繰り返しであるかを表示させる print(x[i]) # x[i]の要素を表示する } }
このfor構文において for(i in 1:n) というのは、意味としてはiを1からnまで1ずつ増やしていく(繰り返す)ということになります。最初にiに1が代入され(1回目の繰り返し)、for構文内(中カッコ { } でくくられている部分)の処理を実行します。次にiに2が代入され(2回目の繰り返し)、その次にiに3が代入され(3回目の繰り返し)、・・・ということです。
この繰り返し処理を利用すれば合計を求める関数を定義することができます。これは数値計算プログラミングの最も基礎の基礎となるプログラムです。
mysum <- function(x){ n <- length(x) # ベクトルの長さを取得 res <- numeric(1) # オブジェクトresの生成 for(i in 1:n) res <- res + x[i] # 合計の計算処理 res # 値を返す }
このプログラムの内容を順に説明していくと、1行目は先ほどと同じく変数nに引数に指定されたベクトルの長さを取得し、それを代入しています。2行目はresというオブジェクトを生成しているわけですが、これは合計を求めるためにresという変数を使うためです。これは初期化ともいい、内部的にはresという変数に値を格納するため場所をコンピュータのメモリ上に確保しているのです。ためしにこの1行を除いて実行してみると、変数resがない(メモリ上に確保されていない)ので「オブジェクト’res’がありません」というエラーが出ます。
続いてfor構文の部分についてですが、for構文以下の命令が1行だけなら { } でくくる必要はありません。ここには res <- res + x[i] という命令が書かれていますが、実際にはどのような処理がされているかを確かめてみましょう。そのために以下のような関数に書き換えてみます。
# ----- 関数定義 ここから ----- mysum <- function(x){ n <- length(x) res <- numeric(1) for(i in 1:n){ res <- res + x[i] cat("i=", i, "res=", res, "\n") } res } # ----- ここまで ----- > # 書き換えた関数mysumの実行例 > x < 1:10 > mysum(x) i= 1 res= 1 i= 2 res= 3 i= 3 res= 6 i= 4 res= 10 i= 5 res= 15 i= 6 res= 21 i= 7 res= 28 i= 8 res= 36 i= 9 res= 45 i= 10 res= 55 [1] 55
上の実行例で表示されているiは何回目の繰り返しかを表しており、その度にresに格納されている値を表示させています。1回目の繰り返し(i = 1)の時点ではres + x[1](i = 1なのでx[1]となる)という命令が実行されています。この時点でresには0が格納されていますから、res + x[1] = 0 + 1 = 1となり、結局resには1が代入されていることになります。2回目の繰り返し(i = 2)ではres + x[2]が実行され、resには1が格納されているのでres + x[2] = 1 + 2 = 3となるわけです。以下、これの繰り返しです。
以上のような繰り返し処理はwhile()を使ってでもできます。while()は引数に指定された論理が真である限り繰り返し処理が実行されます。以下のコードは「iがn以下である」というのが論理で、これが真である限り繰り返し処理が実行されます。つまりiがnより大きくなった時点で繰り返しが終了します。while()は近似計算を行うとき(円周率を計算したりするとき)に何回の繰り返し処理を行えばよいか分からないときに用いられます。一般的にはfor()の方が使う機会は多いことでしょう。
myprog <- function(x){ n <- length(x) i <- 1 while(i <= n){ # i <= nが真である限り繰り返される cat("i=", i, "回目の繰り返し\n") print(x[i]) i <- i + 1 } }
一方repeatを用いても繰り返し処理が可能です。これは既にやったif()とbreakという命令を用いてループを終了させなければなりません。以下の例ではi == 11になったら終了するようにしています。つまりi = 1:10の繰り返しを行うということです。うかつにi == 10としてしまいがちですが、そうするとi == 9の処理が終わった後に i <- i + 1 が実行される(i <- 9 + 1でiに10が代入される)ので、10回目の処理が行われる前にループが終了してしまうことになるのです。
myprog <- function(x){ n <- length(x) i <- 1 repeat{ # repeatはbreakを使わないと永遠にループ処理を続けるので注意!! cat("i=", i, "回目の繰り返し\n") print(x[i]) i <- i + 1 if(i == 11) break # i == 11になったら終了 } }
ところが、以下のコードを見てみてください。先ほどのコードとは i <- i + 1 と if() break の位置が少し違っています。この場合、if()による条件判定の後に i <- i + 1 が実行される(iに格納されている値に1が加算される)ので、if()の論理も i == 10 としておけば10回の繰り返し(i = 1:10)が行われるというわけです。このように、whileやrepaetのようなプログラマーが自らでループ終了を指示する場合、条件判定の位置を工夫する必要があるのです。
myprog <- function(x){ n <- length(x) i <- 1 repeat{ cat("i=", i, "回目の繰り返し\n") print(x[i]) if(i == 10) break i <- i + 1 } }