Distinguish between scripts and functions programmatically - matlab

Given a file name, how can I programmatically distinguish between scripts and functions in MATLAB?
If I attempt to pass an argument to a script, I get Attempt to execute SCRIPT somescript as a function:. Is there a way to detect this without attempting to execute it?
Update: As #craq pointed out, shortly after this question was posted, there was an article about this on MATLAB Central: http://blogs.mathworks.com/loren/2013/08/26/what-kind-of-matlab-file-is-this/

Didn't find a clean solution, but you can probably use try-catch (as #Ilya suggested) and nargin
EDIT - Use function to avoid some naming conflict; use exist to further classify the input (e.g. MEX-file)
function is_script = is_a_script( varargin )
% is_a_script( varargin ) returns one of the following:
% 1: if the input is a script
% 0: if the input is a function
% -1: if the input is neither a function nor a script.
is_script = 0;
switch( exist(varargin{1}) )
case 2
% If the input is not a MEX or DLL or MDL or build-in or P-file or variable or class or folder,
% then exist() returns 2
try
nargin(varargin{1});
catch err
% If nargin throws an error and the error message does not match the specific one for script, then the input is neither script nor function.
if( strcmp( err.message, sprintf('%s is a script.',varargin{1}) ) )
is_script = 1;
else
is_script = -1;
end
end
case {3, 4, 5, 6} % MEX or DLL-file, MDL-file, Built-in, P-file
% I am not familiar with DLL-file/MDL-file/P-file. I assume they are all considered as functions.
is_script = 0;
otherwise % Variable, Folder, Class, or other cases
is_script = -1;
end

If you are willing to use semi-documented features, here is something to try:
function tf = isfunction(fName)
t = mtree(fName, '-file');
tf = strcmp(t.root.kind, 'FUNCTION');
end
This is the same function used in MATLAB Cody and Contests to measure code length.

This is a bit of a hack, but... here is a function that will return true if the argument is a function, and false if it's not. It is possible that there are exceptions where this won't work - I look forward to comments.
EDIT - catching the case where the function is in a mex file...
function b = isFunction(fName)
% tries to determine whether the entity called 'fName'
% is a function or a script
% by looking at the file, and seeing if the first line starts with
% the key word "function"
try
w = which(fName);
% test for mex file:
mx = regexp(w, [mexext '$']);
if numel(mx)>0, b = true; return; end
% the correct thing to do... as shown by YYC
% if nargin(fName) >=0, b = true; return; end
% my original alternative:
fid = fopen(w,'r'); % open read only
while(~feof(fid))
l = fgetl(fid);
% strip everything after comment
f = strtok(l, '%');
g = strtok(f, ' ');
if strcmpi(g, 'function'), b=true; break; end
if strlen(g)>0, b=false; break; end
end
fclose(fid);
catch
fprintf(1, '%s not found!\n');
b = false;
end

Related

How to overload subsref / numArgumentsFromSubscript for functions which have zero output arguments?

