Калькулятор реакций: Как предотвратить появление нескольких десятичных знаков в числе с помощью вычислителя математических выражений?

#javascript #reactjs #react-hooks #calculator

Вопрос:

Как следует из названия, я использую библиотеку математических выражений для выполнения своих вычислений, но у меня возникают проблемы с пониманием логики десятичных знаков и с тем, как предотвратить две десятичные дроби в одном числе внутри массива. я выделил ту часть, которая вызывает у меня проблемы. Вот ссылка на мою песочницу с кодом https://codesandbox.io/s/youthful-frost-2s6v6?file=/src/HookClaculator.js:181-2221

   const [input, setInput] = useState([0]);
  const [notANum, setNotANum] = useState("");
  const [AC, setAC] = useState("");

  const handleInputOneClick = (e) => {
    let checkInput = input;
    checkInput = [...checkInput, parseInt(e, 10)];
    console.log(checkInput);

    if (checkInput[1] !== 0) {
      setInput([...input, parseInt(e, 10)]);
      if (input.length > 23) {
        console.log("exceeded 23");
        setInput(["MAX DIGIT LIMIT REACHED"]);
        setTimeout(() => {
          setInput([0]);
        }, 500);
      }
    }
  };

  const handleSymbolClick = (e) => {
    if (Number.isInteger(input[input.length - 1])) {
      setInput([...input, e]);
    } else if (input[input.length - 1] === ".") {
      setInput([...input, e]);
    } else if (typeof input[input.length - 1] === "string") {
      input.pop();
      setInput([...input, e]);
    }
  };

  const handleDecimalClick = (e) => {
    if (input[input.length - 1] === ".") {
      setInput([...input]);
    } else if (isNaN(input[input.length - 1])) {
      setInput([...input, e]);
    } **else if (Number.isInteger(input[input.length - 1])) {
      setInput([...input, e]);
    }**
  };

  const handleEqualsClick = (e) => {
    let exp = "";

    if (input[0] === 0 amp;amp; input.length <= 1) {
      console.log("hell yeah");
      setNotANum(["NaN"]);
    } else if (input.length > 1) {
      console.log("input length is "   input.length);
      input.forEach((el) => {
        return (exp  = el);
      });

      let value = mexp.eval(exp);

      setInput([value]);
    }
  };

  const handleClickReset = (e) => {
    setAC(e);
    setInput([0]);
  };

  return (
    <div id="calculator-div">
      <HookDisplay input={input} notANum={notANum} AC={AC} />
      <HookButtons
        inputOneClick={handleInputOneClick}
        symbolClick={handleSymbolClick}
        decimalClick={handleDecimalClick}
        handleEqualsButtonClick={handleEqualsClick}
        handleClickReset={handleClickReset}
      />
    </div>
  );
};


  return (
    <div id="calculator-div">
      <HookDisplay input={input} notANum={notANum} AC={AC} />
      <HookButtons
        inputOneClick={handleInputOneClick}
        symbolClick={handleSymbolClick}
        decimalClick={handleDecimalClick}
        handleEqualsButtonClick={handleEqualsClick}
        handleClickReset={handleClickReset}
      />
    </div>
  );
};

 

Ответ №1:

Я думаю, что прежде чем вводить десятичное число в массив, вы можете убедиться, что оно создаст допустимое выражение. Поскольку в настоящее время калькулятор поддерживает базовые операции, число никогда не может иметь более одного десятичного знака, а операндов может быть только на один больше, чем операторов, используя указанный факт. Это условие будет работать (внутри handleDecimalClick):

 let lastIndex = -1;
for (let i = input.length - 1; i >= 0; i--) {
  if (input[i] === ".") {
    lastIndex = i;
  } else if (
    input[i] === " " ||
    input[i] === "-" ||
    input[i] === "*" ||
    input[i] === "/"
  ) {
    break;
  }
}

if (lastIndex !== -1) return;
 

Кроме того, для дополнительной защиты добавлено регулярное выражение, чтобы проверить, является ли выражение допустимым, прежде чем оно отправится на оценку (добавлены нижеприведенные строки в уравнениях):

 const inp = input.join("");
const regex = /(?:(?:^|[- _*/])(?:s*-?d (.d )?(?:[eE][ -]?d )?s*)) $/;
if (!regex.test(inp)) {
  return;
}
 

Я разветвил ваш codesandbox и добавил исправление.