2016-01-19 67 views
1

我在这里是新的,我试图将递归函数转换为迭代。如何将此递归函数转换为迭代函数? (在JavaScript中的var_dump)

我一直在阅读有关该主题的一些日子,我发现一些好的网站给了我想法尝试。但到目前为止,我找不到一个可行的解决方案。

这是我想要转换的代码:

function dump(value, recursionLevel) { 
    if(!recursionLevel) recursionLevel = 0; 

    var vType = typeof value; 
    var out = vType; 

    switch (vType) { 
    case "number": 
    case "boolean": 
     out += ": " + value; 
     break; 
    case "string": 
     out += "(" + value.length + '): "' + value + '"'; 
     break; 
    case "object": 
     if (value === null) { 
     out = "null"; 
     } 
     else if(Array.isArray(value)) { 
     out = 'array(' + value.length + '): {\n'; 
     for(var i = 0; i < value.length; i++) { 
      out += ' '.repeat(recursionLevel) + " [" + i + "]: " + dump(value[i], recursionLevel + 1) + "\n"; 
     } 
     out += ' '.repeat(recursionLevel) + "}"; 
     } 
     break; 
    } 

    return out; 
} 

我无法找到一个方法来转换它,主要是beceause的for循环。任何形式的帮助将不胜感激。

非常感谢!

编辑:

这是代码的最终结果:

递归版本:

function varDumpR(value, indentationLevel) { 
    // CONFIGURABLE. 
    indentationSpaces = ' '; 

    indentationLevel = indentationLevel || 0; 

    // https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ 
    var valueType = ({}).toString.call(value).match(/\s([a-zA-Z]+)/)[1]; 
    var output = ''; 

    if(valueType === 'Null' || valueType === 'Undefined') { 
    output += valueType.toLowerCase(); 
    } 
    else { 
    // This variable is used to distinguish between "primitive" and "object" String/Boolean/Number. 
    var isObject = true; 

    switch(valueType) { 
     case 'Function': 
     output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + '('; 

     var functionLines = value.toString().split('\n'); 
     for(var i = 0, functionLinesLength = functionLines.length; i < functionLinesLength; i++) { 
      // Don't indent the first line. Indent all lines with 2 additional levels except the last one, which is indented with 1 additional level. 
      output += (i != 0 ? '\n' + indentationSpaces.repeat(indentationLevel + 1 + (+(i > 0 && i < functionLinesLength - 1))) : '') + functionLines[i].trim(); 
     } 

     output += ')\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
     break; 
     case 'Arguments': 
     case 'Array': 
     output += valueType + '(' + value.length + ') {\n'; 
     break; 
     case 'String': 
     isObject = value instanceof String; 

     if(isObject) { 
      output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + '(' + value.length + ') "' + value + '"\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
     } 
     else { 
      output += valueType + '(' + value.length + ') "' + value + '"'; // Beware of Strings containing "s. 
     } 
     break; 
     case 'Boolean': 
     isObject = value instanceof Boolean; 
     if(isObject) { 
      output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + '(' + value.toString() + ')\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
     } 
     else { 
      output += valueType + '(' + value.toString() + ')'; 
     } 
     break; 
     case 'Number': 
     isObject = value instanceof Number; 

     // http://cwestblog.com/2014/02/25/javascript-testing-for-negative-zero/ 
     var number = value.valueOf(); 
     var isNegative = (((number = +number) || 1/number) < 0); 
     number = number < 0 ? -number : number; 

     var numberValue = ''; 
     // Integer. 
     if(parseInt(number, 10) == parseFloat(number)) { 
      numberValue = 'Integer'; 
     } 
     // NaN, Infinity, -Infinity. 
     else if(!isFinite(number)) { 
      numberValue = 'Number'; 
     } 
     // Float. 
     else { 
      numberValue = 'Float'; 
     } 

     numberValue += '(' + (isNegative ? '-' : '') + number + ')'; 

     if(isObject) { 
      output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + numberValue + '\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
     } 
     else { 
      output += numberValue; 
     } 
     break; 
     case 'Date': 
     case 'RegExp': 
     output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + ' "' + value.toString() + '"\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
     break; 
     // 'Object' 
     // 'Error' 
     // 'Math' 
     // 'JSON' 
     default: 
     output += (valueType === 'Object' ? 'Object': 'Object(' + valueType + ') '); 
     break; 
    } 

    if(isObject) { 
     if(valueType == 'Arguments' || valueType == 'Array') { 
     for(var i = 0, valueLength = value.length; i < valueLength; i++) { 
      output += indentationSpaces.repeat(indentationLevel) + ' [' + i + ']=>\n ' + indentationSpaces.repeat(indentationLevel) + varDumpR(value[i], indentationLevel + 1) + '\n'; 
     } 
     } 
     else { 
     var objectProperties = []; 
     for(var property in value) { 
      objectProperties.push(property); 
     } 

     output += '(' + objectProperties.length + ') {\n'; 

     for(var i = 0, objectPropertiesLength = objectProperties.length; i < objectPropertiesLength; i++) { 
      output += indentationSpaces.repeat(indentationLevel) + ' ["' + objectProperties[i] + '"]=>\n ' + indentationSpaces.repeat(indentationLevel) + varDumpR(value[objectProperties[i]], indentationLevel + 1) + '\n'; 
     } 
     } 

     output += indentationSpaces.repeat(indentationLevel) + '}'; 
    } 
    } 

    return output; 
} 

