2012-01-03 20 views
5

Hay algunas implementaciones de un hash o un tipo de diccionario en la Mathworks del repositorio de archivos de Exchange. Todo lo que he visto usa la sobrecarga de paréntesis para la referencia de teclas, p.MATLAB subsref: {} con el argumento de cadena falla, ¿por qué?

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

que parece una interfaz razonable. Sería preferible, sin embargo, si usted quiere tener fácilmente los diccionarios que contienen otros diccionarios, para utilizar los apoyos {} en lugar de paréntesis, ya que esto le permite obtener alrededor (, parece arbitraria) Limitación de la sintaxis de MATLAB que múltiples paréntesis no son permitidos, pero múltiples apoyos están permitidos, es decir

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

Así que si quieres ser fácilmente capaz de diccionarios de anidación dentro de los diccionarios,

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

como es un lenguaje común en Perl y es posible y con frecuencia útil en otros idiomas incluyendo Python, entonces a menos que quiera usar n-1 variables intermedias para extraer una entrada de diccionario n capas de profundidad, esto parece una buena opción. Y parecería fácil reescribir subsref y subsasgn las operaciones de la clase a hacer lo mismo para {}, ya que previamente hicieron por (), y todo debería funcionar.

Excepto que no hace cuando lo intento.

Aquí está mi código. (He reducido a un caso mínimo. Sin diccionario real se lleva a cabo aquí, cada objeto tiene una clave y un valor, pero esto es suficiente para demostrar el 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 

Usando este código, puedo asignar como se esperaba:

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

(Y está claro que el trabajo de asignación de la salida de la pantalla por defecto para t.) Así subsasgn parece funcionar correctamente.

Pero no puedo recuperar el valor (subsref no funciona):

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

El mensaje de error que no tiene sentido para mí, y un punto de interrupción en la primera línea ejecutable de mi manejador subsref no es nunca golpear, por lo que al menos superficialmente esto parece un problema de MATLAB, no un error en mi código.

Es evidente que los argumentos de cadena a () subíndices paréntesis están permitidos, ya que esto funciona bien si cambia el código para trabajar con () en lugar de {}. (Excepto que no puede anidar las operaciones de subíndices, que es el objeto del ejercicio).

O bien información sobre lo que estoy haciendo mal en mi código, cualquier limitación que haga que lo que estoy haciendo sea inviable o alternativa las implementaciones de diccionarios anidados serían apreciadas.

Respuesta

9

Respuesta corta, añadir este método a su clase:

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

EDITAR: La respuesta larga.

A pesar de que la firma de la función subsref aparece en la documentación, en realidad es una función varargout: puede producir un número variable de argumentos de salida. Tanto abrazadera y la indexación de puntos puede producir múltiples salidas, como se muestra aquí:

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

El número de salidas que se esperan de subsref se determina basándose en el tamaño de la matriz de indexación. En este caso, la matriz de indexación es de tamaño 3, por lo que hay tres salidas.

Ahora, mirar de nuevo:

t{'foo'} 

¿Cuál es el tamaño de la matriz de indexación? Además 3. A MATLAB no le importa que intente interpretar esto como una cadena en lugar de una matriz. Simplemente ve que la entrada es de tamaño 3 y que su subsref solo puede generar 1 cosa a la vez. Entonces, los argumentos no coinciden. Afortunadamente, podemos corregir las cosas cambiando la forma en que MATLAB determina cuántas salidas se esperan al sobrecargar numel. Citado desde el enlace doc:

Es importante tener en cuenta la importancia de Numel con respecto a la subsref sobrecargado y funciones subsasgn. En el caso de la función subsref sobrecargada para la indexación de llaves y puntos (como se describe en el último párrafo), numel se usa para calcular el número de salidas esperadas (nargout) devueltas de subsref. Para la función de sobreasignación sobrecargada, numel se utiliza para calcular el número de entradas esperadas (nargin) que se asignarán utilizando subsasgn. El valor nargin para la función subsasgn sobrecargada es el valor devuelto por numel más 2 (uno para la variable asignada a, y otro para la estructura matriz de subíndices).

Como diseñador de la clase, debe asegurarse de que el valor de n devuelto por la función integrada de Numel es consistente con el diseño de la clase de ese objeto. Si n es diferente, ya sea del nargout para la función sobrecargada subsref o la nargin para la función sobrecargada subsasgn , entonces es necesario sobrecargar Numel para devolver un valor de n que es consistente con las funciones de la clase subsref y subsasgn. De lo contrario, MATLAB produce errores al llamar a estas funciones.

Y ahí lo tienes.

+0

Genial, eso funciona. Sí, me gustaría saber la respuesta larga. – jmhl

+0

Y gracias por la respuesta larga. Supuse que era algo así después de su primera respuesta, y esto lo aclara. Genial, gracias de nuevo. – jmhl

+2

Desde R2015b, en su lugar, debe sobrecargar la función 'numArgumentsFromSubscript'; consulte las respuestas a http://stackoverflow.com/questions/8713730/matlab-subsref-with-string-argument-fails-why y la página de Mathworks https: // www. mathworks.com/help/matlab/matlab_oop/overloading-numel-subsref-and-subsasgn.html. – nekomatic

Cuestiones relacionadas