codewars.com中的一个问题: https://www.codewars.com/kata/foldr/train/javascript
为数组定义一个折叠函数,该函数实现与内置函数reduceRight()相同的功能。这个问题非常抽象,因为我对函数式编程知之甚少。
我发现最重要的是如何在JavaScript中实现延迟评估。但是我不知道如何处理。
例如,我们有两个函数indexOf
和logging
,indexOf(x)
是一个函数,将在foldr
方法{{1} }是一个包装函数,它告诉我们logging
可以被调用多少次。
indexOf(x)
如果我们不懒惰地实现它并使用递归:
const indexOf = y => function (cur, acc) {
if (cur === y) {
return 0
} else {
return acc + 1 || -1
}
};
const logging = fn => function logging(...a) {
i++;
return fn(...a);
};
变量Object.defineProperty(Array.prototype, "foldr", {
value: function foldr(fn, z) {
return function _foldr(a) {
if (a.length === 0) {
return z
}
return fn(a[0], _foldr(a.slice(1)))
}(this);
}
});
let i = 0
let x = [1, 2, 3].foldr(logging(indexOf(1)), -1)
console.log(`x: ${x}`) // x: 0
console.log(`i: ${i}`) // i: 3
显示该函数已被调用3次,整个数组已被迭代。但是,如果我们观察到i
函数,就会发现如果我们使用惰性求值,就不需要迭代整个数组。
在第一级递归中
indexOf
等于indexOf(1)(a[0], _foldr(a.slice(1)))
,因为indexOf(1)(1, _foldr([2,3]))
应当立即返回0,并且不需要计算第二个参数cur === y
。因此,在codewars.com的测试用例中,_foldr([2,3])
应该为1。
我该如何处理?
答案 0 :(得分:0)
您近在咫尺...:
fn(a[0], () => _foldr(a.slice(1)))
如果您传递函数而不是acc
累加器值,则该函数可以决定是否评估累加器:
const indexOf = y => function (cur, acc) {
if (cur === y) {
return 0; // acc() was not called, ends here
} else {
return acc() + 1 || -1; // acc() gets called, traversal goes on
}
};
答案 1 :(得分:0)
我找到了解决方案。使用Object.prototype.valueOf()
valueOf()方法返回指定对象的原始值。
因为我们想在执行诸如return acc + 1 || -1
之类的算法时调用该函数。
我们可以使用:
Object.defineProperty(Array.prototype, 'foldr', {
value(fn, z) {
let _foldr = (a) => {
if (!a.length) return z;
let r = fn(a[0], { valueOf: () => _foldr(a.slice(1)) });
return (r.valueOf) ? r.valueOf() : r;
};
return _foldr(this);
},
});