迭代版本:

function varDumpI(value) { 
    // CONFIGURABLE. 
    indentationSpaces = ' '; 

    var output = ''; 
    var recursionStack = [{value: value, indentationLevel: 0, output: null}]; 

    while(recursionStack.length > 0) { 
    var entry = recursionStack.pop(); 

    if(entry.output) { 
     output += entry.output; 
    } 
    else { 
     value = entry.value; 
     indentationLevel = entry.indentationLevel; 

     // https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ 
     var valueType = ({}).toString.call(value).match(/\s([a-zA-Z]+)/)[1]; 

     if(valueType === 'Null' || valueType === 'Undefined') { 
     output += valueType.toLowerCase(); 
     } 
     else { 
     // This variable is used to distinguish between "primitive" and "object" String/Boolean/Number. 
     var isObject = true; 

     switch(valueType) { 
      case 'Function': 
      output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + '('; 

      var functionLines = value.toString().split('\n'); 
      for(var i = 0, functionLinesLength = functionLines.length; i < functionLinesLength; i++) { 
       // Don't indent the first line. Indent all lines with 2 additional levels except the last one, which is indented with 1 additional level. 
       output += (i != 0 ? '\n' + indentationSpaces.repeat(indentationLevel + 1 + (+(i > 0 && i < functionLinesLength - 1))) : '') + functionLines[i].trim(); 
      } 

      output += ')\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
      break; 
      case 'Arguments': 
      case 'Array': 
      output += valueType + '(' + value.length + ') {\n'; 
      break; 
      case 'String': 
      isObject = value instanceof String; 

      if(isObject) { 
       output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + '(' + value.length + ') "' + value + '"\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
      } 
      else { 
       output += valueType + '(' + value.length + ') "' + value + '"'; // Beware of Strings containing "s. 
      } 
      break; 
      case 'Boolean': 
      isObject = value instanceof Boolean; 
      if(isObject) { 
       output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + '(' + value.toString() + ')\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
      } 
      else { 
       output += valueType + '(' + value.toString() + ')'; 
      } 
      break; 
      case 'Number': 
      isObject = value instanceof Number; 

      // http://cwestblog.com/2014/02/25/javascript-testing-for-negative-zero/ 
      var number = value.valueOf(); 
      var isNegative = (((number = +number) || 1/number) < 0); 
      number = number < 0 ? -number : number; 

      var numberValue = ''; 
      // Integer. 
      if(parseInt(number, 10) == parseFloat(number)) { 
       numberValue = 'Integer'; 
      } 
      // NaN, Infinity, -Infinity. 
      else if(!isFinite(number)) { 
       numberValue = 'Number'; 
      } 
      // Float. 
      else { 
       numberValue = 'Float'; 
      } 

      numberValue += '(' + (isNegative ? '-' : '') + number + ')'; 

      if(isObject) { 
       output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + numberValue + '\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
      } 
      else { 
       output += numberValue; 
      } 
      break; 
      case 'Date': 
      case 'RegExp': 
      output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + ' "' + value.toString() + '"\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
      break; 
      // 'Object' 
      // 'Error' 
      // 'Math' 
      // 'JSON' 
      default: 
      output += (valueType === 'Object' ? 'Object': 'Object(' + valueType + ') '); 
      break; 
     } 

     if(isObject) { 
      recursionStack.push({output: indentationSpaces.repeat(indentationLevel) + '}'}); 

      if(valueType == 'Arguments' || valueType == 'Array') { 
      // Loop through the array in reverse order to maintain the consistency with the recursive function. 
      for(var i = value.length - 1; i >= 0; i--) { 
       recursionStack.push({output: '\n'}); 
       recursionStack.push({value: value[i], indentationLevel: indentationLevel + 1}); 
       recursionStack.push({output: indentationSpaces.repeat(indentationLevel) + ' [' + i + ']=>\n ' + indentationSpaces.repeat(indentationLevel)}); 
      } 
      } 
      else { 
      var objectProperties = []; 
      for(var property in value) { 
       objectProperties.push(property); 
      } 

      output += '(' + objectProperties.length + ') {\n'; 

      // Loop through the object in reverse order to maintain the consistency with the recursive function. 
      for(var i = objectProperties.length - 1; i >= 0; i--) { 
       recursionStack.push({output: '\n'}); 
       recursionStack.push({value: value[objectProperties[i]], indentationLevel: indentationLevel + 1}); 
       recursionStack.push({output: indentationSpaces.repeat(indentationLevel) + ' ["' + objectProperties[i] + '"]=>\n ' + indentationSpaces.repeat(indentationLevel)}); 
      } 
      } 
     } 
     } 
    } 
    } 

    return output; 
} 

