Argyll
Argyll

Reputation: 9875

Is there a way to declare variable local in nested functions?

Is there a way to declare a variable local in a nested function?

By declare I mean name a variable as usual but enforce that its scope starts in-place. Imagine creating a new nested function in the middle of a large program. There are natural variable names you wish to use and you would not want to worry whether you have to check existing variable names every single time you create a new variable.

To describe the desired effect, I'll use two examples. One minimal. One shows the problem a little better visually.

Short example

function fn1
    var = 1
    function fn2
        local var = 'a';
        function fn3
        end
    end
end

Within fn2 and fn3, var refers to the new variable with starting value 'a' while outside fn2, var with starting value 1 is still available as usual.

Long example

function fn1
    var = 1;
    var2 = 2;
    function fn2
        var2 = 'I can access var2 from fn1. Happy.'
        local var = 'a';  % remove local to run this snippet

        fn3;
        function fn3
            var2 = 'I can access var2 from fn1. Happy.'
            var = 'fn2 cannot safely use variable name var because it may have been used in fn1. But var is the natural name to use in fn2. Sad.';

            var = 1;
            var2 = 2;
        end
    end
    function fn4
        var2 = 'I can also access var2 from fn1. Also happy.'
        var = 'If only local scoping works, I would still be able to access var. Would be happy.';
    end

    fn4;
    fn2;
    var,
    var2,
end
%% desired output, but not real
>> fn1;
var =
    1
var2 =
    2

Is there a way to accomplish the above?

Currently, I do my best ensure that name variables that are not local in nature with special non-generic names and name variables that are obviously local in nature temp# where # is an integer. (I suppose clearing variables after known last use can help sometimes. But I'd rather not have to do that. Local variables are too numerous.) That works for small programs. But with larger programs, I find it hard to avoid inadvertently re-writing a variable that has already been named at a higher scoping level. It also adds a level of complexity in the thought process, which is not exactly good for efficiency, because when creating a program, not all variables are either obviously local or obviously not local. A flexible scoping mechanism would be very helpful.

I write my programs in Sublime Text so I am not familiar with the Matlab editor. Does the editor have visual guards/warning prompts against errors arising from inflexible scoping? Warning that requires visually scanning through the whole program is barely useful but at least it would be something.

Upvotes: 2

Views: 319

Answers (3)

Cris Luengo
Cris Luengo

Reputation: 60453

Nested functions have a very specific use case. They are not intended to avoid having to pass data into a function as input and output arguments, which is to me what you are attempting. Your example can be written using local functions:


function fn1
    var = 1;
    var2 = 2;
    [var,var2] = fn4(var,var2);
    var2 = fn2(var2);
    var,
    var2,
end

function var2 = fn2(var2)
    var2 = 'I can access var2 from fn1. Happy.'
    var = 'a';  % remove local to run this snippet

    [var,var3] = fn3(var,var2);
end

function [var,var2] = fn3(var,var2)
    var2 = 'I can access var2 from fn1. Happy.'
    var = 'fn2 cannot safely use variable name var because it may have been used in fn1. But var is the natural name to use in fn2. Sad.';

    var = 1;
    var2 = 2;
end

function [var,var2] = fn4(var,var2)
    var2 = 'I can also access var2 from fn1. Also happy.'
    var = 'If only local scoping works, I would still be able to access var. Would be happy.';
end

The advantage is that the size of fn1 is much reduced, it fits within one screen and can much more easily be read and debugged. It is obvious which functions modify which variables. And you can name variables whatever you want because no variable scope extends outside any function.

As far as I know, nested functions can only be gainfully used to capture scope in a lambda (function handle in MATLAB speak), you can write a function that creates a lambda (a handle to a nested function) that captures a local variable, and then return that lambda to your caller for use. That is a powerful feature, though useful only situationally. Outside that, I have not found a good use of nested functions. It’s just something you should try to avoid IMO.


Here's an example of the lambda with captured data (not actually tested, it's just to give an idea; also it's a rather silly application, since MATLAB has better ways of interpolating, just bear with me). Create2DInterpolator takes scattered x, y and z sample values. It uses meshgrid and griddata to generate a regular 2D grid representing those samples. It then returns a handle to a function that interpolates in that 2D grid to find the z value for a given x and y. This handle can be used outside the Create2DInterpolator function, and contains the 2D grid representation that we created. Basically, interpolator is an instance of a functor class that contains data. You could implement the same thing by writing a custom class, but that would require a lot more code, a lot more effort, and an additional M-file. More information can be had in the documentation.

interpolator = Create2DInterpolator(x,y,z);
newZ = interpolator(newX,newY);

function interpolator = Create2DInterpolator(x,y,z)
   [xData,yData] = meshgrid(min(x):max(x),min(y):max(y));
   zData = griddata(x,y,z,xData,yData);
   interpolator = @InterolatorFunc;

   function z = InterolatorFunc(x,y)
      z = interp2(xData,yData,zData,x,y);
   end
end

Upvotes: 2

rahnema1
rahnema1

Reputation: 15837

Variables that are defined as input arguments are local to the function. So you can define var as an input argument of fn2*:

function fn2 (var)
...
end

However if you like to define fn2 without changing its signature you need to define an extra level of nesting:

function fn1
    var = 1;
    var2 = 2;
    function fn2

      fn2_impl([]);
      function fn2_impl (var)
        var2 = 'I can access var2 from fn1. Happy.'
        var = 'a';  % remove local to run this snippet

        fn3;
        function fn3
            var2 = 'I can access var2 from fn1. Happy.'
            var = 'fn2 cannot safely use variable name var because it may have been used in fn1. But var is the natural name to use in fn2. Sad.';

            var = 1;
            var2 = 2;
        end
      end
    end
    function fn4
        var2 = 'I can also access var2 from fn1. Also happy.'
        var = 'If only local scoping works, I would still be able to access var. Would be happy.';
    end

    fn4;
    fn2;
    var,
    var2,
end

Here fn2_impl is the actual implementation of fn2 and it inherits all variables that inherited by fn2. var is local to fn2_impl because it is an input argument.

However, I recommend local functions as suggested by @CrisLuengo. If variables need to be shared, using OO style of programming is more readable and maintainable than implicitly sharing by nested functions.

  • Thanks to @CrisLuengo that noted me that it is possible to skip inputs arguments when calling MATLAB functions.

Upvotes: 2

anon
anon

Reputation:

No, there is no way in MATLAB to declare a nested function variable to be local to that function if the variable also exists in the external scope (i.e., the function containing the nested function).

The behavior of Nested Functions is fully described in MATLAB documentation and what you are asking is not possible or at least not documented.

It is specifically stated that the supported behavior is

This means that both a nested function and a function that contains it can modify the same variable without passing that variable as an argument.

and no remedy to prevent this behavior is mentioned in the documentation.

Upvotes: 2

Related Questions