Unexpected result using cellfun with 'size' as first argument on string array

The title of the topic says it all, but here is the result I would not expected.
Is this behaviour intended? or is it a bug?
s=["1"; "2"; "3"]
s = 3×1 string array
"1" "2" "3"
size(s,1)
ans = 3
cellfun(@(s) size(s,1), {s}) % correct
ans = 3
cellfun('size', {s}, 1) % this returns 1, that is not as I expected (not correct), 3 is correct answer
ans = 1

4 Comments

OK, it looks like even some advanced users give me a wrong answer to my question. So I repeat my old comment to a wrong (deleted) answer so that everyone can see cleary why it is wrong
Each element of c = {s} argument of cellfun; meaning s has then 3 rows, and not 1.
If you replace in my code example the input string array with numerical array it will work as expected
s=[1; 2; 3] % replace problematic string input s=["1"; "2"; "3"]
s = 3×1
1 2 3
size(s,1)
ans = 3
c = {s};
cellfun(@(s) size(s,1), c) % correct
ans = 3
cellfun('size', c, 1) % correct
ans = 3
I had forgotten that one could even call cellfun with this syntax!
FYI, works as you would expect on a 3x1 character array as well:
s = ['1'; '2'; '3'];
size(s,1)
ans = 3
cellfun(@(s) size(s,1), {s})
ans = 3
cellfun('size', {s}, 1)
ans = 3
I had forgotten that one could even call cellfun with this syntax!
For good reason. It doesn't appear to be documented.
Yes it is documented:
"Backward Compatibility
You can specify func as a character vector or string scalar, rather than a function handle, but only for a limited set of function names. func can be: 'isempty', 'islogical', 'isreal', 'length', 'ndims', 'prodofsize', 'size', or 'isclass'.
If you specify a function name rather than a function handle:
  • cellfun does not call any overloaded versions of the function.
  • The size and isclass functions require additional inputs to the cellfun function:A = cellfun('size',C,k) returns the size along the kth dimension of each element of C.A = cellfun('isclass',C,classname) returns logical 1 (true) for each element of C that matches the classname argument. This syntax returns logical 0 (false) for objects that are a subclass of classname."

Sign in to comment.

Answers (1)

Not a bug.
On the documentation page for the cellfun function, in the description of the func input argument, there is a section titled "Backward Compatibility". The relevant parts of that section: "If you specify a function name rather than a function handle:
  • cellfun does not call any overloaded versions of the function.
  • The size and isclass functions require additional inputs to the cellfun function"
Essentially, in that case cellfun will call the built-in size function rather than the string class's overload.
s=["1"; "2"; "3"]
s = 3×1 string array
"1" "2" "3"
which size(s) % string overload
size is a built-in method % string method
which size({s}) % built-in function
built-in (/MATLAB/toolbox/matlab/elmat/size)
cellfun(@size, {s}, 'UniformOutput', false) % calls string overload
ans = 1×1 cell array
{[3 1]}
cellfun('size', {s}, 1) % calls built-in
ans = 1
builtin('size', s)
ans = 1×2
1 1
Internally, a string array is a scalar object. It just tells MATLAB (via its size overload) that it has the size of its contents.
This is why I don't recommend calling cellfun (or arrayfun or structfun) with the name of the function to be applied. Use a function handle (could be anonymous) instead.

1 Comment

How many people would know string class overloads size? string is after all stock function and users should not question how it was built.
I'm not convinced the cellfun doc states that clearly the string exception.
Those specific "backward compatible" specific implemented functions for cellfun still have speed advantage over anonymous functions, when it works (on non-string arrays). I rather recommend not use STRING instead. :)

Sign in to comment.

Categories

Products

Release

R2023b

Asked:

on 21 Oct 2023

Edited:

on 21 Oct 2023

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!