接受多个参数的迭代版本:

function varDump() { 
    // CONFIGURABLE. 
    indentationSpaces = ' '; 

    var output = ''; 

    for(arg = 0, argumentsLength = arguments.length; arg < argumentsLength; arg++) { 
    value = arguments[arg]; 
    var recursionStack = [{value: value, indentationLevel: 0, output: null}]; 
    var seenObjects = []; 

    if(arg > 0) { 
     output += '\n'; 
    } 

    while(recursionStack.length > 0) { 
     var entry = recursionStack.pop(); 

     if(entry.output) { 
     output += entry.output; 
     } 
     else { 
     value = entry.value; 
     indentationLevel = entry.indentationLevel; 

     // https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ 
     var valueType = ({}).toString.call(value).match(/\s([a-zA-Z]+)/)[1]; 

     if(seenObjects.indexOf(value) !== -1) { 
      output += '*RECURSION*'; 
     } 
     else if(valueType === 'Null' || valueType === 'Undefined') { 
      output += valueType.toLowerCase(); 
     } 
     else { 
      // This variable is used to distinguish between "primitive" and "object" String/Boolean/Number. 
      var isObject = true; 

      switch(valueType) { 
      case 'Function': 
       output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + '('; 

       var functionLines = value.toString().split('\n'); 
       for(var i = 0, functionLinesLength = functionLines.length; i < functionLinesLength; i++) { 
       // Don't indent the first line. Indent all lines with 2 additional levels except the last one, which is indented with 1 additional level. 
       output += (i != 0 ? '\n' + indentationSpaces.repeat(indentationLevel + 1 + (+(i > 0 && i < functionLinesLength - 1))) : '') + functionLines[i].trim(); 
       } 

       output += ')\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
       break; 
      case 'Arguments': 
      case 'Array': 
       output += valueType + '(' + value.length + ') {\n'; 
       break; 
      case 'String': 
       isObject = value instanceof String; 

       if(isObject) { 
       output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + '(' + value.length + ') "' + value + '"\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
       } 
       else { 
       output += valueType + '(' + value.length + ') "' + value + '"'; // Beware of Strings containing "s. 
       } 
       break; 
      case 'Boolean': 
       isObject = value instanceof Boolean; 
       if(isObject) { 
       output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + '(' + value.toString() + ')\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
       } 
       else { 
       output += valueType + '(' + value.toString() + ')'; 
       } 
       break; 
      case 'Number': 
       isObject = value instanceof Number; 

       // http://cwestblog.com/2014/02/25/javascript-testing-for-negative-zero/ 
       var number = value.valueOf(); 
       var isNegative = (((number = +number) || 1/number) < 0); 
       number = number < 0 ? -number : number; 

       var numberValue = ''; 
       // Integer. 
       if(parseInt(number, 10) == parseFloat(number)) { 
       numberValue = 'Integer'; 
       } 
       // NaN, Infinity, -Infinity. 
       else if(!isFinite(number)) { 
       numberValue = 'Number'; 
       } 
       // Float. 
       else { 
       numberValue = 'Float'; 
       } 

       numberValue += '(' + (isNegative ? '-' : '') + number + ')'; 

       if(isObject) { 
       output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + numberValue + '\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
       } 
       else { 
       output += numberValue; 
       } 
       break; 
      case 'Date': 
      case 'RegExp': 
       output += 'Object(' + '\n' + indentationSpaces.repeat(indentationLevel + 1) + valueType + ' "' + value.toString() + '"\n' + indentationSpaces.repeat(indentationLevel) + ') '; 
       break; 
      // 'Object' 
      // 'Error' 
      // 'Math' 
      // 'JSON' 
      default: 
       output += (valueType === 'Object' ? 'Object': 'Object(' + valueType + ') '); 
       break; 
      } 

      if(isObject) { 
      if(valueType !== 'Math' && valueType !== 'JSON') { 
       seenObjects.push(value); 
      } 

      recursionStack.push({output: indentationSpaces.repeat(indentationLevel) + '}'}); 

      if(valueType == 'Arguments' || valueType == 'Array') { 
       // Loop through the array in reverse order to maintain the consistency with the recursive function. 
       for(var i = value.length - 1; i >= 0; i--) { 
       recursionStack.push({output: '\n'}); 
       recursionStack.push({value: value[i], indentationLevel: indentationLevel + 1}); 
       recursionStack.push({output: indentationSpaces.repeat(indentationLevel) + ' [' + i + ']=>\n ' + indentationSpaces.repeat(indentationLevel)}); 
       } 
      } 
      else { 
       var objectProperties = []; 
       for(var property in value) { 
       objectProperties.push(property); 
       } 

       output += '(' + objectProperties.length + ') {\n'; 

       // Loop through the object in reverse order to maintain the consistency with the recursive function. 
       for(var i = objectProperties.length - 1; i >= 0; i--) { 
       recursionStack.push({output: '\n'}); 
       recursionStack.push({value: value[objectProperties[i]], indentationLevel: indentationLevel + 1}); 
       recursionStack.push({output: indentationSpaces.repeat(indentationLevel) + ' ["' + objectProperties[i] + '"]=>\n ' + indentationSpaces.repeat(indentationLevel)}); 
       } 
      } 
      } 
     } 
     } 
    } 
    } 

    return output; 
} 

