назад до уроку

Розберіть вираз

Арифметичний вираз складається з двох чисел та оператору між ними, наприклад:

  • 1 + 2
  • 1.2 * 3.4
  • -3 / -6
  • -2 - 2

Оператором може бути: "+", "-", "*" або "/".

Додаткові пробіли можуть бути на початку, в кінці чи всередині виразу.

Напишіть функцію parse(expr), яка приймає вираз та повертає масив з 3-ьох елементів:

  1. Перше число.
  2. Оператор.
  3. Друге число.

Наприклад:

let [a, op, b] = parse("1.2 * 3.4");

alert(a); // 1.2
alert(op); // *
alert(b); // 3.4

Регулярний вираз для числа є наступним: -?\d+(\.\d+)?. Його ми створили в рамках попередньої задачі.

Оператором слугуватиме [-+*/]. Дефіс - стоїть першим в квадратних дужках, бо позиція посередині означає діапазон знаків, тоді як нам потрібен лише -.

Символ / має бути екранованим всередині регулярного виразу JavaScript /.../, зробимо це потім.

Нам потрібне число, оператор, тоді ще одне число. Та можливі пробіли між ними.

Повний регулярний вираз: -?\d+(\.\d+)?\s*[-+*/]\s*-?\d+(\.\d+)?.

Він містить 3 частини, з \s* між ними:

  1. -?\d+(\.\d+)? – перше число,
  2. [-+*/] – оператор,
  3. -?\d+(\.\d+)? – друге число.

Аби зробити кожну з цих частин окремим елементом масиву результатів, помістимо їх в круглі дужки: (-?\d+(\.\d+)?)\s*([-+*/])\s*(-?\d+(\.\d+)?).

Код у дії:

let regexp = /(-?\d+(\.\d+)?)\s*([-+*\/])\s*(-?\d+(\.\d+)?)/;

alert( "1.2 + 12".match(regexp) );

Розглянемо результат:

  • result[0] == "1.2 + 12" (повний збіг)
  • result[1] == "1.2" (перша група (-?\d+(\.\d+)?) – перше число, включаючи десяткову частину)
  • result[2] == ".2" (друга група (\.\d+)? – перша десяткова частина)
  • result[3] == "+" (третя група ([-+*\/]) – оператор)
  • result[4] == "12" (четверта група (-?\d+(\.\d+)?) – друге число)
  • result[5] == undefined (п’ята група (\.\d+)? – остання десяткова частина відсутня, тому вона undefined)

Нам потрібні лише числа та оператор, без повного збігу чи десяткових частин, тож проведемо невелику “чистку” результату.

Повний збіг (перший елемент масиву) можна прибрати методом масиву result.shift().

Групи 2 та 5, що містять десяткові частини (.\d+), можна оминути, додавши ?: на початку: (?:\.\d+)?.

Кінцевий варіант:

function parse(expr) {
  let regexp = /(-?\d+(?:\.\d+)?)\s*([-+*\/])\s*(-?\d+(?:\.\d+)?)/;

  let result = expr.match(regexp);

  if (!result) return [];
  result.shift();

  return result;
}

alert( parse("-1.23 * 3.45") );  // -1.23, *, 3.45

As an alternative to using the non-capturing ?:, we could name the groups, like this:

function parse(expr) {
  let regexp = /(?<a>-?\d+(?:\.\d+)?)\s*(?<operator>[-+*\/])\s*(?<b>-?\d+(?:\.\d+)?)/;

  let result = expr.match(regexp);

  return [result.groups.a, result.groups.operator, result.groups.b];
}

alert( parse("-1.23 * 3.45") );  // -1.23, *, 3.45;