Mathematica 9 is now available
THIS IS DOCUMENTATION FOR AN OBSOLETE PRODUCT.
SEE THE DOCUMENTATION CENTER FOR THE LATEST INFORMATION.
Mathematica >

ループと制御構造体

Mathematica プログラムの実行とは,一連のMathematica 式の評価を意味する.単純なプログラムでは,式はセミコロンで区切られ,1つずつ評価されることが多い.しかし,多くの場合は,式を繰り返し評価する必要があり,ある種の「ループ」を使う必要が出てくる.
Do[expr,{i,imax}]反復変数i を刻み幅11からimax に増加させ,式expr を繰り返し評価する
Do[expr,{i,imin,imax,di}]反復変数i を刻み幅diimin からimax に増加させ,式expr を繰り返し評価する
Do[expr,{i,list}]ilist からの値を取ってexpr を評価する
Do[expr,{n}]exprn 回評価する

簡単なループ構成体

i1から4に変化させたときのPrint[i^2]を評価する.
k2から6まで刻み幅2で変化させるループを使い,tに対する割当てを行う.
In[2]:=
Click for copyable input
Out[2]=
関数Doで使われる反復条件はTableSum等の関数と同様に指定する.後者の関数でできたように,一連の反復指定を与えることで,Do式でも複数レベルでネストされたループを構成することができる.
ループを作り,i1から4まで振り,各ij1からi-1までふる.
場合によっては,反復変数の値は変えずに,単に特定の操作を繰り返したい.Tableや他の反復関数を使ってもこの種の反復操作は行えるが,Do式でも行うことができる.
t=1/(1+t)による割当てを3回繰り返す.
In[4]:=
Click for copyable input
Out[4]=
Do式の内部に手続きを入れることもできる.
Nest[f,expr,n]関数f を式exprn 回適用する
FixedPoint[f,expr]関数f を繰り返して式expr に適用し,結果の式が変化しなくなる時点で停止する
NestWhile[f,expr,test]結果の式にtest を適用したものがTrueを返さなくなるまで,式expr に関数f を繰り返して適用する

関数の繰返し適用

Doを使えば,反復変数をいろいろな値に振り特定の式を評価することで一連の操作を繰り返すことが可能である.それでも,「関数を反復的に適用する」に示したような関数型プログラミングの構成体を使うと,分かりやすく,また,効率的なプログラムを記述することができる.例えば,関数Nest[f, x, n]を使えば関数を繰り返し,再帰的に式に適用する,ということが可能になる.
fを3回ネストさせる.
In[6]:=
Click for copyable input
Out[6]=
純関数をネストさせ,前述のDoの例で作った式と同じ計算をする式を作る.
In[7]:=
Click for copyable input
Out[7]=
このように,Nestは,関数を指定した回数だけ繰り返し適用するのに使う.それでは,関数を繰り返し適用させ,結果が変化しなくなったら自動的に停止させる,という処理を行いたい場合はどうするのか.そのときは,関数FixedPoint[f, x]を使い定常点の探索を行うようにすればよい.
FixedPointは,結果が変わらなくなるまで関数を繰り返し適用する.
関数FixedPointを使うことで,カーネルの評価プロセスそのものや,expr//.rules 等の関数操作をまねることができる.FixedPointでは,2回の逐次結果に変化がなくなるまで関数の適用が続けられる.一方NestWhileでは,任意の関数がTrueでなくなるまで続けられる.
Catch[expr]Throw[value]が現れるまで式 expr を評価し続け,現れた時点で値 value を返す
Catch[expr,form]tagform にマッチするThrow[value, tag]が現れるまで式expr を評価し続け,現れた時点で値を返す
Catch[expr,form,f]value の代りにf[value, tag]を返す

評価処理の非局所的な制御

