Comment puis-je indexer un tableau MATLAB retourné par une fonction sans d'abord l'assigner à une variable locale?

363

Par exemple, si je veux lire la valeur intermédiaire magic(5), je peux le faire comme ceci:

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

obtenir value == 13. J'aimerais pouvoir faire quelque chose comme l'un d'eux:

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

de se passer de la variable intermédiaire. Cependant, MATLAB se plaint de Unbalanced or unexpected parenthesis or bracketla première parenthèse avant le 3.

Est-il possible de lire les valeurs d'un tableau / d'une matrice sans d'abord l'assigner à une variable?

Joe Kearney
la source
2
J'ai également trouvé l'article suivant sur ce thème: mathworks.com/matlabcentral/newsreader/view_thread/280225. Quiconque a de nouvelles informations sur ce thème, sera-t-il implémenté?
2
Cette syntaxe fonctionne réellement bien dans Octave. J'ai découvert ce problème uniquement lorsque mes collègues qui utilisent MATLAB rencontraient des problèmes lors de l'exécution de mon code.
sffc
2
MATLAB en bref.
user76284
1
L'extraction récursive fonctionne également dans Scilab ( scilab.org ) depuis la version 6.
Stéphane Mottelet
le testmatrix('magi', 5)(3, 3)Scilab et magic(5)(3, 3)sur Octave travaillent tous les deux comme un charme!
Foad

Réponses:

384

En fait, il est possible de faire ce que vous voulez, mais vous devez utiliser la forme fonctionnelle de l'opérateur d'indexation. Lorsque vous effectuez une opération d'indexation à l'aide de (), vous effectuez en fait un appel à la subsreffonction. Donc, même si vous ne pouvez pas faire cela:

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

Vous pouvez le faire:

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

Moche, mais possible. ;)

En général, il vous suffit de modifier l'étape d'indexation en un appel de fonction afin de ne pas avoir deux jeux de parenthèses qui se suivent immédiatement. Une autre façon de procéder serait de définir votre propre fonction anonyme pour effectuer l'indexation en indice. Par exemple:

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

Cependant, quand tout est dit et fait, la solution de variable locale temporaire est beaucoup plus lisible, et certainement ce que je suggérerais.

gnovice
la source
26
Bien, que sait-tu! même si je suis d'accord, c'est assez moche et probablement moins lisible qu'une solution temp-var. +1 pour une impressionnante connaissance obscure de matlab!
deuxième
57
C'est dégoûtant, mais une réponse très claire. Bon travail! J'aurais dû deviner qu'il y aurait un moyen de revenir en arrière. Je pense que je vais continuer avec la variable temp.
Joe Kearney
29
Gardez à l'esprit que la variable intermédiaire est toujours entièrement créée. Donc, si le but est d'économiser de la mémoire en évitant de créer une variable locale temporaire, pas de chance.
Sam Roberts
8
@SamRoberts: Vous ne pouvez pas vraiment contourner cela dans un langage d'évaluation strict comme Matlab. La principale raison pour laquelle les gens le souhaitent est la concision / lisibilité, et non les économies de mémoire.
Escargot mécanique
5
@SamRoberts: vrai, mais il ne vous sauver de la charge d'appeler clearle temporaire (dont personne ne fait jamais) - le temporaire a tendance à rester plus longtemps
Rody Oldenhuis
131

Il y a juste un bon article de blog sur Loren sur l'Art du Matlab il y a quelques jours avec quelques gemmes qui pourraient aider. En particulier, en utilisant des fonctions d'assistance comme:

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

paren()peut être utilisé comme

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

retournerais

ans = 16

Je suppose également que ce sera plus rapide que la réponse de gnovice, mais je n'ai pas vérifié (utilisez le profileur !!!). Cela étant dit, vous devez également inclure ces définitions de fonction quelque part. Personnellement, je leur ai fait des fonctions indépendantes sur mon chemin, car elles sont super utiles.

Ces fonctions et d'autres sont désormais disponibles dans le module complémentaire Functional Programming Constructs, disponible via l'explorateur de modules complémentaires MATLAB ou sur l' échange de fichiers .

T. Furfaro
la source
2
Il s'agit d'une version légèrement plus générale de la seconde moitié de la réponse de gnovice; bien aussi.
Joe Kearney
Et alors myfunc().attr?
gerrit
@gerrit, comment ça marche? et le champ x.attr () n'est disponible que si vous disposez de la boîte à outils de la base de données.
T. Furfaro
@ T.Furfaro Huh? Si myfunc()renvoie une structure qui comprend un attribut attr, alors pour accéder attractuellement, je dois le faire S = myfunc(); S.attr. La question est de savoir si nous pouvons avoir une fonction d'aide comme getattr(myfunc(), 'attr')par analogie avec les aides parenet curly. Je ne comprends pas ce que cela a à voir avec la boîte à outils de la base de données.
gerrit
2
@gerrit Désolé, confusion totale (je ne savais pas que votre "attr" était arbitraire - dans la base de données db, une telle explicité de champ est définie). Je crois que ce que vous cherchez c'est getfield ()
T. Furfaro
75

