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)
我知道这是一种非答案,但如果您使用对象和动态调度的方法,您想要的更容易.基本上,我们将"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)
让我们从后面开始。假设你想要一个函数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动态指定 的值。