2012-01-03 62 views
5

Mathworks文件交換存儲庫中有一些散列或字典類的實現。我所看到的所有內容都使用括號重載來進行關鍵引用,例如matlab subsref:{}帶字符串參數失敗,爲什麼?

d = Dict; 
d('foo') = 'bar'; 
y = d('foo'); 

這似乎是一個合理的界面。不過,如果你想要容易地使用包含其他字典的字典,使用大括號{}而不是括號,這將是更可取的,因爲這可以避免MATLAB允許使用多個圓括號(似乎是任意的)的語法限制,但是多個括號是允許的,即

t{1}{2}{3} % is legal MATLAB 
t(1)(2)(3) % is not legal MATLAB 

所以,如果你想很容易能夠字典中嵌套的字典,

dict{'key1'}{'key2'}{'key3'} 

的是Perl中的一個常見的成語,是可能的,在其他語言中經常有用包括Python,那麼除非你想使用n-1中間變量提取字典條目n層深,這似乎是一個不錯的選擇。並且重寫班級的subsrefsubsasgn操作似乎很容易,因爲他們之前爲()做了同樣的事情,並且一切都應該起作用。

除了當我嘗試它時沒有。

這是我的代碼。 (我已經將它縮小到最小的情況下,沒有實際的字典在這裏實現,每個對象都有一個鍵和一個值,但這足以說明問題)。

classdef TestBraces < handle 

    properties 
     % not a full hash table implementation, obviously 
     key 
     value 
    end 

    methods(Access = public) 


     function val = subsref(obj, ref) 
      % Re-implement dot referencing for methods. 
      if strcmp(ref(1).type, '.') 
       % User trying to access a method    
       % Methods access 
       if ismember(ref(1).subs, methods(obj)) 
        if length(ref) > 1 
         % Call with args 
         val = obj.(ref(1).subs)(ref(2).subs{:}); 
        else 
         % No args 
         val = obj.(ref.subs); 
        end 
        return; 
       end     
       % User trying to access something else. 
       error(['Reference to non-existant property or method ''' ref.subs '''']); 
      end 
      switch ref.type 
       case '()' 
        error('() indexing not supported.'); 
       case '{}' 
        theKey = ref.subs{1}; 
        if isequal(obj.key, theKey) 
         val = obj.value; 
        else 
         error('key %s not found', theKey); 
        end 
       otherwise 
        error('Should never happen') 
      end 
     end  

     function obj = subsasgn(obj, ref, value) 
      %Dict/SUBSASGN Subscript assignment for Dict objects. 
      % 
      % See also: Dict 
      % 

      if ~strcmp(ref.type,'{}') 
       error('() and dot indexing for assignment not supported.'); 
      end 

      % Vectorized calls not supported 
      if length(ref.subs) > 1 
       error('Dict only supports storing key/value pairs one at a time.'); 
      end 
      theKey = ref.subs{1}; 
      obj.key = theKey; 
      obj.value = value; 
     end % subsasgn   
    end  
end 

使用此代碼,我可以分配預期:

t = TestBraces; 
t{'foo'} = 'bar' 

(而且很明顯,從默認的顯示輸出t分配工作。)所以subsasgn似乎正常工作。

但我不能檢索值(subsref不工作):

t{'foo'} 
??? Error using ==> subsref 
Too many output arguments. 

錯誤信息是沒有意義的我,在我subsref處理程序的第一個可執行行設置斷點是永遠命中,所以至少從表面上看,這看起來像一個MATLAB問題,而不是我的代碼中的錯誤。

顯然字符串參數()括號標允許的,因爲如果你改變了代碼(),而不是{}工作能正常工作。 (除了你不能嵌套下標操作,這是練習的對象)。

無論是我在我的代碼中做錯了什麼,有什麼限制使我做的事情變得不可行,嵌套字典的實現將不勝感激。

回答

9

簡短的回答,這種方法添加到您的類:

function n = numel(obj, varargin) 
    n = 1; 
end 

編輯:長的答案。

儘管subsref的函數簽名出現在文檔中,但它實際上是一個可變參數函數 - 它可以產生可變數量的輸出參數。兩個支架和點索引可以產生多個輸出,如下所示:

>> c = {1,2,3,4,5}; 
>> [a,b,c] = c{[1 3 5]} 
a = 
    1 
b = 
    3 
c = 
    5 

subsref預期輸出的數量來確定基於所述索引數組的大小。在這種情況下,索引數組的大小是3,所以有三個輸出。

現在,再看看:

t{'foo'} 

什麼是索引數組的大小?此外,MATLAB不關心你打算把它解釋成字符串而不是數組。它只是看到輸入的大小是3,而你的subsref一次只能輸出1個東西。所以,爭論不匹配。幸運的是,我們可以通過改變MATLAB確定通過重載numel來預計有多少輸出的方式來糾正問題。從文檔鏈接引用:

重要的是要注意numel與 重載的subsref和subsasgn函數的重要性。如果 支撐和點索引過載subsref函數(如最後一段中描述的 ),numel用於計算從subsref返回的期望輸出(nargout)的數量。對於超負荷子函數,numel用於計算使用subsasgn分配的預期輸入(nargin)的數量。 重載subsasgn函數的值是由numel加2 (一個用於分配變量,另一個用於結構 下標數組)返回的值。

作爲類設計者,你必須確保內置函數返回的n的值與 那個對象的類設計一致。如果n與 過載的subsref函數的nargout或重載的subsasgn 函數的nargout不同,則需要過載numel以返回值爲n的 與類的subsref和subsasgn函數一致。 否則,MATLAB在調用這些函數時會產生錯誤。

而你有它。

+0

偉大的,那有效。是的,我想知道很長的答案。 – jmhl 2012-01-03 15:39:40

+0

感謝您的長時間答覆。我猜你在第一個答案後就是這樣,這就說明了這一點。太好了,再次感謝。 – jmhl 2012-01-03 16:03:01

+2

由於R2015b應該重載'numArgumentsFromSubscript'函數 - 請參閱http://stackoverflow.com/questions/8713730/matlab-subsref-with-string-argument-fails-why和Mathworks頁面https:// www的答案。 mathworks.com/help/matlab/matlab_oop/overloading-numel-subsref-and-subsasgn.html。 – nekomatic 2016-10-18 11:39:28

相關問題