测试代码:

(function testVarDump() { 

    var func1 = function(par1, par2) { 
    var sum; 
    sum = par1 + par2; 
    return sum; 
} 

    function func2(par1, par2) { 
    var sum; 
    sum = par1 + par2; 
    return sum; 
} 

    var date = new Date(2016, 1, 21); 
    date.prop = 'date'; 

    var regex = new RegExp(/a/); 
    regex.prop = 'regex'; 

    var error = new Error('ERROR'); 
    error.prop = 'error'; 

    var math = Math; 
    math.prop = 'math'; 

    var json = JSON; 
    json.prop = 'json'; 

    var circular = []; 
    circular[0] = 0; 
    circular[1] = circular; 

    var test = [ 
    'a', String('a'), new String('a'), 
    true, Boolean(true), new Boolean(true), 
    12, 12.6, 0, -0, NaN, Infinity, -Infinity, 
    Number(12), Number(12.6), Number(0), Number(-0), Number(NaN), Number(Infinity), Number(-Infinity), 
    new Number(12), new Number(12.6), new Number(0), new Number(-0), new Number(NaN), new Number(Infinity), new Number(-Infinity), 
    null, undefined, 

    ['a', String('a'), new String('a'), 
    true, Boolean(true), new Boolean(true), 
    12, 12.6, 0, -0, NaN, Infinity, -Infinity, 
    Number(12), Number(12.6), Number(0), Number(-0), Number(NaN), Number(Infinity), Number(-Infinity), 
    new Number(12), new Number(12.6), new Number(0), new Number(-0), new Number(NaN), new Number(Infinity), new Number(-Infinity), 
    null, undefined], 

    { 
     a: [{aa: 1, bb: 2}, Object(), new Object()], 
     b: [func1, func2, new Function, function() { return false; }, Function(), new Function()], 
     c: [arguments], 
     d: [date, Date(), new Date(2016, 1, 21)], 
     e: [regex, /a/, RegExp(/a/), new RegExp(/a/)], 
     f: [error, Error('ERROR'), new Error('ERROR')], 
     g: [math, Math], 
     h: [json, JSON] 
    }, 
    ] 

    console.log(varDumpR(test)); 
    console.log(varDumpI(test)); 
    console.log(varDump(test, circular)); 
})('arg1', 'arg2'); 

