1

What I have:

I have a matlab script called myscript.m which uses the p-coded helper functions fcn_A.p and fcn_B.p (which I wrote and have the source code for).

What I want:

I'd like to distribute those to others as a single unit such that:

  • The main script can be run through >> myscript
  • The helper functions can accessed outside of the main script - but still be protected.
  • >> myscript runs a script - not a function. I.e. variables it defines are set in the matlab base environment
  • Everything is distributed as one directory or a single file.

As far as possible, I'd also like to avoid protecting myscript. That way, it can be used as an example of how to use fcn_A and fcn_B.

Edit: Optimally, I'd like a file or folder that you just move into your path or working directory and then it all works, without even changing the path.

What I tried:

Putting everything into the same script file doesn't fulfill the second requirement.

Making it a class (either through a classdef file or a @myscript directory) doesn't fulfill the third requirement.

Making it a package (using a +myscript directory) doesn't fulfill the first requirement.

What I believe I need

I believe this could be solved if there was a way to define a 'default' function of a package. Kind of like how the contents of +mypackage/Contents.m is displayed when you type help mypackage.

Is there a way to do this?

4 Answers4

1

Add the folder that holds your functions and script to the MATLAB path. Fulfills all 4 conditions.

  • Except, arguably, the last one. I should have been clearer in the question that I wanted to avoid this solution. Optimally, I'd like a file or folder that you just move into your path or working directory and then it all works. Thanks for the suggestion, though. – thomasloven Aug 27 '15 at 14:11
  • You never said something about not modifying the path in your question. With that additional requirement, I think it's impossible. – Daniel Aug 27 '15 at 14:15
  • @thomasloven In my opinion it fulfills the last one (being distributed in one folder). On another hand: you'd rather copy files outside MATLAB than typing `addpath('/this/path');` in Command window? Is messy, creates "versions" scattered all over the system for each user, wastes space. Okay, the third argument is not such a big deal nowadays. –  Aug 27 '15 at 14:28
1

The way I (and mathworks) would typically do this:

  1. Make sure you have all relevant files in a folder
  2. Zip it

Now you just have 1 file for distribution, perhaps even including documentation

Note that this is actually what matlab itself does. See this link for example:

Ismemberf on matlab file exchange

Dennis Jaheruddin
  • 21,208
  • 8
  • 66
  • 122
  • Thank you. This is close, but not quite what I was hoping for. Optimally, I'd like a file or folder that you just move into your path or working directory and then it all works - no unzipping or anything. I have updated my question to contain this wish. – thomasloven Aug 27 '15 at 14:17
1

You cannot have myscript as a script if you want a single file solution. But if the purpose of myscript is only for providing example usage and assigning output in base workspace you can achieve this using a function or class.

With a function you can put all the usage as a string and then display the string when myscript is called with no arguments or some special argument like '-help'. You can assign the outputs from myscript in the base workspace using assignin. For example,

function myscript(varargin)

if nargin == 0
 disp('Usage: myscript(fcn_a...');
 return;
end

if strcmp(varargin{1}, 'A')
  y = fcn_A(varargin{2:end});
else
  y = fcn_B(varargin{2:end});
end
assignin('base','y',y);

end

function y = fcn_A(varargin)
   y = 1;
end
function y = fcn_B(varargin)
   y = 2;
end

Now you can p-code this entire function in one file. You can do a similar setup with a class. There it might be better to show the usage using a disp method and then provide fcn_A and fcn_B as static methods of the class.

Navan
  • 4,407
  • 1
  • 24
  • 26
  • Thanks. The `assignin` was the hint I needed to find a solution that works the way I want (almost). Regarding the `if nargin==0`-clause of your code, I think using a help comment would work better. Then you can get the usage description through `>> help myscript`. – thomasloven Aug 28 '15 at 12:26
  • You can get help only if you do not p-code myscript. Otherwise you need to add an additional file for help. – Navan Aug 28 '15 at 13:32
  • I thought you could, but as it turns out you are absolutely correct. I must have tested my theory with the `.m`-file still in the directory. My bad. – thomasloven Aug 28 '15 at 19:09
1

It turns out that all of my requirements can in fact be fulfilled by a class by using assignin.

My solution would look like this:

Files:

@myscript
@myscript/myscript.m
@myscript/fcn_A.p
@myscript/fnc_B.p

@myscript/myscript.m

classdef myscript
  methods (Static)
    fcn_A
    fcn_B
  end
  methods
    function self = myscript()
      % Using a variable defined in the calling environment (if defined)
      outside_variable = evalin('caller', 'outside_variable', NaN);
      if isnan(outside_variable)
        outside_variable = 'default value';
      end
      ... code goes here ...
      outside_variable = self.fcn_A(parameters, outside_variable);
      % Putting the variable back in the calling environment (even if not defined before)
      assignin('caller', 'outside_variable', outside_variable);
    end
  end
end

Now:

  • Running >> myscript will run the class constructor and return an empty class object, which I can just ignore.
  • myscript.fcn_A and myscript.fcn_B can be called since they are static class functions.
  • variable_to_save will be set in the base environment as expected.
  • Moving the @myscript directory into the path at a new computer is enough to make it work.

There is one remaining problem, though. If fcn_A or fcn_B is a class, this will not work, since MATLAB doesn't allow stacked classes. Bummer! I did not mention this in the question, because I couldn't imagine it would make a difference...

  • Assignin `caller` would be right, assume your script is called in a function. There are some disadvantages I see.1) [matlab oop is much slower than normal function calls](http://stackoverflow.com/questions/1693429/is-matlab-oop-slow-or-am-i-doing-something-wrong). When I need OOP I accept this perform, but I would not pay that price just to save a addpath 2) assignin or eval is often accused to be bad programming practice. It violates the variable scopes making code more difficult to debug, especially because mlint does not understand assignin and gives wrong advice. – Daniel Aug 28 '15 at 12:55
  • Good point. Updated answer. `assignin` and `eval` are indeed bad practice in my opinion. This solution ain't pretty, but it works... – thomasloven Sep 01 '15 at 10:43