1

This function is supposed to return a function handle to the nested function inside, but if the variable x is set to a negative value in the outer function, it doesn't work.

The inner nested function is just a constant function returning the value of the variable x that is set in the outer function.

function t=test(x)
    x=-1;
    function y=f()
        y=x;
    endfunction
    t=@f;
endfunction

If I try to evaluate the returned function, e.g. test()(3), I get an error about x being undefined. The same happens if x is defined as a vector with at least one negative entry or if x is argument of the function and a negative default value is used for evaluation. But if I instead define it as some nonnegative value

function t=test(x)
    x=1;
    function y=f()
        y=x;
    endfunction
    t=@f;
endfunction,

then the returned function works just fine. Also if I remove the internal definition of x and give the value for x as an argument to the outer function (negative or not), like

function t=test(x)
    function y=f()
        y=x;
    endfunction
    t=@f;
endfunction

and then evaluate e.g. test(-1)(3), the error doesn't occur either. Is this a bug or am misunderstanding how function handles or nested functions work?

The Octave documentation recommends using subfunctions instead of nested functions, but they cannot access the local variables of their parent function and I need the returned function to depend on the input of the function returning it. Any ideas how to go about this?

overglow
  • 13
  • 4

2 Answers2

3

This is a bug that was tracked here:

https://savannah.gnu.org/bugs/?func=detailitem&item_id=60137

Looks like it was fixed and will be gone in the next release.

Also, to explain the different behavior of negative and positive numbers: I experimented a bit, and no variable that is assigned a computed value is being captured:

function t=tst()
  x = [5,3;0,0]; # captured
  y = [5,3;0,0+1]; # not captured
  z = x + 1; # not captured
  function y=f()
      
  endfunction
  t=@f;
endfunction
>> functions(tst)
ans =

  scalar structure containing the fields:

    function = f
    type = nested
    file =
    workspace =
    {
      [1,1] =

        scalar structure containing the fields:

          t = @f
          x =

             5   3
             0   0


    }

The different behavior of negative and positive numbers are probably caused by the minus sign - before the numbers being treated as a unary operator (uminus).

Cem
  • 1,276
  • 4
  • 17
1

As of octave version 5.2.0 the nested function handles were not supported at all. I'm going to guess that is the novelty of the version 6.

In octave functions are not variables, the engine compiles\translates them at the moment of reading the file. My guess would be that behavior you are observing is influenced by your current workspace at the time of function loading.

The common way for doing what you are trying to do was to generate the anonymous (lambda) functions:

function t = test1(x=-1)
    t = @()x;
end
function t = test2(x=-1)
    function s = calc(y,z)
        s = y + 2*z;
    end
    t = @(a=1)calc(a,x);
end

Note that default parameters for the generated function should be stated in lambda definition. Otherwise if you'd call it like test2()() it would not know what to put into a when calling calc(a,x).

If you are trying to create a closure (a function with associated state), octave has limited options for that. In such a case you could have a look at octave's object oriented functionality. Classdef might be useful for quick solutions.

Dimitry
  • 2,204
  • 1
  • 16
  • 24
  • Yes, nested functions are useful to create a closure that is slightly more complicated than what you can do with an anonymous function (multiple statements, loops, etc). This is really the only application of nested functions I've found truly useful. Of course you can substitute a class, but that's a lot more work. It is unfortunate that Octave doesn't support this. – Cris Luengo Jun 23 '21 at 14:50
  • @Dimitry: Thanks for your explanation, to me your answer bears another question though: If there is no closure, how come that the function `test2(-1)` still works in the top-level environment even when the variable name `x` and the function name `calc` are not defined there? Octave shows that the function body of `test2(-1)` is `@(a)calc(a,x)`, so shouldn't the `x` and `calc` references return an error in a call like `test2(-1)(3)` if there is no closure? – overglow Jun 24 '21 at 22:53
  • @Dimitry: I just reread your answer again and saw you wrote 'limited options', so I guess there are some closures, it just doesn't generally work. – overglow Jun 24 '21 at 22:59
  • Guess it depends on the definition of the closure. When lambda function is generated, it uses values available in the scope it is generated in. Since such lamda is not saved properly I guess it does store the local values somewhere separately from the function rather than compling their values into the code. So it probably is a closure. Though usually closures are also capable of modifying their own state. This is not the case for octave lambda functions. – Dimitry Jun 25 '21 at 09:35
  • However, this is mostly defined by implementation, not by the design of the language. So it may or may not change as the engine is developed. – Dimitry Jun 25 '21 at 09:45