Arguments accept char, string or "cellstr" (cell array of char or string)

67 visualizaciones (últimos 30 días)
I have a large number of functions I eventually intend to be redistributable at my company. I had started creating my own variable validation routines but, fortunately I found the built in arguments block before I got too far down that road. It's very useful in data validation.
It's nice that arguments will allow not just strictly the given type/class of variable but will also allow anything that is convertible to that type (e.g. you can pass a string to char or char to string and any numeric types are also automatically converted). If you have two different types, it's a little trickier, but I've figured out how to use mustBeA and an array of strings of the types I want to accept.
One thing haven't figured out how to do effectively using this method. I have multiple aguments in multiple functions that can accept an input that is a char array, a string, or a cell array of char or string. I can use mustBeA to accept char, string, or cell - but I don't want to accept just any cell variable, it needs to be a cell array of char or string only, and it's not directly convertible. I know some MATLAB functions have sort of defined a type called cellstr which is a cell array of char or string - but that doesn't appear to be a checkable type in the arguments block.
I can, of course, add a block in my actual code to see if the argument matches iscell and then loop through the array and check all the elements but it would be nice if there's a way to do it within the arguments block.
Is there a way to do this inside the arguments block?

Respuestas (3)

dpb
dpb el 8 de Feb. de 2023
You write a custom validation function that your code in the arguments block calls for special cases if you're using the supplied arguments checking routines.
I've not really adopted it as don't write "production" MATLAB code for others to use, but my "trick" for the specific case you've outlined above looks like
function res=myfunction(arg)
argOK=true;
try
cellstr(arg); % throws error if any cell content isn't char or string
catch
argOK=false;
... % do whatever need here -- throw error, fixup if can, whatever...
end
...
The less tricky, more straightforward way would be to use something like
isOK=all(cellfun(@(c)ischar(c)|isstring(c),arg),'all');
in the custom arg test function.
  4 comentarios
Captain Karnage
Captain Karnage el 8 de Feb. de 2023
Don't get me started on curly braces. I'm working on converting some other code from cell arrays to standard arrays. Also tends to execute more efficiently. I'm finding out that you really only need cells if you have heterogeneous data (or - one exception - if you want a column of char vectors of different lengths), and even then sometimes you can avoid that with matlab.mixin. I've also found that using table and timetable without being careful will automatically create cells sometimes when it doesn't necessarily have to and trying to avoid that as well.
However, I want to support this type being passed to my function as I'm doing this from the perpsective of "what could other users feed to my function"
Matt Cooper
Matt Cooper el 7 de Mzo. de 2023
Editada: Matt Cooper el 7 de Mzo. de 2023
The cellfun method should also check for cell arrays of cells containing text:
tf = all(cellfun(@(x)ischar(x)|isstring(x)|iscellstr(x),x),'all');

Iniciar sesión para comentar.


Les Beckham
Les Beckham el 8 de Feb. de 2023
Interesting issue. Looks like you have to create a "custom validation function". See if something like this works for you.
a = ones(1, 3);
Test(a)
Error using solution>Test
Invalid argument at position 1. inputArg1 must be string or char or cellstr
Test('a')
Test("a")
Test({'a', 'b'})
Test({"a", "b"})
function Test(inputArg1)
arguments
inputArg1 {mustBeStringCharOrCellstr(inputArg1)}
end
disp(inputArg1)
end
function mustBeStringCharOrCellstr(arg)
if ~isstring(arg) && ~ischar(arg) && ~iscellstr(arg)
eid = 'Test:notValid';
msg = 'inputArg1 must be string or char or cellstr';
throwAsCaller(MException(eid, msg))
end
end
  3 comentarios
