¿Cómo puedo indexar una matriz MATLAB devuelta por una función sin asignarla primero a una variable local?

363

Por ejemplo, si quiero leer el valor medio de magic(5), puedo hacerlo así:

M = magic(5);
value = M(3,3);

para llegar value == 13. Me gustaría poder hacer algo como uno de estos:

value = magic(5)(3,3);
value = (magic(5))(3,3);

prescindir de la variable intermedia. Sin embargo, MATLAB se queja sobre Unbalanced or unexpected parenthesis or bracketel primer paréntesis antes del 3.

¿Es posible leer valores de una matriz / matriz sin asignarlo primero a una variable?

Joe Kearney
fuente
2
También encontré el siguiente artículo sobre este tema: mathworks.com/matlabcentral/newsreader/view_thread/280225 Alguien tiene nueva información sobre este tema, ¿se implementará?
2
Esta sintaxis en realidad funciona bien en Octave. Solo descubrí este problema cuando mis colegas que usan MATLAB tenían problemas para ejecutar mi código.
sffc
2
MATLAB en pocas palabras.
user76284
1
La extracción recursiva también funciona en Scilab ( scilab.org ) desde la versión 6.
Stéphane Mottelet
¡tanto testmatrix('magi', 5)(3, 3)en Scilab como magic(5)(3, 3)en Octave funcionan a la perfección!
Foad

Respuestas:

384

En realidad, es posible hacer lo que quiera, pero debe usar la forma funcional del operador de indexación. Cuando realiza una operación de indexación utilizando (), en realidad está haciendo una llamada a la subsreffunción. Entonces, aunque no puedas hacer esto:

value = magic(5)(3, 3);

Usted puede hacer esto:

value = subsref(magic(5), struct('type', '()', 'subs', {{3, 3}}));

Feo, pero posible. ;)

En general, solo tiene que cambiar el paso de indexación a una llamada de función para que no tenga dos conjuntos de paréntesis inmediatamente seguidos. Otra forma de hacerlo sería definir su propia función anónima para hacer la indexación con subíndice. Por ejemplo:

subindex = @(A, r, c) A(r, c);     % An anonymous function for 2-D indexing
value = subindex(magic(5), 3, 3);  % Use the function to index the matrix

Sin embargo, cuando todo está dicho y hecho, la solución de variable local temporal es mucho más legible, y definitivamente lo que sugeriría.

gnovice
fuente
26
¡Bueno, qué sabes! aunque estoy de acuerdo en que es bastante feo, y probablemente menos legible que una solución temp-var. ¡+1 para un conocimiento impresionante de matlab oscuro!
segundo
57
Eso es asqueroso, pero una respuesta muy clara. ¡Buen trabajo! Debería haber adivinado que habría un camino hacia atrás. Creo que continuaré con la variable temporal.
Joe Kearney
29
Sin embargo, tenga en cuenta que la variable intermedia todavía está completamente creada. Entonces, si el propósito es ahorrar memoria al no tener que crear una variable local temporal, no hay suerte.
Sam Roberts
8
@SamRoberts: Realmente no puedes evitar eso en un lenguaje de evaluación estricto como Matlab. La razón principal por la que la gente quiere esto es la concisión / legibilidad, no el ahorro de memoria.
Caracol mecánico
55
@SamRoberts: cierto, pero hace Ahorro a partir de la carga de la llama clearen el temporal (que alguna vez lo hace nadie) - tiende el temporal a quedarse más tiempo
Rody Oldenhuis
131

Hubo una buena publicación de blog en Loren sobre el Arte de Matlab hace un par de días con un par de gemas que podrían ayudar. En particular, el uso de funciones auxiliares como:

paren = @(x, varargin) x(varargin{:});
curly = @(x, varargin) x{varargin{:}};

donde paren()se puede usar como

paren(magic(5), 3, 3);

volvería

ans = 16

También supongo que esto será más rápido que la respuesta de gnovice, pero no lo he verificado (¡¡¡Usa el generador de perfiles !!!). Dicho esto, también debe incluir estas definiciones de funciones en alguna parte. Personalmente, los he convertido en funciones independientes en mi camino, porque son súper útiles.

Estas funciones y otras ahora están disponibles en el complemento Construcciones de programación funcional que está disponible a través del Explorador de complementos de MATLAB o en el Intercambio de archivos .

T. Furfaro
fuente
2
Esta es una versión un poco más general de la segunda mitad de la respuesta de gnovice; también bueno.
Joe Kearney
¿Qué hay de myfunc().attr?
gerrit
@gerrit, ¿cómo ayuda? y el campo x.attr () no está disponible a menos que tenga la caja de herramientas de la base de datos.
T. Furfaro
@ T.Furfaro ¿Eh? Si myfunc()devuelve una estructura que incluye un atributo attr, para acceder attractualmente tengo que hacerlo S = myfunc(); S.attr. La pregunta es si podemos tener una función auxiliar como getattr(myfunc(), 'attr')en analogía con los pareny curlyauxiliares. No entiendo qué tiene que ver esto con la caja de herramientas de la base de datos.
gerrit
2
@gerrit Perdón, confusión total (no sabía que su "atributo" era arbitrario; en el db tb se define tal explicidad de campo). Creo que lo que estás buscando es getfield ()
T. Furfaro
75

¿Cómo te sientes al usar características no documentadas?

>> builtin('_paren', magic(5), 3, 3)               %# M(3,3)
ans =
    13

