在javascript中将循环函数转换为递归函数

时间:2018-02-02 18:40:44

标签: javascript function loops recursion tail-recursion

我试图教自己如何编写递归函数,有人建议尝试将循环转换为递归。所以我试图将第一个for循环函数更改为递归函数。这是我的代码:

// Function that uses for loop.
function onlyOne(value1, value2, value3) {
    var array = [value1, value2, value3];
    var count = 0;
    for(var i = 0; i < array.length; i++) {
        if(!!array[i] === true) {
            count ++;
        }
    } if(count === 1) {
        return true;
    } else {
        return false;
    }
}

// Function that uses recursion.
function onlyOne2(a, b, c) {
    var array = [a, b, c];
    var count = 0;
    var numTrue = 0;
    if(!!array[count] === true) {
        numTrue++;
    }
    if(count === array.length-1) {
        if(numTrue === 1) {
            return true;
        } else {
            return false;
        }
    }else {
        count++;
        return onlyOne2(a, b, c);
    }
}

console.log(onlyOne2(true, false, false));

如果只有一个参数是真实的,那么每个函数的目的是返回true。否则该函数返回false。 for循环功能正常工作。但是,当我使用递归函数时,我得到错误:超出了最大调用堆栈大小。我想知道我做错了什么。谢谢你的帮助!

9 个答案:

答案 0 :(得分:1)

您的方法存在一些问题

  • if(!!array[i] === true)直接使用布尔值:if(array[i])

您要求true值返回true

if (count === 1) { // <- Use this comparison to return the specific value.
    return true;
    ^
} else {
    return false;
    ^
}

直接返回比较:return count === 1;

在函数onlyOne2中,停止循环的“递归情况”不正确。

 count === array.length-1
 ^         ^ 

您必须使用索引i

请查看包含这些修补程序的代码段

// Function that uses for loop.
function onlyOne(value1, value2, value3) {
  var array = [value1, value2, value3];
  var count = 0;
  for (var i = 0; i < array.length; i++) {
    if (array[i]) {
      count++;
    }
  }

  return count === 1;
}

console.log(onlyOne(true, false, false));

function onlyOne2(value1, value2, value3, count, i) {
  var array = [value1, value2, value3];

  if (i === array.length) return count === 1;
  
  if (array[i]) count++;

  return onlyOne2(value1, value2, value3, count, ++i);
}

console.log(onlyOne2(true, false, false, 0, 0));

请参阅?现在正在使用递归来处理循环。

答案 1 :(得分:1)

您可以为需要的truthy值和rest parameters ...计算递归函数的计数器。

它适用于tail recursion和任意数量的参数。

&#13;
&#13;
function only(count, v, ...rest) {
    if (v) {                       // check value
        if (!count) {              // check count is zero
            return false;          // because found more truthy than needed
        }
        count--;                   // decrement count
    }
    if (!rest.length) {            // check length is zero
        return !count;             // return not count
    }
    return only(count, ...rest);   // tail call with count and rest
}

console.log(only(1, false, false, false)); // false
console.log(only(1, false, false, true));  //  true
console.log(only(1, false, true, false));  //  true
console.log(only(1, false, true, true));   // false
console.log(only(1, true, false, false));  //  true
console.log(only(1, true, false, true));   // false
console.log(only(1, true, true, false));   // false
console.log(only(1, true, true, true));    // false
console.log('-----');
console.log(only(2, false, false, false)); // false
console.log(only(2, false, false, true));  // false
console.log(only(2, false, true, false));  // false
console.log(only(2, false, true, true));   //  true
console.log(only(2, true, false, false));  // false
console.log(only(2, true, false, true));   //  true
console.log(only(2, true, true, false));   //  true
console.log(only(2, true, true, true));    // false
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

答案 2 :(得分:0)

