Search code examples
matlabglobal-variablesstatepersistent-storagelocal-functions

Clear persistent variables in local functions from within the main function


I have a code which consists of a single file containing multiple functions, some of which use persistent variables. In order for it to work correctly, persistent variables must be empty.

There are documented ways to clear persistent variables in a multi-function file, such as:

clear functionName % least destructive
clear functions    % more destructive
clear all          % most destructive

Unfortunately, I cannot guarantee that the user remembers to clear the persistent variables before calling the function, so I'm exploring ways to perform the clearing operation at the beginning of the code. To illustrate the problem, consider the following example:

function clearPersistent(methodId)
if ~nargin, methodId = 0; end

switch methodId
  case 0 
    % do nothing
  case 1
    clear(mfilename);
  case 2
    eval(sprintf('clear %s', mfilename));
  case 3
    clear functions;
  case 4
    clear all;
end

subfunction();
subfunction();
end

function [] = subfunction()
persistent val

if isempty(val)
  disp("val is empty");
  val = 123;
else
  disp("val is not empty");
end
end

When first running this, we get:

>> clearPersistent
val is empty
val is not empty

I would expect that running the function again at this point, with any of the non-0 inputs would result in the val variable being cleared, but alas - this is not the case. After val is set, unless we use one of the alternatives shown in the top snippet externally, or modify the .m file, it remains set.

My question: Is it possible to clear persistent variable in subfunctions from within the body of the main function, and if yes - how?

In other words, I'm looking for some code that I can put in clearPersistent before calling the subfunctions, such that the output is consistently:

val is empty
val is not empty

P.S.

  1. Here's a related past question (which doesn't deal with this specific use case): List/view/clear persistent variables in Matlab.

  2. I'm aware of the possibility of rewriting the code to not use persistent variables at all (e.g. by passing data around, using appdata, adding a 'clear' flag to all subfunctions, etc.).

  3. Please note that editing the source code of the function and saving implicitly clears it (along with all persistent variables).

  4. I'm aware that the documentation states that "The clear function does not clear persistent variables in local or nested functions.


Additional background on the problem:

The structure of the actual code is as follows:

Main function (called once)
  └ Global optimization solver (called once)
    └ Objective function (called an unknown N≫1 times)
      └ 1st function that uses persistents
      └ 2nd function that uses persistents

As mentioned in the comments, there are several reasons why some variables were made persistent:

  1. Loose coupling / SoC: The objective function does not need to be aware of how the subfunctions work.
  2. Encapsulation: It is an implementation detail. The persistent variables do not need to exist outside the scope of the function that uses them (i.e. nobody else ever needs them).
  3. Performance: The persistent variables contain matrices that are fairly expensive to compute, but this operation needs to happen only once per invocation of the main function.

One (side?) effect of using persistent variables is making the entire code stateful (with two states: before and after the expensive computations). The original issue stems from the fact that the state was not being correctly reset between invocations of the main function, causing runs to rely on a state created with previous (and thus invalid) configurations.

It is possible to avoid being stateful by computing the one-time values in the main function (which currently only parses user-supplied configurations, calls the solver, and finally stores/displays outputs), then passing them alongside the user configurations into the objective function, which would then pass them on to the subfunctions. This approach solves the state-clearing problem, but hurts encapsulation and increases coupling, which in turn might hurt maintainability.

Unfortunately, the objective function has no flag that says 'init' etc., so we don't know if it's called for the 1st or the nth time, without keeping track of this ourselves (AKA state).

The ideal solution would have several properties:

  1. Compute expensive quantities once.
  2. Be stateless.
  3. Not pass irrelevant data around (i.e. "need to know basis"; individual function workspaces only contain the data they need).

Solution

  • clear fname and clear functions removes the M-file from memory. The next time you run the function, it is read from disk again, parsed and compiled into bytecode. Thus, you slow down the next execution of the function.

    Clearing a function or sub-function from within a function thus does not work. You're running the function, you cannot clear its file from memory.

    My solution would be to add an option to subfunction to clear its persistent variable, like so:

    function clearPersistent()
    
    subfunction('clear');
    subfunction();
    subfunction();
    end
    
    function [] = subfunction(option)
    persistent val
    
    if nargin>0 && ischar(option) && strcmp(option,'clear')
       val = [];
       return
    end
    
    if isempty(val)
      disp("val is empty");
      val = 123;
    else
      disp("val is not empty");
    end
    
    end
    

    Of course you could initialize your value when called as subfunction('init') instead.


    A different solution that might work for your usecase is to separate the computation of val and its use. I would find this easier to read than any of the other solutions, and would be more performant too.

    function main()
    val = computeval();
    subfunction(val);
    subfunction(val);
    end
    

    Given your edit, you could put the objective function in a separate file (in the private subdirectory). You will be able to clear it.

    An alternative to persistent variables would be to create a user class with a constructor that computed the expensive state, and another method to compute the objective function. This could also be a classdef file in the private subdirectory. I think this is nicer because you won’t need to remember to call clear.

    In both these cases you don’t have a single file containing all the code any more. I think you need to give up on one of those two ideals: either break data encapsulation or split the code across two files (code encapsulation?).