【问题标题】:How to efficiently determine the depth of closures如何有效地确定闭包的深度
【发布时间】:2015-08-24 17:24:57
【问题描述】:

就闭包而言,如果只考虑 返回另一个函数的函数,如下例所示。我们如何在运行时以编程方式确定此闭包的深度

假设这个函数F的深度为2:

function F(a){ // 1
    return function G(b){ // 2
        return 'something';
    }
}

这个函数'H'的深度是3。

function H(a){ // 1
   return function(b){ //2
      return function(c){} //3
   }
}

到目前为止,我已经编写了一些简单的脚本来确定闭包的深度,方法是迭代检查函数的返回类型,直到它返回除函数之外的其他类型。 但我的脚本仅适用于每个嵌套函数不需要参数的情况。见以下代码:

function F(){
    return function(){
        return function(){
            return 1;
        }
   }
}

function depth(f){
    var n = 1;
    var ret = f();
    while (typeof(ret)=='function'){
        ret = ret(); 
        n++;
    }
    return n; 
}

上面的简单函数 depth 正确地确定了 F 的深度为 3。但是,在某些实际情况下,例如以函数为参数并调用它的闭包,我的函数 depth无法处理。

function F2(a,f){
    return function(g){
        return f(a) + g(a); // This will break my depth function
   }
}

*我的问题有什么方法可以更有效地确定我的闭包深度吗?尤其是在上面提到的某些情况下(将函数作为参数并调用的闭包它)。可以动态处理可能会或可能不会将某些函数作为参数的闭包的通用方法将不胜感激。

P.S.让我缩小范围如下。

  • 只考虑纯嵌套函数。
  • 每一层嵌套函数总是返回相同的类型。
  • 函数的深度是恒定的。
  • 没有一个嵌套函数是递归的。

【问题讨论】:

标签: javascript closures


【解决方案1】:

这更像是一个评论而不是一个答案,但最好在这里格式化。

除非您提供一组所有必需的参数,否则这不是一个可解决的问题。
考虑计算以下的“深度”

function F(a) {
    if (a) {
        return function G(b) {
            return 0;
        }
    } else {
        return 1;
    }
}

