Использование регулярных выражений для разбиения математической формулы в массив - PullRequest
1 голос
/ 02 апреля 2019

Я хочу получить формулу плоской строки и разделить ее на массив, разделенный на несколько факторов. Застревание вокруг скобок и поиск помощи.

Я использовал сканирование регулярных выражений и несколько фильтров, чтобы попытаться получить полученный массив.

Мои текущие тесты такие:

 describe 'split algorithm' do

      it 'can split a flat algorithm' do
        algo = 'ABC * DEF * GHI Round(3) = JKL * MNO * PQR Round(0) = SAVE'
        actual = split_algo(algo)
        expected = ['ABC', '* DEF', '* GHI', 'Round(3)', '= JKL', '* MNO', '* PQR', 'Round(0)', '= SAVE']
        expect(actual).to eq expected
      end

      it 'can split an algorithm with parenthesis' do
        algo = '(ABC + DEF + (GHI * JKL)) - ((MNO + PQR + (STU * VWX)) * YZ) Round(0) + SUM(AAA) = SAVE'
        actual = split_algo(algo)
        expected = ['(', 'ABC', '+ DEF', '+', '(', 'GHI', '* JKL', ')', ')', '-', '(', '(', 'MNO', '+ PQR', '+', '(', 'STU', '* VWX', ')', ')', '* YZ', ')', 'Round(0)', '+ SUM', '(', 'AAA', ')', '= SAVE']
        expect(actual).to eq expected
      end

end

С помощью следующего кода я могу заставить первую половину пройти отлично:

def split_algo(algorithm)
   pattern = /(?:(\ (\*\ |\+\ |\-\ |\\\ |\=\ )\S*))|(\S*)/
   matches = algorithm.scan(pattern)
   matches.each_with_index { |match, index| matches[index]=match.compact }
   arr = []
   matches.each do |match|
     arr << match.max_by(&:length).strip
   end
   arr.delete('')
   arr
end

Я попытался изменить pattern, чтобы он соответствовал скобкам:

pattern = (\(|\))|(?:(\ (\*\ |\+\ |\-\ |\\\ |\=\ )\S*))|(\S*)

Но это охватывает только скобки в начале формулы.

Ответы [ 2 ]

0 голосов
/ 03 апреля 2019

Мы можем определить следующее регулярное выражение.

R = /
    # split after an open paren if not followed by a digit
    (?<=\()      # match is preceded by an open paren, pos lookbehind
    (?!\d)       # match is not followed by a digit, neg lookahead
    [ ]*         # match >= 0 spaces
    |            # or
    # split before an open paren if paren not followed by a digit
    (?=          # begin pos lookahead
      \(         # match a left paren...
      (?!\d)     # ...not followed by a digit, neg lookahead
    )            # end pos lookahead
    [ ]*         # match >= 0 spaces        
    |            # or
    # split before a closed paren if paren not preceded by a digit
    (?<!\d)      # do not follow a digit, neg lookbehind
    (?=\))       # match a closed paren, pos lookahead
    [ ]*         # match >= 0 spaces        
    |            # or
    # split after a closed paren
    (?<=\))      # match a preceding closed paren, pos lookbehind
    [ ]*         # match >= 0 spaces        
    |            # or
    # match spaces not preceded by *, = or + and followed by a letter 
    (?<![*=+\/-]) # match is not preceded by one of '*=+\/-', neg lookbehind
    [ ]+         # match one or more spaces
    |            # or
    # match spaces followed by a letter 
    [ ]+         # match one or more spaces
    (?=\()       # match a left paren, pos lookahead
    /x           # free-spacing regex definition mode

В первом примере мы имеем следующее.

algo1 = 'ABC * DEF * GHI Round(3) = JKL * MNO * PQR Round(0) = SAVE'
expected1 = ['ABC', '* DEF', '* GHI', 'Round(3)', '= JKL', '* MNO',
             '* PQR', 'Round(0)', '= SAVE']
algo1.split(R) == expected1
  #=> true

Во втором примере мы имеем следующее.

algo2 = '(ABC + DEF + (GHI * JKL)) - ((MNO + PQR + (STU * VWX)) * YZ) Round(0) + SUM(AAA) = SAVE'
expected2 = ['(', 'ABC', '+ DEF', '+', '(', 'GHI', '* JKL', ')', ')', '-',
             '(', '(', 'MNO', '+ PQR', '+', '(', 'STU', '* VWX', ')', ')',
             '* YZ', ')', 'Round(0)', '+ SUM', '(', 'AAA', ')', '= SAVE']
algo2.split(R) == expected2
  #=> true 

Регулярное выражение обычно записывается следующим образом:

R = /(?<=\()(?!\d) *|(?=\((?!\d)) *|(?<!\d)(?=\)) *|(?<=\)) *|(?<![*=+\/-]) +| +(?=\()/

В режиме свободного пробела я заключил пробелы в класс символов ([ ]);иначе они будут удалены перед оценкой выражения.В этом нет необходимости, если регулярное выражение написано условно.

0 голосов
/ 02 апреля 2019

Я закончил, выполнив следующее, которое, кажется, работает:

Добавлен вызов нового метода, split_paren(arr) в конце split_algo.

def split_paren(algo_arr)
  pattern = /Round\(\d*\)/
  arr = []
  algo_arr.each do |step|
    f = step.split(/(\(|\))/) unless step =~ pattern
    f.delete('') if f.class == Array
    f.nil? ? arr << step : f.each{|s| arr << s.strip}
  end
  arr
end

Если кто-нибудьхочет ответить на лучший способ сделать это, пожалуйста, не стесняйтесь ответить.В противном случае я приму ответ и немного закрою вопрос здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...