I would like to have a class which wraps up a containers.Map. In addition, I want to be able use () indexing to access the sub-map from the parent class, for example:
>> map = containers.Map('Foo', 'Bar');
>> ex = Example(map);
>> ex('Foo')
ans =
Bar
The code for this is below, and it works well. The only problem I am having is with other methods defined on the class. According to the docs, I understand I need to override numArgumentsFromSubscript (somehow?) to help nargout. My crude attempt at simply using numel(obj) as I've seen mentioned online works in most cases, but not when the function you are calling has no output arguments (in which case numel(obj) == 1 ~= 0).
Using the example code below,
>> ex.outGoodbye
ans =
Goodbye
Great! However,
>> ex.sayHello
Error using Example/sayHello
Too many output arguments.
Error in Example/subsref (line 17)
[varargout{1:nargout}] = builtin('subsref', obj, struct);
How can you fix this?
classdef Example
% =====================================================================
properties
map
end
% =====================================================================
methods
% -----------------------------------------------------------------
function obj = Example(map)
obj.map = map;
end
% -----------------------------------------------------------------
function varargout = subsref(obj, struct)
if strcmp(struct(1).type, '()')
[varargout{1:nargout}] = builtin('subsref', obj.map, struct);
else
[varargout{1:nargout}] = builtin('subsref', obj, struct);
end
end
% -----------------------------------------------------------------
function n = numArgumentsFromSubscript(obj, struct, indexingContext)
n = numel(obj); % Necessary to overload subsref - for some reason
end
% -----------------------------------------------------------------
function obj = subsasgn(obj, struct, varargin)
if strcmp(struct(1).type, '()')
obj = builtin('subsasgn', obj.map, struct, varargin{:});
obj = Example(obj);
else
obj = builtin('subsasgn', obj, struct, varargin{:});
end
end
% -----------------------------------------------------------------
function sayHello(obj)
disp('Hello'); % nargout == 0. Does NOT work
end
% -----------------------------------------------------------------
function out = outGoodbye(obj)
out = 'Goodbye'; % nargout > 0. Works
end
% -----------------------------------------------------------------
end
% =====================================================================
end
So digging into this a little further, you have a few options for how to get around this behavior.
method(obj) Calling Convention
You could simply change the way that you're calling the class method. Instead of using the dot notation, you could simply use the standard function notation.
sayHello(ex)
%// vs.
ex.sayHello()
This will avoid calling subsref when calling a method. Also, this is actually the fastest way to call a method of a class in the current iteration of MATLAB's OOP. Additionally, this would require no changes to your current code.
Use nargout to determine number of method outputs
Another option is to add a special case in subsref or numArgumentsFromSubscript that looks for methods called using the dot notation. Then you can explicitly determine the number of output arguments of the method using the following call to nargout.
nArgs = nargout('classname>classname.methodname');
Then you would use that rather than numel(obj). This could be implemented in either numArgumentsFromSubscript or subsref.
numArgumentsFromSubscript
function n = numArgumentsFromSubscript(obj, struct, indexingContext)
%// Check if we are calling obj.method
if strcmp(struct(1).type, '.') && ...
ischar(struct(1).subs) && ismember(struct(1).subs, methods(obj))
%// Determine the package (if any)
cls = class(obj);
parts = regexp(cls, '\.', 'split');
%// Import the package (if any) just into the namespace of this method
if numel(parts) > 1
import(cls);
end
%// Determine number of outputs for this method
n = nargout(sprintf('%s>%s.%s', parts{[end end]}, struct(1).subs));
else
%// Default to numel(obj)
n = numel(obj);
end
end
subsref
function varargout = subsref(obj, struct)
if strcmp(struct(1).type, '()')
[varargout{1:nargout}] = builtin('subsref', obj.map, struct);
%// Check if we are calling obj.method
elseif strcmp(struct(1).type, '.') && ...
ischar(struct(1).subs) && ismember(struct(1).subs, methods(obj))
%// Determine the package (if any)
cls = class(obj);
parts = regexp(cls, '\.', 'split');
%// Import the package (if any) just into the namespace of this method
if numel(parts) > 1
import(cls);
end
%// Determine number of outputs for this method
nout = nargout(sprintf('%s>%s.%s', parts{[end end]}, struct(1).subs));
%// Call builtin subsref with this number of outputs
[varargout{1:nout}] = builtin('subsref', obj, struct);
else
[varargout{1:nargout}] = builtin('subsref', obj, struct);
end
end
Summary
subsref is a pain and a lot of times you end up with a lot of logic to determine what is being called and you'll be right only about 80% of the time. I think that the first approach is the most straightforward, would likely be the most performant (you skip all of the checks in subsref) and deals better with arrays of objects.
If you really wanted to keep the obj.method notation, I would probably recommend changing numArgumentsFromSubscript (rather than subsref) to keep the number of output argument stuff in one place.
Update
Based upon your feedback that the nargout trick doesn't work in cases where the class is contained within a package (foo.bar.Example). I have added a workaround to the above examples. The trick is to call import('foo.bar.Example') prior to calling nargout('Example>Example.sayHello').

