Como posso indexar um array MATLAB retornado por uma função sem primeiro atribuí-lo a uma variável local?

363

Por exemplo, se eu quiser ler o valor do meio magic(5), posso fazer o seguinte:

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

para conseguir value == 13. Eu gostaria de poder fazer algo como um destes:

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

dispensar a variável intermediária. No entanto, o MATLAB reclama Unbalanced or unexpected parenthesis or bracketdo primeiro parêntese antes do 3.

É possível ler valores de uma matriz / matriz sem primeiro atribuí-lo a uma variável?

Joe Kearney
fonte
2
Também encontrei o seguinte artigo sobre este tema: mathworks.com/matlabcentral/newsreader/view_thread/280225 Alguém tem novas informações sobre este tema, elas serão implementadas?
2
Essa sintaxe realmente funciona bem no Octave. Eu só descobri esse problema quando meus colegas que usam o MATLAB estavam com problemas para executar meu código.
Sffc
2
MATLAB em poucas palavras.
user76284
11
Extração recursiva também trabalha em Scilab ( scilab.org ) desde a versão 6.
Stéphane Mottelet
o testmatrix('magi', 5)(3, 3)Scilab e o magic(5)(3, 3)Octave funcionam como um encanto!
Carrega 16/07/19

Respostas:

384

Na verdade, é possível fazer o que você deseja, mas é necessário usar a forma funcional do operador de indexação. Quando você executa uma operação de indexação usando (), na verdade está fazendo uma chamada para a subsreffunção. Portanto, mesmo que você não possa fazer isso:

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

Você pode fazer isso:

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

Feio, mas possível. ;)

Em geral, você só precisa alterar a etapa de indexação para uma chamada de função para não ter dois conjuntos de parênteses imediatamente após o outro. Outra maneira de fazer isso seria definir sua própria função anônima para fazer a indexação subscrita. Por exemplo:

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

No entanto, quando tudo estiver dito e feito, a solução temporária de variável local é muito mais legível, e definitivamente o que eu sugeriria.

gnovice
fonte
26
Bem, o que você sabe! embora eu concorde que é muito feio e provavelmente menos legível do que uma solução temp-var. +1 para obter um conhecimento impressionante e obscuro do Matlab!
segundo
57
Isso é nojento, mas uma resposta muito clara. Bom trabalho! Deveria ter adivinhado que haveria um caminho de volta para ele. Acho que continuarei com a variável temp.
Joe Kearney
29
Lembre-se de que a variável intermediária ainda é totalmente criada. Portanto, se o objetivo é economizar memória, não sendo necessário criar uma variável local temporária, sem sorte.
Sam Roberts
8
@ SamRoberts: Você realmente não consegue contornar isso em uma linguagem de avaliação rigorosa como o Matlab. A principal razão pela qual as pessoas desejam isso é concisão / legibilidade, não economia de memória.
Caracol mecânico
5
@SamRoberts: verdade, mas não salvá-lo do fardo de chamar cleara temporária (que ninguém nunca faz) - o temporário tende a ficar por mais tempo
Rody Oldenhuis
131

Havia apenas um bom post sobre Loren na Art of Matlab, alguns dias atrás, com algumas jóias que poderiam ajudar. Em particular, o uso de funções auxiliares como:

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

onde paren()pode ser usado como

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

retornaria

ans = 16

Eu também suporia que isso será mais rápido que a resposta do gnovice, mas não marquei (Use o criador de perfil !!!). Dito isto, você também precisa incluir essas definições de função em algum lugar. Pessoalmente, eu as fiz funções independentes no meu caminho, porque elas são super úteis.

Essas funções e outras agora estão disponíveis no complemento Functional Programming Constructs, disponível no MATLAB Add-On Explorer ou no File Exchange .

T. Furfaro
fonte
2
Esta é uma versão um pouco mais geral da segunda metade da resposta do gnovice; também é bom.
31813 Joe Kearney
Que tal myfunc().attr?
gerrit
@gerrit, como é que ajuda? e o campo x.attr () não estará disponível, a menos que você tenha a caixa de ferramentas do banco de dados.
T. Furfaro 21/03
@ T.Furfaro Hein? Se myfunc()retorna uma estrutura que inclui um atributo attr, para acessar attratualmente eu preciso fazer S = myfunc(); S.attr. A questão é se podemos ter uma função auxiliar como getattr(myfunc(), 'attr')em analogia aos auxiliares parene curly. Não entendo o que isso tem a ver com a caixa de ferramentas do banco de dados.
Gerrit 21/03
2
@gerrit Desculpe, total confusão (eu não sabia que o seu "attr" era arbitrário - no db tb existe uma explicação de campo tão definida). Eu acredito que o que você está procurando é getfield ()
T. Furfaro 04/04
75