Do式にCatchを作用させる.評価中にThrowが現れるとCatchの処理は停止され,そのときのiの値が返される.
ThrowCatchを組み合せて使うと,Mathematica の評価プロセスを監視することができるため便利である.評価を監視したい式には,Catchを作用させておく.評価処理中にThrowが現れると,その時点で進行中の処理は停止し,Throwに与えられている引数の現行値が返される.
Scanを作用させ,Print関数をリストの各値に逐次に適用する.処理終了後,空文字(Null)が返される.
Throwが現れた時点で,Scan処理は停止する.Catchの戻り値として,Throwの引数の持つ現行値が返される.
Mapを使っても同じ結果を得ることができる.ただし,Mapだと,評価中にThrowが現れないときはリストが返されることになる.
ThrowCatchを使えば,関数的なプログラミング構成体における操作処理を特定条件が満たされるまで続行させ,満たされなくなった時点で中断させることができる.Throwで評価を中断すると,得られる結果は途中結果である.途中結果なので,式は,仮に最後まで評価を続けたときの式とは構成上多少違ってくることに注意してほしい.
関数を繰り返し適用し,リストを生成する.
In[13]:=
Click for copyable input
Out[13]=
Throwがないため,上の式と同じ結果が得られる.
In[14]:=
Click for copyable input
Out[14]=
今度は,NestListの評価が中断され,1つあるThrowの引数が返される.
In[15]:=
Click for copyable input
Out[15]=
ThrowCatchはともに大域的な働き方をする.Throwどこに,またどのように配されても,一度それが現れると,評価処理は必ず中断され,制御は直接Catchに戻される.
Throwによりfの評価が中断され,Catchの戻り値としてThrowの引数であるaが出力される.fは無視される.
In[16]:=
Click for copyable input
Out[16]=
関数を定義する.この関数は,引数に10より大きい値を入力した場合,Throwを発生させる.
In[17]:=
Click for copyable input
Throwは起らない.
In[18]:=
Click for copyable input
Out[18]=
この場合,gの評価中に起るThrowにより,制御はCatchに戻される.
In[19]:=
Click for copyable input
Out[19]=
短いプログラムでThrowCatchを使うなら,Throw[value]Catch[expr]の最も単純な書式を使うだけでよい.しかし,構成部分が増えプログラムが長くなると,Throw[value, tag]Catch[expr, form]の書式を使った方がよいだろう.タグtag と形form を局所的なものとしておくことで,ThrowCatchがプログラムの特定部分だけで機能するようにすることができる.
タグがあるため,Throwにより投げ出される値は内側のCatchでキャッチされる.
In[20]:=
Click for copyable input
Out[20]=
今度は,外側のCatchによりキャッチされる.
In[21]:=
Click for copyable input
Out[21]=
パターンでCatchの認識するタグを指定してもよい.
In[22]:=
Click for copyable input
Out[22]=
こうすればタグaを完全に局所的なものとして維持できる.
In[23]:=
Click for copyable input
Out[23]=
ここでの注意点は,Throwで使うタグは定数である必要はない.通常,それはどんな式であっても構わない.
Throwのタグの値が4より小さければ内側のCatchがキャッチし,Do式の処理が続行する.4以上になったら,外側のCatchで止められる.
In[24]:=
Click for copyable input
Out[24]=
Throw[value, tag]Catch[expr, form]の書式を使った場合,Catchの返してくる値はThrowに指定した第1引数value になる.また,Catchの書式をCatch[expr, form, f]にすると,単なるvalue ではなく,任意関数fを作用させた式f[value, tag]が返される.
fThrowの値とタグに適用される.
In[25]:=
Click for copyable input
Out[25]=
Throwがなければ,fは決して使われない.
In[26]:=
Click for copyable input
Out[26]=
While[test,body]条件式test を判定し結果がTrueならば,式body を評価する.条件式が真でなくなるまで式の評価を繰り返す
For[start,test,incr,body]start を評価し,条件式test を判定し結果がTrueならば,式body と式incr を評価する.条件式が真でなくなるまで2式の評価を繰り返す

一般的なループ構成体