How to create multiple functions in single matlab file

I have a C++ file that has some functions in single .cpp file such as
int func1(x)
{
return 1;
}
void func2(x)
{
return 1;
}
int func3(x)
{
return 1;
}
Now, I want to write all above functions in a single matlab file. (If I added an own matlab file for each function, my folder would grow very huge.) Could you suggest to me an easy as well as very clear way to do this in MATLAB?
Currently, I am doing it this way:
function f = perform(func, x);
switch(func)
case 'f1'
f = func1(x);
case 'f2'
f = func2(x);
case 'f3'
f = func3(x);
otherwise
error(['Unknown function ', func]);
end
See local or nested functions. Local functions are most analagous to other programming language syntax.
Local functions, for example:
function f = perform(func, x);
switch(func)
case 'f1'
f = func1(x);
case 'f2'
f = func2(x);
case 'f3'
f = func3(x);
otherwise
error(['Unknown function ', func]);
end
end
function func1(x)
disp(sprintf('func1: %u\n', x))
end
function func2(x)
disp(sprintf('func2: %u\n', x))
end
function func3(x)
disp(sprintf('func3: %u\n', x))
end
Note that neither local nor nested functions are externally callable.
You could organize the functions in package, see Packages Create Namespaces, one for each cpp file as
+module1
func1.m
func2.m
+module2
func1.m
You can then call the functions as
module1.func1(x)
module2.func1(x)
You can, call local functions externally if you set up a bunch of function handles. Not that I recommend this, but I got this setup from a colleague. This code is not robust -- there are almost certainly cases where it'll fail or do bad things.
% code to create callable handles for all subfuncs in some file
%
% first, in the file containing subfuncs:
% this is the "setup" main func
function [p]=To_Call_Subfuncs()
[p,n]=update_function_list();
end
% this creates all the handles by pseudogrepping the subfunction names
function [p,function_names]=update_function_list()
function_names={};
% making p empty allows one to call, e.g. funclist.firstfunc()
p=[];
f=fopen([mfilename() '.m'],'r');
while ~feof(f),
line=fgetl(f);
s= strfind( strtrim(line),'function ') ;
if length(s) && s(1)==1,
s0=find(line=='=');
s1=find(line=='(');
if length(s0)==0,s0=9;end;
if(length(s1)==0), s1 = numel(line)+1;end; %to handle the -1 later on
function_name= strtrim( [line(s0(1)+1:s1(1)-1)] );
if length(function_name),
function_names{end+1}=function_name;
eval( sprintf('p.%s=#%s;', function_name, function_name) );
end;
end;
end;
end
%
%follow this with the functions we actually want to call
function a = firstfunc(x)
a=x;
end
function b = secondfunc(x)
b = x^2;
end
function cnot = thirdfunc
cnot= 17;
end
%
%
% next, in the m-file where some function is calling these subfuncs,
% set up the handles with:
% run(fullfile(dirpath,'the_mfile.m'))
% the_mfile=ans;
% because I don't think run() returns a value --
%% only the called mfile does.
% now you can call the_mfile.firstfunc() and so on.

Matlab - Do action after every line

