Разделение строки с помощью вложенных скобок только на верхнем уровне, где «уровень» определяется скобками

Я пытаюсь создать регулярное выражение, которое позволит мне разделить строки ниже только на центральную запятую.

str_1 <- "N(0, 1)"
str_2 <- "N(N(0.1, 1), 1)"
str_3 <- "N(U(0, 1), 1)"
str_4 <- "N(0, T(0, 1))"
str_5 <- "N(N(0, 1), N(0, 1))"

Думайте о них как о параметрах распределений. Теперь я хотел бы остановиться на запятой «высший уровень».

Некоторые детали: Числа могут быть десятичными числами, как положительными, так и отрицательными. Они всегда будут сгруппированы внутри U(), N(), LN() или T() и разделены запятой. Дополнительные группы будут добавлены позже, поэтому требуется более общее решение или легко расширяемое. Что я хочу сделать, так это разделить выражения на запятую «верхнего уровня».

Теперь первый случай str_1 прост, используя:

unlist(strsplit(str_1, ",", perl = TRUE))

Прежде чем я продолжу, мне нужно знать, есть ли у меня вложение. Я знаю, что у меня будет более одного N, U, LN или T, если есть вложенность. Итак, чтобы проверить, я сделал (для str_2):

length(attr(gregexpr("(N|LN|U|T)", str_2, perl = TRUE)[[1]], "match.length")) > 1

Установив, есть ли у меня вложенность (может быть, это более чистый способ проверить это?), я могу приступить к разбиению оставшихся строк. Однако именно здесь я застрял. Учитывая, что я не могу считать запятые, так как случаи str_2, str_3 и str_4 будут неоднозначными. Как мне убедиться, что я разделяю только центральную запятую?

Я ожидаю следующие результаты (таким образом, обрезав первую букву и круглую скобку и последнюю круглую скобку)

# str_2
"N(0.1, 1)" "1"

# str_3
"U(0, 1)" "1"

# str_4
"0" "T(0, 1)"

# str_5
"N(0, 1)" "N(0, 1)"

Я хотел бы остаться с базой R, чтобы уменьшить количество зависимостей для кода, если это возможно. Буду признателен за любую оказанную помощь. Также возможно, что это не решается регулярным выражением, но требует программного подхода, возможно, с помощью рекурсии, как это предлагается в этом вопросе Java.

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

Tim Biegeleisen 10.12.2020 14:18
strsplit(substring(str_2, 3, nchar(str_2)-1), ",(?![^()]*\\))", perl=TRUE)
Wiktor Stribiżew 10.12.2020 14:19

Лучше всего использовать инструмент лексического анализа (lex) и анализатор грамматики (yacc). Однако существуют разновидности регулярных выражений, которые могут сканировать вложенные структуры. Также можно выполнять несколько синтаксических анализов с помощью любого механизма регулярных выражений: 1. аннотировать уровень вложенности каждой скобки, 2. сканировать открывающую и закрывающую скобку одного уровня, 3. удалять аннотацию вложенности. Подробности смотрите на twiki.org/cgi-bin/view/Blog/BlogEntry201109x3

Peter Thoeny 10.12.2020 23:32
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
763
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Если ваши векторы символов имеют формат, который вы показали, вы можете добиться того, что вам нужно, с помощью одного регулярного выражения PCRE:

(?:\G(?!^)\s*,\s*|^N\()\K(?:\d+|\w+(\([^()]*(?:(?1)[^()]*)*\)))(?=\s*,|\)$)