Captain Karnage
Captain Karnage el 8 de Feb. de 2023
So... I love your answer... but I was about to adapt this code and while looking up something else, I found an overlooked already existing function! mustBeText does the same thing... (also not including an actual cell array of strings, but I think I can live with that as I typically don't use that form factor and I'll probably just remove support for it from my functions)
Les Beckham
Les Beckham el 8 de Feb. de 2023
Editada: Les Beckham el 8 de Feb. de 2023
Good to know. I actually haven't used the argument validation feature very much, so it is nice to learn new things about it so that, if and when I do use it, I will be more prepared.
Creating a cell array of strings, to me, sounds like just poor design, since you can already have arrays of arbitrary length strings. Note that the Matlab editor will even flag the creation of a cell array of strings with a Code Analyzer warning, and offer to fix it by converting it to an array of strings (square brackets instead of curly ones.)
So, maybe you should answer your own question with the mustBeText solution and accept that answer so people looking at this in the future will know about that solution. Note that you can also vote for answers that helped you (such as dpb's and mine) even if they aren't the "right" or accepted answer.

Iniciar sesión para comentar.


Captain Karnage
Captain Karnage el 8 de Feb. de 2023
Editada: Captain Karnage el 8 de Feb. de 2023
If cell arrays of strings wasn't a requirement, MATLAB already has an easy built-in solution with mustBeText:
arguments
thisarg { mustBeText(thisarg) };
end
To support cell arrays of strings - I've bobbled back and forth on what method I wanted to use. In the end I created a custom validation function derived from the source code of MATLABs mustBeText function. I added acceptance of a cell array of strings andI took it one step further and made it validate with any number of layers of cells. It has a safe recursion (by "safe" I mean it takes a layer off each time and so eventually must reach an end and cannot create an infinite loop). I struggled with naming this dang thing in a non-overbearing way that also says what it does, but here is my end result:
function mustContainOnlyText(arg)
%mustContainOnlyText validates that input contains only text
% argument is char, string, or a cell array of strings
% Intended for use within arguments block to validate an input
if ~containsOnlyText(arg)
eid = 'custom:validators:mustContainOnlyText';
msg = 'Value must be char, character vector, string array or scalar, or cell array of character vectors or strings.';
throwAsCaller(MException(eid, msg));
end
end
which calls the function:
function tf = containsOnlyText(text)
%containsOnlyText - checks that passed text contains only text
% In this context:
% "text" refers to char or string types
% "contains" means it could be text by itself,
% or either type contained within an array or cell
% Returns true if it contains only text
%Single Boolean Statement
%Removes outer cell layer and recurses if the passed text is in a cell array
%Text passed to next version is horizontally concatenated version
tf = ischar(text) || ...
isstring(text) || ...
iscell(text) && ( containsOnlyText( [text{:}] ) ); %If there are layered cells
end
and I can then add to the arguments block:
arguments
thisarg { mustContainOnlyText(thisarg) };
end
I'll probably tweak this some - I know that if someone passes a heterogenous array of cells it will cause an exception in the containsOnlyText function and not send the validation exception message (but, ultimately, it will still check for the correct type).
I'm contemplating adding try / catch in per @dpb. I'm normally opposed to a Try/Catch in "production" code because it could mask an exception - and I believe you should work to make the exception not possible instead of allowing the exception then patching around it. In this case though, try / catch would only serve to change the exception to a different exception.
Thank you to both @dpb and @Les Beckham who helped get me to this answer. And also yes, simple arrays of strings are much better than cell arrays of strings.
  3 comentarios
Captain Karnage
Captain Karnage el 8 de Feb. de 2023
Editada: Captain Karnage el 8 de Feb. de 2023
I changed the name of the function several times and forgot to update the text in the code in this post. D'Oh. I just edited it to fix that.
Les Beckham
Les Beckham el 8 de Feb. de 2023
I suspected as much, but had to ask :).

Iniciar sesión para comentar.

Categorías

Más información sobre Get Started with MATLAB en Help Center y File Exchange.

Productos


Versión

R2022b

Community Treasure Hunt

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

Start Hunting!

Translated by