Javascript 将循环转换为递归函数

Javascript 将循环转换为递归函数,javascript,string,function,recursion,Javascript,String,Function,Recursion,昨天我编写了一个函数来计算字符串中“a”字符的数量。我的老师让我把代码重构成一个递归函数,但我真的不知道怎么做 我想要一些关于这个主题的反馈,顺便说一句,我是JavaScript的绝对初学者 function numberOfA(n){ var numberA =0; for (i=0; i<=n.length; i++){ if(n.charAt(i)== "a" ){ numberA++;} } return numberA; } 提前谢谢 好

昨天我编写了一个函数来计算字符串中
“a”
字符的数量。我的老师让我把代码重构成一个递归函数,但我真的不知道怎么做

我想要一些关于这个主题的反馈,顺便说一句,我是JavaScript的绝对初学者

function numberOfA(n){
var numberA =0;

for (i=0; i<=n.length; i++){

    if(n.charAt(i)== "a"  ){
        numberA++;}
    }
return numberA;

}

提前谢谢

好吧,递归的基本概念是用更小的版本来解决问题

您有一个函数,
numberOfA
,它提供字符串(或者子字符串)的长度

假设您有字符串
“javascript”
,第一个字符串位于索引2

合乎逻辑的说法是,字符串中
a
s的数量等于1加上第一个
a
之后整个子字符串中
a
s的数量

因此,您要做的是将
1
添加到子字符串
vascript
as的数量中


这里有一些psudocode

function numA(str)
{
    var substring = substr(index_of_first_a, str.length - index_of_first_a
    return 1 + numA(substring);
}
试试这个:

function numberOfA(n) {
    return n == "" ? 0 : (n.charAt(0) == "a" ? 1 : 0) + numberOfA(n.substring(1))
}
下面是它的工作原理:

  • 如果
    n
    是空字符串,则返回
    0
    并完成递归。这是递归的基本情况
  • 否则,如果字符串中第一个位置的字符是
    “a”
    则添加一个,如果不是,则添加零,并通过从字符串中删除第一个字符来推进递归。这是递归的递归步骤

如您所见,每个递归解决方案必须至少有一个基本情况和一个递归步骤。

递归的目标是生成一个调用自身的函数。
您可能有相互递归——函数A调用函数B,调用函数A……但这里肯定不需要,而且更适合于当您知道需要做两件不同的事情(每个函数一件)并且知道需要以蛙跳模式来做它们时

递归在考虑循环时发挥作用。
通常,当您使用循环进行操作时,可能会导致两个或三个循环相互重叠。
递归不是担心管理循环,而是一种思考循环的单个迭代中会发生什么的方法,并且只编写执行该操作所需的代码

奇异递归的一个非常简单的例子可能是将数组的所有元素记录到控制台。
这不是一个实际的例子——这是一个微不足道的例子,其中包含了制作实际例子所需的大部分内容

var array = [ "one", "two", "three", "four" ];

function listNextItem (array, index) {
    var item = array[index];
    if (!item) { return; }

    console.log(item);
    listNextItem(array, index + 1);
}

listNextItem(array, 0);
我创建了一个非常简单的函数,它看起来像是最里面的循环。
它根据
数组[索引]
设置一个项变量
如果它不存在,我们就完成了,我们可以从函数中返回,所以我们不想永远继续下去(这在递归中非常重要)

如果确实存在,我们将记录该项的值。 然后我们调用完全相同的函数,并将完全相同的数组传递给它,但是我们将
index+1
的值传递给它

这是否改变了任何人的生活,或使环路过时?
不是真的

但这是获得递归的第一步

function recursiveAddOne (current, max) {
    if (current === max) { return current; }
    return 1 + recursiveAddOne(current + 1, max);
}

var total = recursiveAddOne(0, 3); // === 3 + 1 + 1 + 1
total; // 6
下一步是从递归中获取
返回值

function recursiveAddOne (current, max) {
    if (current === max) { return current; }
    return 1 + recursiveAddOne(current + 1, max);
}

var total = recursiveAddOne(0, 3); // === 3 + 1 + 1 + 1
total; // 6
通常在返回语句中,我会将答案发送回外部世界中的变量。
我仍在这样做,但在这里,我向同一个函数添加了一个
调用
,作为返回的一部分

那有什么用?
在内部函数返回之前,外部函数不能返回值。
在其内部函数返回之前,内部函数无法返回值

…一直到我的终止条件满足为止。 这个条件向它的外部函数返回一个值。这个外部函数向它的外部函数返回那个附加值……一直到最外层的函数得到所有其他函数的值,然后返回给外部世界

这就像给每一个俄罗斯的Matryoshka(“babushka”)娃娃一件作品。
你从最大的一个开始,然后从里面一直到最小的一个。
最小的一个先做它的工作,然后把它交给下一个,下一个做它的工作,然后把它交给下一个……一直到你再次外出为止。


<!DOCTYPE html><html lang="en"><body><script>
var foo = function foo() {
console.log(arguments.callee); // logs foo()
// callee could be used to invoke recursively the foo function (e.g. arguments.callee())
}();
</script></body></html>
var foo=函数foo(){ console.log(arguments.callee);//logs foo() //被调用方可用于递归调用foo函数(例如arguments.callee()) }();

arguments.callee函数将调用当前正在执行的方法。

Just FWIW:使用递归算法计算字符串中特定字符的出现频率完全没有理由。赋值非常愚蠢。这不是你的问题,但如果你想知道“为什么”,答案是“没有充分的理由”。“这是一个愚蠢的要求,因为linearsearch对于这个功能来说要好得多,但我可能会用递归调用代替for语句来调用下一个字符,正如Sam所建议的那样。@T.J.Crowder我不会说这是一个微不足道的目的,在输出方面确实没有任何实际好处。但我要说的是,这是一种非常安全的方法,可以让人们深入了解递归的概念,而不是通过二叉树导航,或者解析JS中密集嵌套的对象/数组的大型结构。@T.J.Crowder你是通过给学生生产问题来教编程入门课的吗?@Norguard:是的。我当然想提出一个更好的问题,但现在已经很晚了,我该退休了。正如你所说,你不想一次向所有人扔太多东西。我认为Sam的算法更好,这将迭代每个字符,他的字符跳到不同的
a
s@SamIam,同意。这太容易了,所以,得到一个答案,并把它作为你自己的工作。当然,它仍然要求OP诚实并将标记放置到位。
arguments.callee
is和is d
<!DOCTYPE html><html lang="en"><body><script>
var foo = function foo() {
console.log(arguments.callee); // logs foo()
// callee could be used to invoke recursively the foo function (e.g. arguments.callee())
}();
</script></body></html>