1. 程式人生 > >Matlab中的類定義 classdef

Matlab中的類定義 classdef

Matlab也可以寫面向物件的程式碼,首先表現在可以定義類,以及可以繼承,使用類(class)有很多好處,其中一個重要的好處便是解決變數名衝突和讓函式、物件的結構清晰。class的static function可以在不定義類的例項直接呼叫類的成員函式,比如定義

  1. classdef tools <handle
  2.     methods (Static = true)  
  3.         function a = test(b, c)  
  4.             a = b + c;  
  5.         end  
  6.     end  
  7. end  

然後可以直接通過
a = tools.test(b, c);
呼叫函式。

classdel是定義類名,後面的<是表示繼承,Matlab允許多重繼承,繼承自handle類,handle類定義了很多關於object的處理函式,例如addListener以及notify還有delete等對物件的函式,是一個抽象類,不能例項化,具體可以看一下參考文獻【2】。

類的方法是放在methods ...end裡面。

下面是一個稍微複雜的類的定義

  1. classdef TensileData  
  2.    properties  
  3.       Material = 'carbon steel';  
  4.       SampleNumber = 0;  
  5.       Stress  
  6.       Strain  
  7.    end  
  8.    properties (Dependent)  
  9.       Modulus  
  10.    end  
  11.    methods  
  12.       function td = TensileData(material,samplenum,stress,strain)  
  13.          if nargin > 0  
  14.             td.Material = material;  
  15.             td.SampleNumber = samplenum;  
  16.             td.Stress = stress;  
  17.             td.Strain = strain;  
  18.          end  
  19.       end % TensileData  
  20.    end  
  21.    methods  
  22.       function obj = set.Material(obj,material)  
  23.          if ~(strcmpi(material,'aluminum') ||...  
  24.                strcmpi(material,'stainless steel') ||...  
  25.                strcmpi(material,'carbon steel'))  
  26.             error('Material must be aluminum, stainless steel, or carbon steel')  
  27.          end  
  28.          obj.Material = material;  
  29.       end % Material set function  
  30.       function modulus = get.Modulus(obj)  
  31.          ind = find(obj.Strain > 0); % Find nonzero strain  
  32.          modulus = mean(obj.Stress(ind)./obj.Strain(ind));  
  33.       end % Modulus get function  
  34.       function obj = set.Modulus(obj,~)  
  35.          fprintf('%s%d\n','Modulus is: ',obj.Modulus)  
  36.          error('You cannot set Modulus explicitly');  
  37.       end  
  38.       function disp(td)  
  39.          fprintf(1,'Material: %s\nSample Number: %g\nModulus: %1.5g\n',...  
  40.             td.Material,td.SampleNumber,td.Modulus);  
  41.       end % disp  
  42.       function plot(td,varargin)  
  43.          plot(td.Strain,td.Stress,varargin{:})  
  44.          title(['Stress/Strain plot for Sample ',num2str(td.SampleNumber)])  
  45.          xlabel('Strain %')  
  46.          ylabel('Stress (psi)')  
  47.       end % plot  
  48.    end  
  49.    methods (Access = 'private') % Access by class members only  
  50.       function m = CalcModulus(td)  
  51.          % Over-simplified calculation of Elastic Modulus  
  52.          ind = find(td.Strain > 0); % Find nonzero strain  
  53.          m = mean(td.Stress(ind)./td.Strain(ind));  
  54.       end % CalcModulus  
  55.    end  
  56. end % classdef  

在上述程式碼中,

classdef TensileData

  ...

end

是定義一個TensileData類。程式碼:

 properties
      Material = 'carbon steel';
      SampleNumber = 0;
      Stress
      Strain
   end
 是定義這個類的屬性,也就是C++中類的成員變數。但是和C++不同的是,matlab中的類定義還有一個比較特別的地方的,就是定義依賴屬性,如下述程式碼:

 properties (Dependent)
      Modulus
   end

