如何将此递归函数转换为迭代函数? (javascript中的var_dump)

时间:2016-01-19 09:59:12

标签: javascript function recursion iteration

我是新来的,我正在尝试将递归函数转换为迭代函数。

我已经读了几天这个主题了,我找到了一些很好的网站给了我尝试的想法。但到目前为止我找不到合适的解决方案。

这是我要转换的代码:

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;
}

我无法找到转换它的方法,主要是因为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');

备注:

原始代码来自这里:

它最终与此类似(我没有复制它,但两者都非常相似):

1 个答案:

答案 0 :(得分: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;
}