递归函数的高阶函数?

mjs*_*mjs 19 javascript recursion y-combinator higher-order-functions

有没有办法通过高阶函数"包装"递归函数,以便递归调用也被包装?(例如,在每次调用时记录函数的参数.)

例如,假设我们有一个函数,sum()它通过将头部添加到尾部的总和来返回数字列表的总和:

function sum(a) {
    if (a.length === 0) {
        return 0;
    } else {
        return a[0] + sum(a.slice(1));
    }
}
Run Code Online (Sandbox Code Playgroud)

有没有办法编写一个高阶函数,logging()它将sum()函数作为输入,并返回一个函数,sum()在每次递归调用时输出参数?

以下不起作用:

function logging(fn) {
    return function(a) {
        console.log(a);
        return fn(a);
    }
}

sum2 = logging(sum);
sum2([1, 2, 3]);
Run Code Online (Sandbox Code Playgroud)

实际产量:

[1, 2, 3]
-> 6
Run Code Online (Sandbox Code Playgroud)

预期产量:

[1, 2, 3]
[2, 3]
[3]
[]
-> 6
Run Code Online (Sandbox Code Playgroud)

这是否sum()可以重写,以便它可以与Y Combinator风格的"递归"一起使用?

function sum_core(g) {
    return function (a) {
        if (a.length === 0) {
            return 0;
        } else {
            return a[0] + g(a.slice(1));
        }
    };
}

sum = Y(sum_core);
sum([1, 2, 3]);
// -> 6
Run Code Online (Sandbox Code Playgroud)

hug*_*omg 7

我知道这是一种非答案,但如果您使用对象和动态调度的方法,您想要的更容易.基本上,我们将"rec"存储在可变单元格中,而不是必须传递它.

它有点类似于sum = logging(sum)(而不是sum2 =)但更惯用,并且不会硬编码我们发送的可变引用的名称.

var obj = {
  sum : function(a){
    if (a.length === 0) {
      return 0;
    } else {
      return a[0] + this.sum(a.slice(1)); // <-- dispatch on `this`
    }
  }
}

function add_logging(obj, funcname){
   var oldf = obj[funcname];
   obj[funcname] = function(/**/){
     console.log(arguments);
     return oldf.apply(this, arguments);
   }
}

add_logging(obj, 'sum');
Run Code Online (Sandbox Code Playgroud)


Aad*_*hah 3

让我们从后面开始。假设你想要一个函数traceSum

> traceSum([1, 2, 3]);
[1, 2, 3]
[2, 3]
[3]
[]
6
Run Code Online (Sandbox Code Playgroud)

您可以traceSum按如下方式实施:

function traceSum(a) {
    console.log(a);
    if (a.length === 0) return 0;
    else return a[0] + traceSum(a.slice(1));
}
Run Code Online (Sandbox Code Playgroud)

从这个实现中我们可以创建一个通用trace函数:

function trace(f) {
    return function (a) {
        console.log(a);
        return f(trace(f), a);
    };
}
Run Code Online (Sandbox Code Playgroud)

这类似于 JavaScript 中 Y 组合器的实现方式:

function y(f) {
    return function (a) {
        return f(y(f), a);
    };
}
Run Code Online (Sandbox Code Playgroud)

您的traceSum函数现在可以实现为:

var traceSum = trace(function (traceSum, a) {
    if (a.length === 0) return 0;
    else return a[0] + traceSum(a.slice(1));
});
Run Code Online (Sandbox Code Playgroud)

不幸的是,如果您无法修改该sum函数,那么您就不能这样做,因为您需要某种方法来动态trace指定要回调的函数。考虑:

function sum(a) {
    if (a.length === 0) return 0;
    else return a[0] + sum(a.slice(1));
}
Run Code Online (Sandbox Code Playgroud)

您不能使用trace上述函数,因为函数内部sum将始终引用函数本身。无法sum动态指定 的值。