Main Content

subsref メソッドおよび subsasgn メソッドのコード パターン

R2021b 以降では、インデックス付けをカスタマイズする場合、subsref および subsasgn をオーバーロードする代わりに、モジュール インデックス スーパークラスを使用するプロセスが推奨されています。詳細については、オブジェクトのインデックス付けのカスタマイズを参照してください。

インデックス付きの参照と代入のカスタマイズ

ユーザー定義クラスは、組み込みクラスと同じインデックス付け動作を行います。クラスは、MATLAB® がインデックス式を評価するために呼び出す関数をオーバーロードすることにより、インデックス操作をカスタマイズできます。インデックス付きの参照と代入の特殊な動作を定義するときは、関数 subsref と関数 subsasgn をオーバーロードします。

subsref メソッドおよび subsasgn メソッドの構文

MATLAB は、クラスの subsref メソッドと subsasgn メソッドを以下の引数で呼び出します。

メソッド入力出力

b = subsref(obj,s)

  • obj — インデックス式で使用するオブジェクトまたはオブジェクト配列

  • s — インデックス付け構造体

b — インデックス式の結果

obj = subsasgn(obj,s,b)

  • obj — インデックス式で使用するオブジェクトまたはオブジェクト配列

  • s — インデックス付け構造体

  • b — 代入する値

obj — 代入後のオブジェクトまたはオブジェクト配列

引数の数の変更

クラスの設計により、既定のインデックス操作で定義された数と異なる数の値をインデックス操作によって返したり代入したりする必要がある場合は、関数 numArgumentsFromSubscript をオーバーロードして、subsrefnargout および subsasgnnargin を制御します。詳細と例は、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 は MyClasssubsasgn メソッドを、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.

関連するトピック