我试图绕过递归函数式编程概念。
考虑以下场景(是的,我可以使用.length
来获取数组的长度):
https://jsfiddle.net/ur5gc397/
$(function(){
function arrLen(arr) {
var count = 0;
$.each(arr, function() {
count = count + 1;
});
return count;
}
var a = ["a", "b", "c", "d"];
var lengthOfA = arrLen(a);
})
我想重新说明这一点,以便我没有将变量count
设置为0,然后每次迭代我的数组时都会改变值。我可以设置某种递归调用自身的函数,然后以正确的值返回函数吗?
答案 0 :(得分:2)
我刚刚修改了你的代码:
$(function(){
function arrLen(arr, count) {
if(!arr[count]) return count;
count++;
return arrLen(arr, count);
}
var a = ["a", "b", "c", "d"];
var lengthOfA = arrLen(a, 1);
$('body').html(lengthOfA)
})
答案 1 :(得分:1)
如果绝对必须以递归方式测试数组的长度,你可以这样做:
function arrLen(arr) {
if (!("0" in arr)) { // [].length is banned
return 0;
}
return arrLen(arr.slice(0, -1)) + 1;
}
请注意,这是 非常非常愚蠢的想法 。
我们的想法是,在每次递归时,我们从数组中切掉一个元素(最后一个,使用slice(0,-1)
)并返回1
,而不是调用arrLen
。其余的数组返回。
序列看起来像这样:
arrLen(["a", "b", "c", "d"])
calls arrLen(["a", "b", "c"])
calls arrLen(["a", "b"])
calls arrLen(["a"])
calls arrLen([])
returns 0 == 0
returns 0 + 1 == 1
returns 0 + 1 + 1 == 2
returns 0 + 1 + 1 + 1 == 3
returns 0 + 1 + 1 + 1 + 1 == 4
答案 2 :(得分:1)
对于计算一个值reduce
是使用什么。它真的不那么特别。想象一下,你想要总结每个元素。
[1,2,3,4,5].reduce((a,e) => a+e, 0); // => 15
reduce
你可以用递归实现:
Array.prototype.myReduce = function(proc,init) {
var arr = this;
var nLen = this.length;
function helper(acc, nIndex) {
return nIndex >= nLen ?
acc :
helper(proc(acc, arr[nIndex]), nIndex+1);
}
return helper(init, 0);
}
[1,2,3,4,5].myReduce((a,e) => a+e, 0); // => 15
请注意,实际的实现不会使用递归,因为JS没有优化尾调用,因此循环结构会更加高效。例如。 Ramda是一个功能库,它提供了使用合成制作程序的方法,但Ramda中的映射和缩减是通过循环实现的。
修改强>
如果你的结构确实不是一个数组但是像链接列表那么你需要通过检查这是否是单身空元素来检查你是否在最后。下面是一个带有reduce
的单链表的示例实现,除了访问器和停止条件之外,它在本答案中看起来很像:
function List() {}
List.prototype.cons = function(a) {
var c = new List();
c.a = a;
c.d = this;
return c;
}
List.prototype.car = function() {
return this.a;
}
List.prototype.cdr = function() {
return this.d;
}
List.prototype.reduce = function(proc, init) {
function helper (lst, acc) {
return lst === emptyList ?
acc :
helper(lst.cdr(), proc(acc, lst.car()));
}
return helper(this, init);
}
List.prototype.length = function() {
return this.reduce(acc => acc+1, 0);
}
List.prototype.reverse = function() {
return this.reduce((acc, a) => acc.cons(a),emptyList);
}
// Singleton empty list
window.emptyList = new List();
var example = emptyList.cons(1).cons(2).cons(9);
example.reduce((a,e)=>a+e,0); //=> 12
example.length(); // ==> 3
example.reverse(); // ==> (1,2,9)
答案 3 :(得分:1)
如果你想使用for / forEach循环,那么就没有递归的地方了。迭代和递归用于实现相同的结果 - 您应该选择其中一个。
<强>迭代:强>
与您的代码类似,只是更简单一些。保持计数,增加并返回。
function arrayLength(array) {
let count = 0;
for (const i of array) count++;
return count;
}
console.log(arrayLength([1, 2, 3, 4, 5]));
console.log(arrayLength([]));
<强>递归:强>
检查数组中的下一个元素是否存在,如果存在,继续使用下一个索引递归调用该函数。该函数返回最终索引+ 1,除了0是特殊的。 (请注意,这不适用于sparse arrays,如评论中所述。)
function arrayLength(array, index) {
index = index || 0;
if (array[index+1]) {
return arrayLength(array, index+1);
} else if (index === 0) {
return 0;
} else {
return index + 1;
}
}
console.log(arrayLength([1, 2, 3, 4, 5]));
console.log(arrayLength([]));
答案 4 :(得分:0)
您可以在不使用任何for循环的情况下使用此功能。
function arrLen(array, length=0) {
if (array[0] === undefined) return length;
length++;
const newArray = array.slice(1);
return arrLen(newArray, length)
}
var a = ["a", "b", "c", "d"];
var lengthOfA = arrLen(a);
首先,我试图获取基本情况。一旦发现数组中没有元素,我的递归将停止工作。如果数组中没有剩余元素,则array[0] === undefined
。我已将长度初始化为零,以便每次迭代后都可以增加长度值,而我使用length++
完成了该操作。我的另一个目标是在每次迭代后从数组中删除第一个元素,这就是为什么我使用slice方法。然后,我在返回的函数中使用了这些新的array和length值。