壊れる理由
nest.m
ネストされた関数を持つと、それを囲む関数のワークスペースが「静的ワークスペース」になるため、呼び出し時にエラーが発生します。つまりeval()
、 、assignin()
、またはその他の「動的」手法を使用して変数名を追加することはできません。その関数のテキストで明示的に割り当てられた変数のみが許可されます。スクリプトを評価してローカル変数を定義すること (呼び出し時に行っていることfuncs.m
) は「動的」であるため、ネストされた関数を含む関数では禁止されています。main にはmain.m
ネストされた関数がなく、したがって「動的な」ワークスペースであるため、機能します。
静的ワークスペースとネストされた関数で動作するように変更するには、いくつかの方法があります。最初に質問することは、それらを本当に無名関数にする必要があるかどうかです。
代わりにパッケージ関数を使用する
それらを匿名関数自体にする必要がない場合は、それらを分割して、それぞれを通常の関数として独自の.m
ファイルに入れます。例: bg.m
、I.m
、AiNotj.m
など。その後、それらはすべて、そのディレクトリ内の他のすべての機能で利用できます。
それがファイルの混乱になる場合、またはそれらをスコープして、本当に必要な選択された関数 (つまり、現在 を呼び出している関数funcs()
) だけが利用できるようにしたい場合は、それらをパッケージに貼り付けることができます。というサブディレクトリを作成し+myfuncs
、小さな関数ファイルをすべてそこに移動します。例: +myfuncs/bq.m
, . (プレフィックスは、ディレクトリがパッケージであることを Matlab に伝えます。) 次に、現在呼び出している場所の直接の代わりとして実行することで、それらすべてを関数スコープに取り込むことができます。+myfuncs/I.m
+myfuncs/AiNotj.m
+
import myfuncs.*
funcs()
function nest(par1,...,parN)
import myfuncs.*;
function neededOnlyHere(par100)
bq(q,A) % This will work, resolving to myfuncs.bq
end
import myfuncs.*
コマンドラインから を実行して、インタラクティブに利用できるようにすることもできます。
これはおそらく、Matlab 自体がこのような関連する関数のクラスターを整理する方法であり、私の最初のアプローチです。それは最も「臭い」私見です。便宜上、単一のファイルでそれらすべてを編集できるようにしたい場合はfuncs.m
、Perl または funcs.m を解析するもので小さなコードマンガーを記述し、それらすべてを前処理ステップとして同等の個々の関数として出力できます。(このように M ファイルで複数のトップレベル関数を定義できないのは少し残念だと思いますが、まあまあです。)
本当に匿名関数を使用する必要がある場合は、いくつかの回避策があります。
構造体で関数を渡す
funcs() 関数を変更して、ローカル変数名の代わりにフィールド名を使用して、これらすべての無名関数の構造体を実際に返すことができます。
function out = funcs
out.bq=@(q,A) q*A; %Bolded q
out.I=@(ii,jj,A) find(A(ii,:)==1 & A(jj,:)==0);
out.AiNotj=zeros(1,Ncut);
このためには、すべての関数参照の前に、それらを保持している構造体名を付ける必要があります。これがどれほど大きな取引であるかはわかりません。
function nest(par1,...,parN)
fs = funcs;
function neededOnlyHere(par100)
fs.bq(q,A) %This needs the functions of the funcs
end
変数の事前割り当て
funcs() をそのまま動作させるには、使用するすべての関数名で変数を静的に事前に割り当てることができるため、Matlab パーサーはそれらを静的に割り当てられた変数として認識します。次に funcs() を呼び出すと、既存の変数の値が再割り当てされます。これは動的ワークスペースで許可されています。
function nest(par1,...,parN)
[bq, I, AiNotj] = deal(); % Preallocate all names from funcs
funcs;
function neededOnlyHere(par100)
bq(q,A) %This needs the functions of the funcs
end
新しい関数名が追加されるたびに funcs を使用するすべてのファイルを再編集する必要があるため、これは少し面倒です。funcs.m
少なくとも、"[bg, I, AiNotj,...] = deal();" を解析して出力することで、そのコード行を自動生成する小さな perl スクリプトを作成できます。見つかったすべての関数を使用して、それをコードにコピーするだけです。
これを行う別の方法は、funcs が実際にすべての関数を出力リストに返すようにすることです。funcs.m
これには、既存の無名関数の順序を削除または変更しない限り、新しい関数を に追加しても機能し続けるという利点があります。
function [bg,I,AiNotj] = funcs()
bg = ...
I = ...
% And then in the calling functions:
[bg,I,AiNotj] = funcs(); % which you can copy and paste from funcs.m's header