Como você se sente ao usar recursos não documentados:

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

ou para matrizes de células:

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

Como mágica :)


ATUALIZAR:

Más notícias, o hack acima não funciona mais no R2015b ! Tudo bem, era uma funcionalidade não documentada e não podemos confiar nela como um recurso suportado :)

Para aqueles que se perguntam onde encontrar esse tipo de coisa, procure na pasta fullfile(matlabroot,'bin','registry'). Há um monte de arquivos XML lá que listam todos os tipos de brindes. Esteja avisado de que chamar algumas dessas funções diretamente pode travar facilmente sua sessão do MATLAB.

Amro
fonte
@RodyOldenhuis: Eu não me lembro agora, eu acho que deve ter lido isso em algum código enterrado;)
Amro
2
O operador dois pontos (:) deve ser usado com apóstrofos ':'para evitar o erro Undefined function or variable "builtin".
Dominik
@ Dominik: certo, digamos que você queira dividir a segunda coluna, isso seria: builtin('_paren', magic(5), ':', 2)(em certos lugares, ele funciona sem as aspas diretamente, :ao contrário ':', como quando executado no prompt de comando diretamente, não de dentro de uma função. isso é um bug no analisador)!
Amro
2
Suponho que não há alguma maneira de usar endisso?
Knedlsepp
2
@knedlsepp: Não, infelizmente toda a end-trickery não trabalho nesta sintaxe, você vai ter que ser explícito em sua indexação .. (mesma limitação se aplica para a maioria das outras respostas listadas)
Amro
54

Pelo menos no MATLAB 2013a você pode usar getfieldcomo:

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

para obter o elemento em (1,2)

Ian M. García
fonte
5
Este é realmente um bom método. Alguma desvantagem?
mmumboss
6
@mmumboss: Esse é um comportamento não documentado; essa funcionalidade pode desaparecer sem aviso prévio em versões futuras. Além disso, não há desvantagens.
Daniel
6
A partir do MATLAB2017b, essa funcionalidade está documentada.
njspeer
15

infelizmente a sintaxe like magic(5)(3,3)não é suportada pelo matlab. você precisa usar variáveis ​​intermediárias temporárias. você pode liberar a memória após o uso, por exemplo

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

Observe que, se você comparar os tempos de execução com a maneira padrão (atribuir o resultado e acessar as entradas), eles serão exatamente iguais.

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

Na minha opinião, a linha inferior é: o MATLAB não tem ponteiros, você precisa conviver com isso.

titus
fonte
6

Poderia ser mais simples se você criar uma nova função:

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

e depois use:

value = getElem(magic(5), 3, 3);
Vugar
fonte
11
mas é exatamente subrefisso que faz ... mas de uma maneira mais geral.
Shai
2
sim, de maneira mais geral, mas não amigável ... muito feia na minha opinião.
Vugar
4

Sua notação inicial é a maneira mais concisa de fazer isso:

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

Se você estiver fazendo isso em um loop, basta reatribuir M todas as vezes e ignorar a declaração clara também.

Andreas GS
fonte
6
Concordo que isso é mais conciso, e limpar é uma boa idéia em um loop, como você diz, mas a questão era especificamente se a atribuição intermediária pode ser evitada.
Joe Kearney
1

Para complementar a resposta de Amro, você pode usar em fevalvez de builtin. Realmente não há diferença, a menos que você tente sobrecarregar a função do operador:

BUILTIN (...) é igual a FEVAL (...), exceto que ela chamará a versão interna original da função, mesmo que exista uma sobrecarregada (para que isso funcione, você nunca deve sobrecarregar BUILTIN).

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

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

O interessante é que fevalparece ser um pouquinho mais rápido que builtin(em ~ 3,5%), pelo menos no Matlab 2013b, o que é estranho, pois é fevalnecessário verificar se a função está sobrecarregada, ao contrário 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
fonte
Na verdade, não é estranho: o MATLAB mantém uma lista de funções definidas, não há muito o que fazer. fevalfaz a coisa "normal" e, portanto, pode fazer pleno uso desta lista. builtindeve procurar em outro lugar para encontrar apenas funções incorporadas. É provável que este caso não seja otimizado quase tanto quanto o caso “normal”, porque por que você investiu na otimização de algo que não é usado com muita frequência?
Cris Luengo