注:

原代码是从这里取:

它结束了是真的类似这样的(我没有抄袭,但都是真的类似):

+0

您试过的代码在哪里? – Gavriel

+0

你能举一个你的“价值”的例子吗? – Hornth

+0

我想删除递归的原因是因为我想能够将多个变量传递给函数,如php函数。当第二个参数通过递归传递时,也可以进行某种操作,比如['ignore it',recursionLevel + 1],这样你就知道是否递归调用它,但这是不好的编程。毕竟,我不得不说,这是我想做的事情,因为我认为这一定是可能的,也许不那么难,而且学习新事物也很好,我认为这是一个很好的挑战。 – Pegasus123

回答

1

这听起来很像你为家庭作业所要做的那种问题。为了解决这个问题,你需要一个堆栈。在JavaScript中,堆栈由数组实现 - 它们已经有了你需要的push()和pop()方法。

这里有一个小程序,它在JavaScript概念中转储(不完全是JSON,但类似) - 将输出格式更改为您的需要作为练习。

function dump(value) { 
    var stack=[{value:value}]; 
    var out = ""; 
    while (stack.length>0) { 
    var entry = stack.pop(); 
    if (entry.output) { 
     out+=entry.output; 
    } 
    else { 
     value = entry.value; 
     switch(typeof value) { 
     case "number": 
     case "boolean": 
      out += value; 
      break; 
     case "string": 
      out += '"'+value+'"'; // beware of Strings containing "s 
      break; 
     case "object": 
      if (value === null) { 
       out += "null"; 
      } 
      else if (Array.isArray(value)) { 
       out += "["; 
       stack.push({output:"]"}); 
       for (var i=value.length-1; i>=0; i--) { 
       stack.push({value: value[i]}); 
       if (i>0) { 
        stack.push({output:","}); 
       } 
       } 
      } 
      else { 
       out += "{"; 
       stack.push({output:"}"}); 
       var s = ""; 
       var f; 
       for (f in value) { 
       if (s) { 
        stack.push({output: s}); 
       } 
       stack.push({value: value[f]}); 
       stack.push({output: f+":"}); 
       s = ","; 
       } 
      } 
      break; 
     } 
    } 
    } 
    return out; 
} 
+0

非常感谢你,其实这是我为了娱乐而做的事情,它不是与学校相关的东西。我会尝试通过缩进将您的代码转换为我想要的格式。谢谢。 – Pegasus123

+0

很高兴看到你喜欢它。也许你想把它标记为接受的答案? –

+0

对不起,迟到了,我没有时间,直到今天 – Pegasus123