o para matrices de células:

>> builtin('_brace', num2cell(magic(5)), 3, 3)     %# C{3,3}
ans =
    13

Justo como magia :)


ACTUALIZAR:

Malas noticias, ¡el truco anterior ya no funciona en R2015b ! Está bien, era una funcionalidad no documentada y no podemos confiar en ella como una característica compatible :)

Para aquellos que se preguntan dónde encontrar este tipo de cosas, busquen en la carpeta fullfile(matlabroot,'bin','registry'). Hay un montón de archivos XML que enumeran todo tipo de golosinas. Tenga en cuenta que llamar a algunas de estas funciones directamente puede bloquear fácilmente su sesión de MATLAB.

Amro
fuente
@RodyOldenhuis: No recuerdo ahora, creo que debí haberlo leído en algún código oculto;)
Amro
2
El operador de dos puntos (:) debe usarse con apóstrofes ':'para evitar el error Undefined function or variable "builtin".
Dominik
@Dominik: correcto, digamos que quiere cortar la segunda columna, eso sería: builtin('_paren', magic(5), ':', 2)(en ciertos lugares funciona sin las citas directamente :en lugar de ':', como cuando se ejecuta en el símbolo del sistema directamente no desde el interior de una función. Supongo que eso es un error en el analizador!)
Amro
2
¿Supongo que no hay forma de usar endesto?
knedlsepp
2
@knedlsepp: No, desafortunadamente todo el endtruco no funciona en esta sintaxis, tendrá que ser explícito en su indexación ... (La misma limitación se aplica para la mayoría de las otras respuestas enumeradas)
Amro
54

Al menos en MATLAB 2013a puedes usar getfieldcomo:

a=rand(5);
getfield(a,{1,2}) % etc

para obtener el elemento en (1,2)

Ian M. García
fuente
55
Este es realmente un buen método. ¿Algún inconveniente?
mmumboss
66
@mmumboss: Ese es un comportamiento indocumentado, esta funcionalidad puede desaparecer sin previo aviso en futuras versiones. Además de esto no hay inconvenientes.
Daniel
66
A partir de MATLAB2017b, esta funcionalidad está documentada.
njspeer
15

desafortunadamente la sintaxis como magic(5)(3,3)no es compatible con matlab. necesita usar variables intermedias temporales. puede liberar la memoria después de su uso, p. ej.

tmp = magic(3);
myVar = tmp(3,3);
clear tmp
segundo
fuente
12

Tenga en cuenta que si compara los tiempos de ejecución con la forma estándar (asigne el resultado y luego acceda a las entradas), son exactamente iguales.

subs=@(M,i,j) M(i,j);
>> for nit=1:10;tic;subs(magic(100),1:10,1:10);tlap(nit)=toc;end;mean(tlap)

ans =

0.0103

>> for nit=1:10,tic;M=magic(100); M(1:10,1:10);tlap(nit)=toc;end;mean(tlap)

ans =

0.0101

En mi opinión, la conclusión es: MATLAB no tiene punteros, hay que vivir con ellos.

titus
fuente
6

Podría ser más simple si realiza una nueva función:

function [ element ] = getElem( matrix, index1, index2 )
    element = matrix(index1, index2);
end

y luego úsalo:

value = getElem(magic(5), 3, 3);
Vugar
fuente
1
pero esto es exactamente lo que subrefhace ... pero de una manera más general.
Shai
2
sí, de manera más general, pero no amigable ... demasiado feo en mi opinión.
Vugar
4

Su notación inicial es la forma más concisa de hacer esto:

M = magic(5);  %create
value = M(3,3);  % extract useful data
clear M;  %free memory

Si está haciendo esto en un bucle, puede reasignar M cada vez e ignorar la declaración clara también.

Andreas GS
fuente
66
Estoy de acuerdo en que esto es más conciso, y la limpieza es una buena idea en un bucle, como usted dice, pero la pregunta era específicamente si se puede evitar la asignación intermedia.
Joe Kearney el
1

Para complementar la respuesta de Amro, puede usar en fevallugar de builtin. Realmente no hay diferencia, a menos que intentes sobrecargar la función del operador:

BUILTIN (...) es lo mismo que FEVAL (...) excepto que llamará a la versión incorporada original de la función incluso si existe una sobrecargada (para que esto funcione, nunca debe sobrecargar BUILTIN).

>> feval('_paren', magic(5), 3, 3)               % M(3,3)
ans =
    13

>> feval('_brace', num2cell(magic(5)), 3, 3)     % C{3,3}
ans =
    13

Lo interesante es que fevalparece ser un poco más rápido que builtin(en ~ 3.5%), al menos en Matlab 2013b, lo cual es extraño dado que fevalnecesita verificar si la función está sobrecargada, a diferencia de builtin:

>> tic; for i=1:1e6, feval('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 49.904117 seconds.
>> tic; for i=1:1e6, builtin('_paren', magic(5), 3, 3); end; toc;
Elapsed time is 51.485339 seconds.
nirvana-msu
fuente
En realidad no es extraño: MATLAB mantiene una lista de funciones definidas, no hay mucha búsqueda que hacer. fevalhace lo "normal" y, por lo tanto, puede hacer uso completo de esta lista. builtindebe buscar en otra parte para que solo encuentre funciones integradas. Probablemente este caso no se optimiza tanto como el caso "normal", porque ¿por qué invertiría dinero en optimizar algo que no se usa con mucha frecuencia?
Cris Luengo