Is there an elegant way to tell matlab to perform a predefined action after the execution of every line in a certain script? By elegant I mean no calling of the action after every line, but rather something like a simple command given at the start of the script.
Example:
Action --> disp('Performing Action');
script:
a = 1;
b = 2;
c = 3;
So the desirable outcome is that after each assignment (of a, b and c), the disp() command would be performed.
You can automatically create a modifed file that has the desired action included at the end of each line:
action = 'disp(''Performing Action'');'; %// action to be added at the end of each line
file = 'script.m'; %// original script
file_out = 'script_out.m'; %// modified script with action added
x = importdata(file); %// cell array of strings. Each line of the
%// original file is a string
x = strcat(x, {' ; '}, action); %// add action at the end of each string,
%// preceded with `;` in case the original line
%// didn't include that
fid = fopen(file_out, 'w');
fprintf(fid, '%s\n', x{:}); %// write output file
fclose(fid);
a = 1;
disp('Performing Action');
b = 2;
disp('Performing Action');
c = 3;
disp('Performing Action');
Or, if you can do this in a loop
for ii = 1:3
a(ii) = ii;
disp('Performing Action');
end
Actually making it output something after every line is not very matlab, but you could of course just loose all the semicolons and thus make it display all variables if you want to track where in the script you are.
I'd suggest a verbose switch in your code. Set it to 0 for no output and 1 for output (or use multiple levels if so desired)
if verbose > 0
disp('Performing Action');
end
This way you can easily switch the output on or off, depending on need.
For the code-reading and appending piece, see Louis Mendo's answer at https://stackoverflow.com/a/32137053/5211833
Here is my attempt. You can:
read the function line by line
execute each line, followed by your custom function.
NOTE
This won't work with functions containing for, if, etc..
You can eventually improve the code by passing a function handler with your custom action to runEachLine (and feval with that).
Here a code sample (partly based on this):
foo.m
function foo(args)
a = args{1};
b = 2;
c = a + b;
end
runEachLine.m
function runEachLine( mfile, args )
if nargin < 1
error('No script m-file specified.');
end
if ~strcmp(mfile(end-1:end),'.m')
mfile = [mfile '.m'];
end
if ~exist(mfile,'file')
error(['Cannot access ' mfile])
end
% Read function file
M = textread(mfile,'%s','delimiter','\n');
% Remove empty lines
M = M(~cellfun('isempty',M));
% Input arguments
assignin('base', 'args', args);
% Skipping first line: function [...] = func_name(...)
% Skipping last line : end
for k=2:length(M)-1
try
% Execute each line
evalin('base',M{k})
% Execute your custom function
disp(['Performing Action: ' M{k}]);
catch ME
error('RunFromTo:ScriptError',...
[ME.message '\n\nError in ==> ' mfile ' at ' num2str(k) '\n\t' M{k}]);
end
end
end
Usage:
>>runEachLine('foo.m', {4});
Result:
>> runEachLine('foo.m', {4})
Performing Action: a = args{1};
Performing Action: b = 2;
Performing Action: c = a + b;
>>
No, there is not.
What you present in your original question is just a simple usage of the = operator.
If you want to overload the default behaviour of MATLAB then you should consider creating a class and define the desired behaviour for each operator or function needed.

Is it possible to force case-sensitivity in MATLAB for file operations like LOAD?

MATLAB is case-sensitive for calling functions, even on Windows:
>> edit Untitled
>> untitled
Cannot find an exact (case-sensitive) match for 'untitled'
Is there a way to enforce case sensitivity on Windows for other functions, like load?
>> a = 3;
>> save a a
>> load A
The problem is that this code will run fine on Windows, but will error if I send it to a friend on Unix.
One way to enforce case-sensitivity for functions dealing with files, regardless of the platform you are running, is to write a wrapper for such functions.
For example, in the case of load, I came up with the following drop-in replacement:
function varargout = myload(fname, varargin)
% make sure filename ends with MAT extension
[~,~,ext] = fileparts(fname);
if isempty(ext), fname = [fname '.mat']; end
% open file (searching entire MATLAB path)
fid = fopen(fname,'r');
if fid < 0, error('file not found'); end
% get fullpath to opened file, and close file handle
filename = fopen(fid);
fclose(fid);
% extract the filename
[~,name,ext] = fileparts(filename);
filename = [name ext];
% compare against original name (case-sensitive)
if ~strcmp(fname,filename)
error('Cannot find an exact (case-sensitive) match for file');
end
% load the MAT-file
S = load(fname, varargin{:});
% assign output
if nargout > 0
varargout{1} = S;
else
fn = fieldnames(S);
for i=1:numel(fn)
assignin('caller', fn{i}, S.(fn{i}))
end
end
end
I may have missed a few cases in the above implementation, but you get the idea..

