|
2.5.8 即時的な定義と遅延的な定義
Mathematicaで割当てを行うには,2つの違った方法があることに読者はすでに気が付いているかもしれない.つまり, lhs = rhsと lhs := rhsである.これらの形の間にある基本的な違いは,いつ式 rhsが評価されるかにある. lhs = rhsは即時型の割当てを表し,右辺 rhsは定義した時点で評価される.これに対して, lhs := rhsは遅延型の割当てを表し, rhsは,割当てが行われるときには評価されず, lhsの値が要求されるときに毎回評価される.

2種類の割当て
演算子 :=を使い関数 exを定義する.
In[1]:= ex[x_] := Expand[(1 + x)^2]
:=を使ったので,定義は未評価のまま維持される.
In[2]:= ?ex


演算子 =で割当てを行うと,右辺は即座に評価される.
In[3]:= iex[x_] = Expand[(1 + x)^2]
Out[3]= 
ここで保存された定義は, Expandコマンドの結果である.
In[4]:= ?iex


exが実行されるときに初めて Expandの操作が行われる.
In[5]:= ex[y + 2]
Out[5]= 
iexは,その引数を展開済みの式に代入し, exとは違った答を返す.
In[6]:= iex[y + 2]
Out[6]= 
上の例から分かるように, =と :=はともに関数を定義する上で便利だが,違った意味を持っている.このため,状況に応じて使い分ける必要がある.
使い分けの目安として次の手順を取るとよい.割当てを,ある式の最終的な「値」を与えるものととらえるならば,演算子 =を使う.また,割当てを,値を見出すための「コマンド」としてとらえるならば,演算子 :=を使う.どちらかはっきりしないときは,通常, =ではなく, :=を使うと無難である.

演算子 =と :=による割当て操作の解釈
関数の定義でおそらく, :=は, =より多く使われるだろうが,次に示す重要な場合には,必ず =を使い関数を定義しなければならない. ある計算を行い,シンボル的パラメータxにより答が得られるとき,xを各種の特定値としたときに結果がどうなるかを調べる必要がよくでてくる.そのようなときは,1つの方法として,演算子 /.を使い, xについて適切な規則を適用することが考えられる.しかし,通常は, =を使い,引数を xとした関数を定義した方がより便利になる.
xに依存した式を作る.
In[7]:= D[Log[Sin[x]]^2, x]
Out[7]= 
名前をxとした値の引数を取る関数を定義する.
In[8]:= dlog[x_] = %
Out[8]= 
これは, xを 1 + aとしたときの結果である.
In[9]:= dlog[1 + a]
Out[9]= 
上の例において重要な点は,パターン x_に現れる名前 xは特別な意味を持たないということである.他の任意の式に現れる xと同様に,単なるシンボルでしかない.

式を評価するための関数の定義
=と :=は,関数定義だけでなく変数への値の割当てにも使うことができる. x = valueと入力すれば,値 valueは即座に評価され,結果は xに割り当てられる.一方, x := valueと入力すると, valueはすぐには評価されない.入力されたままの形で維持され,実際に, xが使われるときに毎回再評価される.
これは, Random[ ]を評価し,得られた擬似乱数を r1に割り当てる.
In[10]:= r1 = Random[ ]
Out[10]= 
Random[ ]は未評価のまま維持され,r2 が実際に参照されるときに毎回再評価される.
In[11]:= r2 := Random[ ]
r1と r2の値を調べる.
In[12]:= {r1, r2}
Out[12]= 
r1の値は変わっていない.しかし, r2が使われると,その都度,新たな乱数が生成される.
In[13]:= {r1, r2}
Out[13]= 
数珠つなぎに割当て関係を構築していく際は,即時型の割当てと遅延型の割当ての間にある違いは,特に重要である.
aを 1とする定義を行う.
In[14]:= a = 1
Out[14]= 
a + 2が評価され,結果 3が riの値として割り当てられる.
In[15]:= ri = a + 2
Out[15]= 
a + 2は評価されないまま保持される. rdの値が要求されると,そのたびに再評価される.
In[16]:= rd := a + 2
値を確認する.この時点で, riと rdは同じである.
In[17]:= {ri, rd}
Out[17]= 
aの値を変える.
In[18]:= a = 2
Out[18]= 
rdには新たな aの値が使われるが, riは古い値のままである.
In[19]:= {ri, rd}
Out[19]= 
t := rhsの遅延型の割当てを使えば,変化する「状況」に応じた値を右辺 rhsに割り当てることができる. tを参照するときは,その都度,それが依存するオブジェクトの最新値が使われて右辺 rhsが再評価される.
遅延型の割当て式の右辺は未評価のまま保持される.
In[20]:= t := {a, Factor[x^a - 1]}
aに4を割り当てたときのtの値を求める.
In[21]:= a = 4; t
Out[21]= 
aを 6にするとどうだろうか.
In[22]:= a = 6; t
Out[22]= 
上の例で,シンボル aは tの値を左右する「大域変数」として働く.パラメータをたくさん使う必要があり,また,それらの値をあまり変える必要がなければ,この割当て方法は便利でよいだろう.しかし,変数が他の変数への隠された依存性を持つようなときは,この方法で変数の割当てを行うとかえって混乱させるかもしれないので注意が必要である.関数を,それが必要とするすべてのパラメータをその引数として定義することで,依存性を明示化しておいた方がよい.

2種類の変換規則
割当てに,即座に行うものと,後で行うものの2種類があるように,変換規則にも即時型と遅延型が ある.
関係式の右辺は即座に計算される.
In[23]:= f[x_] -> Expand[(1 + x)^2]
Out[23]= 
このような規則は実用的な価値がないが,入力してみる.
In[24]:= f[x_] -> Expand[x]
Out[24]= 
規則の右辺は未評価の形のまま保持される.規則が使われるときに毎回評価さ れる.
In[25]:= f[x_] :> Expand[x]
Out[25]= 
この規則を適用させると,初めて右辺の式が展開される.
In[26]:= f[(1 + p)^2] /. f[x_] :> Expand[x]
Out[26]= 
割当ての場合と同じように,置換する値が確定しているときは, ->を使うようにし,また,式の値を求めるためのコマンドを与えるときには, :>を使うようにする.
|