3.1 for で繰り返す
3.1.1 和を求める:for ... end
例題 ex3101a/b/c は第1章の例題 1401a/b とほとんど同じです.
プログラミング言語には,一般に, 同じ処理を繰り返すための仕組みが用意されています. for ループ と呼ばれるひとまとまりの記述は, 繰り返し処理のための最も基本的な仕組みです. あらかじめ決められた回数の繰り返し処理を実行するためによく使われます.
MATLAB での for ループは for 文 (for sentence)という構文で書かれます。 例題 ex3101a に示されているように,この構文は
for index = values
statements
end
というものです.for index = values と end の間に書かれた statements を,
決められた回数だけ実行します.statements はいくつあってもかまいませんが,
例題 ex3101a では S1 = S1 + I;
と S2 = S2 + I.^2;
の2つです.
for index = values,および,end の行の最後には,セミコロンはつけません.
セミコロンがついていなくても,これらの行に関しては何も表示されません.
上記 for 文での index は繰り返しを制御するための変数です. 例題 ex3101a では I です. 数字を横方向に並べた行ベクトルが values として与えられると, まず最初の要素が index にセットされます. 例題 ex3101a では,values として与えられたのは というベクトルですから,I の最初の値は 1 となります. index の値は,statements が繰り返されるたびに, ベクトルの次の値に更新されます. index の値がベクトルの最終要素の値(例題 ex3101a では 100)となり, その値のもとで statements が実行されると, for ループは終了します.
for ループの繰り返し回数は values として与えられるベクトルの要素の数です.
I = 1:100
とすれば100回,I = 2:2:100
とすれば50回になります.
例題 ex3101b のように,for 文を1行に書くことはできますが, このように書かない方がよいでしょう. プログラムが読みにくくなります.
例題 ex3101c での「繰返しの部分にベクトルを置くこともできます」という説明は少し奇妙です. 例題 ex3101a/b でも,この部分にはベクトルが使われています. 例題 ex3101c ではベクトルを変数(Loop)に代入しているだけです.正確には, 「ベクトルを代入した変数を置くこともできます」ということです.
3.1.2 for 文をネストする
for 文をネストするとは,for 文の中に for 文を記述することです. すなわち,for 文の statements に for 文を記述します.
例題 ex3102a は行列 のすべての成分を合計するプログラムです. このプログラムの4行目は,下図のように, 行を分けてfor 文の構造をわかりやすくする方がいいでしょう.
ネストされた for 文において変数の値がどのように更新されていくかを理解するためには, 更新された値を表示させるとよいでしょう. 例題 ex3102a の場合,変数 I, J, S の値が更新されるたびに表示されるよう, 下のようにプログラムを書き換えてみましょう.
A = [1 2 3 4; 5 6 7 8; 9 10 11 12];
S = 0;
for I = 1:3
I
for J = 1:4
J
S = S + A(I, J);
S
end
end
S
プログラムを実行すると,更新された変数の値を表示しながら処理が進みます. 起きていることを追いかけてみましょう.
外側の for ループでは,最初に I の値が 1 にセットされます.
すぐに内側の for ループに入ります.J の値が 1 にセットされます.
行列 の
成分は 1 なので,S = S + A(I, J);
というステートメントにより,
0 であった S の値が 1 に更新されます.
これで内側の for ループ1回目の処理は終わりです.
内側の for ループの最初に戻り,J の値が 2 にセットされます.
変数の現在の値は,
,
,
,
です.
行列 の
成分は 2 なので,S = S + A(I, J);
というステートメントにより,
1 であった S の値が 3 に更新されます.
内側の for ループの最初に戻り,J の値が 3 にセットされます.
行列 の
成分は 3 なので,S の値が更新されて 6 となります.
内側の for ループで J の値が 4 にセットされ, S の値は 10 に更新されます. J の値が 1 のときから 4 のときまで for ループの処理を繰り返したので, ここで内側のループを一度抜けます. 外側のループに戻って,I の値が 2 にセットされます.
内側のループに入り,J の値が 1 にセットされます. 行列 の 成分は 5 なので,S の値が更新されて 15 となります. 内側の for ループの最初に戻り,J の値が 2 にセットされます.
このように処理を繰り返して, , , のときの S の値の更新が終わると,内側のループを抜け, すぐに外側のループも抜けます. 最終的な S の値を表示してプログラムの実行は終わりです.
MATLAB の for 文
for index = values
statements
end
では,index の values として,行ベクトルだけでなく, 行列を与えることもできます. それを示しているのが例題 ex3102b です.
例題 ex3102b での,ループを制御する index は C
です.
わかりにくければ I
に変えてください(2か所あります).
変数の値が更新されていく様子を観察するため, プログラムを下のように書き換えてみましょう.
A = [1 2 3 4; 5 6 7 8; 9 10 11 12];
S = 0;
for C = A
C
S = S + sum(C);
S
end
S
プログラムを実行すると,更新された変数の値を表示しながら処理が進みます. 起きていることを追いかけてみましょう.
行列
が for 文の index(この例題では C)の values として与えられると,
最初のループではこの行列の第1列が C にセットされます.
ループの中で,sum(C)
は第1列の和である 15 を返します.
行ベクトルでも列ベクトルでも,sum 関数の引数として指定されると,
要素の総和が返されます.
コマンドウィンドウで
sum([1 5 9]')
とキーインして確認してください.変数 S の初期値は 0 だったので,
S = S + sum(C);
というステートメントにより,
S の値は 15 に更新されます.
1回目のループでの処理はこれで終わりです. ループの最初に戻って, C に行列 の第2列がセットされます.
ループの中で,sum(C)
は第2列の和である 18 を返します(この結果は表示されていません).
S の値は 15 だったので,
S = S + sum(C);
というステートメントによってこれに 18 が加えられ,S の値は 33 に更新されます.
ループの最初に戻って,
C に行列 の第3列がセットされます.
このように処理を繰り返して,for 文の index である変数 C に行列 の第4列がセットされ, 変数 S の値が更新されるとプログラムは終わりです. 最終的な結果(すなわち,行列 のすべての成分の和)が表示されます.
行列の成分の総和を計算するプログラムは,実は for 文を使わずに書くことができます. MATLAB のプログラミングでは,その方が簡潔で実行が高速となるので,よいプログラムです. 例題 ex3301b で学習します.
3.2 while で繰り返す
3.2.1 和を求める:while ... end
繰り返しのための,for ループと並んでよく用いられる仕組みが,while ループ です.MATLAB での while ループは while 文(while stentence)と呼ばれる構文で書かれます.
テキスト p.88 に示されているように,while 文の構文は
while expression
statements
end
というものです.テキストでは expression(式)の部分に「終了条件」と書いてあります. この「終了条件」は,繰り返しを終える条件のように読めますが, 実際には,この条件が満たされている間は statements に書かれた処理を繰り返します. while 文の expression(式)が評価されると, 論理値(2.3.10節)である「真(1)」あるいは「偽(0)」が返されます. 真であれば statements に書かれた処理が実行されます. 偽であれば statements は実行されず,while ループを抜けます.
同じ計算を行う,
例題 ex3101a(for 文)と ex3201a(while 文)をよく比較してください.
例題 ex3201a(while 文)では,index である変数 I の初期値を,
while 文の外側で与えています(プログラムの1行目).
変数 I の値の更新は I = I + 1
というように,
while 文のステートメントで明示的に書きます.
例題 ex3201a では,1行目で変数 I の値を 0 としています.
2行目から始まる while ループで,I < 100
という条件が満たされているか,つまり,この
命題(proposition)が真であるかどうかがテストされます.
最初は I = 0 ですので,この命題は真であり,テストの結果は論理値の 1 となります.
コマンドウィンドウで 0 < 100
とキーインしてみてください,
論理値であることを意味する logical という表示と共に 1 が返されます.
テストの結果が 1 ならば,3行目に書かれた while 文のステートメントが実行されます.
ステートメントが実行されると,while ループの最初に戻ります,
このとき,index である I の値は更新されて 1 になっています.
命題 I < 100
は真ですので,while 文のステートメントが実行されます.
このように処理を繰り返し,index である変数 I の値が100となったとき,
命題 I < 100
は偽で,テストの結果は論理値の 0 となります.
そうすると,while 文のステートメントは実行されず,
while ループを抜けて次(プログラム5行目)に進みます.
例題 ex3201a での,S1 = S1 + I;
および
S2 = S2 + I.^2;
という計算で用いられる変数
I の値は 1 から 100 です.しかし,プログラムでは I の初期値は 0 で,
I の値が 100 のときは while 文は実行されません.
変数 I の初期値を 1 として,
I の値が 100 のときは while 文を実行するようにプログラムを書き換えた方が,
わかりやすいかもしれません.以下のように書き換えることができます.
while 文の中で index である変数 I の値を更新するタイミングを最後にしています.
S1 = 0; S2 = 0;
I = 1;
while I <= 100
S1 = S1 + I;
S2 = S2 + I^2;
I = I + 1;
end
S1, S2
プログラムは構造が見やすいように書くべきであり, 例題 ex3201b のように書くことはやめた方がよいでしょう.
このセクションの補足解説の最後に,命題についての説明をしておきます.
命題(proposition)の一般的な定義は「真偽を判断できる文」です.
MATLAB では「コマンドウィンドウにキーインすると何らかの数値が返ってくるもの」と考えるとよいでしょう.
このときの値が 0 であれば「偽」の命題,0 以外であれば「真」の命題となります.
たとえば,変数 x に何か数値が代入されていれば.
x とキーインすればその数値が返されますので,x は命題です.
テキスト p.88 で述べているように,x および y が命題であるとき,
これらを
関係演算子(relational operator)でつないだ式(たとえば
x==y
)も命題となります.
関係演算子については 2.3.11 節で学習しました.
定数も,キーインすればその数値が返されますから,命題と考えることができます. テキストでは「定数命題」と書かれていますが,あまり使われない言葉です.
命題に 論理演算子(logical operator)を適用したものも命題です.
たとえば,~5
とキーインすると,論理値の 0 が返されます.
関係演算子については 2.3.10 節で学習しました.
3.2.2 while 文をネストする
例題 ex3202a は例題 3102a の while ループバージョンです. 繰り返しを制御する変数 I および J は while 文の直前で初期値を与えます. これら変数の値の更新は,while ループの継続条件のテストの直後に行われていますが, これは絶対ではありません.例題 ex3201a の解説でプログラムを書き換えたとき, 変数 I の値の更新は while ループの末尾でした.
繰り返しを制御する変数 I の初期値をプログラムの2行目で与えていますから,
ここで J の初期値も与えてしまえばよさそうに思うかもしれません.
つまり,2行目を
S = 0; I = 0, J = 0;
として,5行目から J = 0;
を消去すればよさそうに見えます.
しかし,これはうまくいきません.
試してみて,なぜうまくいかないか考えてみてください.
例題 ex3202a での行列 は3行4列ですから,
while 文でのループの継続条件が
I < 3
および J < 4
となってるのは最後の行と列が除外されているようで,
不自然に思えるかもしれません.
練習問題として,継続条件が
I <= 3
および J <= 4
となるようプログラムを書き替えてみてください.
ヒントは例題 ex3201a で同様の書き換えをしたプログラムにあります.
例題 ex3202b は例題 3102b の while ループバージョンです.
例題 ex3102b では繰り返しを制御する index の値が行列でしたが(プログラム3行目の
C = A
),
例題 ex3202b では while ループの継続をコントロールする変数 J の値は整数ですので,
理解が簡単かもしれません.
プログラム3行目の sum(A(;, J))
は行列
の第 J 列の要素の総和を返します.
変数の値がどのように更新されていくかを理解するためには, 例題 ex3102a の解説で述べたように,更新された値を表示させるとよいでしょう. プログラムを以下のように書き換えて実行してみてください.
A = [1 2 3 4; 5 6 7 8; 9 10 11 12];
S = 0; J = 0;
while J < 4
J = J + 1;
J
sum(A(:, J)) % 次の行で使われる値をここで表示
S = S + sum(A(:, J));
S
end
S
3.3 どちらが速いか:計算時間
3.3.1 計算時間を測る:tic, toc
例題 ex3301b の2行目では,sum(A)
が返す結果を
sum 関数の引数としています.
コマンドウィンドウで,
A = [1 2 3 4; 5 6 7 8; 9 10 11 12]
sum(A)
とキーインして, sum 関数の引数が行列のときどのような結果が返されるのかを確認してください. 列ごとの和が求められ,行ベクトルが返されることがわかります.
例題 ex3301c は,行列のすべての要素の和を3通りの方法で計算し, 計算に要する時間を比較しています. 少し長いプログラムです. 1.7 節で学習したように,長いプログラムは, テキストに書かれたプログラムを全部入力してから実行するのではなく, 部分的に動作をテストすることを繰り返して完成させます. まずは Program 1 までを書いてテストしましょう. それから Program 2 を書き加え,最後に Program 3 と書いて全体を完成させます.
もうひとつ重要なことは,大きなデータをいきなり扱うことはせず, 小さなデータでテストすることです. プログラムの1行目で 1,000 行 1,000 列の行列 を作成していますが, 最初はもっと小さなサイズの行列でテストしてください.
プログラムがエラーなく動作していても, エラーにはならないミスがプログラムにあり, 意図した計算が行われていないかもしれません. Program 1 から Program 3 のすべてにおいて, S の最終的な値が同じかどうかを確認しておいた方がよいでしょう. toc でタイマーを止める直前か直後に,S の値を表示させてください.
例題 ex3301c の Program 1 をスタートさせたときにワークスペースにあるオブジェクトは N と A です. Program 2 をスタートさせたときには,これらに加え,I, J, S, T1 があります. この条件をそろえた方が公平だと考えるならば, Program 2 の前に,I, J, S, T1 をクリアすればよいでしょう. 具体的には,Program 1 の終わりで,
S % S の最終的な値を表示
T1 = toc
clear I J S T1;
% Program 2
とします.Program 3 についても同様のことを行います.
例題 ex3301c のプログラムを書き上げた後, このプログラムの一部(たとえば,Program 1)だけを実行したいことがあるかもしれません. このようなときには, プログラムをいくつかのセクション( コードセクション と呼びます)に分割して実行するということができます. その方法を説明します.
Program 1 だけをひとつのコードセクションにしたいとします.
コードセクションの開始行(% Program 1: for
)の先頭にカーソルを置きます.
[エディター] タブの [編集] セクションにある [挿入] ボタングループで、
[セクション] のアイコン(下図)をクリックします.
Program 1 の先頭に2つのパーセント記号(%%)が挿入されます. [セクション] のアイコンをクリックするのではなく, この記号をキー入力することもできます.
次のコードセクションの開始行の先頭にカーソルを置き, [セクション] のアイコンをクリックします. 2つのパーセント記号(%%)が挿入され,そこからは次のセクションとなります. これで Program 1 だけがひとつのコードセクションとなりました. 例題 ex3301c は,これで3つのコードセクションに分割されたことになります. N と A を与える第1行.Program 1,Program2 以降です.
コードセクションを実行するには, 実行したいコードセクションの中にカーソルを置きます. [エディター] タブの [実行] セクションで,[セクションの実行] のアイコンをクリックします. [セクションの実行] のアイコンをクリックするかわりに, [Ctrl] キーを押しながら [Enter/Return] キーを押すことでもセクションを実行できます.
N と A は必要なので最初に第1行を実行しましょう. 次に,Program 1 のセクションコードを実行しましょう.
3.3.2 領域の確保と並列化
領域の確保とは,変数にベクトルや行列を代入するとき, そのベクトルや行列のサイズにあわせた領域をメモリ上に確保しておくということです. 変数を箱にたとえるメタファーでは, 入れるものにあわせた大きさの箱を用意するということです. たとえば,10冊の本を段ボール箱に入れたいとき, それができる大きさの段ボールを用意するでしょう. 最初は小さな箱に本を入れ, 途中で入らなくなったら大きな箱に取り換えるということは非効率です. ところが,MATLAB では, 最初は小さな箱に本を入れるというプログラムを書くことができてしまいます. そうならないよう,初めから十分な大きさの箱を用意します.
並列化とは,同時に複数の処理を実行することです. ここでは,ベクトルや行列の要素に対して, for 文や while 文を使って逐次的に(各要素を順に)処理するのではなく, ベクトルや行列を引数にとる関数を使って同時に処理するということです. これは例題 ex3301c の Program 3 ですでに行ったことです. 例題 ex3302 でも Program 3 が並列化を行っています,
例題 ex3302 では N に大きな値(50001)が代入されています. 3.3.1 節の解説で述べたように, 大きなデータをいきなり扱うことはせず, 小さなデータでテストする ことにしましょう.最初は 10 ぐらいの値でプログラムをテストするとよいでしょう. 最終的なプログラムでは, 50001 ではなく,50000 というきりのよい数字を使ってもかまいません.
Program 1 と Program 2 では,N の値を小さくして, for ループによってベクトル Y の値が更新されていく様子を観察してみましょう. ループ中のステートメントで,式の末尾にあるセミコロンを外します. Program 1 を以下のように書き換えて実行してみます.
% Program 1: for
clear; N = 10;
tic
for I = 1:N+1
Y(I) = sin((I - 1) ./ N)
end
T1 = toc
実行結果は以下のようになります.出力の最初の部分と最後の部分を示します.
最初のループで,ベクトル Y には の値である 0 が保存されます.ベクトル Y の要素はこのひとつだけで, ベクトルのサイズは 1(1行1列の行列)です. 次のループでは,ベクトルの要素がひとつ増やされ, 第2の要素に の値である 0.0998 が保存されます. ベクトルのサイズは 2(1行2列の行列)です. 最後のループでは,ベクトル Y の11番目の要素に の値である 0.8415 が保存されます. ベクトルのサイズは 11(1行11列の行列)です.
Program 2 もベクトル Y が更新されていく様子を同様に観察してください. Program 1 との違いがよくわかると思います.
3.3.3 インデックス利用による高速化
このセクションはスキップしてください.
例題 ex3303a は,テキストも述べているように, プログラム4行目でのインデックスの設定がやっかいです. 行列 B の第 (I - 1) .* 2 + II 行には,行列 A の第 II 行が配置されます. このプログラムの for ループでの I および II の値に対して, 行列 B の第 (I - 1) .* 2 + II 行は,以下の表のように決まります.
I | II | (I - 1) .* 2 + II |
---|---|---|
1 | 1 | (1 - 1) × 2 + 1 = 1 |
1 | 2 | (1 - 1) × 2 + 2 = 2 |
2 | 1 | (2 - 1) × 2 + 1 = 3 |
2 | 2 | (2 - 1) × 2 + 2 = 4 |
3 | 1 | (3 - 1) × 2 + 1 = 5 |
3 | 2 | (3 - 1) × 2 + 2 = 6 |
行列 B の第 (J - 1) .* 3 + JJ 列には,行列 A の第 JJ 列が配置されます. このプログラムの for ループでの J および JJ の値に対して, 行列 B の第 (J - 1) .* 3 + JJ 列は, 以下の表のように決まります.
J | JJ | (J - 1) .* 3 + JJ |
---|---|---|
1 | 1 | (1 - 1) × 3 + 1 = 1 |
1 | 2 | (1 - 1) × 3 + 2 = 2 |
1 | 3 | (1 - 1) × 3 + 3 = 3 |
2 | 1 | (2 - 1) × 3 + 1 = 4 |
2 | 2 | (2 - 1) × 3 + 2 = 5 |
2 | 3 | (2 - 1) × 3 + 3 = 6 |
この2つの表から,行列 B の各成分に,行列 A のどの成分が配置されるのかわかります. たとえば,B(2,3) は A(2,3) です.B(4,5) は A(2,2) です.
例題 3303a は,行列 A の特定の要素をひとつずつ,逐次的に取り出して, 行列 B を構成しています. これを並列化することを考えます. 3.3.2 節では,ベクトルや行列を引数に取る関数を使用して並列化を行いました. 例題 3303b では,行列の要素を指定して取り出す A(M, N) という演算において, インデックス M および N にベクトルあるいは行列を与えることによって並列化を実現します. 具体的には,(3.7) 式に示されているように,
というインデックスを用います. これらインデックスを用いることでなぜ目標の行列 B が得られるのか不思議に思うでしょうが, まずはこのインデックスの構成方法から理解していきましょう.
例題 ex3303b の2行目では,インデックス M を,MIndex という名前で作成しています. まずは
MIndex = (1:2)'
によって,
という列ベクトル MIndex を作成します. つづいての,
MIndex = MIndex(:, ones(1, 3))
は,
MIndex = MIndex(:, [1 1 1])
と書くことと同じです.この意味を理解するには,列ベクトル MIndex を作成してから,
MIndex(:, [1])
MIndex(:, [1 1])
MIndex(:, [1 1 1])
という3行をコマンドウィンドウで順にキーインしてみるとよいでしょう. 以下のような結果が得られます.
MIndex から取り出す行はコロン(:)が指定されていますから, すべての行が取り出されます. つまり,MIndex の全体がそのまま取り出されます. そうしておいて,取り出す列を
[1]
と指定すれば,MIndex の第1列が取り出されます. MIndex は列ベクトルで,1列しかありませんから,これは MIndex そのものです.
取り出す列を
[1 1 1]
と指定すれば,MIndex の第1列を取り出すことを3回繰り返し, 横方向に並べることで,行列
が作成されます.これをあらためて MIndex とします.
例題 ex3303b の3行目では,これと同じ方法で,インデックス NIndex を作成しています.
それでは最後に,
であるときの,A(M, N) の意味を理解しましょう. 例題 ex3303b では,このインデックス M は MIndex, インデックス N は NIndex という名前がつけられています.
まず,A から取り出す行として MIndex という指定を行うことは, どのような意味なのでしょうか? 例題 ex3303b の1行目から2行目を実行した後,コマンドウィンドウで,
A(MIndex, :)
A([1 2 1 2 1 2], :)
という2行を順にキーインしてください.以下のような結果が得られるので,この2行が同じ意味であることがわかります.
つまり,行列 A から取り出される行は,インデックス MIndex によって, 下図のように決められていることがわかりました.
このことから,NIndex による列の取り出しは,
というように決められるのではないかと予想できます.確かめてみましょう. 例題 ex3303b の第3行を実行した後,コマンドウィンドウで,
A(MIndex, NIndex)
A(MIndex, [1 2 3 1 2 3])
という2行を順にキーインしてください.以下のような結果が得られるので,この2行が同じ意味であることがわかります.
これで例題 3303b は完全に理解できたでしょう. 例題 3303c を行って,行列のインデックスを用いた高速化の効果を確かめてください.
3.4 if で分岐する
3.4.1 実行したりしなかったり:if ... end
条件が満たされているかどうかをテストして, その結果に応じて処理を分岐させる仕組みを実現するのが if 文 です.
例題 ex3401a のプログラムでは,1行目と5行目のセミコロンをはずして, X の値を表示させた方がわかりやすいでしょう. 2つ目の if 文では X > 0 という命題(式)が偽となるので, 実行文は実行されません.
3.4.2 こっちやあっちを実行したり:if ... else ... end
MATLAB での if ... else ... end という構文は Excel の if 関数に対応します. Excel の if 関数は
=if(判定条件,真の場合,偽の場合)
という書式ですね.MATLAB では
if 判定条件
真の場合の実行文
else
偽の場合の実行文
end
となります.実行文は複数書くことができます.
MATLAB の if 文は
if expression
statements
end
という構文が基本であり, elseif ブロック(3.4.3 節)および else ブロックはオプションです. 例題 ex3402 は
if expression
statements
else
statements
end
という構造をしています.
例題 ex3402 で,変数 X の値がプログラム中で決められているのは少しつまらないですね. 入力を受け付け,その数値の正負を判定したいです. これを行うには,プログラムの1行目を
X = input('Enter X: ');
と書き換えてください.プログラムを実行すると, コマンドウィンドウで X の値の入力を求められます. 適当な数値を入力して [Enter/Return] キーを押してください. 例題 ex3403 も同様に書き換えるとよいでしょう.
3.4.3 3つに分けて実行する:if ... elseif ... else ... end
3つ以上のケースに処理を分けたいときには elseif を使います. 注意すべきは,elseif の後には必ず判定条件が書かれるということです. 最初の if の後に書かれた命題(式)が偽のとき, elseif の後に書かれた第2の条件がテストされます.
命題(式)の真偽のテストがこのように階層的になっていることは, 例題 ex3403 と同じ動作をする次のプログラムから始めるとわかりやすいかもしれません.
X = 0
if X > 0
disp('X is positove.');
else
if X < 0
disp('X is negative.');
else
disp('X is zero');
end
end
このプログラムでは else のブロックに if ... else を埋め込んでいます. ここで,連続する else と if をまとめて elseif と書き, if がひとつ消えることにあわせて end をひとつ消すと, 例題 ex3403 のプログラムとなります.
3.5 switch で分岐する
3.5.1 値で場合分けする:switch ... case
いくつかのケースに処理を分けたいとき, switch 文 を使うことができます. この文ではケースを並列します. 命題のテストが階層的であった elseif と異なり, 並列的に列挙されたケースの中からひとつ選ばれ, そこに書かれた処理が実行されます.文の構造は
switch switch_expression
case case_expression
statements
case case_expression
statements
...
otherwise
statements
end
となります.
最初の switch_expression に使えるのは,数値,あるいは文字を返す式です.
たとえば,例題 ex3501a では sign(X)
という式が使われています.
この sign 関数は,数値を引数にとり,
その数値の符号に対応した数値を返します.
式 switch_expression が値を返すと, その値は case_expression として書かれた値と照合されます. 値が一致すると,その case に書かれているステートメントが実行されます. 値の照合は上に位置する case から順に行われます. 一致してその case に書かれた処理が実行されると. そこより下に書かれた case での照合は行われず.switch 文を抜けます.
最後の otherwise ブロックはオプションであり,なくてもかまいません. しかし,式 switch_expression が返した値に一致する case がなかった場合の処理として, このブロックを用意する方がよいでしょう.
例題 ex3501b のように,ひとつの case に複数の case_expression を指定することもできます. この場合,複数の数値(あるいは文字列)は { } の中に並べます. これはテキストで扱わない セル配列(cell array)というデータ型です.
3.6 ループの中止と継続
3.6.1 みつかれば中止する:break
例題 ex3601 で用いられている clock 関数がどのような値を出力するのか, コマンドウィンドウでキーインして確かめてみましょう.
この出力は 2020年7月4日18時50分15秒
という時刻を表しています.
出力の最初に
1.0e+03 *
という表記があることに注意してください.
これは
という意味です.
したがって,1.0e+03 * 2.0200
は 2020 になります.
他の数値も同様です.
指数表示がわかりにくければ,出力形式を shortG にしてください.
以下のように簡潔な表示がなされます.
例題 ex3601 のプログラム2行目から始まる while ループは
etime(clock, T0) < 5
という条件が満たされている限り継続します.
ここで,変数 T0 にはプログラム1行目の clock 関数が出力した時刻が保存されています.
これはプログラムの1行目が実行された時刻です.
プログラム2行目の clock 関数は変数 T0 に保存されている時刻より後の時刻を返すはずです.
これら2つの時刻の差が5秒以内であれば,while ループに書かれている処理が実行されます.
プログラム3行目の fix 関数がどのように数値を丸めるか, テキストでの「0 方向に切り捨てて整数にする」という意味がよくわからなければ, 下図のようにコマンドウィンドウで確認してください. 数直線上で,0 に近い方にある整数値にするという意味です. 切り捨てではなく四捨五入にしたければ round 関数を用います.
生成される乱数の値の範囲が 0 以上 9999 以下の整数ですから, while ループ1回あたりで 2010 という値が生成される確率は1万分の1です. こんな小さな確率の事象は起きないように思うかもしれませんが, ループは高速に何度も繰り返すので,5秒もあればこの値はほぼ確実に得られます. 最近のPCなら1秒あれば十分でしょう.
練習問題をひとつ出しておきます. 例題 ex3601 で 2010 という値が得られたとき, 何回目のループで得られたかを表示するようにプログラムを書き換えてみましょう. 次の 3.6.2 節で学習する例題 ex3602 がヒントになると思います.
3.6.2 みつかっても続ける:continue
MATLAB のドキュメンテーションでは, 「continue ステートメントは,for ループまたは while ループ内の残りの命令をスキップし, 次の反復を開始します」と書かれています. ループの中で if 文に組み込んで使用されます. 条件が満たされたときに,ループでのそれ以下の部分をスキップし, 次のループ(厳密には,次のループの実行条件のテスト)を開始します.
例題 ex3602 は,continue の機能を説明する例としてはいいですが, プログラムとしてはあまり良いプログラムではありません. なぜなら,while 文を
while etime(clock, T0) < 5
X = fix(rand() * 10000);
if X == 2010
N = N + 1;
end
end
と書けば,continue を使わずに済むからです. もう少し複雑な処理を行うループでないと, continue を使う必要はなさそうです. これは無理に使うものではなく,使わずに済むならその方がいいです.