如何解决如何从迭代方法编写递归函数?
在使用递归时,我将如何在下面编码相同的代码,我采用了迭代方法,如果有人可以帮助我使用递归对它进行编码,我将非常感谢! 这是我的代码:
function returnNumberOfPalindromes(word) {
function checkForPalindrome(chunk) {
let chunkArray = [];
for (const letter of chunk) {
chunkArray.push(letter);
}
if (chunkArray.reverse().join('') === chunk) {
tally += 1;
}
}
let tally = 0;
for (let index1 = 0,index2 = 2; index1 <= word.length; index1++,(index2 = index1 + 2)) {
for (; index2 <= word.length; index2++) {
let chunk = word.slice(index1,index2);
checkForPalindrome(chunk);
}
}
console.log(tally);
}
returnNumberOfPalindromes("kayak");
returnNumberOfPalindromes("aya");
returnNumberOfPalindromes("appal");
returnNumberOfPalindromes("addadaadd");
解决方法
一系列重构
在一系列重构中,我们将最终获得以下代码:
const isPalindrome = (s) =>
s.length == 0 ? true : s[0] === s[s.length - 1] && isPalindrome(s.slice(1,-1))
const countPalindromicPrefixes = (s) =>
s.length < 2 ? 0 : (isPalindrome(s) ? 1 : 0) + countPalindromicPrefixes(s.slice (0,-1))
const countPalindromes = (s) =>
s.length < 2 ? 0 : countPalindromicPrefixes(s) + countPalindromes(s.slice(1))
console .log (countPalindromes ("kayak"));
console .log (countPalindromes ("aya"));
console .log (countPalindromes ("appal"));
console .log (countPalindromes ("addadaadd"));
console .log (countPalindromes ("madamimadam"));
每个步骤都有一个片段(默认情况下是隐藏的,所以它不会太长)表明我们到目前为止还没有破坏任何东西。
起点
初始代码如下:
function returnNumberOfPalindromes(word) {
function checkForPalindrome(chunk) {
let chunkArray = [];
for (const letter of chunk) {
chunkArray.push(letter);
}
if (chunkArray.reverse().join('') === chunk) {
tally += 1;
}
}
let tally = 0;
for (let index1 = 0,index2 = 2; index1 <= word.length; index1++,(index2 = index1 + 2)) {
for (; index2 <= word.length; index2++) {
let chunk = word.slice(index1,index2);
checkForPalindrome(chunk);
}
}
console.log(tally);
}
function returnNumberOfPalindromes(word) {
function checkForPalindrome(chunk) {
let chunkArray = [];
for (const letter of chunk) {
chunkArray.push(letter);
}
if (chunkArray.reverse().join('') === chunk) {
tally += 1;
}
}
let tally = 0;
for (let index1 = 0,index2);
checkForPalindrome(chunk);
}
}
console.log(tally);
}
returnNumberOfPalindromes("kayak");
returnNumberOfPalindromes("aya");
returnNumberOfPalindromes("appal");
returnNumberOfPalindromes("addadaadd");
修复for
循环
将内部循环的初始索引作为外部循环增量的一部分进行更新是很奇怪的。这更常见,我敢说,更合乎逻辑:
for (let index1 = 0; index1 <= word.length; index1++) {
for (let index2 = index1 + 2; index2 <= word.length; index2++) {
let chunk = word.slice(index1,index2);
checkForPalindrome(chunk);
}
}
function returnNumberOfPalindromes(word) {
function checkForPalindrome (chunk) {
if (chunk.split('').reverse().join('') === chunk) {
tally += 1;
}
}
let tally = 0;
for (let index1 = 0; index1 <= word.length; index1++) {
for (let index2 = index1 + 2; index2 <= word.length; index2++) {
let chunk = word.slice(index1,index2);
checkForPalindrome(chunk);
}
}
console.log(tally);
}
returnNumberOfPalindromes("kayak");
returnNumberOfPalindromes("aya");
returnNumberOfPalindromes("appal");
returnNumberOfPalindromes("addadaadd");
returnNumberOfPalindromes("madamimadam");
简化checkForPalindrome
checkForPalindrome
中完成的许多工作都是不必要的。 String.prototype.split已经将字符串转换为字符数组。因此,我们可以将该部分更改为:
function checkForPalindrome (chunk) {
if (chunk.split('').reverse().join('') === chunk) {
tally += 1;
}
}
function returnNumberOfPalindromes(word) {
function checkForPalindrome (chunk) {
if (chunk.split('').reverse().join('') === chunk) {
tally += 1;
}
}
let tally = 0;
for (let index1 = 0; index1 <= word.length; index1++) {
for (let index2 = index1 + 2; index2 <= word.length; index2++) {
let chunk = word.slice(index1,index2);
checkForPalindrome(chunk);
}
}
console.log(tally);
}
returnNumberOfPalindromes("kayak");
returnNumberOfPalindromes("aya");
returnNumberOfPalindromes("appal");
returnNumberOfPalindromes("addadaadd");
returnNumberOfPalindromes("madamimadam");
将局部函数设为纯函数
我们的内部函数checkForPalindrome
没有返回值。相反,它正在更新tally
的状态。这使得它很难理解。让我们对其进行更改,以使checkForPalindrome
返回true
或false
,并根据该结果更新tally
:
function checkForPalindrome (chunk) {
return chunk.split('').reverse().join('') === chunk
}
if (checkForPalindrome(chunk)) {
tally += 1
}
function returnNumberOfPalindromes(word) {
function checkForPalindrome (chunk) {
return chunk.split('').reverse().join('') === chunk
}
let tally = 0;
for (let index1 = 0; index1 <= word.length; index1++) {
for (let index2 = index1 + 2; index2 <= word.length; index2++) {
let chunk = word.slice(index1,index2);
if (checkForPalindrome(chunk)) {
tally += 1
}
}
}
console.log(tally);
}
returnNumberOfPalindromes("kayak");
returnNumberOfPalindromes("aya");
returnNumberOfPalindromes("appal");
returnNumberOfPalindromes("addadaadd");
returnNumberOfPalindromes("madamimadam");
拉出内部功能
仍然专注于checkForPalindrome
,我们可以注意到它现在是确定字符串是否为回文符的有用函数。因此,我们不妨将其从主要功能中脱颖而出,并使其独立。
但是函数返回true
或false
的常见约定是将其命名为is
。在这种情况下,isPalindrome
肯定更有意义。因此,我们更改为:
function isPalindrome (word) {
return word.split('').reverse().join('') === word
}
和
if (isPalindrome(chunk)) {
tally += 1
}
function isPalindrome (word) {
return word.split('').reverse().join('') === word
}
function returnNumberOfPalindromes(word) {
let tally = 0;
for (let index1 = 0; index1 <= word.length; index1++) {
for (let index2 = index1 + 2; index2 <= word.length; index2++) {
let chunk = word.slice(index1,index2);
if (isPalindrome(chunk)) {
tally += 1
}
}
}
return tally;
}
console .log (returnNumberOfPalindromes("addadaadd")); //=> 7
旋转此递归
现在迈出重要一步。我们要使它递归。让我们检查一下主循环在做什么:
for (let index1 = 0; index1 <= word.length; index1++) {
for (let index2 = index1 + 2; index2 <= word.length; index2++) {
let chunk = word.slice(index1,index2);
if (isPalindrome(chunk)) {
tally += 1
}
}
}
此代码在索引中循环两次,找到所有从索引0开始的子字符串,然后所有从索引1开始的子字符串,然后从索引2开始的所有子字符串,依此类推。对于每个子字符串,它都会测试字符串是否是回文,以及如果是的话,我们增加计数。
几乎可以肯定,这种双循环意味着我们还要进行两次递归。因此,我们可以认为它具有两个功能。
一个函数获取一个字符串并查看该字符串的所有前缀(例如,对于“ goat”,前缀将为“ goat”,“ goa”,“ go”和“ g”)并计算数字是回文式的。如果单词长度少于两个字符,则该单词没有回文,并且我们返回0
;否则,如果单词是回文,则返回1
,如果不是,则返回0
,然后添加导致递归调用的结果掉了最后一个字符:
function returnNumberOfPalindromicPrefixes(word) {
if (word .length < 2) {
return 0
}
return (isPalindrome(word) ? 1 : 0) + returnNumberOfPalindromicPrefixes(word.slice(0,-1))
}
第二个函数在字符串的开始处重复出现。同样,如果单词少于两个字符,我们将再次返回0
。否则,我们将调用前缀函数的结果添加到对通过删除第一个字符形成的字符串的递归调用中:
function returnNumberOfPalindromes(word) {
if (word .length < 2) {
return 0
}
return returnNumberOfPalindromicPrefixes(word) + returnNumberOfPalindromes(word.slice(1))
}
function isPalindrome(word) {
return word.split('').reverse().join('') === word
}
function returnNumberOfPalindromicPrefixes(word) {
if (word .length < 2) {
return 0
}
return (isPalindrome(word) ? 1 : 0) + returnNumberOfPalindromicPrefixes(word.slice(0,-1))
}
function returnNumberOfPalindromes(word) {
if (word .length < 2) {
return 0
}
return returnNumberOfPalindromicPrefixes(word) + returnNumberOfPalindromes(word.slice(1))
}
console .log (returnNumberOfPalindromes("kayak"));
console .log (returnNumberOfPalindromes("aya"));
console .log (returnNumberOfPalindromes("appal"));
console .log (returnNumberOfPalindromes("addadaadd"));
console .log (returnNumberOfPalindromes("madamimadam"));
插曲
这时我们有了合理的代码。我们已经将其重构为相当逻辑和清晰的递归函数。 (这也与Applet123的答案非常相似。)
这可能对您(或您的老师)来说足够了。
但是我认为还有更多有用的更改。认为接下来的几个步骤很不错。
功能命名
这听起来有些琐碎,但是名称returnNumberOf<Whatever>
太可怕了。 count<Whatever>
,numberOf<Whatever>
可能是sizeOf<Whatever>
会更好。
我将选择第一个,这将为我们提供名称countPalindromicPrefixes
和countPalindromes
。
function isPalindrome (word) {
return word.split('').reverse().join('') === word
}
function countPalindromicPrefixes(word) {
if (word .length < 2) {
return 0
}
return (isPalindrome(word) ? 1 : 0) + countPalindromicPrefixes(word.slice(0,-1))
}
function countPalindromes(word) {
if (word .length < 2) {
return 0
}
return countPalindromicPrefixes(word) + countPalindromes(word.slice(1))
}
console .log (countPalindromes("kayak"));
console .log (countPalindromes("aya"));
console .log (countPalindromes("appal"));
console .log (countPalindromes("addadaadd"));
console .log (countPalindromes("madamimadam"));
更现代的JS
使用箭头函数而不是函数声明会清理很多代码。 Conditional (ternary) operators 也可以。使用这些,我们可以变成这样:
function countPalindromes(word) {
if (word .length < 2) {
return 0
}
return countPalindromicPrefixes(word) + countPalindromes(word.slice(1))
}
对此:
const countPalindromes = (word) =>
word .length < 2 ? 0 : countPalindromicPrefixes(word) + countPalindromes(word.slice(1))
并对其他功能执行类似的操作。
const isPalindrome = (word) =>
word.split('').reverse().join('') === word
const countPalindromicPrefixes = (word) =>
word.length < 2 ? 0 : (isPalindrome(word) ? 1 : 0) + countPalindromicPrefixes(word.slice(0,-1))
const countPalindromes = (word) =>
word.length < 2 ? 0 : countPalindromicPrefixes(word) + countPalindromes(word.slice(1))
console .log (countPalindromes ("kayak"));
console .log (countPalindromes ("aya"));
console .log (countPalindromes ("appal"));
console .log (countPalindromes ("addadaadd"));
console .log (countPalindromes ("madamimadam"));
参数命名
我们在此处使用参数名称“ word”作为功能。但是,我们真的要说一句话吗?句子“一个人,一个计划,一条运河:巴拿马!”被广泛描述为回文。 (虽然它不适合我们的测试,但我们可以通过在执行当前测试之前降低值的大小并删除任何非字母字符来轻松地对其进行修复。)事实上,我在其中添加了一个测试用例,以捕获另一个经典回文集的版本,“夫人,我是亚当。”
那么输入的内容可能是单词还是句子?也许还有一个短语?人们有时会说数字是回文的,例如2112。所以“单词”不是这些的正确名称。我们的似乎采用了任意字符串。因此,我们应该将参数重命名为捕获该参数的名称。也许是“ str”?也许是“弦”?我将提出一些更激进的建议。我们真的不知道类型,它是一个单行函数,因此在这里仍然具有“字符串”味道的单字符名称似乎是完全合理的。我将选择“ s”。请注意,有些人讨厌这个想法。那可能包括您的教练,所以要小心。 (如果有人反对,您总是可以将他们引向John DeGoes撰写的有关此想法的文章Descriptive Variable Names: A Code Smell。但是他们可能仍然会开始向您扔东西,所以要小心。)
所以我们可以这样写:
const countPalindromicPrefixes = (s) =>
s.length < 2 ? 0 : (isPalindrome(s) ? 1 : 0) + countPalindromicPrefixes(s.slice(0,-1))
const isPalindrome = (s) =>
s.split('').reverse ().join('') === s
const countPalindromicPrefixes = (s) =>
s.length < 2 ? 0 : (isPalindrome(s) ? 1 : 0) + countPalindromicPrefixes(s.slice(0,-1))
const countPalindromes = (s) =>
s.length < 2 ? 0 : countPalindromicPrefixes(s) + countPalindromes(s .slice(1))
console .log (countPalindromes ("kayak"));
console .log (countPalindromes ("aya"));
console .log (countPalindromes ("appal"));
console .log (countPalindromes ("addadaadd"));
console .log (countPalindromes ("madamimadam"));
所有事情都递归
除了类分配外,我不会执行以下操作。我们已经有了isPalindrome
的工作版本,该版本可读且有效。但是,由于这显然是递归分配,因此最好编写isPalindrome
的递归版本。
为此,我们可以通过以下方式来递归地考虑这个问题:如果第一个和最后一个字符匹配,则字符串是一个回文,而中间的字符串也是一个回文。空字符串在这里被视为回文,单字符字符串也被视为回文。 (在这种情况下,第一个和最后一个字符指向同一位置,所以它们当然是相同的。)我们可以这样写:
const isPalindrome = (s) =>
s.length == 0 ? true : s[0] === s[s.length - 1] && isPalindrome(s.slice(1,-1))
请注意,对于此处的一般问题,我们并未将1个字符的子字符串视为回文,但这是在主要函数中处理的。 isPalindrome
很高兴地这样报告他们。
const isPalindrome = (s) =>
s.length == 0 ? true : s[0] === s[s.length - 1] && isPalindrome(s.slice(1,-1))
const countPalindromes = (s) =>
s.length < 2 ? 0 : countPalindromicPrefixes(s) + countPalindromes(s.slice(1))
console .log (countPalindromes ("kayak"));
console .log (countPalindromes ("aya"));
console .log (countPalindromes ("appal"));
console .log (countPalindromes ("addadaadd"));
console .log (countPalindromes ("madamimadam"));
,
我不会递归地写checkForPalindrome
,因为这样做很简单:
function checkForPalindrome(str) {
// we can use .split("") to convert to an array of characters
return str.split("").reverse().join("") == str;
}
function palindromesAtStart(str) {
// lengths 0 and 1 can't be palindromes
if (str.length < 2) {
return 0;
}
// if it's a palindrome add 1
const diff = checkForPalindrome(str) ? 1 : 0;
// now shorten the string and check again
return diff + palindromesAtStart(str.slice(0,-1));
}
function returnNumberOfPalindromes(str) {
if (str.length < 2) {
return 0;
}
// total palindromes is palindromes at start plus palindromes not at start
return palindromesAtStart(str) + returnNumberOfPalindromes(str.slice(1));
}
console.log(returnNumberOfPalindromes("kayak"));
console.log(returnNumberOfPalindromes("aya"));
console.log(returnNumberOfPalindromes("appal"));
console.log(returnNumberOfPalindromes("addadaadd"));
从本质上讲,字符串中的回文可以包含第一个索引,也可以不包含第一个索引。因此,我们可以编写一个简单的递归函数(palindromesAtStart
)来计算包含第一个索引的回文数,然后将其添加到不包含第一个索引的回文数中。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。