A class to simulate missing arguments in function calls
1 view (last 30 days)
Show older comments
My goal is to design a class named "Unknown", that simulates missing arguments in function calls.
For example, this script
fun(Unknown)
function fun(a)
a %or any other line of code that includes the "a" variable
end
should produce an error in the same way this script does
fun()
function fun(a)
a %or any other line of code that includes the "a" variable
end
Is there any way of achieving this?
My current, hopeless ideas:
- Overload some sort of "caller" function in the Unknown class to prevent any MATLAB function to be called on an Unknown object - if that "caller" function exists
- Apply some strange property to the Unknown class that does what I want
3 Comments
Stephen23
on 7 Feb 2020
Rather than reinventing the wheel, you could just use standard MATLAB ways to handle this:
Accepted Answer
Matt J
on 7 Feb 2020
Edited: Matt J
on 7 Feb 2020
Clearly, I would have to check for each input argument to be ~isempty(), or ~isnan(), which could be another technique. However, in this way, a 1-line function becomes a 5-lines function.
No, it would become at most a 2-line function. You would naturally write a separate check_for_unknowns.m mfunction that checks all the arguments for Unknowns and re-use that utility in every function that needs it:
fun(1, 2, Unknown, 4);
function x = fun(a, b, c, d)
check_for_unknowns(a,b,c,d)
x = a + b + c + d;
end
function check_for_unknowns(varargin)
map=cellfun(@(c)~isa(c,'Unknown'),varargin);
assert(all(map),'Unknown variable type entered');
end
Moreover, anonymous functions, which can only be one-line, would benefit from the usage of such "Unknown" class.
You can extend this idea to anonymous functions by writing the following additional utility mfile,
function varargout=myfeval(funHandle,varargin)
check_for_unknowns(varargin{:})
[varargout{1:nargout}]=feval(funHandle,varargin{:});
end
and now you can do 1-line things like,
>> fun=@(a,b,c,d) a+b+c+d;
>> x=myfeval(fun,1,Unknown,3,4);
More Answers (3)
Matt J
on 7 Feb 2020
Edited: Matt J
on 7 Feb 2020
Seems to me that you can effectively accomplish what you want by defining a trivial class with no properties or methods,
classdef Unknown
end
Now, pretty much everything you try to do with the class within a function call will raise an error, e.g.,
>> fun=@(a,b,c,d) a+b+c+d;
>> fun(1,Unknown,3,4)
Undefined operator '+' for input arguments of type 'Unknown'.
Error in @(a,b,c,d)a+b+c+d
0 Comments
Steven Lord
on 7 Feb 2020
So you want your function to be able to accept 0, 1, 2, 3, or 4 input arguments? In that case you probably want varargin.
function x = addThemUp(varargin)
narginchk(0, 4) % Error if addThemUp is called with < 0 inputs (impossible) or > 4 inputs
x = sum([varargin{:}]);
end
or if you want to do this with +:
function x = addThemUp(varargin)
narginchk(0, 4) % Error if addThemUp is called with < 0 inputs (impossible) or > 4 inputs
x = 0;
for whichone = 1:nargin
x = x + varargin{whichone};
end
end
Let's try this with a couple sets of inputs:
>> addThemUp(1, 2, 4)
ans =
7
>> addThemUp(1, 2)
ans =
3
>> addThemUp()
ans =
0
>> addThemUp(1, 2, 3, 4, 5)
Error using addThemUp (line 2)
Too many input arguments.
0 Comments
Matt J
on 10 Feb 2020
Edited: Matt J
on 10 Feb 2020
The main drawback is that one must always use myfeval, which is not very readable.
Yet another solution, which might address that, is to create your own class of function handles,
classdef Unknown
end
classdef SmartFun %class for functions with prechecks
properties (Constant)
builtins=smartBuiltins;
end
properties
fHandle
end
methods
function obj=SmartFun(fHandle) %constructor
obj.fHandle=fHandle;
end
function varargout=subsref(obj,S)
switch S.type
case '()'
SmartFun.check_for_unknowns(S.subs{:});
[varargout{1:nargout}]=feval(obj.fHandle,S.subs{:});
otherwise
error 'Undefined indexing operation'
end
end
end
methods (Static)
function check_for_unknowns(varargin)
map=cellfun(@(c)~isa(c,'Unknown'),varargin);
assert(all(map),'Unknown variable type entered');
end
end
end
function safecall=smartBuiltins
m=methods('double');
for i=1:numel(m)
safecall.(m{i})=SmartFun(str2func(m{i}));
end
end
and now you can do things like
>> fun=SmartFun( @(a,b,c,d) a+b+c+d );
>> fun(1,2,3,4)
ans =
10
>> fun(1,2,Unknown,4)
Error using SmartFun.check_for_unknowns (line 32)
Unknown variable type entered
Error in SmartFun/subsref (line 17)
SmartFun.check_for_unknowns(S.subs{:});
3 Comments
Matt J
on 10 Feb 2020
Edited: Matt J
on 10 Feb 2020
Unfortunately, I think you are right. Matlab does not currently provide a mechanism that lets an object implicitly call a method when it is passed to a function as an argument. The closest thing to that, I think would be subsindex, but that is for indexing expressions, not function call expressions. Therefore, I don't think there is a way to you have what you want with the exact syntax you want.
However, some of the issues you cite above can be mitigated by adding a Constant property to SmartFun, as in my recent edit. This gives a way to pre-instantiate all the methods of class double as SmartFun objects and use them with a more readable, Python-like syntax:
>> safecall=SmartFun.builtins;
>> safecall.atan2(1,1)
ans =
0.7854
>> safecall.sind(90)
ans =
1
>> safecall.sqrt(Unknown)
Error using SmartFun.check_for_unknowns (line 35)
Unknown variable type entered
Error in SmartFun/subsref (line 20)
SmartFun.check_for_unknowns(S.subs{:});
See Also
Categories
Find more on Construct and Work with Object Arrays in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!