上面代码的问题是每次函数递归countnumTrue时再次声明,因此发生无限循环,这是正确的代码(声明count和{ {1}}全球:

numTrue

答案 3 :(得分:0)

这很有趣。好的,所以我们有一个函数,如果数组中的其中一个项目是真的,则返回true。让我们考虑一下递归的基本要求。

基本情况,数组为空:

if (!array.length)
  return false;

基本情况,数组中只有一个元素:

if (array.length == 1)
  return !!array[0] === true

否则:

let next = f(rest-of-array)

if (!!array[0] === true)
  return !next
else
  return next

JavaScript代码:

function f(arr, i){
  // Same as !array.length
  if (i == arr.length)
    return false;

  // Same as array.length == 1
  if (i == arr.length - 1)
    return !!arr[i]

  // Same as f(rest-of-array)
  let next = f(arr, i + 1)

  if (!!arr[i])
    return !next

  return next
}

输出:

   f([undefined,2,0], 0)
=> true
   f([1,2,undefined], 0)
=> false
   f([undefined,false,1], 0)
=> true
   f([5,false,false], 0)
=> true

答案 4 :(得分:0)

您可以将任何for循环转换为递归:

let a=0, b=1,
for (let n=5; n > 0; n--) {
  const tmpa = a;
  a = b; 
  b += tmpa;
}

此处abn更改,原因是具有第6个斐波纳契数的a值。所以我们将它们作为参数并返回:

function forLoop(n, a, b) {
  if(n > 0) {
    return forLoop(n - 1, b, a + b);
  }
  return a;
}
const a = forLoop(5, 0, 1);

因此对于使用for循环的函数

function onlyOne(value1, value2, value3) {
  const array = [value1, value2, value3];
  let count = 0;
  for(var i = 0; i < array.length; i++) {
    if(!!array[i] === true) {
      count ++;
    }
  } 
  if(count === 1) {
    return true;
  }
  return false;
}

您要更改的绑定是counti

function onlyOne(value1, value2, value3) {
  function iterate(count, i) {
    if (i < array.length) {
      if (!!array[i] === true) {
        return iterate(count + 1, i + 1);
      }
      return iterate(count, i + 1);
    }
    return count;
  }
  const array = [value1, value2, value3];
  const count = iterate(0, 0);
  if(count === 1) {
    return true;
  }
  return false;
}

由于你只有3个元素,这可能已经足够好但想象你想为数组实现这样的事情,你应该计划尽可能快地返回。在for版本中,它看起来像这样:

function onlyOne(arr) {
  let count = 0;
  const len = array.length; // cache
  for (index = 0; index < len; index++) {
    if (arr[index]) {
      count++; 
      if(count > 1) return false; // early return
    }
  }
  return count === 1;
}

与递归相同:

function onlyOne(arr) {
  function iterate (index, count) {
    if (index < len && count <= 1) {
      return iterate(
        index + 1, 
        count + (array[index] ? 1 : 0)
      );
    }
    return count;
  }
  const len = array.length; // cache
  const count = iterate(0, 0);
  return count === 1;
}

注意它并不完全相同,因为我们期望迭代计数,return只返回被调用者。因此,这里的早期返回只是不迭代更多的值,但我们仍然返回一个作为最终结果主题的计数。您可以使用for代替breakreturn中执行相同操作。

同样适用于any / some,它应该返回第一个true和all,它应该为第一个false值返回false。当它无法改变答案时,迭代每个元素都没有意义。

答案 5 :(得分:0)

你不妨试试这个。你不需要改变你的函数结构或任何东西,它可以使用任意数量的参数。

function onlyOne(a,b,c){
 let arr = Array.from(arguments);
 let count = 0;
 let item = arr.shift();
 if(true === item){
    ++count;
 }
 if(arr.length > 0) {
    count += onlyOne.apply(this,arr);
    return count == 1;
 }
 return count;
}

答案 6 :(得分:0)

我建议从一个更简单的例子开始:

function logRecurse(i, arr) {
    if (i < arr.length) {
        console.log(arr[i]);
        logRecurse(i+1, arr);
    }
}

var array = [false, false, true, false];
logRecurse(0, array);

然后添加return语句:

function truthyRecurse(i, arr) {
    if (i < arr.length) {
         if (arr[i]) { // could be just: return arr[i] || truthyRecurse(...)
            return true;
         } else {
            return truthyRecurse(i+1, arr);
         }
    }
    return false;
}

var array = [false, false, true, false];
console.log(truthyRecurse(0, array));

但递归的真正用处在于目录结构。下一步吧!您可以将数组放入数组中来模拟它:

function logFiles(dir) {
    // log filename (typeof string) or recurse into dir
}

var dir = [
    "file1",
    "file2",
    [ // sub dir
        "subfile1",
        [ // sub-sub dir
            "sub-subfile1",
            "sub-subfile2",
            "sub-subfile3",
        ],
        "subfile2",
    ]
];

logFiles(dir);

答案 7 :(得分:0)

这是另一种解决方法 - 我最近写了另一个答案,旨在帮助学习者使用递归来实现高级思维。这里使用的技术是详细写的。如果您有兴趣,可以read it here

const None =
  Symbol ()

const loop = ([ x = None, ...xs ], foundTruthy = false) =>
  x === None
    ? foundTruthy
    : Boolean (x)
      ? foundTruthy
        ? false
        : loop (xs, true)
      : loop (xs, foundTruthy)

const onlyOne = (...xs) =>
  loop (xs, false)

console.log
  ( onlyOne ()                 // false
  , onlyOne (0)                // false
  , onlyOne (1)                // true
  , onlyOne (0, '', false, 1)  // true
  , onlyOne (0, 0, 0, true, 1) // false
  )

注意loop可以是通用的,以便onlyOne不需要依赖专门的助手 - mind the stack overflow

const None =
  Symbol ()

const always = x => _ =>
  x

const recur = (...values) =>
  ({ recur, values })

const loop = (f = always (null) , acc = f ()) =>
  acc && acc.recur === recur
    ? loop (f, f (...acc.values))
    : acc

const onlyOne = (...list) =>
  loop (([ x = None, ...xs ] = list, foundTruthy = false) =>
    x === None
      ? foundTruthy
      : Boolean (x)
        ? foundTruthy
          ? false
          : recur (xs, true)
        : recur (xs, foundTruthy))

console.log
  ( onlyOne ()                 // false
  , onlyOne (0)                // false
  , onlyOne (1)                // true
  , onlyOne (0, '', false, 1)  // true
  , onlyOne (0, 0, 0, true, 1) // false
  )

Nina的答案是函数参数如何降低程序复杂性的一个很好的例子 - 这个改编显示了如何用函数表达式编写相同的程序

const None =
  Symbol ()

const only = (count = 1, x = None, ...xs) =>
  x === None
    ? count === 0
    : Boolean (x)
      ? only (count - 1, ...xs)
      : only (count, ...xs)
      
console.log
  ( only ()                           // false
  , '---'
  , only (0)                          // true
  , only (0, false)                   // true
  , only (0, true)                    // false
  , '---'
  , only (1)                          // false
  , only (1, false)                   // false
  , only (1, false, true)             // true
  , only (1, false, true, true)       // false
  , '---'
  , only (2, false, true)             // false
  , only (2, false, true, true)       // true
  , only (2, false, true, true, true) // false
  )

答案 8 :(得分:0)

你以错误的方式解决这个问题。您正在尝试将整个函数转换为递归函数。相反,正如所建议的那样,您只需要将循环转换为递归函数:

  

我试图教自己如何编写递归函数,有人建议尝试将循环转换为递归。

所以,让我们先看看你原来的功能。我为你清理了一下:

&#13;
&#13;
sticky
&#13;
&#13;
&#13;

请注意,我注释了循环的状态和循环变量。这些将是我们的递归函数的参数。我们将使用状态的初始值和循环变量作为输入调用递归函数来替换assert("Test 1", onlyOne(false, 0, "") === false); assert("Test 2", onlyOne(false, 0, "x") === true); assert("Test 3", onlyOne(false, 1, "") === true); assert("Test 4", onlyOne(false, 1, "x") === false); assert("Test 5", onlyOne(true, 0, "") === true); assert("Test 6", onlyOne(true, 0, "x") === false); assert("Test 7", onlyOne(true, 1, "") === false); assert("Test 8", onlyOne(true, 1, "x") === false); function onlyOne(a, b, c) { const array = [a, b, c]; // +-- state of the loop // | +-- initial value of the state // | | // v v var count = 0; // +-- loop variant // | +-- initial value of the loop variant // | | // v v for (let i = 0; i < 3; i++) if (array[i]) count++; return count === 1; } function assert(test, condition) { console.log(test, condition ? "passed" : "failed"); }循环。递归函数的结果将是循环的最终状态:

&#13;
&#13;
for
&#13;
&#13;
&#13;

希望您现在知道如何将任何循环转换为递归函数。