這表示Modulus這個屬性是個依賴屬性,它的值是通過其他屬性計算得到的,其中Dependent的預設屬性值為True。

它的值是通過下面函式實現的:

% Modulus get function

function modulus = get.Modulus(obj)
         ind = find(obj.Strain > 0); % Find nonzero strain
         modulus = mean(obj.Stress(ind)./obj.Strain(ind));
end

類的方法(函式)的定義是以methods ... end的的形式出現的。如下面的類方法的定義:

methods
      function td = TensileData(material,samplenum,stress,strain)
         if nargin > 0
            td.Material = material;
            td.SampleNumber = samplenum;
            td.Stress = stress;
            td.Strain = strain;
         end
      end 
end

該函式塊定義了TensileData建構函式方法。上述程式碼中的最後一個方法 methods (Access = 'private')

中的Access = 'private'表示該方法僅能被類本身訪問和修改,是個私有成員方法。其中屬性Access又可分為

SetAccess和GetAccess,屬性值和Access相同。

面向物件的使用必然導致程式的開銷變高,關於相關的討論可以看一下【1】:

封裝後的函式多次呼叫會話費相當長的時間

所以到底該不該封裝,還需取決於實際情況,如果函式本身特別簡單,並且會被迴圈呼叫,最好還是通過m檔案函式的形式。之前MIT大牛給出了更多地建議

  1. 雖然for-loop的速度有了很大改善,vectorization(向量化)仍舊是改善效率的重要途徑,尤其是在能把運算改寫成矩陣乘法的情況下,改善尤為顯著。
  2. 在不少情況下,for-loop本身已經不構成太大問題,尤其是當迴圈體本身需要較多的計算的時候。這個時候,改善概率的關鍵在於改善迴圈體本身而不是去掉for-loop。
  3. MATLAB的函式呼叫過程(非built-in function)有顯著開銷,因此,在效率要求較高的程式碼中,應該儘可能採用扁平的呼叫結構,也就是在保持程式碼清晰和可維護的情況下,儘量直接寫表示式和利用built-in function,避免不必要的自定義函式呼叫過程。在次數很多的迴圈體內(包括在cellfun, arrayfun等實際上蘊含迴圈的函式)形成長呼叫鏈,會帶來很大的開銷。
  4. 在呼叫函式時,首選built-in function,然後是普通的m-file函式,然後才是function handle或者anonymous function。在使用function handle或者anonymous function作為引數傳遞時,如果該函式被呼叫多次,最好先用一個變數接住,再傳入該變數。這樣,可以有效避免重複的解析過程。
  5. 在可能的情況下,使用numeric array或者struct array,它們的效率大幅度高於cell array(幾十倍甚至更多)。對於struct,儘可能使用普通的域(欄位,field)訪問方式,在非效率關鍵,執行次數較少,而靈活性要求較高的程式碼中,可以考慮使用動態名稱的域訪問。
  6. 雖然object-oriented從軟體工程的角度更為優勝,而且object的使用很多時候很方便,但是MATLAB目前對於OO的實現效率很低,在效率關鍵的程式碼中應該慎用objects。
  7. 如果需要設計類,應該儘可能採用普通的property,而避免靈活但是效率很低的dependent property。如非確實必要,避免過載subsref和subsasgn函式,因為這會全面接管對於object的介面呼叫,往往會帶來非常巨大的開銷(成千上萬倍的減慢),甚至使得本來幾乎不是問題的程式碼成為效能瓶頸。

參考文獻:

【1】http://zhiqiang.org/blog/it/class-wrapper-functions-in-matlab.html

【2】http://cn.mathworks.com/help/matlab/ref/handle.html?searchHighlight=handle

【3】http://www.cnblogs.com/magic-cube/archive/2011/11/08/2241580.html

【4】http://anony3721.blog.163.com/blog/static/5119742011911111232557/