I am writing a wrapper for plot
that automates some tasks that I find myself doing frequently.
An example code snippet might look like
function myplot(x,y,varargin)
plot(x,y,varargin{:})
xlabel('x axis')
ylabel('y axis')
end
I'm using Matlab's varargin
to pass additional arguments to plot
. However, I find that I might want to pass my own optional arguments in varargin. For example, I might want to write something like
>> myplot(1:10, 1:10, 'r', 'LineWidth', 2, 'legend', {'Series 1'})
to have the function automatically include a legend in the plot - that is, I want to be able to mix my own keyword arguments with the ones that you can supply to plot. Short of writing a full parser for my varargs, is there a way to do this simply and reusably in Matlab?
I've tried to use the inputParser
object, but that would require me to manually add every possible additional argument to plot (and a default for it) which doesn't seem ideal.
A varargs method is one that accepts a variable number of arguments. Variable-length arguments could be handled in two ways prior to JDK 5. One method employs overloaded methods (one for each), while another places the arguments in an array and then passes this array to the method. Both are potentially error-prone and necessitate more code.
In order to define vararg, ... (three dots) is used in the formal parameter of a method. A method that takes variable number of arguments is called a variable-arity method, or simply a varargs method. As you can clearly see, you had to overload sumNumber () method to make it work for 3 arguments.
A method can have only one varargs parameter. In the above program, the compiler gets confused if you try to invoke the test () method even though test () methods are overloaded and accepts different number of arguments. The compiler doesn’t know which method to call.
With Python, we can use the *args or **kwargs syntax to capture a variable number of arguments in our functions. Using *args, we can process an indefinite number of arguments in a function's position.
inputParser
may still be the best choice. You can construct the object for your additional arguments, and lump all the parameterName/parameterValue pairs that you want to pass to plot
into Unmatched
.
function myplot(x,y,varargin)
parserObj = inputParser;
parserObj.KeepUnmatched = true;
parserObj.AddParamValue('legend',false);
%# and some more of your special arguments
parserObj.parse(varargin);
%# your inputs are in Results
myArguments = parserObj.Results;
%# plot's arguments are unmatched
tmp = [fieldnames(parserObj.Unmatched),struct2cell(parserObj.Unmatched)];
plotArgs = reshape(tmp',[],1)';
plot(x,y,plotArgs{:})
xlabel('x axis')
ylabel('y axis')
if myArguments.legend
%# add your legend
end
end
To support the Linespec argument without interfering with input parser parameters that have a name of 4 characters or less (e.g., 'grid' or 'bins'), you should have a slightly more sophisticated way to check the validity of the Linespec argument than '@(x) ischar(x) && numel(x) <=4'. This check would return true also for 'grid' and 'bins', although they are no valid Linespec arguments.
The following function will return true only if a valid Linespec argument is entered:
function isls = islinespec(x)
isls = false;
if ~ischar(x)
return;
end
lineStyleSpecifiers = {'--','-.','-',':'};
markerSpecifiers = {'square','diamond','pentagram','hexagram','+','o','*','.','x','s','d','^','v','>','<','p','h'};
colorSpecifiers = {'r','g','b','c','m','y','k','w'};
for oo=1:length(lineStyleSpecifiers)
k = strfind(x,lineStyleSpecifiers{oo});
if ~isempty(k)
x(k:(k+length(lineStyleSpecifiers{oo})-1)) = [];
break;
end
end
for oo=1:length(markerSpecifiers)
k = strfind(x,markerSpecifiers{oo});
if ~isempty(k)
x(k:(k+length(markerSpecifiers{oo})-1)) = [];
break;
end
end
for oo=1:length(colorSpecifiers)
k = strfind(x,colorSpecifiers{oo});
if ~isempty(k)
x(k:(k+length(colorSpecifiers{oo})-1)) = [];
break;
end
end
if isempty(x)
isls = true;
end
(Admittedly, the function could have been written more elegantly with regular expressions, but it does the trick.)
Example usage:
parserObj = inputParser;
parserObj.KeepUnmatched = true;
parserObj.AddParamValue('legend',false);
parserObj.AddParamValue('grid',true);
parserObj.addOptional('LineSpec','-', @(x) islinespec(x));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With