Running a MATLAB code fragment without namespace pollution

I'm writing a version of Python's doctest test-runner, for MATLAB (it partly works...). For this to work, I need to run the code in people's examples in their m-file help. I want variables to carry over from one line to the next, e.g.
% >> I = 5 + 33; % expect no output
% >> I
%
% I =
%
% 38
%
To run the tests, I have a loop over matches to the REGEX that searches for the tests. For each match, I evalc the example and make sure the result matches:
for I = 1:length(examples)
try
got = evalc(examples(I).source);
catch exc
got = ['??? ' exc.message];
end
% process the result...
end
The problem is that the example's definition of I has now clobbered the loop variable in my loop, since the assignments carry over from the eval into the outer scope. I looked around for something capable of creating a new scope/workspace, but evalin can only re-use the caller's workspace, which is even worse. I've also considered options with calling a sub-function or save/load, but not gotten anywhere, but maybe I just haven't thought hard enough.
So I guess I need to just name all my variables doctest__system__* and live with the namespace problems... unless you have another idea for a strategy to avoid variable name conflicts?
A very interesting project for sure.. I think the best option you have is to write a separate function to execute the tests, and use a unique prefix for all variable inside of this function to avoid name conflict. Here my attempt at this:
function [PREFIX_b varargout] = testContext(PREFIX_src, PREFIX_srcOutput)
%# TESTCONTEXT Executes the source code and tests for
%# equality against the expected output
%#
%# Input:
%# PREFIX_src - source to execute, cellarry of statements
%# PREFIX_srcOutput - output to expect, cellarray of output of each statement
%#
%# Output:
%# PREFIX_b - true/false for success/failure of test
%# note that the output is strtrim()'ed then strcmp()'ed
%# varargout{1} - variable names assigned in this confined context
%# varargout{2} - variable values assigned
%#
%# Example 1:
%# source = { 'I = 5+33;' 'I' };
%# output = { [], ['I =' char(10) ' 38'] };
%# b = testContext(source, output);
%#
%# Example 2:
%# source = { 'I = 5+33; J = 2;' 'K = 1;' 'disp(I+J+K)' };
%# output = { [], [], '41' };
%# [b varNames varValues] = testContext(source, output);
%#
%# See also: eval evalc
%#
PREFIX_b = true;
try
%# for each statement
for PREFIX_i=1:numel(PREFIX_src)
%# evaluate
PREFIX_output = evalc( PREFIX_src{PREFIX_i} );
PREFIX_output = strtrim(PREFIX_output); %# trim whitespaces
%# compare output
if ~isempty( PREFIX_srcOutput{PREFIX_i} )
if ~strcmp(PREFIX_output,PREFIX_srcOutput{PREFIX_i})
PREFIX_b = false;
return
end
end
end
if nargout > 1
%# list created variables in this context
%#clear ans
PREFIX_vars = whos('-regexp', '^(?!PREFIX_).*'); %# java regex negative lookahead
varargout{1} = { PREFIX_vars.name };
if nargout > 2
%# return those variables
varargout{2} = cell(1,numel(PREFIX_vars));
for PREFIX_i=1:numel(PREFIX_vars)
[~,varargout{2}{PREFIX_i}] = evalc( PREFIX_vars(PREFIX_i).name );
end
end
end
catch ME
warning(ME.identifier, ME.message)
PREFIX_b = false;
varargout{1} = {};
varargout{2} = {};
end
end
I assume you are able to parse the m-file to recover the examples to test, where you have each statement along with its expected output.
As an example, consider this simple test embedded in the header of a function:
I = 5 + 33;
J = 2*I;
disp(I+J)
Since only the last statement has an output, we test it as:
source = {'I = 5 + 33;' 'J = 2*I;' 'disp(I+J)'};
output = {[], [], '114'};
[b varNames varValues] = testContext(source, output)
the results:
b =
1
varNames =
'I' 'J'
varValues =
[38] [76]
It shows whether the test passed of failed. Optionally, the function returns a list of variables created in that context along with their values.