【问题标题】:function handle to nested function not working for some values of parameter嵌套函数的函数句柄不适用于某些参数值
【发布时间】:2021-09-05 05:56:07
【问题描述】:

这个函数本来应该是返回一个函数句柄给里面的嵌套函数,但是如果在外面的函数中把变量x设置为负值,就不起作用了。

内部嵌套函数只是一个常量函数,返回在外部函数中设置的变量 x 的值。

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

如果我尝试评估返回的函数,例如test()(3),我收到一个关于 x 未定义的错误。如果 x 被定义为具有至少一个负条目的向量,或者如果 x 是函数的参数并且使用负默认值进行评估,则会发生同样的情况。但如果我改为将其定义为一些非负值

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

那么返回的函数就可以正常工作了。另外,如果我删除 x 的内部定义并将 x 的值作为外部函数的参数(是否为负),例如

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

然后评估例如test(-1)(3),该错误也不会发生。这是一个错误还是我误解了函数句柄或嵌套函数的工作原理?

Octave 文档建议使用子函数而不是嵌套函数,但它们无法访问其父函数的局部变量,我需要返回的函数依赖于返回它的函数的输入。有什么想法可以解决这个问题吗?

【问题讨论】:

    标签: octave nested-function function-handle


    【解决方案1】:

    这是一个在这里跟踪的错误:

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

    看起来它已修复,将在下一个版本中消失。

    另外,为了解释负数和正数的不同行为:我做了一些实验,并没有捕获分配 computed 值的变量:

    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
    
    
        }
    

    负数和正数的不同行为可能是由于在将数字视为一元运算符(uminus)之前的减号-

    【讨论】:

      【解决方案2】:

      从八度版本5.2.0 开始,根本不支持嵌套函数句柄。我猜这是第 6 版的新颖之处。

      八度函数不是变量,引擎在读取文件时编译\翻译它们。我的猜测是,您观察到的行为会受到函数加载时当前工作空间的影响。

      做你想做的事情的常用方法是生成匿名 (lambda) 函数:

      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
      

      请注意,生成函数的默认参数应在 lambda 定义中说明。否则,如果您将其称为test2()(),它在调用calc(a,x) 时将不知道将什么放入a

      如果您尝试创建闭包(具有关联状态的函数),则 octave 的选项有限。在这种情况下,您可以查看 octave 的 object oriented 功能。 Classdef 可能对快速解决方案很有用。

      【讨论】:

      • 是的,嵌套函数对于创建比使用匿名函数(多语句、循环等)稍微复杂的闭包很有用。这确实是我发现真正有用的嵌套函数的唯一应用。当然,您可以替换一个类,但这需要更多的工作。不幸的是 Octave 不支持这个。
      • @Dimitry:感谢您的解释,但对我来说,您的回答还有另一个问题:如果没有关闭,为什么函数 test2(-1) 即使在顶级环境中仍然有效变量名x 和函数名calc 没有在那里定义? Octave 显示 test2(-1) 的函数体是 @(a)calc(a,x),所以如果没有闭包,xcalc 引用不应该在像 test2(-1)(3) 这样的调用中返回错误吗?
      • @Dimitry:我刚刚重新阅读了你的答案,看到你写了“有限选项”,所以我猜有一些关闭,它通常不起作用。
      • 猜猜这取决于闭包的定义。生成 lambda 函数时,它使用生成它的范围内可用的值。由于此类 lamda 未正确保存,我猜它确实将本地值与函数分开存储在某处,而不是将它们的值编译到代码中。所以它可能是一个闭包。虽然通常闭包也能够修改自己的状态。对于 octave lambda 函数,情况并非如此。
      • 但是,这主要是由实现定义的,而不是由语言的设计定义的。因此,随着引擎的开发,它可能会发生变化,也可能不会发生变化。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-20
      • 1970-01-01
      相关资源
      最近更新 更多