Que pensez-vous de l'utilisation de fonctionnalités non documentées:

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

ou pour les réseaux de cellules:

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

Comme par magie :)


MISE À JOUR:

Mauvaise nouvelle, le hack ci-dessus ne fonctionne plus dans R2015b ! C'est très bien, c'était une fonctionnalité non documentée et nous ne pouvons pas nous y fier en tant que fonctionnalité prise en charge :)

Pour ceux qui se demandent où trouver ce type de chose, regardez dans le dossier fullfile(matlabroot,'bin','registry'). Il y a un tas de fichiers XML qui répertorient toutes sortes de goodies. Soyez averti que l'appel direct de certaines de ces fonctions peut facilement planter votre session MATLAB.

Amro
la source
@RodyOldenhuis: Je ne m'en souviens pas maintenant, je suppose que je dois l'avoir lu dans un code enfoui;)
Amro
2
L'opérateur deux points (:) doit être utilisé avec des apostrophes ':'pour éviter l'erreur Undefined function or variable "builtin".
Dominik
@Dominik: à droite, disons que vous voulez couper la 2ème colonne, ce serait: builtin('_paren', magic(5), ':', 2)(à certains endroits, cela fonctionne sans les citations directement :par opposition à ':', comme lors de l'exécution dans l'invite de commande directement pas à l'intérieur d'une fonction. Je suppose c'est un bug dans l'analyseur!)
Amro
2
Je ne suppose pas qu'il existe un moyen d'utiliser endcela?
knedlsepp
2
@knedlsepp: Non, malheureusement, toute la endtricherie ne fonctionne pas dans cette syntaxe, vous devrez être explicite dans votre indexation .. (La même limitation s'applique à la plupart des autres réponses répertoriées)
Amro
54

Au moins dans MATLAB 2013a, vous pouvez utiliser getfieldcomme:

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

pour obtenir l'élément à (1,2)

Ian M. García
la source
5
C'est en fait une belle méthode. Des inconvénients?
mmumboss
6
@mmumboss: C'est un comportement non documenté, cette fonctionnalité pourrait disparaître sans préavis dans les futures versions. En plus de cela, aucun inconvénient.
Daniel
6
Depuis MATLAB2017b, cette fonctionnalité est documentée.
njspeer
15

malheureusement, la syntaxe magic(5)(3,3)n'est pas supportée par matlab. vous devez utiliser des variables intermédiaires temporaires. vous pouvez libérer de la mémoire après utilisation, par exemple

tmp = magic(3);
myVar = tmp(3,3);
clear tmp
seconde
la source
12

Notez que si vous comparez les temps d'exécution avec la méthode standard (attribuez le résultat puis accédez aux entrées), ils sont exactement les mêmes.

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

À mon avis, l'essentiel est: MATLAB n'a pas de pointeurs, vous devez vivre avec.

titus
la source
6

Cela pourrait être plus simple si vous créez une nouvelle fonction:

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

puis l'utiliser:

value = getElem(magic(5), 3, 3);
Vugar
la source
1
mais c'est exactement ce qui subreffait ... mais d'une manière plus générale.
Shai
2
oui, façon plus générale, mais pas sympa ... à beaucoup moche à mon avis.
Vugar
4

Votre notation initiale est la façon la plus concise de le faire:

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

Si vous faites cela en boucle, vous pouvez simplement réaffecter M à chaque fois et ignorer la déclaration claire également.

Andreas GS
la source
6
Je suis d'accord que c'est plus concis, et la compensation est une bonne idée en boucle, comme vous le dites, mais la question était précisément de savoir si l'affectation intermédiaire peut être évitée.
Joe Kearney
1

Pour compléter la réponse d'Amro, vous pouvez utiliser à la fevalplace de builtin. Il n'y a vraiment aucune différence, sauf si vous essayez de surcharger la fonction opérateur:

BUILTIN (...) est le même que FEVAL (...) sauf qu'il appellera la version intégrée d'origine de la fonction même s'il existe une surcharge (pour que cela fonctionne, vous ne devez jamais surcharger BUILTIN).

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

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

Ce qui est intéressant, c'est que cela fevalsemble être un tout petit peu plus rapide que builtin(de ~ 3,5%), du moins dans Matlab 2013b, ce qui est bizarre étant donné qu'il fevalfaut vérifier si la fonction est surchargée, contrairement à 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
la source
Ce n'est en fait pas étrange: MATLAB conserve une liste de fonctions définies, il n'y a pas tant de recherches à faire. fevalfait la chose «normale» et peut donc utiliser pleinement cette liste. builtindoit rechercher ailleurs pour qu'il ne trouve que des fonctions intégrées. Ce cas n'est probablement pas optimisé presque autant que le cas «normal», car pourquoi mettriez-vous de l'argent pour optimiser quelque chose qui n'est pas utilisé très souvent?
Cris Luengo