11

Under what circumstances can I pass a literal : to a Matlab function? I have discovered through experimentation, that sometimes, a literal : is passed on as the string ':', but in other situations, an error is raised. For example:

>> type writeargs

function writeargs(varargin)

disp(varargin);

end

>> writeargs(:)
Undefined variable writeargs.

>> writeargs(:, 1)
Undefined variable writeargs.

>> writeargs(:, 1, :)
    ':'    [1]    ':'

>> writeargs(:, :, :)
    ':'    ':'    ':'

>> writeargs(1, 2, :, 4, 5)
    [1]    [2]    ':'    [4]    [5]

>> writeargs(1, 2, :, end)
Error using writeargs
Too many output arguments.

I have the impression that this syntax is permitted if at least three arguments are passed. That appears arbitrary. What is the legal syntax here?

Edit: A comment asked for a use-case. A use case may be whenever my arguments are going to be used as slices or indices. In Python code I've had cases where I passed slice-objects to a method. One use case is for a function such as inspired by this answer, where a small function is used to circumvent Matlabs inability to interpret magic(5)(3, :), and one could write a helper function and call it with paren(magic(5), 3, :).

Community
  • 1
  • 1
gerrit
  • 24,025
  • 17
  • 97
  • 170
  • Are you looking for MATLAB bugs? `writeargs` is a function, not a matrix. Why would you specify a colon as an argument? – Eitan T Mar 28 '13 at 15:28
  • 3
    @EitanT Whenever my arguments are going to be used as slices or indices. For example, I have a function `y = p(x, varargin)` defined as `y = x(varargin{:});`, inspired by [this answer](http://stackoverflow.com/a/14811531/974555), and when my function call is really a workaround for the lack of `magic(5)(3, :)` in Matlab, it's nice to pass `:` directly as in `p(magic(5), 3, :)`. – gerrit Mar 28 '13 at 15:30
  • 1
    Interesting, but you should've specified that in the question itself. Anyway, you should see the answer for [this related question](http://stackoverflow.com/questions/8746547/passing-a-colon-as-argument-of-a-function-in-matlab)... I believe that the answer that you're looking for is that you cannot pass `:` to a function, but you can to an object (class). Alternatively, you can pass a colon character (`':'`) but that is probably not what you want. – Eitan T Mar 28 '13 at 15:38
  • @EitanT I've edited the question. Thanks for the pointer to the related question. – gerrit Mar 28 '13 at 15:44
  • 2
    @gerrit I'm thinking some combination of `:` tricks matlab into calling `subsref` for a variable named `writeargs` instead of calling your function. – JustinBlaber Mar 28 '13 at 15:59

2 Answers2

2

You are not supposed to pass a literal : to a function. The colon's use for indexing only works on variables directly (see here and here). The colon is no real object and has no type. Were you supposed to use it as a function argument, it would need to be a typed object. Of course, it may be confusing that function calls and variable indexing have the same syntax. But if you ask for a legal syntax for using the colon as function argument, there is none.

That being said, it nevertheless works in some cases, as you observed. That's due to some preprocessing step MATLAB takes and only MathWorks has a handle on. It almost seems that MATLAB calls the function via subsref-like prepocessing when you give it three or more arguments (or two arguments, both being colons), but not when you give it less than that. Go figure. MathWorks will avoid giving out any decisive explanation for that. I suspect, MATLAB uses stringified colons internally after applying subsref, because that's what you see when you receive literal colons in your function, and indexing operations seems to work like that throughout. E.g. try >> m(3, ':');

My proposal for your use case is essentially this answer (also referenced in the comments to your question) but hides the indexing in a function named paren, as you suggested. Also, it uses default parenthesis syntax instead of calling subsref, but it's the same anyway. Use stringified colons!

function result = paren(variable, varargin)

    result = variable(varargin{:});

Then call something like:

>> paren(magic(5), 3, ':');

In summary, you should not count on using a literal : as function argument in MATLAB, even though it may work for special cases. Use a string colon ':'.

Side note

You can use subsref for calling functions:

>> subsref(@magic, substruct('()', {3}))

In this way you can chain function call and referencing:

>> subsref(subsref(@magic, substruct('()', {3})), substruct('()', {':'}))

But this really is just the same as using a temporary variable. The inner subsref is evaluated first, and its result passed as input argument into the outer subsref call.

Even with subsref's chaining mechanism, you can not force MATLAB to accept two succeeding pairs of parentheses, like magic(3)(:).

>> magic(3)(:)
??? Error: ()-indexing must appear last in an index expression.

>> subs(1) = substruct('()', {3});
>> subs(2) = substruct('()', {':'});
>> subsref(str2func('magic'), subs)
??? Error using ==> subsref
Only a dot field name can follow ()'s.
Community
  • 1
  • 1
Alex
  • 1,125
  • 8
  • 8
1

writeargs(:) and writeargs(:,1) are treating writeargs as a locally scoped variable and trying to use the colon operator to index a "non existent" variable.

with more than three arguments the function is being called... sorry I don't understand why this behaviour exists, but I suspect it is implicitly calling subsref with one or two arguments, not with three or more.

you can test it by putting a breakpoint on the disp call in your function, you will see it is only hit when more than three arguments are given

janh
  • 1,926
  • 2
  • 11
  • 13
  • 1
    `writeargs(:,:)` outputs `':' ':'` so it's not just about 3 or more variables btw. I think we'd need to see the `subsref` source to make any valid conclusions here... – JustinBlaber Mar 29 '13 at 15:23
  • i think your right, if you look at the comments of subsref it seems that the arguments inside brackets are passed into the subsref function, colons are always passed as strings... presumably the default subsref implementation handles this and the writeargs example is only presenting the results of subsref – janh Mar 30 '13 at 05:29