構造化アプローチを取るなら,DoNestFixedPoint等の関数を使いループを構築するとよい.また,ThrowCatchを併せて使えば,ループを監視,制御することも可能である.一方,構造にとらわれないループを作りたい場合は,関数WhileForを使うとよい.任意条件下で繰返し処理を停止できるループを構築することができる.
Whileループは,条件が満たされなくなるまで実行される.
関数WhileForは,C言語のwhileforに構文上似ている.ただし,違いもある.例えば,Forループのコンマとセミコロンの使い方はC言語と逆である.
Forループの簡単な例を見てみる.i++iの値を1増加させる.
少し複雑なForループを作ってみよう.実行させると,ループは,条件式i^2<10が満たされなくなった時点で終了する.
WhileForのループにおいて,まず評価されるのは条件式であり,その次に,本体の式が評価される.つまり,Trueにならなければその時点でWhileおよびForループは終了してしまう.このため,本体の式が評価されるのは条件式がTrueになるときだけである.
ループの条件式にはもとからFalseが与えられているので,本体の式Print[x]は完全に無視される.
In[30]:=
Click for copyable input
WhileForによるループ,または,その他の手続きにおいて,式の評価は特定の手順に従って行われる.この評価手順をMathematica プログラムの実行における制御フローと呼ぶ.
一般に,制御フローはなるべく単純なものがよい.制御フローが,プログラム実行中に生成される特定値に依存すればするほど,プログラムの構造や動作が分かりづらいものになってしまう.
WhileForのループ体を使うより関数的な構成体を使った方が,制御フローをより単純なものにできる.WhileForのループでは,制御フローがプログラム実行時の条件式の値に左右されてしまうので,ループが複雑な構成になりがちである.とは言え,ループであっても,制御フローは,普通その本体で与えられた式の値に依存するようなことはない.
それでも,場合によっては,本体の式の値に依存した形でフローを変えてやらなければならない.これを行う方法として,まず,関数的なプログラミングの考え方に適合した手法,つまり,関数ThrowCatchを使う方法が考えられる.さらに,Mathematica にはC言語で提供される制御フローの操作用関数に相当した各種の関数が用意されているので,それらを利用するのも便利である.
Break[]Breakが位置するレベルにあるループから退避する
Continue[]現行ループの次のステップに進む
Return[expr]手続きやループを終了し,関数から制御を戻し,値 expr を返す
Goto[name]現行手続きにあるLabel[name]位置にジャンプする
Throw[value]制御を戻し,Catchの戻り値として値 value を返す(非局所的なリターン)

フローの制御関数

t19を超え,制御がBreak[]に達した時点でループが停止する.
k<3のときは,Continue[]に制御が行き,t+=2の計算は実行されずにループが続行される.
Return[expr]を使い,戻り値を指定することも可能である.ネストされた関数式から一挙に退避できることから,Throwを非局所的なリターン機能と考えることもできる.エラー処理に利用すると便利である.
Returnを使った例を見てみる.必ずしもReturnを使わなくてもこの手続きを記述することができる.
In[33]:=
Click for copyable input
引数が5より大きいので,手続きでは最初のReturnが使われる.
In[34]:=
Click for copyable input
Out[34]=
引数が負の数のときはエラー値errorを返すように関数を定義する.
In[35]:=
Click for copyable input
実行してもThrowに制御は移らない.
In[36]:=
Click for copyable input
Out[36]=
今度は,負になるからThrowが生成され,Catchの戻り値はerrorになる.
In[37]:=
Click for copyable input
Out[37]=
Continue[]Break[]の関数はループの始点もしくは終点に制御を移すために使う.場合によっては,ループの外にある手続き内の任意の位置に制御を移したい.そのようなときは,転送先を関数Labelであらかじめラベル指定をしてから関数Gotoを使い実際に制御を移す.
LabelGotoを使ったループを構成する.q6を超えるまでループが続行する.
注意してほしいが,Gotoを使うには同じ手続き内にLabel指定がなければならない.ただし,Goto指定を使うとフロー構造が入り組んだものになり,プログラムがどう機能しているのか分かりづらくなるので使用には注意が必要である.
Ask a question about this page  |  Suggest an improvement  |  Leave a message for the team