GNU Octave 2.1.x 日本語マニュアル/ステートメント
12 ステートメント
[編集]ステートメントは,単純な定数式,あるいは入れ子になったループや条件式の複雑なリストになるでしょう。 ifやwhileなどのような制御ステートメントは,Octave プログラムにおける実行の流れを制御します。 すべての制御ステートメントは,単純な式と区別するために,ifやwhileのような特別なキーワードで開始します。 多くの制御ステートメントは,他のステートメントを含んでいます。 たとえば,ifステートメントは,実行されたりされなかったりする他のステートメントを含みます。 各々の制御ステートメントには,その制御ステートメントの終了を明記するような,対応する終了ステートメントがあります。 たとえば,endif というキーワードは,ifステートメントの終端をマークし,endwhileはwhileステートメントの終端をマークします。 キーワードendは,より具体的な終了キーワードが期待できるようなところではどこでも使用することができます。 しかし,より具体的な終了キーワードを使用するほうが好ましいのです。 なぜならば,それを使用すると,Octaveは,ミスマッチやend句の付け忘れを診断しやすくなるからです。 ifやwhileのようなキーワードと,それに対応する終端ステートメントの間に含まれる一連のステートメントは,本文(body)と呼ばれます。
12.1 ifステートメント
[編集]ifステートメントは,Octave の意志決定を行うステートメントです。 ifステートメントには,3種類の基本的な使い方があります。 その最も単純な使い方は,以下のようなものです。
if (condition)
then-body
endif
condition は,ステートメントの残り部分が実行するであろうことを制御する式です。 then-body は,condition が真であるときのみ実行されます。 ifステートメントにおける条件は,その値がゼロでないならば真,その値がゼロならば偽であると見なされます。 もしifステートメントの条件式の値がベクトルまたは行列ならば,その要素のすべてがゼロでない場合のみ真であると見なされます。 ifステートメントの2番めの使い方は,次のようなものです。
if (condition)
then-body
else
else-body
endif
もしconditionが真ならば,then-bodyを実行します。 そうでないならば,else-body を実行します。 例を示します。
if (rem (x, 2) == 0)
printf ("x is even\n");
else
printf ("x is odd\n");
endif
この例において,もし式
rem (x, 2) == 0
が真ならば(すなわち,x が2 で割り切れるならば), 最初のprintfステートメントが評価され,そうでないならば2 番めのprintfステートメントが評価されます。
ifステートメントの第3 の,そして最後の使い方は,複数の決定事項が,ひとつのステートメン トに結びつけられているようにします。 それは,以下のようなものです。
if (condition)
then-body
elseif (condition)
elseif-body
else
else-body
endif
任意の数のelseif節を入れてもかまいません。 各々の条件は,順にテストされ,もしある条件が真 であるならば,それに対応するbody を実行します。 もしどの条件も真でなく,else節が存在するな らば,その本文を実行します。 else句は1 つだけ入れることができ,それはステートメントの最後の 部分に置かなければなりません。
以下のサンプルにおいて,もし最初の条件が真であれば(すなわちxの値が2で割り切れるならば),最初のprintfステートメントが実行されます。 もしそれが偽ならば2 番めの条件が検定され,それが真ならば(すなわちxの値が3で割り切れるならば),2番めのprintfステートメントが実行されます。 どれでもないならば,3番めのprintfステートメントを実行します。
if (rem (x, 2) == 0)
printf ("x is even\n");
elseif (rem (x, 3) == 0)
printf ("x is odd and divisible by 3\n");
else
printf ("x is odd\n");
endif
elseifというキーワードは,Fortranでは許容されているようなelse ifと綴ってはいけません。 もしそうするならば,elseとifの間のスペースは,Octave は,別のif内に新しいifがあるように扱うように命令します。 たとえば,もし次のように書くならば,
if (c1)
body-1
else if (c2)
body-2
endif
Octave は,最初のifステートメントを完了するために,追加入力を期待するでしょう。 もしOctaveを対話的に使用しているならば,追加的な入力のためにプロンプトを出し続けるでしょう。 もしOctaveがこの入力をファイルから読み込んでいるならば,endステートメントが欠損しているかミスマッチである旨のエラーを吐くでしょう。 あるいは,より具体的なendステートメント(endif,endforなど)を使っていないとすれば, 何の警告メッセージを表示することなく,単に正しくない結果を生み出すでしょう。 上のステートメントを以下のように書き直すと,エラーを見つけるのがずっと容易になります。
if (c1)
body-1
else
if (c2)
body-2
endif
Octave がステートメントをどのようにグループ化するのかを示すための字下げをしてあります。 Chapter13 [Functions and Scripts]を参照してください。
warn_assign_as_truth_value
[編集][Built-in Variable]
もしwarn_assign_as_truth_valueの値がゼロでないならば,以下のようなステートメントについて警告を発します。
if (s = t)
...
なぜならば,このようなステートメントは普通には起こりえず,かわりに以下のように書きたかったように思われるからです。
if (s == t)
...
whileあるいはifステートメントの条件内に代入演算子を含むコードを書く場合に有効です。 たとえば,以下のようなコードは,C 言語ではごくふつうに使われます。
while (c = getc())
...
warn_assign_as_truth_valueに0をセットすることにより,このようなステートメントについての全ての警告を回避することが可能です。 しかし,そのようなステートメントは,以下のような本当のエラーを見逃すことにもなります。
if (x = 1) # intended to test (x == 1)!
...
このような場面において,さらにもうひと組のかっこを付けて書くことにより特定のステートメントに対してエラーを抑えることが可能です。 たとえば,以前のサンプルを,
while ((c = getc()))
...
と書くことにより,このステートメントに対する警告を表示しないようにするだろう。 一方で,条件文において使用される他の代入について,警告を発するようになります。 warn_assign_as_truth_valueの初期値は1です。
12.2 switchステートメント
[編集]switchステートメントは,Octave のバージョン2.0.5 で導入されました。 これは実験的なものであると考えるべきですし,実装の詳細はOctave の将来のバージョンでわずかに変更される可能性があり ます。 もし実際のプログラムにおいて,この新しいコマンドを試そうとしたときの経験を共有したいと思ったり, コメントがあるならば,maintainers@octave.orgに寄せてくだされば幸いです (でも,もしバグを発見したと思ったならば,bug@octave.org に報告してください)。 switchステートメントの一般的な形式は,以下のようになります。
switch expression
case label
command_list
case label
command_list
...
otherwise
command_list
endswitch
- 識別子switch,case,otherwiseおよびendswitch はキーワードになっています。
- label(ラベル)には任意の式を置くことができます。
- 重複するlabel は認められません。
最初にマッチしたlabel に対応するcommand list が実行さ れます。
- 少なくとも1つのcase label command_list句がなければなりません。
- otherwise command_list句は,あってもなくてもかまいません。
- 全ての他のendキーワードと同じように,endswitchもendに置き換えることができます。
しかし,endswitchを使用する構文を使うと,より解釈されやすくなります。
- 場合分けは排他的なので,C言語のswitch ステートメントの場合のような「下に素通りする」ようなことはありません。
- command list 要素はオプションではありません。
リストをオプショナルにすることは,ラベルとコマンドリストの間に区切りが必要であるという意味になります。 一方,以下のような書き方について,
switch (foo)
case (1) -2
...
これは,特にC 言語のプログラマにとって,驚くべき結果になります。 以下のコードと同じ挙動をします。
switch (foo)
case (1)
case (2)
doit ();
...
2 この実装は単純指向であり,現在のところ等価なifブロックを上回る実性能を提供するわけではありません。 たとえ,全てのラベルが整数の定数であったとしても,です。 おそらく,これにおける将来のバリエーションは,全ての定数整数ラベルを検出し,ジャンプ表による性能の向上ができるようになるでしょう。
warn_variable_switch_label
[編集][Built-in Variable]
もし、この変数の値がゼロではないとき、スイッチのラベルが定数あるいは定数式でないなら、Octaveは警告を印刷します、
12.3 whileステートメント
[編集]プログラミングにおいて,ループは,連続して2回以上実行される(あるいは,少なくとも実行される可能性がある)プログラムの一部分を意味します。 whileステートメントは,Octave における最も単純なループ実行ステートメントです。 これは,条件が真である限り,そのステートメントを繰り返し実行します。 ifステートメントにおける条件と同様に,whileにおける条件は,その値がゼロでないときに真,ゼロのときに偽であると見なされます。 もしwhileステートメントにおける条件式の値がベクトルまたは行列であるならば,要素のすべてがゼロでないときのみ,真であると見なされます。 Octave のwhileステートメントは,以下のように見えます。
while (condition)
body
endwhile
ここで,body はループの本体と呼ばれるステートメント(またはそのリスト)であり,condition は, そのループがどのくらいのあいだ実行を維持するかを制御する式です。 whileステートメントが行う最初のことは,condition をテストすることです。 もしcondition が 真ならば,body 部分を実行します。 body がを実行した後,condition が再びテストされ,これがま だ真であればbody を再度実行します。 この過程を,condition が真でなくなるまで繰り返します。 もしcondition が最初に偽であれば,ループの本体を一度も実行しません。 この例では,フィボナッチ数列の10 番めまでの要素を含む変数fibを生成します。
fib = ones (1, 10);
i = 3;
while (i <= 10)
fib (i) = fib (i-1) + fib (i-2);
i++;
endwhile
ここでループの本体には,2 つのステートメントを含みます。 このループは以下のように動作します:まず,iの値を3 にセットします。 その後,whileは,iが 10 以下であるかどうかをテストします。 この場合はiが3 に等しいので,fibのi番めの要素の値に は,直前の連続する2 値の和をセットします。 その後,i++がiの値をインクリメントし,ループを繰 り返します。 このループは,iが11 に達するときに終了します。 条件と本体の間に,改行は必要ありません。 しかし,本体が非常に単純でない場合は,改行を入れるとプログラムが読みやすくなります。 変数warn_assign_as_truth_valueの説明については,Section 12.1 [The if Statement]を参照してください。
12.4 do-untilステートメント
[編集]do-untilステートメントは,条件が真になるまであるステートメントを繰り返し実行することを除き,whileステートメントと同様です。 その結果,ループの本体は,少なくとも一度は必ず実行されます。 ifステートメントの条件と同じように,do-untilステートメントにおける条件は,その値が ゼロでないならば真,ゼロならば偽になります。 do-untilステートメントにおける条件式の値がベクトルまたは行列ならば,その要素のすべてがゼロでないときに限り真であると見なされます。 Octaveのdo-untilステートメントは,以下のように見えます。
do
body
until (condition)
bodyは,ループの本体と呼ぶ(単数または複数の)ステートメントであり,conditionは,ループがどのくらい続くかをコントロールする式です。 この例では,フィボナッチ数列の10番めまでの要素を含む変数fibを生成します。
fib = ones (1, 10);
i = 2;
do
i++;
fib (i) = fib (i-1) + fib (i-2);
until (i == 10)
条件と本体の間に,改行は必要ありません。 しかし,本体が非常に単純でない場合は,改行を入れるとプログラムが読みやすくなります。 変数warn_assign_as_truth_valueの説明については,Section 12.1 [The if Statement]を参照してください。
12.5 forステートメント
[編集]forステートメントは,ループの反復回数をカウントすることを容易にしてくれます。 forステートメントの一般的な型は,以下のように見えます。
for var = expression
body
endfor
ここでbody は,何らかのステートメントやステートメントのリストを表し,expression は何らかの妥当な式です。 また,var はいくつかの型を取ることができます。 通常,その変数は単純な変数名あるいはインデックス付きの変数です。 もしexpression の値が構造体ならば,var もリストになります。 以下のSection 12.5.1 [Looping Over Structure Elements]を参照してください。 forステートメントにおける代入式は,Octave の通常の代入ステートメントとは少し異なる動作をします。 式の完全な結果を代入するかわりに,反復のたびにvar に式の各列を代入します。 もしexpression が範囲,行ベクトル,あるいはスカラであれば,varの値は,ループ本体を実行するたびに,スカラとなるでしょう。 もしvar が列ベクトルあるいは行列ならば,var は,ループの本体を実行するたびに,列ベクトルになるでしょう。 以下の例では,フィボナッチ数列の10 番めまでの要素を含むベクトルを生成するための別の方法を示しています。 今回,forステートメントを用いています。
fib = ones (1, 10);
for i = 3:10
fib (i) = fib (i-1) + fib (i-2);
endfor
このコードは,3から10までの値を含む範囲を生成するため,最初に式3:10を評価することから動作します。 その後,変数iには,範囲の最初の要素が代入され,ループの本体を一度実行します。 ループ本体の終端に達したとき,その範囲の次の値をiに代入し,ループ本体を再度実行します。 この過程は,代入すべき要素が存在しなくなるまで継続します。
すべてのforループは,whileループとして書き直すことは可能ですが,Octave 言語では両ステートメントをサポートしています。 これは,for ループは入力が短くて済み,より自然な考え方ができるという理由によるものです。 反復の回数をカウントすることは,ループにおいてとても当たり前のことであり,ループの一部としてカウントを考えることは,ループ内で何らかのカウントを行うよりは容易なことです。
12.5.1 構造体の要素をまたぐループ
[編集]forステートメントの特殊な使い方として,構造体の全ての要素にまたがるループができるようにな るというものです。
for [ val, key ] = expression
body
endfor
forステートメントのこの使い方において,expression の値は構造体でなければなりません。 もしそうならば,これ以上要素が存在しなくなるまで,key とval には,要素名と,その反復において対応する値がセットされます。 たとえば,以下のように使うことができます。
x.a = 1
x.b = [1, 2; 3, 4]
x.c = "string"
for [val, key] = x
key
val
endfor
a key = a
a val = 1
a key = b
a val =
a
a 1 2
a 3 4
a
a key = c
a val = string
その要素は,何か特定の順序でアクセスしません。 もし,特定の方法によってそのリストを通したループを行う必要があるならば,struct_elements 関数を使用するか, あなた自身でソートを行う必要があります。 key 変数は,省略することができます。 もし省略すると,ブラケット[ ] も省略できます。 これは,要素名を知る必要がないとき,全ての構造体要素の値を通して循環するときに役立ちます。
12.6 breakステートメント
[編集]breakステートメントは,それを囲む最も内側のforまたはwhile ループの外側にジャンプします。 breakステートメントは,ループの本体内でのみ使用することができます。 以下の例は,与えられた整数を除すことのできる最小の値を発見し,それが素数かどうかを識別します。
num = 103;
div = 2;
while (div*div <= num)
if (rem (num, div) == 0)
break;
endif
div++;
endwhile
if (rem (num, div) == 0)
printf ("Smallest divisor of %d is %d\n", num, div)
else
printf ("%d is prime\n", num);
endif
最初のwhileステートメントにおいて剰余がゼロのとき,Octaveは直ちにループを脱出します。 これは,Octaveが直ちにそのループに続くステートメントに進み,処理を続けることを意味します (これは,exitステートメントとは大きく異なります。exitは,Octave プログラム全体を停止します)。 以前のものと等価な,別のプログラムを示します。 これは,whileステートメントのcondition が,if内部のbreakに,どのように置き換わるのがよいかを示しています。
num = 103;
div = 2;
while (1)
if (rem (num, div) == 0)
printf ("Smallest divisor of %d is %d\n", num, div);
break;
endif
div++;
if (div*div > num)
printf ("%d is prime\n", num);
break;
endif
endwhile
12.7 continueステートメント
[編集]continueステートメントは,breakのように,forまたはwhileループの内部でのみ使用できます。
これは,ループ本体の残り部分を飛び越え,直ちに次の循環を開始します。 これとbreak(ループ全 体を脱出する)とを対比します。 以下に例を示します。
# print elements of a vector of random
# integers that are even.
# first, create a row vector of 10 random
# integers with values between 0 and 100:
vec = round (rand (1, 10) * 100);
# print what we're interested in:
for x = vec
if (rem (x, 2) != 0)
continue;
endif
printf ("%d\n", x);
endfor
vec の要素のひとつが奇数ならば,この例は,その要素の値を表示するステートメントをスキップ し,ループの先頭にあるステートメントに戻って継続します。
これは,continueステートメントの実用的な例ではありません。 しかし,これがどのように動 作するかを,はっきりと理解できるようにはなるはずです。 通常は,以下のように書くことになるで しょう。
for x = vec
if (rem (x, 2) == 0)
printf ("%d\n", x);
endif
endfor
12.8 unwind_protectステートメント
[編集]Octaveは,Lispのunwind-protectを真似た例外処理の一部方法についてサポートしています。 unwind_protectブロックの一般的な書き方は,以下のようなものです。
unwind_protect
body
unwind_protect_cleanup
cleanup
end_unwind_protect
ここでbody とcleanup は,ともにオプションであり,任意のOctave 式またはコマンドを含めるこ とができます。 cleanup における式は,どのように制御がbody を抜けるかに関わらず,実行される ことが保証されます。
これは,グローバル変数を一時的に変更することによって起こりうるエラーを防ぐために役立ちま す。 たとえば,以下のコードは,たとえインデックス操作を実行している間にエラーが発生したとし ても,組み込み変数warn_fortran_indexing の元の値を復元することになります。
save_warn_fortran_indexing = warn_fortran_indexing;
unwind_protect
warn_fortran_indexing = 1;
elt = a (idx)
unwind_protect_cleanup
warn_fortran_indexing = save_warn_fortran_indexing;
end_unwind_protect
unwind_protectが無いならば,インデックス操作を実行している間にエラーが発生したときに, 組み込み変数warn_fortran_indexingが元の値を復元することはできません。 なぜならば,評価 はエラーの時点で停止してしまい,値を復元するためのステートメントは実行されないからです。
12.9 tryステートメント
[編集]unwind protectに加え,Octaveでは,例外処理の別の(限定された)書き方をサポートしています。
tryブロックの一般的な書き方は,以下のようなものです。
try
body
catch
cleanup
end_try_catch
ここでbodyとcleanupは,ともにオプションであり,任意のOctave式またはコマンドを含め ることができます。 cleanupにおけるステートメントは,bodyにおいてエラーが発生したときにのみ実行されます。
body が実行されているあいだ,警告やエラーメッセージは何も表示されません。 body の実行中にエラーが発生すると,cleanup は,表示されるはずだったメッセージのテキストにアクセスするた めの関数lasterrを使用することができます。 これは,eval (try, catch)とすることに同じですが,上の関数がより効率的です。 なぜならば,そのコマンドは,try およびcatch ステートメントを毎回解釈する必要がないからです。 lasterr関数について,より多くの情報はChapter 14 [ErrorHandling]を参照してください。
Octave のtry ブロックは,Lisp のcondition-case 形式の非常に限られたバリエーションです(異 なるエラーのクラスを別個に処理することができないためです)。 おそらく,同じ点において,Octaveにはエラーのクラス分けについて,いくつかの種類があり,try-catch は,Lisp のcondition-case と 同じようにパワフルになり得るでしょう。
12.10 継続行
[編集]Octave言語において,大部分のステートメントは改行文字で終了し,ある行から次の行までステートメントを継続するためには, 改行文字を無視するようにOctaveに伝えねばなりません。 行末に... あるいは\という文字がある行は,Octave のパーサによってトークンに分割される前に,次の行を連結します。 たとえば,以下のような行について
x = long_variable_name ...
+ longer_variable_name \
- 42
これは,1行を形成します。 2行めのバックスラッシュ文字は,割り算の記号ではなく,継続文字として解釈されます。 文字列定数の内部で発生しない継続行については,継続記号および改行文字の間に,空白文字とコメントが出現してもかまいません。 たとえば,
x = long_variable_name ... # コメント1
+ longer_variable_name \ # コメント2
- 42 # 最後のコメント
このステートメントは,上で示した式と等価です。 文字定数の内部では,改行の直前の行末に継続記号が出現しなければなりません。 かっこの内部で発生する入力は,継続行を使用する必要なしに,次の行へと続きます。 たとえば,面倒な継続記号を付ける必要なく,以下のようなステートメントを書くことは可能です。
if (fine_dining_destination == on_a_boat
|| fine_dining_destination == on_a_train)
seuss (i, will, not, eat, them, sam, i, am, i,
will, not, eat, green, eggs, and, ham);
endif