【讨论】:

  • OP 仅在 10m 前从他的问题中排除了此类情况 :-)
  • @Bergi 不是每个人都在阅读问题的更新版本,但感谢您的说明。
  • 这种情况使事情变得更加复杂,正如对问题的答复中所讨论的那样。所以我决定退出这个案子。
  • 很可能不排除对else { return function H(b) { return function J(c) {return 1;}} 的更新,当然,这取决于 OP 对“同一类型”的精确定义。
  • @ScottSauyet 嗯,这有点棘手。然而,用一般方法解决可变深度可能太难了。
【解决方案2】:

您也许可以更改所有函数,以便它们也能够在没有参数的情况下运行,出于您的反思目的?
由于在深度计算期间,函数调用不应进行任何其他有用的计算或无论如何都有任何副作用。它们应该只返回一个函数或一个非函数值。
既然你说:“每一层嵌套函数总是返回相同的类型。 函数的深度是恒定的。”在遇到空参数列表时返回相应类型的虚拟响应的决定应该很简单。

【讨论】:

  • 你的意思是通过移除函数体来改变每一层函数吗?我不太确定如何实际实现这一点,也许是通过猴子补丁?有点不知道如何以编程方式更改函数而不对其(嵌套)返回值进行任何更改。
  • @TaoP.R.这里我的意思是你在函数头代码中添加了几行代码,而不是删除它的主体或改变它的 defined 功能。如果您想要编程解决方案,请参阅我的第二个答案。
【解决方案3】:

这是另一种方法。这通过观察代码的行为方式在运行时起作用。
我的函数Reflector 是这种情况下的特殊酱汁。
请参阅https://jsfiddle.net/rffv34oz/8/ 以运行示例。

函数Reflector 做了四件事:
1。它通过返回钩子而不是原始函数来“检测”带有钩子 (fh) 的函数。效果是在调用该检测函数之前调用钩子。
2。当调用检测函数时,它会转发调用 - 它调用原始函数。并准备好捕获函数结果。
3。当函数返回时,它会检查结果。如果结果也是一个函数,它存储新的最大深度。
4。另外,当返回的结果是一个函数时,钩子也会检测返回的函数,因此步骤(1)再次应用于新的返回函数。

//Your function You want to USE and REFLECT on too.
function F(){ 
    //return 3;
    return function(){
        //return 2;
        return function(){
            return 1;
        }
   }
}

//Your function You want to USE and REFLECT on too.
function H(a){ // 1
    return function(b){ //2
        return function(c){ //3
            return a + b + c;   
        }
   }    
}

//this function is the machinery that knows how to USE the functions You want to measure. 
//But this function only USES them, but does no reflection.
function BlackboxF(f) { 

    var r1 = f();
    if (typeof r1 == "function")
    {
        var r2 = r1();
        if (typeof r2 == "function")
        {
            var r3 = r2();  

            return r3;
        }
        else 
        {
            return r2;   
        }
    }
    else 
    {
         return r1;   
    }

}

//this function is the machinery that knows how to USE the functions You want to measure. 
//But this function only USES them, but does no reflection.
function BlackboxH(f) { 

    var r1 = f(1);
    var r2 = r1(2);
    var r3 = r2(4);
    return r3;

}

var maxdepth = 1;

//This is the special agent for reflecting code as it runs
function Reflector(f, depth) { 

    if (!depth)
        depth = 1;

    //1. It "instruments" a function with a hook (`fh`) by returning the hook 
    //instead of the original function. The effect is that the hook is called 
    //before that instrumented function is called.
    var fh = function()     
    {
        //2. When the instrumented function is called, it forwards the call - it calls 
        //the original function. And remains ready for capturing the function result.
        var r = f.apply(this, arguments);    

        //3. When the function returns, it inspects the result. 
        //If the result is also a function, it stores new maximum depth.
        if (typeof r == "function")
        {
            maxdepth = Math.max(maxdepth, depth + 1);

            //4. Also, when the returned result was a function, the hook also 
            //instruments the returned function, so that step (1) is again applied 
            //for the new returned function.
            return Reflector(r, depth + 1);        
        }
        else 
        {
            return r;
        }
    };

    return fh;
}

if (false) //The simple case with no arguments
{
    var F2 = Reflector(F);
    var RF = BlackboxF(F2);

    document.getElementById("result").textContent = "Result: " + RF;
    document.getElementById("depth").textContent = "Depth: " + maxdepth;
}
else //A more complicated case with arguments
{
    var H2 = Reflector(H);
    var RH = BlackboxH(H2);

    document.getElementById("result").textContent = "Result: " + RH;
    document.getElementById("depth").textContent = "Depth: " + maxdepth;
}

【讨论】:

  • 您的反射器方法对我来说看起来很有趣。但是,它看起来比我预期的要复杂一些。你能解释一下你是怎么设计的吗?
  • @TaoP.R.我在答案文本中添加了关于Reflector 工作原理的解释,并将解释也插入到代码中。现在更清楚还是您想澄清更多?此外,如果您愿意,可以进一步升级此方法,以便它能够同时反映多个“根”函数(FH)并分别报告每个函数的深度。我现在没有添加这样的代码,因为这样答案会变得太长。但是如果有必要,我可以添加关于同时观察多个“根”函数需要采取的方法的一般解释。
  • 所以最重要的因素是“我们需要知道如何将适当的参数传递给每个函数”。这种反射器在一般情况下似乎效果很好。
  • @TaoP.R.如果您愿意将参数类型预定义为基本类型(例如,数字),并且您允许为所有这些参数使用一些基值(例如,零),那么有一个技巧可以绕过对机器的需求知道如何使用这些功能,并自动生成调用。实际上,对基本类型的需求并不严格 - 只是可能,但允许使用一些公共基本值的需求是必要的。
  • 我认为您的方法可能会给我一些想法。我将尝试进一步扩展和定制它以使其更加灵活。我所需要的只是一种我们不需要事先知道如何调用的通用方法,但是可以扩展您的方法来处理它(我预见到)。所以我接受了。
猜你喜欢
  • 2019-08-31
  • 1970-01-01
  • 1970-01-01
  • 2013-03-20
  • 2016-07-19
  • 1970-01-01
  • 2012-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多