нужна помощь с регулярными выражениями js

Мне надо оборачивать выражения рядом с которыми стоит восклицательный знак в функцию факториал.

Например:

5! -> factorial(5)

Но при этом:

5!! -> factorial( factorial(5) )

Вообще выражение может быть любой сложности, например:

5 + 3 - ((44 - 38)! - 3)!

Я долго думал как реализовать это с помощью регулярный выражений, и не смог. Но не обязательно использовать регулярные выражения. Если есть идеи, как реализовать это без регулярок, можете писать.


Ответы (1 шт):

Автор решения: Daniil Loban

В целом, я поддерживаю комментарии под вопросом, что это задача разбора грамматики. Но так как в вопросе есть пункт: Если есть идеи, как реализовать это без регулярок, можете писать., напишу вариант как это можно сделать относительно просто, если не предполагаются выражения больше n!!

Принцип алгоритма:

  • пройти с конца по строке, найти ее диапазоны к которым применяется факториалы. например [[8,24], [9, 18], ...] где 8 - начало выражения, 24 - конец перед символом!

  • далее для безболезненной вставки (чтобы не потерять диапазоны) строка бьется на буквы (массив), теперь вставки не будут влиять на данные о диапазонах.

  • делаются вставки с помощью splice для каждого диапазона

  • массив объединяется в строку - выводится результат

function getStart(s, index){
   let brackets = 0
   for(let i=index-1; i >= 0; i--){
      if (s[i] === ')') brackets++
      if (brackets && s[i] === '(') brackets--
      // возвращаем позицию открывающей скобки
      if (brackets === 0 && s[i] !== '!' ) return i  
   }
}

function replaceFactorial(s, ranges){
   if (ranges.length === 0) return s
   let range = null
   let result = s.split('')
   while(range = ranges.shift()){
      if (s[range[1]-1] === ')'){ // были скобки
         result.splice(range[0] , 1, 'factorial(')
         result.splice(range[1], 1 )
      } else if (s[range[1]+1] === '!'){  // двойной факториал
         result.splice(range[0] , 0, 'factorial(')
         result.splice(range[1] + 2, 1 , ')' )
      } else { // выражение было без скобок, например: 5!!
         result.splice(range[0] , 0, 'factorial(')
         result.splice(range[1] + 1, 1, ')' )
      }
   }
   return result.join('')
}

function transform(s){
   let ranges = []
   for(let i=s.length-1; i >= 0; i--){
      if(s[i] === '!'){
         ranges.push([getStart(s, i), i])
      } 
   }
   return replaceFactorial(s, ranges)
}

let test ='5! + 3 - ((44 - 38)! - 3)! + 7!!' 
console.log(test) 
console.log(transform(test))

→ Ссылка