subsref メソッドおよび subsasgn メソッドのコード パターン
R2021b 以降では、インデックス付けをカスタマイズする場合、subsref および subsasgn をオーバーロードする代わりに、モジュール インデックス スーパークラスを使用するプロセスが推奨されています。詳細については、オブジェクトのインデックス付けのカスタマイズを参照してください。
インデックス付きの参照と代入のカスタマイズ
ユーザー定義クラスは、組み込みクラスと同じインデックス付け動作を行います。クラスは、MATLAB® がインデックス式を評価するために呼び出す関数をオーバーロードすることにより、インデックス操作をカスタマイズできます。インデックス付きの参照と代入の特殊な動作を定義するときは、関数 subsref と関数 subsasgn をオーバーロードします。
subsref メソッドおよび subsasgn メソッドの構文
MATLAB は、クラスの subsref メソッドと subsasgn メソッドを以下の引数で呼び出します。
| メソッド | 入力 | 出力 |
|---|---|---|
|
|
|
|
|
|
引数の数の変更
クラスの設計により、既定のインデックス操作で定義された数と異なる数の値をインデックス操作によって返したり代入したりする必要がある場合は、関数 numArgumentsFromSubscript をオーバーロードして、subsref の nargout および subsasgn の nargin を制御します。詳細と例は、numArgumentsFromSubscript を参照してください。
インデックス式を説明するインデックス付け構造体
インデックス付け構造体には、インデックス式を説明する情報が含まれています。クラス メソッドはインデックス付け構造体の情報を使用して式を評価し、カスタム動作を実装します。
たとえば、CustomIndex クラスは、インデックス式で使用できるプロパティを定義します。
classdef CustomIndex properties DataArray end end
オブジェクトを作成し、関数 magic で作成された 5 行 5 列の行列を DataArray プロパティに代入します。
a = CustomIndex; a.DataArray = magic(5);
次の添字付き参照式は 5 行 5 列の行列の最初の行を返します。
a.DataArray(1,:)
ans =
17 24 1 8 15次の式は DataArray プロパティに格納された配列の最初の行に新しい値を代入します。
a.DataArray(1,:) = [1 2 3 4 5];
この代入ステートメントは、以下を使用しています。
'.'タイプの参照ドットの後に続くプロパティ名 (つまり、
DataArray)かっこ内のインデックスの範囲 (
1,:)
インデックス付け構造体は、この情報を type フィールドと subs フィールドに格納しています。
インデックス付け構造体の値
インデックス式を実行するときに、クラスが関数 subsref および subsasgn をオーバーロードしている場合、MATLAB はクラスのこれらのメソッドを呼び出します。メソッドに渡される引数の 1 つはインデックス付け構造体です。インデックス付け構造体には、次の 2 つのフィールドがあります。
type— 使用可能な 3 つのインデックス付けタイプ'.'、'()'、'{}'のいずれかsubs— 式で使用されるインデックスのプロパティ名または cell 配列 (:およびendを含む) をもつcharベクトル
インデックス式が複合式の場合、MATLAB は構造体の配列 (インデックス付けのレベルごとに 1 つの struct) を渡します。たとえば、次の式では、
a.DataArray(1,:)
インデックス付け構造体配列 S は以下の値をもちます。
S(1).typeは、最初のインデックス操作がドットであることを示す'.'に設定されるs(1).subsはプロパティ名'DataArray'に設定される
インデックス付けの 2 番目のレベルはインデックス付け構造体の 2 番目の要素に含まれます。
S(2).typesは、2 番目のインデックス操作が小かっこによるインデックス付けであることを示す'()'に設定されるS(2).subsは、インデックス{[1],[:]}を含む cell 配列に設定される
インデックス メソッドの一般的なパターン
関数 subsref と関数 subasgn をオーバーロードするには、次を行います。
インデックス付け構造体の
typesフィールドとsubsフィールドを使用して、完全なインデックス式を判別する。クラスによってサポートされるインデックス操作に特化した動作を実装する。
MATLAB による呼び出しに応じて適切な値または変更されたオブジェクトを返す。
switch ステートメントは、インデックス付けの最初のレベルの検出に便利な方法です。インデックス付けには、ドット、小かっこ、中かっこの 3 つのタイプがあります。switch ステートメントの case ブロックごとに、インデックス付けの第 1 レベルのタイプで始まるすべてのインデックス式を実装します。
メソッドには、クラスがサポートするすべてのインデックス式を実装しなければなりません。特定のインデックス付けタイプをカスタマイズしない場合は、組み込み関数を呼び出してその式を処理します。
インデックス付け構造体配列の長さとインデックス付けタイプを使用して、複合インデックス式の条件付きステートメントを定義します。
subsref メソッドのコードのフレームワーク
以下の subsref メソッドのフレームワークは、条件付きステートメント内でインデックス付け構造体の情報を使用する方法を示します。アプリケーションには、ここに示されていない他の式を含めることができます。
function varargout = subsref(obj,s) switch s(1).type case '.' if length(s) == 1 % Implement obj.PropertyName ... elseif length(s) == 2 && strcmp(s(2).type,'()') % Implement obj.PropertyName(indices) ... else [varargout{1:nargout}] = builtin('subsref',obj,s); end case '()' if length(s) == 1 % Implement obj(indices) ... elseif length(s) == 2 && strcmp(s(2).type,'.') % Implement obj(ind).PropertyName ... elseif length(s) == 3 && strcmp(s(2).type,'.') && strcmp(s(3).type,'()') % Implement obj(indices).PropertyName(indices) ... else % Use built-in for any other expression [varargout{1:nargout}] = builtin('subsref',obj,s); end case '{}' if length(s) == 1 % Implement obj{indices} ... elseif length(s) == 2 && strcmp(s(2).type,'.') % Implement obj{indices}.PropertyName ... else % Use built-in for any other expression [varargout{1:nargout}] = builtin('subsref',obj,s); end otherwise error('Not a valid indexing expression') end
戻り値に varargout を使用すると、メソッドがオブジェクト配列を処理できるようになります。たとえば、次のような式でコンマ区切りリストを返すことをサポートするとします。
[x1,...xn] = objArray.PropertyName(Indices)
この式は、2 つの要素をもつインデックス付け構造体配列を生成します。第 1 レベルのタイプはドット ('.')、第 2 レベルは小かっこ ('()') です。配列内の各値で varargout cell 配列を作成します。
case '.' ... if length(s)==2 && strcmp(s(2).type,'()') prop = s(1).subs; % Property name n = numel(obj); % Number of elements in array varargout = cell(1,n); % Preallocate cell array for k = 1:n varargout{k} = obj(k).(prop).(s(2).subs); end end ... end
subsasgn パターン
以下の subsasgn メソッドのフレームワークは、代入操作を実装する条件付きステートメント内でインデックス付け構造体を使用する方法を示します。
function obj = subsasgn(obj,s,varargin) % Allow subscripted assignment to uninitialized variable if isequal(obj,[]) % obj = ClassName.empty; end switch s(1).type case '.' if length(s) == 1 % Implement obj.PropertyName = varargin{:}; ... elseif length(s) == 2 && strcmp(s(2).type,'()') % Implement obj.PropertyName(indices) = varargin{:}; ... else % Call built-in for any other case obj = builtin('subsasgn',obj,s,varargin{:}); end case '()' if length(s) == 1 % Implement obj(indices) = varargin{:}; elseif length(s) == 2 && strcmp(s(2).type,'.') % Implement obj(indices).PropertyName = varargin{:}; ... elseif length(s) == 3 && strcmp(s(2).type,'.') && strcmp(s(3).type,'()') % Implement obj(indices).PropertyName(indices) = varargin{:}; ... else % Use built-in for any other expression obj = builtin('subsasgn',obj,s,varargin{:}); end case '{}' if length(s) == 1 % Implement obj{indices} = varargin{:} ... elseif length(s) == 2 && strcmp(s(2).type,'.') % Implement obj{indices}.PropertyName = varargin{:} ... % Use built-in for any other expression obj = builtin('subsasgn',obj,s,varargin{:}); end otherwise error('Not a valid indexing expression') end end
代入ステートメントの右側の値に varargin を使用すると、メソッドがオブジェクト配列を処理できるようになります。たとえば、次のような式でコンマ区切りリストを代入することをサポートするとします。
C = {'one';'two';'three'};
[objArray.PropertyName] = C{:}この式は、ドット タイプ ('.') のインデックス付けをもつインデックス付け構造体を生成します。代入ステートメントの右側の cell 配列 C はコンマ区切りリストを生成します。次コードは、オブジェクト配列の各プロパティに 1 つのリスト項目を代入します。
case '.' if length(s)==1 prop = s(1).subs; % Property name n = numel(obj); % Number of elements in array for k = 1:n obj(k).(prop) = varargin{k}; end end end
初期化されていない変数を使用した、添字による代入
初期化されていない変数の要素に代入を行うと、代入の右辺でクラスの subsasgn メソッドが呼び出されます。たとえば、次のクラスでは、単にかっこのインデックス付けを行うために組み込みの subsasgn メソッドを呼び出す subsasgn メソッドを定義します。
classdef MyClass methods function obj = subsasgn(obj,s,varargin) switch s(1).type case '()' obj = builtin('subsasgn',obj,s,varargin{:}); end end end end
初期化されていない変数の 1 番目の要素に MyClass のオブジェクトを代入しようとすると (次のステートメントでは B(1))、MATLAB は MyClass の subsasgn メソッドを、1 番目の引数に空の double ([]) を指定して呼び出します。subsasgn メソッドにはクラスのオブジェクトを渡さなければならないため、この代入はエラーになる場合があります。
clear B
B(1) = MyClass;The following error occurred converting from MyClass to double:
Conversion to double from MyClass is not possible.
Error in MyClass/subsasgn (line 6)
obj = builtin('subsasgn',obj,s,varargin{:});subsasgn メソッドはこの状況を検出し、クラスがこのタイプの代入をサポートしていない場合に有用なエラー メッセージを返すことや、入力をクラスのオブジェクトに変換して subsasgn に渡すことなど、適切なアクションを実行することができます。
たとえば、MyClass は初期化されていない変数への添字による代入を許可できるため、subsasgn メソッドの 1 番目の引数を空の double から空の MyClass オブジェクトに変更できます。
関数 isequal を使用して、入力と empty 静的メソッドをチェックし、空のオブジェクトを作成します。
classdef MyClass methods function obj = subsasgn(obj,s,varargin) if isequal(obj,[]) obj = MyClass.empty; end obj = builtin('subsasgn',obj,s,varargin{:}); end end end
初期化されていない変数への添字による代入は、以前は発生していたエラーを回避するようになりました。
clear B
B(1) = MyClass;
B = MyClass with no properties.