使用递归JavaScript的闭包

时间:2014-12-12 20:43:25

标签: javascript recursion closures

//reverse a string with recursion and a closure
    function r(str){
        var i = str.length-1,results=[],j=0;
        function _r(str){
            if(i===0){
                return results.join('') + str[0];
            }
            console.log('i = ' + i);
            results[j] = str[i];
            i--,j++;
            console.log('j = ' + j);    
            return _r(str);    
        }

        return _r(str);
    }

我对上述代码有两个问题:

  1. 上面的代码是否打破了(我的无知显示)函数编程的无状态特性?
  2. 如果str是一个大字符串,那么这个实现比没有使用闭包的解决方案更慢/更多内存密集吗?

1 个答案:

答案 0 :(得分:2)

是的,你使用功能范例。

在你的情况下,你只是使用递归来循环,并且使用函数之外的变量完成处理。它与使用全局变量没什么不同(除了它们不是全局变量,而是外部函数的本地变量)。

要使用函数方法反转字符串,您应该考虑字符串的反向由last_char +(中间部分的反向)+ first_char组成。 该定义自然地扩展为递归函数...例如:

function rev(str) {
    if (str.length < 2) {
        // Empty string or a single char... reverse = input
        return str;
    } else {
        return str[str.length-1] + rev(str.slice(1, -1)) + str[0];
    }
}

这不使用明确的状态(因为您可能会注意到根本没有任何分配)。

如果您正在寻找尾部调用可优化版本,请考虑:

function rev(todo, done) {
    if (todo === "") {
        return done;
    } else {
        return rev(todo.slice(1), todo[0] + (done||""));
    }
}

在这种情况下的想法是处理案例必须是return <recursive-call>(在前面的例子中没有发生这种情况,因为递归的结果在返回之前向每一端添加了一个char)。

如果您的代码最终返回递归调用的未处理结果,则该函数可以进行尾调用优化,因为不需要堆栈空间。换句话说,递归调用变成了一个简单的循环。

最后这是另一个版本,不是纯粹的功能,但它看起来与你正在尝试的类似:

function rev(str) {
    function rev1(v, i, j) {
        if (i >= j) {
            return v.join("");
        } else {
            var t=v[i]; v[i]=v[j]; v[j]=t;
            return rev1(v, i+1, j-1);
        }
    }
    return rev1(str.split(""), 0, str.length-1);
}

这不是纯函数,因为向量元素是交换的(你可以看到有赋值)但是使用尾调用可优化递归来进行循环。请注意,rev1不是闭包而是函数(不捕获任何状态,也可以将其置于rev之外)。