Посмотрите демонстрацию регулярного выражения . Подробности

  • (?:\G(?!^)\s*,\s*|^N\() - конец предыдущего успешного совпадения (\G(?!^)), а затем запятая, заключенная с нулем или более пробелами (\s*,\s*), или строка N( в начале строки (^N\()
  • \K — оператор сброса совпадений, который отбрасывает весь текст, совпавший до сих пор, из текущего буфера памяти совпадений.
  • (?: - начало группы без захвата
    • \d+ - одна или несколько цифр
    • | - или
    • \w+ - один или несколько символов слова
    • (\([^()]*(?:(?1)[^()]*)*\)) - Группа 1 (необходима для правильной работы рекурсии): (, затем любой ноль или более символов, кроме ( и ), затем ноль или более вхождений шаблона группы 1 (рекурсивно), а затем ноль или более символов, кроме ( и ), а затем символ )
  • ) - конец незахватывающей группы
  • (?=\s*,|\)$) - сразу же следует ноль или более пробелов, а затем запятая или ) символ в конце строки.

Посмотрите демонстрацию регулярного выражения :

strs <- c("N(0, 1)", "N(N(0.1, 1), 1)", "N(U(0, 1), 1)", "N(0, T(0, 1))", "N(N(0, 1), N(0, 1))")
p <- "(?:\\G(?!^)\\s*,\\s*|^N\\()\\K(?:\\d+|\\w+(\\([^()]*(?:(?1)[^()]*)*\\)))(?=\\s*,|\\)$)"
regmatches(strs, gregexpr(p, strs, perl=TRUE))
# => [[1]]
#    [1] "0" "1"
#    
#    [[2]]
#    [1] "N(0.1, 1)" "1"        
#    
#    [[3]]
#    [1] "U(0, 1)" "1"      
#    
#    [[4]]
#    [1] "0"       "T(0, 1)"
#    
#    [[5]]
#    [1] "N(0, 1)" "N(0, 1)"

Спасибо за прекрасное объяснение компонентов выражения. Очень признателен.

edsandorf 15.12.2020 11:39

Задайте s как вектор символов строк. Мы вычисляем совокупное количество левых скобок минус совокупное количество правых скобок и заменяем любую запятую, для которой эта разница равна 0, точкой с запятой, а затем разделяем ее.

Для этого мы используем gsubfn, который похож на gsub, за исключением того, что замена не обязательно должна быть строкой, но может быть прото-объектом. Метод pre прото-объекта запускается в начале каждой строки, а метод fun запускается для каждого совпадения с шаблоном, переданным в gsubfn. Метод pre, определенный ниже, устанавливает lev в 0, где lev будет содержать кумулятивную разницу, описанную выше. fun запускается при каждом совпадении с левой скобкой, правой скобкой или запятой, и каждый раз, когда мы получаем совпадение:

  • левая скобка будет увеличиваться lev
  • правая скобка будет уменьшаться lev
  • запятая будет испускать точку с запятой, заменяющую запятую, если lev == 0

Удалите мусор в начале и конце ввода s, используя sub, запустите gsubfn и затем разделите результат точкой с запятой. В конце мы упрощаем результат во фрейм данных. Каждый вектор выходных символов здесь имеет длину 2, но если они могут иметь разную длину, то опустите as.data.frame.

library(gsubfn)
library(magrittr)


# s is char vec; rm is TRUE if 1st two chars & last one to be removed
# output is list of char vecs
Split <- function(s, rm = TRUE) {
  p <- proto(
    pre = function(this) this$lev <- 0,
    fun = function(this, x) {
      this$lev <- this$lev + ( x == "(" ) - ( x == ")" )
      if (x == "," && this$lev == 0) ";" else x
    }
  )

  if (rm) s <- sub("^..(.*).$", r"{\1}", s)
  s %>% gsubfn(r"{[\(\),]}", p, .) %>% strsplit(" *; *")
}

# test 1
s <- c(str_1 = "N(0, 1)", str_2 = "N(N(0.1, 1), 1)", str_3 = "N(U(0, 1), 1)", 
       str_4 = "N(0, T(0, 1))", str_5 = "N(N(0, 1), N(0, 1))")
s %>% Split %>% as.data.frame
##   str_1     str_2   str_3   str_4   str_5
## 1     0 N(0.1, 1) U(0, 1)       0 N(0, 1)
## 2     1         1       1 T(0, 1) N(0, 1)

Обратите внимание, что это может работать с любым количеством аргументов:

# test 2
w <- "lognormal(N(0, 1), 1), lognormal(0, U(0, 1)), beta(U(1, 1), 2), N(0, 1)"
w %>% Split(rm = FALSE)  %>% unlist
## [1] "lognormal(N(0, 1), 1)" "lognormal(0, U(0, 1))" "beta(U(1, 1), 2)"     
## [4] "N(0, 1)"   

Если учесть, что структура осталась прежней, то можно было бы сделать:

lapply(parse(text=strings), function(x)c(deparse(x[[2]]), deparse(x[[3]])))

[[1]]
[1] "0" "1"

[[2]]
[1] "N(0.1, 1)" "1"        

[[3]]
[1] "U(0, 1)" "1"      

[[4]]
[1] "0"       "T(0, 1)"

[[5]]
[1] "N(0, 1)" "N(0, 1)"

strings <- c("N(0, 1)", "N(N(0.1, 1), 1)", "N(U(0, 1), 1)", "N(0, T(0, 1))", "N(N(0, 1), N(0, 1))")

Другие вопросы по теме