How can I index a MATLAB array returned by a function without first assigning it to a local variable?
Solution 1
It actually is possible to do what you want, but you have to use the functional form of the indexing operator. When you perform an indexing operation using ()
, you are actually making a call to the subsref
function. So, even though you can't do this:
value = magic(5)(3, 3);
You can do this:
value = subsref(magic(5), struct('type', '()', 'subs', {{3, 3}}));
Ugly, but possible. ;)
In general, you just have to change the indexing step to a function call so you don't have two sets of parentheses immediately following one another. Another way to do this would be to define your own anonymous function to do the subscripted indexing. For example:
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
However, when all is said and done the temporary local variable solution is much more readable, and definitely what I would suggest.
Solution 2
There was just good blog post on Loren on the Art of Matlab a couple days ago with a couple gems that might help. In particular, using helper functions like:
paren = @(x, varargin) x(varargin{:});
curly = @(x, varargin) x{varargin{:}};
where paren()
can be used like
paren(magic(5), 3, 3);
would return
ans = 16
I would also surmise that this will be faster than gnovice's answer, but I haven't checked (Use the profiler!!!). That being said, you also have to include these function definitions somewhere. I personally have made them independent functions in my path, because they are super useful.
These functions and others are now available in the Functional Programming Constructs add-on which is available through the MATLAB Add-On Explorer or on the File Exchange.
Solution 3
How do you feel about using undocumented features:
>> builtin('_paren', magic(5), 3, 3) %# M(3,3)
ans =
13
or for cell arrays:
>> builtin('_brace', num2cell(magic(5)), 3, 3) %# C{3,3}
ans =
13
Just like magic :)
UPDATE:
Bad news, the above hack doesn't work anymore in R2015b! That's fine, it was undocumented functionality and we cannot rely on it as a supported feature :)
For those wondering where to find this type of thing, look in the folder fullfile(matlabroot,'bin','registry')
. There's a bunch of XML files there that list all kinds of goodies. Be warned that calling some of these functions directly can easily crash your MATLAB session.
Solution 4
At least in MATLAB 2013a you can use getfield
like:
a=rand(5);
getfield(a,{1,2}) % etc
to get the element at (1,2)
Solution 5
unfortunately syntax like magic(5)(3,3)
is not supported by matlab. you need to use temporary intermediate variables. you can free up the memory after use, e.g.
tmp = magic(3);
myVar = tmp(3,3);
clear tmp
Joe Kearney
Updated on July 08, 2022Comments
-
Joe Kearney almost 2 years
For example, if I want to read the middle value from
magic(5)
, I can do so like this:M = magic(5); value = M(3,3);
to get
value == 13
. I'd like to be able to do something like one of these:value = magic(5)(3,3); value = (magic(5))(3,3);
to dispense with the intermediate variable. However, MATLAB complains about
Unbalanced or unexpected parenthesis or bracket
on the first parenthesis before the3
.Is it possible to read values from an array/matrix without first assigning it to a variable?
-
second over 13 yearswell what do you know! though i agree it's pretty ugly, and probably less readable than a temp-var solution. +1 for impressive obscure matlab knowledge!
-
Joe Kearney over 13 yearsThat's disgusting, but a very clear answer. Good work! Should've guessed there'd be a back way into it. I'll think I'll carry on with the temp variable.
-
Sam Roberts over 12 yearsBear in mind that the intermediate variable is still fully created though. So if the purpose is to save memory by not having to create a temporary local variable, no luck.
-
Mechanical snail over 12 years@SamRoberts: You can't really get around that in a strict-evaluation language like Matlab. The main reason people want this is conciseness/readability, not memory savings.
-
Joe Kearney over 11 yearsI agree that this is more concise, and clearing is a good idea in a loop, as you say, but the question was specifically whether the intermediate assignment can be avoided.
-
Joe Kearney about 11 yearsThis is a slightly more general version of the second half of gnovice's answer; also good.
-
gerrit about 11 yearsWhat about
myfunc().attr
? -
T. Furfaro about 11 years@gerrit, how does at help? and the x.attr() field isn't available unless you have the database toolbox.
-
gerrit about 11 years@T.Furfaro Huh? If
myfunc()
returns a structure that includes an attributeattr
, then to accessattr
currently I need to doS = myfunc(); S.attr
. The question is if we can have a helper function likegetattr(myfunc(), 'attr')
in analogy to theparen
andcurly
helpers. I don't understand what this has to do with the database toolbox. -
T. Furfaro about 11 years@gerrit Sorry, total confusion ( I wasn't aware that your "attr" was arbitrary -- in the db tb there's such a field explicity defined ). I believe what you're looking for is getfield()
-
Shai about 11 yearsbut this is exactly what
subref
does... but in a more general way. -
Admin about 11 yearsyes, more general way, but not friendly... to much ugly in my opinion.
-
Rody Oldenhuis almost 11 years@SamRoberts: true, but it does save you from the burden of calling
clear
on the temporary (which no-one ever does) -- the temporary tends to stick around longer -
Amro almost 11 years@RodyOldenhuis: I dont recall now, I guess I must have read it in some buried code ;)
-
vim over 10 yearsWonderful! I stumbled around the internet for hours to find this! I'm going to stick with python.
-
mmumboss about 10 yearsThis is actually a nice method. Any drawbacks?
-
rayryeng almost 10 yearsHot damn. I didn't know this was possible. In the end, I'm still going to stick with the temp variable!
-
Dominik over 9 yearsThe colon (:) operator must be used with apostrophes
':'
to avoid the errorUndefined function or variable "builtin"
. -
Amro over 9 years@Dominik: right, say you want to slice the 2nd column, that would be:
builtin('_paren', magic(5), ':', 2)
(in certain places it does work without the quotations directly as:
as opposed to':'
, like when running in the command prompt directly not from inside a function. I guess that's a bug in the parser!) -
knedlsepp about 9 yearsAny way to use this with
end
, as invectorReturningFunction()(1:end-1)
? -
knedlsepp about 9 yearsI don't suppose there is some way to use
end
with this? -
Amro about 9 years@knedlsepp: No, unfortunately the whole
end
-trickery doesn't work in this syntax, you'll have to be explicit in your indexing.. (Same limitation applies for most other listed answers) -
Daniel about 9 years@mmumboss: That's undocumented behaviour, this functionality may disappear without notice in future versions. Besides this no disadvantages.
-
gnovice about 9 years@knedlsepp: Unfortunately, I haven't been able to figure out a way to get
end
to work with thesubsref
syntax I used above. -
Dev-iL almost 9 years@knedlsepp - According to docs of
end
: "In that context,end = (size(x,k))
when used as part of the kth index.". So I would say that the possibility of usingend
withsubsref
has a slight causality problem: because you're trying to findsize(x)
beforex
is known. However, "for the sake of freedom of shooting oneself in the foot", I suppose what you want is possible if you recursively dosubsref
(within the indexing term i.e.subsref(...,{1:size(subsref(...))})
) and obtain the result so that you could know its size... :) -
knedlsepp almost 9 years@Dev-iL: Would have been cool to have a feasible solution for this, but I think in the end I can still live without it. :-)
-
Dev-iL almost 9 years@knedlsepp - I managed to do this using a crazily complicated command that relies on matlab's interface with python, and python's negative-indexing ability. Here goes:
vectorReturningFunction = @()1:10; endminus = 4; double(py.array.array('d',py.ast.literal_eval(wrap_system(['python -c "import sys; print eval(sys.argv[1])[0:-int(sys.argv[2])]" ' regexprep(char(py.array.array('d',reshape(vectorReturningFunction(),1,[])).tolist()), ' ', '') ' ' num2str(endminus)]))))
. wherewrap_system()
is:function out = wrap_system(cmd); [~,out] = system(cmd);
. If you post a question I'll explain more.. -
BIOStheZerg over 7 years(I know this is a year-old discussion, but...) Well, if you use the other possibility, creating a custom function (anonymous or not), you can convert any (-1, 0, inf,...) index to
end
inside the function... -
nekomatic over 7 yearsI've edited in a link to the add-on containing a range of these functions, which is available through the MATLAB add-on explorer or the file exchange.
-
njspeer about 6 yearsAs of MATLAB2017b, this functionality is documented.
-
Edric almost 6 yearsThis solution can be refined a tiny bit by using
substruct
like so:subsref(magic(5), substruct('()', {3, 3}))
. -
Cris Luengo almost 6 yearsIt is actually not strange: MATLAB keeps a list of defined functions, there is not that much searching to do.
feval
does the “normal” thing and therefore can make full use of this list.builtin
must search elsewhere so it finds only built in functions. Likely this case is not optimized nearly as much as the “normal” case, because why would you put money into optimizing something not used very often? -
Rody Oldenhuis over 5 yearsJust out of curiosity - if you issue
[ subsref(A,substruct('{}',{':'})) ]
, it will not behave the same as[ A{:} ]
...any ideas for a workaround there? -
gnovice over 5 years@RodyOldenhuis: Using the functional form for indexing appears to also require you to specify the number of output arguments you want from the function, the default being one. The output from the operation is a comma-separated list, but only the first one is captured. Any attempt to capture multiple output arguments would not give you a simple way to concatenate them into a vector. It's all a moot point, however, since you could probably just use
cell2mat
:cell2mat(A)
-
Minh Nghĩa about 4 years@SamRoberts Will it be automatically
clear
ed right after that? -
Sam Roberts about 4 years@MinhNghĩa The intermediate variable will be cleared at the same time as any other variable - i.e. when it goes out of scope. That would not usually be immediately after the statement, but rather at the end of the function (although it could be different if the user does something like press Ctrl-C).
-
ZR Han about 3 yearsHow do I get a column or a row of the output? Such as
a(1, :)
. I've triedgetfield(rand(5), {1, 1:5})
andgetfield(rand(5), {1:5, 1})
which work fine, but are not elegant. -
Cris Luengo about 3 yearsThe
clear
statement will significantly slow down your code, it's better to leave it out unlessM
is terribly big and you're running out of memory somewhere. -
Andreas GS over 2 years@JoeKearney understood. Perhaps it's my novice level of Matlab, but intermediate values are computed in every answer given, if only implicitly in some. Is that correct? In any case, thanks for the feedback!
-
Jacob Lee over 2 yearsNice; I'd just run across these two functions, and now I have a solid use case for them.
-
John about 2 years@ZRHan: You can use
getfield(rand(5), {1, ':'})