在函数递归期间维护字符串变量 - JavaScript

时间:2018-03-30 23:45:32

标签: javascript string variables recursion scope

我创建了一个简单的函数,可以将数字转换为罗马数字。除了一件事,似乎一切都正常。

通过我的代码中的每次递归,包含罗马数字的字符串将重置为""。

通过函数递归来维护这样的变量的最佳实践是什么?

我尝试声明变量romanStr =""在全局范围内,并删除程序中的条件声明,当然这是有效的。但我知道这是最糟糕的做法。

例如,取数字1234,转换为罗马数字是" MCCXXXIV"。我的程序只返回" IV",最后一次递归的结果。



function convertToRoman(num) {
  console.log(`START FUNCTION FROM THE BEGINNING`);
  console.log(`current num: ${num}`);
  if (typeof romanStr === "undefined") {
    var romanStr = "";
  }
  const bNumbers = [1000, 500, 100, 50, 10, 5, 1];
  const romanSymbols = {
    0: ["M"],
    2: ["C", "D", "M"],
    4: ["X", "L", "C"],
    6: ["I", "V", "X"]
  };
  const arraySelector = arrNum => num >= arrNum;
  let symbolSetIndex = bNumbers.findIndex(arraySelector);
  console.log(`symbolSetIndex: ${symbolSetIndex}`);
  let symbolSet = romanSymbols[symbolSetIndex];
  console.log(`symbolSet: [${symbolSet}]`);
  let numString = num.toString();
  let numeral = parseInt(numString[0]);
  console.log(`numeral: ${numeral}`);
  let nextNum = parseInt(numString.substr(1));
  console.log(`nextNum: ${nextNum}`);

  // CONDITIONAL STATEMENTS //
  if (symbolSetIndex === 0) {
    for (let i = 1; i <= numeral; i++) {
      romanStr = `${romanStr}${symbolSet[0]}`;
    }
    return convertToRoman(nextNum);
  }
  if (numeral < 4) {
    for (let i = 1; i <= numeral; i++) {
      romanStr = `${romanStr}${symbolSet[0]}`;
    }
  }
  if (numeral === 4) {
    romanStr = `${romanStr}${symbolSet[0]}${symbolSet[1]}`;
  }
  if (numeral === 5) {
    romanStr = `${romanStr}${symbolSet[1]}`;
  }
  if (numeral === 6) {
    romanStr = `${romanStr}${symbolSet[1]}${symbolSet[0]}`;
  }
  if (numeral > 6) {
    romanStr = `${romanStr}${symbolSet[1]}`; // requires the 5 numeral first
    for (let i = 1; i <= numeral - 6; i++) {
      romanStr = `${romanStr}${symbolSet[0]}`;
    }
  }
  if (numeral === 9) {
    romanStr = `${romanStr}${symbolSet[2]}${symbolSet[1]}`;
  }

  if (numString.length === 1) {
    return romanStr;
  }
  return convertToRoman(nextNum);
}

console.log(convertToRoman(5214));
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:2)

您无需通过所有调用保留变量。将当前值连接到递归返回的值。

function convertToRoman(num) {
  console.log(`START FUNCTION FROM THE BEGINNING`);
  console.log(`current num: ${num}`);
  var romanStr;
  const bNumbers = [1000, 500, 100, 50, 10, 5, 1];
  const romanSymbols = {
    0: ["M"],
    2: ["C", "D", "M"],
    4: ["X", "L", "C"],
    6: ["I", "V", "X"]
  };
  const arraySelector = arrNum => num >= arrNum;
  let symbolSetIndex = bNumbers.findIndex(arraySelector);
  console.log(`symbolSetIndex: ${symbolSetIndex}`);
  let symbolSet = romanSymbols[symbolSetIndex];
  console.log(`symbolSet: [${symbolSet}]`);
  let numString = num.toString();
  let numeral = parseInt(numString[0]);
  console.log(`numeral: ${numeral}`);
  let nextNum = parseInt(numString.substr(1));
  console.log(`nextNum: ${nextNum}`);

  // CONDITIONAL STATEMENTS //
  if (symbolSetIndex === 0) {
    for (let i = 1; i <= numeral; i++) {
      romanStr = `${romanStr}${symbolSet[0]}`;
    }
    return romanStr + convertToRoman(nextNum);
  }
  if (numeral < 4) {
    for (let i = 1; i <= numeral; i++) {
      romanStr = `${romanStr}${symbolSet[0]}`;
    }
  }
  if (numeral === 4) {
    romanStr = `${romanStr}${symbolSet[0]}${symbolSet[1]}`;
  }
  if (numeral === 5) {
    romanStr = `${romanStr}${symbolSet[1]}`;
  }
  if (numeral === 6) {
    romanStr = `${romanStr}${symbolSet[1]}${symbolSet[0]}`;
  }
  if (numeral > 6) {
    romanStr = `${romanStr}${symbolSet[1]}`; // requires the 5 numeral first
    for (let i = 1; i <= numeral - 6; i++) {
      romanStr = `${romanStr}${symbolSet[0]}`;
    }
  }
  if (numeral === 9) {
    romanStr = `${romanStr}${symbolSet[2]}${symbolSet[1]}`;
  }

  if (numString.length === 1) {
    return romanStr;
  }
  return romanStr + convertToRoman(nextNum);
}

console.log(convertToRoman(5214));

答案 1 :(得分:1)

递归是一种功能性遗产,因此将其与功能风格一起使用将产生最佳效果。罗马数字算法是我第一次看到它以功能风格表达时真正引起我注意的事情之一:CodeReview.SE: Converting to Roman Numerals

@sdcvvc提供了精美的编码

toRoman :: Integer -> String
toRoman 0 = "N"
toRoman x | x > 0 = snd $ foldl f (x,[]) convMap
  where f (n,s) (rn, rs) = (l, s ++ concat (genericReplicate k rs))
              where (k,l) = divMod n rn

简单性确实令人惊讶。我不能相信上面提供的算法的任何部分,但我可以为你翻译成JavaScript。

我之所以这样,是因为它向您展示了一种从完全不同的角度解决问题的方法。 CodeReview主题提供的其他答案提供了更深入的见解。我强烈建议你检查一下:D

const divMod = (n, d, k) =>
  k (n / d >> 0, n % d)

const foldl = (f, init, xs) =>
  xs.reduce (f, init)

const replicate = (n, s) =>
  s.repeat (n)

const snd = ([ _, x ]) =>
  x

const convMap =
  [ [1000,"M"], [900,"CM"], [500,"D"], [400,"CD"], [100,"C"]
  , [90,"XC"], [50,"L"], [40,"XL"], [10,"X"], [9,"IX"], [5,"V"]
  , [4,"IV"], [1,"I"]
  ]

const toRoman = (x = 0) =>
  x === 0
    ? "N"
    : snd ( foldl ( ([ n, s ], [ rn, rs ]) =>
                      divMod (n, rn, (k, l) =>
                        [ l, s + replicate (k, rs) ])
                  , [ x, [] ]
                  , convMap
                  )
          )

console.log
  ( toRoman (0)       // N
  , toRoman (7)       // VII
  , toRoman (66)      // LXVI
  , toRoman (99)      // XCIX
  , toRoman (1984)    // MCMLXXXIV
  )