関数操作
適用させるための関数名としてLogを与える:
このように,Wolfram言語では,関数名は他の式と同様に扱うことができる.この機能は,Wolfram言語の持つシンボル操作機能からくる大切な結果である.このことによって,これから示す各種の関数操作が可能になっている.
LogやIntegrate等の組込み関数は,普通,数や代数式に代表されるデータに対して使われる.これに対して,関数的な操作をもたらすWolfram言語関数は,普通のデータだけでなく関数自体にも作用させることができる.このため,例えば,関数操作の関数InverseFunctionは,関数名を引数として取り,その関数の逆関数を表すことができる.
InverseFunctionは関数操作を行う.つまり,Wolfram言語関数を引数としてとり,その関数の逆を表す別の関数を返す:
InverseFunctionの返す結果は,データに作用させることができる関数である:
純粋にシンボル的な演算子としてInverseFunctionを使うこともできる:
読者がシンボル的処理の言語に詳しくなければ,以下の節で説明される多くの関数操作が何をするものか分からないかもしれない.関数操作は,最初理解しづらいかもしれないが,説明についてきてほしい.必ず有意義な情報を得ることができる.関数操作は,概念上,そして実践上で,Wolfram言語を最も効果的に使うための1つの方法を提供してくれる.
Nest[f,x,n] | 関数 f を x に n 回繰り返し適用し,リストを生成する |
NestList[f,x,n] | リスト{x,f[x],f[f[x]],…}を生成する.ここで f は n 回まで繰り返される |
Nestを使い,関数を繰り返し適用する:
FixedPoint[f,x] | 結果に変化がなくなるまで関数 f を反復適用する |
FixedPointList[f,x] | リスト{x,f[x],f[f[x]],…}を生成し,要素が変化しなくなったとき停止する |
NestWhile[f,x,test] | |
NestWhileList[f,x,test] | |
NestWhile[f,x,test,m]
,
NestWhileList[f,x,test,m] | |
各ステップで最新の m 個の結果を test の引数として与える | |
NestWhile[f,x,test,All]
,
NestWhileList[f,x,test,All] | |
これまでのすべての結果を引数として test に与える |
この考えを,引数が2つの関数にも使えるように一般化しておくことは大切である.その場合も,関数を繰り返し適用することができるが,得られる各結果は,次の関数適用で必要な2つの引数のうち1つしか提供することができない.そこで,別に2つ目の引数群をリストで用意しておき,そこから第2引数を逐次取り出し各ステップに当てる,という手順を使うと便利である.
FoldList[f,x,{a,b,…}] | リスト{x,f[x,a],f[f[x,a],b],…}を構築する |
Fold[f,x,{a,b,…}] | FoldList[f,x,{a,b,…}]で列挙した最終要素だけを出力する |
これは,FoldListの実行例である:
FoldとFoldListの機能を使うことで,読みやすくまた効率的なプログラムをWolfram言語で書くことができる.場合によっては,FoldとFoldListを,第2引数でインデックスされている関数の族を単純なネストで生成するものと考えるとよい.
まるで組込み関数のFromDigitsのようになった:
f[{a,b,c}]のような式において,リストを関数の引数として与える.場合によっては,関数をリスト全体ではなく,リストの各要素に直接適用させたい.そのようなときWolfram言語では,Applyを使う.
こうするとTimes[a,b,c]が得られ,リストの要素の積を計算することができる:
Apply[f,{a,b,…}] | リストの要素に関数 f を適用し,f[a,b,…]を計算する |
Apply[f,expr]または f@@expr | 関数 f を式のトップレベルに適用する |
MapApply[f,expr] または f@@@expr | 関数 f を式の第1レベルに適用する |
Apply[f,expr,{1}] | f@@@expr に等しい |
Apply[f,expr,lev] | 関数 f を式の指定されたレベルに適用する |
リストの取扱いにおいて,リストの要素ひとつひとつに関数を作用させなければならないときがある.そのようなときは,Wolfram言語ではMapを使う.
Map[f,{a,b,…}] | 入力リストの各要素(第1レベル)に f を作用させ,リスト {f[a],f[b],…}を構成する |
Map[f,expr] または f/@expr | 式 expr のレベル1の各部分に関数 f を適用する |
MapAll[f,expr] または f//@expr | 式 expr にあるすべての部分に関数 f を適用する |
Map[f,expr,lev] | 式 expr の任意レベル lev の各部分に関数 f を適用する |
Mapにレベル指定をしておくことで,適用させたい関数を式のどのレベルにでも適用させることが可能である.また,MapAtを使えば,関数を適用させたい式の部分を位置で指定することもできる.位置は,「式の部分抽出」で説明してある添数の指定法に従い指定する.
MapAt[f,expr,{part1,part2,…}] | f を式 expr の指定された部分に適用する |
MapAtはどんな式にも使えるが,部分は完全形の並び順に基づいて指定されることに注意:
MapIndexed[f,expr] | 式の要素に f を適用する(各要素の添数を第2引数とした f を返す) |
MapIndexed[f,expr,lev] | 式において任意のレベルの各部分に f を適用する(各部分の添数からなるリストを第2引数とした f を返す) |
Mapを使うことで,単引数の関数をある式の任意部分に適用することができる.しかし,関数が複数の引数を必要とし,それを複数の式に並列的に適用したい場合には使えない.そのようなときは,MapThreadを代りに使う.
MapThread[f,{expr1,expr2,…}] | expri の複数式において同じ位置の要素をまとめ,各まとまりに対して f を適用する |
MapThread[f,{expr1,expr2,…},lev] | expri の複数式において指定したレベルにある同じ位置の部分をまとめ,各まとまりに対して f を適用する |
MapThreadの適用対象となる式はいくつあっても構わないが,それらは同じ構造であることが必要である:
Map等の関数を使うことで,部分的に変更した式を生成することができる.場合によっては,新しい式を構築しないで,単に特定の関数を式の部分に作用させる,ということだけを行いたい.例えば,適用させる関数が割当て操作や出力の生成等の副作用をもたらす場合はこれに当たる.
Scanは,関数を各要素に適用することで得られた結果を評価はするが,新たな式は生成しない:
Scan操作は,深さ優先探索で(木表示の葉の部分)から始まる:
Function[x,body] | x を指定される任意引数で置換することで得られる純関数を定義する |
Function[{x1,x2,…},body] | 複数の引数を取る純関数を定義する |
body& | 引数が,1つなら #,複数なら #1, #2, #3等と指定された純関数を定義する |
NestやMap等の関数操作が使われるとき,適用される関数は必ず指定されなければならない.これまでに示した例では,関数の「名前」を明記することでこれを行ってきた.純関数と呼ばれる式の構成形式を使うことで,関数名を宣言しなくても,引数に適用させることができる関数を定義することができる.
純関数を宣言する方法がいくつか用意されている.どの方法を使おうが基本的な考えは,適切な引数が与えられるときに特定の関数を計算してくれるオブジェクトを構築することにある.例えば,fun が純関数なら,fun[a]は,a を引数としこの関数を評価する.
純関数をMap式の中で使うこともできる:
Nestでも使うことができる:
関数を繰り返し使う場合は,f[x_]:=body の書式で関数を定義し,関数の参照はその名前 f で行った方がよいだろう.しかし,関数を一度しか使わない場合は,関数をどう呼ぶかを考える必要がない純関数の形で関数を定義した方がよいだろう.
形式論理やLISPプログラミング言語に精通した読者なら,Wolfram言語の純関数が 式や,匿名関数に相当したものだと気が付いたかもしれない.また,純関数は,演算子に関する純粋に数学的な考えに近いものでもある.
関数を繰り返し使わなければ名前を付けてもあまり意味がないように,純関数の引数に名前を付けてもあまり意味がない.Wolfram言語では,純関数を記述する際,特に引数に名前を付けなくてもよいようになっている.名前の代りに,#n の書式で引数の位置を指定するだけでよい.純関数では,#n は,n 番目に与える引数を指す.#だけだと第1引数を意味する.
アンド記号の短縮形を使う際に注意する点として,式のまとめ方がある.「演算子の入力形」で示すように,アンド記号はどちらかというと低い優先順位を持つ.このため,例えば,#1+#2&のような式は,カッコなしで記述することができる.その反面,書式 option->(fun&)を使い,純関数全体を変換規則の一部として使うときはカッコがないと問題になる.
Array[f,n] | n 個の関数を要素としたリスト{f[1],f[2],…}を生成する |
Array[f,{n1,n2,…}] | 要素の位置を引数とする関数成分 f からなる多次元リスト n1×n2×…を生成する |
NestList[f,x,n] | 関数 f を n 回直前の結果に繰り返し適用し,n 個の逐次結果を並べたリスト{x,f[x],f[f[x]],…}を生成する |
FoldList[f,x,{a,b,…}] | {x,f[x,a],f[f[x,a],b],…}の形のリストを生成する |
ComposeList[{f1,f2,…},x] | {x,f1[x],f2[f1[x]],…}の形のリストを生成する |
「リスト要素の操作」において,位置に基づいたリストの要素抽出の仕方を説明した.多くの場合,並び順に基づいた抽出ではなく,要素の値に基づいた抽出が必要になる.
Select[list,f]を使うと,関数 f を判定条件としてリスト list の要素を選択することができる.つまり,Selectは,list の各要素に f を適用し,結果がTrueとなる要素だけを保持しておく.
純関数の値をTrueとする入力リストの要素,つまり,4以上の値を持つ要素だけを選択させる:
Selectはリスト以外の一般式にも適用することができる.
Function[x,x^2]を頭部として使う.式の値は引数の二乗になる:
Wolfram言語には,純関数と同じように動作する構造体がいくつかあるが,それらは,主に,数値解析等で使われる特定種の関数を表すためにある.また,すべての構造体に対していえるが,基本は,使いたい関数に関する完全な情報を備えた頭部を与える,ということにある.
Function[vars,body][args] | 純関数 |
InterpolatingFunction[data][args] | 数値解析において使われる近似関数(InterpolationとNDSolveにより生成される) |
CompiledFunction[data][args] | コンパイルされた数値関数(Compileにより生成される) |
LinearSolveFunction[data][vec] | 行列解法関数(LinearSolveにより生成される) |
これは,InterpolatingFunctionオブジェクトである:
例として,微分を考えてみよう.「導関数の表し方」で詳しく説明するが,f'のような式は導関数を表し,それはfに関数演算子を適用することで求めることができる.Wolfram言語では,fは,Derivative[1][f]と表現される.つまり,Derivative[1]は「関数演算子」としてとらえられ,それはfに適用されるとき,fと表される別の関数を与えることになる.
f[x]のような式は,演算子 f を式 x に適用させることで形成されるものと考えることができる.また,f[g[x]]のような式は,2つの演算子 f,g の組合せの結果を x に適用させたものと考えることができる.
Composition[f,g,…] | 複数の関数 f, g, …を組み合せる |
RightComposition[f,g,…] | f, g, …の右側から組み合せる |
InverseFunction[f] | 関数 f の逆関数を求める |
Identity | 恒等関数 |
RightCompositionは逆の順序で組み合せる:
Throughを使うことで,式の形をより明示的な形に変換することができる:
Throughを使えばそれが可能になる:
Identity[expr] | 恒等関数 |
Through[p[f1,f2][x],q] | p が q と等しければ,p[f1[x], f2[x]]を返す |
Operate[p,f[x]] | p[f][x]を返す |
Operate[p,f[x],n] | 演算子 p を f の第 n レベルに適用する |
MapAll[p,expr,Heads->True] | 演算子 p を式 expr の頭部を含むすべての部分に適用する |
Expand等の関数は,自動的に式の頭部まで作用することはない:
Operateを使うことで,特別に任意関数を式の頭部に作用させることができる:
Wolframシステムは,式の構造を変更するための強力な基本機能を備えている.それらの機能を使うことで,結合則や分配則等の数学的性質を規定することができ,また,分かりやすくて効率的なプログラムの土台を作ることができる.
ここでは式の構成そのものを操作する方法を説明する.「属性」において,式の頭部に対して適切な属性を割り当てることで,そのいくつかの操作を特定の頭部を備えたすべての式に自動的に適用可能にする方法を説明する.
Wolframシステムの関数Sort[expr]を使うことで,リストだけでなく,任意の頭部を持った式に対してその構成要素の並べ替えを行うことができる.この関数を使えば,結合性や対称性の数学的性質を任意関数に与えることができるようになる.
Sortを使い引数リストを標準的順序に並べ替える:
Sort[expr] | リストや式の要素を標準的順序で並べ替える |
Sort[expr,pred] | 並べ替え順序を決める関係判定式 pred を使い式の要素を並べ替える |
Ordering[expr] | 並べ替えたときの要素の順序を与える |
Ordering[expr,n] | 並べ替えたときの最初の n 個の要素の順序を与える |
Ordering[expr,n,pred] | 関係判定式 pred により並べ替えたときの最初の n 個の要素の順序を与える |
OrderedQ[expr] | |
Order[expr1,expr2] | expr1が expr2の前に位置するときは1を返し,後ろに位置するときは-1を返す |
このSort式に与えた第2引数は,ペア要素が正しく並んでいるかどうかを判断するための条件式である.実行すると,リストの要素が降順に並べ替えられる:
Flatten[expr] | 式 expr と同じ頭部を持つすべてのネストした関数を平坦化 (フラット)する |
Flatten[expr,n] | n 番目以下のレベルでネストした関数を平坦化する |
Flatten[expr,n,h] | 頭部 h を持つ関数を平坦化する |
FlattenAt[expr,i] | 式 expr の第 i 要素のみを平坦化する |
Flattenは,ネストした関数を平坦化してくれる:
Flattenを使えば,リストや式に連番要素を「つぎはぎ」的に取り込むことができる:
Distribute[f[a+b+…,…]] | f を加法式に分配し,式 f[a,…]+f[b,…]+… を構成する |
Distribute[f[args],g] | 頭部 g を持った引数に f を分配する |
Distribute[expr,g,f] | 式の頭部が f のときだけに分配する |
Distribute[expr,g,f,gp,fp] | f を g に分配し,それらをそれぞれ fp と gp で置換する |
f をPlus式に分配させると,通常,f[a+b]のような式は f[a]+f[b]の形に「展開」される.関数Expandは,同様な操作をTimes等の標準的な代数演算子に対して行う.これに対して,Distributeを使うと,分配操作を任意の演算子について行うことができるようになる.
Thread[f[{a1,a2},{b1,b2}]] | f をリスト形の各引数に並列的に適用し,式{f[a1,b1],f[a2,b2]}を構成する |
Thread[f[args],g] | 頭部 g を持った args の成分についてだけ並列的な関数の適用を行う |
「オブジェクトの集合を作る」と,さらに,「属性」でより詳しく説明してあるが,Wolframシステムの組込み関数の多くは,そのままでリストに対して並列的に作用することができる.このため,引数として現れるリストなら,その要素に対して並列的に作用することができる.
Logのような組込み済みの数学関数は,リスト要素に直接適用可能なので,自動的にリストの各要素に作用する:
しかし,Logは方程式の項には自動的に適用されない:
それでも,Threadを使えば,方程式の両辺に関数を作用させることができる:
Outer[f,list1,list2]は,複数リスト list1と list2の要素を可能な限りの組合せで掛け合せ,その結果を f と組み合せる.Outerは,「テンソル」で説明するテンソルの直積集合を一般化したものととらえることができる.
この例では,Outerは,ブール値の下三角行列を生成する:
頭部が同じ式からなる式の列であれば,どんな列にでもOuterを適用することができる:
これは,Innerで構築された構造体である:
関数Flattenを使えばすべてのサブリストを平坦化することができる:
また,FlattenAtを使えば,位置により平坦化したいサブリストを指定することができる:
Sequenceオブジェクトを使うと,その成分は自動的に順番付けされるので特別な平坦化操作は必要なくなる:
Sequence[e1,e2,…] | 適用される任意関数に対して順々に自動的に与えられる引数の列を表す |
Sequenceは,どんな関数にでも動作する:
これは,Sequenceの一般的な使い方の一例である: