在功能递归过程中维护字符串变量-JavaScript



我创建了一个简单的功能,该功能应该将数字转换为罗马数字。看来一切都正常工作,除了一件事。

通过我的代码中的每一个递归,保存罗马数字的字符串已重置为"。

通过功能中的递归维护这样的变量的最佳实践是什么?

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

例如,以转换为罗马数字的数字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));

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

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

递归是一种功能遗产,因此使用功能样式的方式将产生最佳结果。罗马数字算法是当我第一次看到功能风格表达时真正震惊的事情之一:codereview.se:转换为罗马数字。

@sdcvc提供了一个美丽的编码

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
  )

最新更新