4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

MATLAB使いのハード屋のために教えてやるか、"クラス"ってやつをよ……

Posted at

クラス概念を誰も理解していないのは弊社の特殊なアレなのではないかと思うけど、似たような現場があったら悲しいから書いておこう。

次のブログ記事をたいへん参考にしました:

クラスとは

クラスはデータの定義を行う構造体に、そのデータを処理する関数も一緒に埋め込んだものだと考えてください。

クラスを利用することでデータとデータの処理方法をセットにすることが出来ます。そうすればデータに対する間違った処理をコーディングしてしまうリスクを回避できますし、定義したデータをどのように扱えばいいのか読みやすくなります。

クラス定義

Matlabでは次のようにクラス定義が行えます。

(例とはいえ何のためのクラスなのかわからなさすぎるので後で書き換えようかな)

Pointer.m
classdef Pointer < handle                      % handleクラスを継承するPointerクラスの定義
    properties (SetAccess='protected',GetAccess='protected')   % プロパティ変数の定義ブロックの開始
        value                                  % プロパティ変数valueの宣言
    end
    methods                                    % メソッドの定義ブロックの開始
        function obj = Pointer(init_value)     % コンストラクタ
            obj.value = init_value;            % プロパティ変数valueに引数init_valueを設定
        end
        function value = get(obj)              % getメソッドの定義 メソッドは自分で自由に名付けて自由に定義してね
            value = obj.value;                 % 返り値としてプロパティ変数valueを返す
        end
        function set(obj, new_value)           % setメソッドの定義 メソッドは自分で自由に名付けて自由に定義してね
            obj.value = new_value;             % プロパティ変数valueを引数new_valueに設定
        end
    end
end

作成したクラスは次のように利用できます。

>> p = Pointer(1);     % インスタンス化
>> p.get()             % メソッド呼び出し get(p)でも可

ans =
        1

>> p.set(2);           % メソッド呼び出し set(p,2)でも可
>> p.get()

ans =
        2

インスタンスの特徴

handleクラスを継承するクラスからつくられるインスタンスは参照型データになります。

>> p1 = Pointer(1);
>> p2 = p1;
>> p2.set(2);
>> p1.get()

ans =
        2

別インスタンスであれば当然別のデータとして扱うことが出来ます。

>> p1 = Pointer(1);
>> p2 = Pointer(1);
>> p1.set(2);
>> p2.set(3);
>> p1.get()

ans =
        2

>> p2.get()

ans =
        3

インスタンスを値渡しでコピーしたいとき

クローンメソッドを定義しましょう

Pointer.m
classdef Pointer < handle
    properties (SetAccess='protected',GetAccess='protected')
        value 
    end
    methods 
        function obj = Pointer(init_value)  
            obj.value = init_value;  
        end
        function value = get(obj)  
            value = obj.value; 
        end
        function set(obj, new_value)  
            obj.value = new_value; 
        end
+        function pointer = clone(obj)        % 自分と同じプロパティ変数を持つインスタンスを返すメソッド
+            pointer = Pointer(0);
+            pointer.value = obj.value;
+        end
    end
end
>> p1 = Pointer(1);
>> p2 = p1.clone();
>> p2.get()

ans =
        1

>> p2.set(2);
>> p1.get()

ans =
        1

>> p2.get()

ans =
        2

継承

人類には早すぎるから教えない方がいいのかもしれない……

ポリモーフィズム

継承の説明を省いたことによりここの説明が難しくなった
何やってるのか勘で気づいてほしい

CounterクラスとMeterクラスはどちらもshowメソッドを持っているものとする

Main.m
function Main
    c1 = Counter(10);
    c2 = Meter(10);
    do(c1);
    do(c2);
end

function do(c)
    c.show();
end
結果
10
**********

一行目の結果はCounterクラスのメソッドの実行結果
二行目の結果はMeterクラスのメソッドの実行結果

つまりdo関数から見れば、入って来た引数に
「おら、仕事しろ」
と命令すれば、各々のクラスが自分の職務を全うしてくれるわけです。

こういうの知らないとdo関数の中でswitchとかif文書いて……っていうのをあちこちの関数で繰り返すことになって大変でしょ

私の前任者たちは数千行ものswitch文とif文のつけたしをあちこちの関数で繰り返していました。それを一人で保守する仕事を今しています。まじでやめてくれ

MATLAB R2011a以降では異種混合配列によって同一のスーパークラスを親に持つ異なるサブクラスのインスタンスを配列に出来ます。

MathWorks:異種混合クラス階層の設計

こんな感じで多重継承するか

classdef Counter() < handle & matlab.mixin.Heterogeneous 

もっときれいにやるには親クラスをインターフェースとして定義してあげて、親クラスを継承するサブクラスを使ってください。

classdef InterfaceCounter() < handle & matlab.mixin.Heterogeneous 
classdef Counter() < InterfaceCounter
classdef Meter() < InterfaceCounter

演算子のオーバーロード

クラスのインスタンスに対する演算子(+や=など)の働き方も自由に定義できます。

String.m
classdef String < handle
    properties(SetAccess='protected',GetAccess='protected')
        args
    end
    methods
        function obj = String(args)
            obj.args = args;
        end
        function ret = plus(obj,s)              % plus演算子(+)のオーバーライド
            str = [obj.args s.args];            % お互いのプロパティ変数argsを合体
            ret = String(str);                  % 新しいStringオブジェクトを合体結果で初期化して返す
        end
        function ret = eq(obj,s)                % eq演算子(==)のオーバーライド
            ret = strcmp(obj.args, s.args);     % お互いのプロパティ変数argsを比較
        end
        function ret = ne(obj,s)                % ne演算子(~=)のオーバーライド
            ret = ~strcmp(obj.args, s.args);    % お互いのプロパティ変数argsを比較
        end
        function ret = char(obj)                % charライブラリのオーバーライド
            ret = obj.args;                     % プロパティ変数argsを返す
        end
    end
end
>> a = String('Hello');
>> b = String('World');
>> a == b                  % eq(a,b)と同じ意味

ans =
        0

>> char(a + b)             % char( plus(a,b) )と同じ意味

ans =
        HelloWorld

オーバーロード可能な演算子の一覧

実はクロージャで疑似的にクラスが作成できます

MATLABは1ファイルで1クラスしか定義できないので、たまにこういうことやってるけど、あまりやらんほうがいいのかもね。

Counter.m
function obj = Counter(i)
    function count
        i = i + 1;
    end
    obj.count = @count;
    function show
        disp(i);
    end
    obj.show = @show;
end
4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?