2012-01-03 14 views
5

Esistono alcune implementazioni di una classe hash o dizionario nel repository di Scambio file di Mathworks. Tutto ciò che ho visto utilizza l'overloading delle parentesi per la referenziazione delle chiavi, ad es.sottosuolo MATLAB: {} con argomento stringa fallisce, perché?

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

che sembra un'interfaccia ragionevole. Sarebbe preferibile, tuttavia, se si desidera avere facilmente dizionari che contengono altri dizionari, utilizzare le parentesi graffe {} anziché parentesi, in quanto ciò consente di aggirare la limitazione della sintassi di MATLAB (arbitraria, sembra) che non sono consentite più parentesi ma multiple parentesi graffe sono consentiti, cioè

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

Quindi, se si vuole essere facilmente in grado di dizionari nido all'interno di dizionari,

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

come è un idioma comune in Perl ed è possibile e spesso utile in altre lingue incluso Python, quindi a meno che non si voglia utilizzare n-1 variabili intermedie per estrarre una voce dizionario n livelli profondi, questa sembra una buona scelta. E sembrerebbe facile riscrivere le operazioni della classe subsref e subsasgn per fare la stessa cosa per {} come hanno fatto in precedenza per () e tutto dovrebbe funzionare.

Tranne che non quando lo provo.

Ecco il mio codice. (Ho ridotto a un caso di minima. Nessun dizionario vero e proprio è implementato qui, ogni oggetto ha una chiave e un valore, ma questo è sufficiente per dimostrare il problema.)

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 

Utilizzando questo codice, posso assegnare come previsto:

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

(Ed è chiaro che il lavoro di assegnazione dall'uscita di visualizzazione predefinita per t.) Così subsasgn sembra funzionare correttamente.

Ma non riesco a recuperare il valore (subsref non funziona):

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

Il messaggio di errore non ha senso per me, e un punto di interruzione nella prima riga eseguibile del mio subsref gestore non è mai colpire, quindi almeno superficialmente questo sembra un problema di MATLAB, non un bug nel mio codice.

Chiaramente argomenti stringa a () pedice parentesi sono autorizzati, dal momento che questo funziona bene se si modifica il codice per lavorare con () invece di {}. (Tranne che non è possibile nidificare le operazioni di pedice, che è l'oggetto dell'esercizio.)

O intuizione su cosa sto facendo male nel mio codice, eventuali limitazioni che rendono ciò che sto facendo non fattibile, o alternativa le implementazioni dei dizionari nidificati sarebbero apprezzate.

risposta

9

Risposta breve, aggiungi questo metodo per la classe:

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

EDIT: La risposta lunga.

Nonostante il modo in cui la firma della funzione subsref appare nella documentazione, è in realtà una funzione di varargout - può produrre un numero variabile di argomenti di output. Sia brace e indicizzazione dot possono produrre uscite multiple, come illustrato di seguito:

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

Il numero di risultati attesi da subsref è determinato sulla base delle dimensioni della matrice di indicizzazione. In questo caso, l'array di indicizzazione è la dimensione 3, quindi sono disponibili tre output.

Ora, guardare di nuovo:

t{'foo'} 

Qual è la dimensione della matrice indicizzazione? Inoltre 3. A MATLAB non interessa che tu intenda interpretarlo come una stringa invece di un array. Vede solo che l'input è di dimensione 3 e il sottosoggetto può emettere solo 1 cosa alla volta. Quindi, gli argomenti non corrispondono. Fortunatamente, possiamo correggere le cose cambiando il modo in cui MATLAB determina quanti output sono previsti sovraccaricando numel. Citato dal link doc:

E 'importante notare il significato di Numel per quanto riguarda la subsref sovraccarico e funzioni subsasgn. Nel caso della funzione subsref sovraccaricata per l'indicizzazione di coppie e punti (come descritto nell'articolo nell'ultimo paragrafo), numel viene utilizzato per calcolare il numero di uscite previste (nargout) restituite da subsref da . Per la funzione subsasgn sovraccaricata, numel viene utilizzato per calcolare il numero di ingressi previsti (nargin) da assegnare utilizzando subsasgn. Il valore di nargin per la funzione subsasgn overload è il valore restituito da numel plus 2 (uno per la variabile assegnata a, e uno per la struttura matrice di pedici).

Come progettista di classe, è necessario assicurarsi che il valore di n restituito da la funzione numel incorporata sia coerente con la progettazione di classe dell'oggetto . Se n è diverso dal nargout per la funzione subsref sovraccaricata o da nargin per la funzione subsasgn sovraccaricata , è necessario sovraccaricare numel per restituire un valore di n che è coerente con le funzioni subsref e subsasgn della classe. In caso contrario, MATLAB produce errori durante la chiamata di queste funzioni.

E ce l'hai.

+0

Ottimo, funziona. Sì, mi piacerebbe conoscere la lunga risposta. – jmhl

+0

E grazie per la lunga risposta. Immagino che fosse qualcosa di simile dopo la tua prima risposta, e questo lo chiarisce. Ottimo, grazie ancora. – jmhl

+2

Dato che R2015b dovresti invece sovraccaricare la funzione 'numArgumentsFromSubscript' - vedi le risposte a http://stackoverflow.com/questions/8713730/matlab-subsref-with-string-argument-fails-why e la pagina di Mathworks https: // www. mathworks.com/help/matlab/matlab_oop/overloading-numel-subsref-and-subsasgn.html. – nekomatic

Problemi correlati