[英]How to convert this recursive function to a iterative one? (var_dump in javascript)
I'm new here and I'm trying to convert a recursive function to iterative. 我在这里是新手,正在尝试将递归函数转换为迭代函数。
I have been reading about the subject for some days and I have found some good sites that gave me ideas to try. 我已经阅读了几天有关该主题的文章,并且发现了一些不错的站点,这些站点使我可以尝试一些想法。 But I couldn't find a working solution so far. 但到目前为止,我找不到可行的解决方案。
This is the code I'm trying to convert: 这是我要转换的代码:
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;
}
I can't find a way to convert it, mostly beceause of the for loop. 我找不到转换它的方法,主要是因为for循环。 Any kind of help will be much appreciated. 任何帮助将不胜感激。
Thank you so much! 非常感谢!
EDIT: 编辑:
This is the final result of the code: 这是代码的最终结果:
Recursive version: 递归版本:
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;
}
Iterative version: 迭代版本:
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;
}
Iterative version that accepts multiple parameters: 接受多个参数的迭代版本:
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;
}
Test code: 测试代码:
(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');
NOTES: 笔记:
The original code was taken from here: 原始代码是从这里获取的:
It ended up being really similar to this one (I didn't copy it, but both are really similar): 最终确实与此相似(我没有复制它,但两者确实相似):
This sounds a lot like the kind of problem you have to do for homework. 这听起来很像您要做家庭作业时遇到的问题。 To solve that, you are going to need a stack. 为了解决这个问题,您将需要一个堆栈。 In JavaScript, stacks are implemented by arrays - they already have the push() and pop() method you need. 在JavaScript中,堆栈是通过数组实现的-它们已经具有所需的push()和pop()方法。
Here is a little program that does the dump in JavaScript notion (not exactly JSON, but similar) - changing the output format to your needs is left as an exercise. 这是一个以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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.