Найти неиспользуемые символы в строке - PullRequest
2 голосов
/ 07 октября 2019

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

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

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

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

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

input_string <- c("a/b", "a#b", "a//b", "a-b", "a,b", "a.b")
orig_sep <- sep <- "/" ## first guess as a separator
while(any(grepl(sep, input_string, fixed = TRUE))) {
  sep <- paste0(sep, orig_sep)
}
print(sep)
# "///"

Ответы [ 2 ]

2 голосов
/ 07 октября 2019

В случае, если можно найти 1 ASCII, вы можете использовать table.

tt <- table(factor(strsplit(paste(input_string, collapse = ""), "")[[1]]
       , rawToChar(as.raw(32:126), TRUE)))
names(tt)[tt==0]

rawToChar(as.raw(32:126), TRUE) дает вам все ASCII, которые используются в качестве уровней факторов. И table считает все случаи. Если есть хотя бы один 0, вы можете использовать его.

Если вам нужно 2 ASCII, вы можете попробовать следующее, возвращая все возможные разделители:

x <- rawToChar(as.raw(32:126), TRUE)
x <- c(outer(x, x, paste0))
x[!sapply(x, function(y) {any(grepl(y, input_string, fixed=TRUE))})]

Или для n-ASCII:

orig_sep  <- x <- rawToChar(as.raw(32:126), TRUE)
sep  <- x[0]
repeat {
  sep <- x[!sapply(x, function(y) {any(grepl(y, input_string, fixed=TRUE))})]
  if(length(sep) > 0) break;
  x <- c(outer(x, orig_sep, paste0))
}
sep

Поиск 1-2 ASCII только с sapply -циклом и с разделителем минимальной длины.

x <- rawToChar(as.raw(32:126), TRUE)
x <- c(x, outer(x, x, paste0))
x[!sapply(x, function(y) {any(grepl(y, input_string, fixed=TRUE))})][1]
#[1] " "

Если вы хотите узнать, сколько разсимвол должен быть повторен, чтобы работать в качестве разделителя, так как вы делаете это в вопросе, вы можете использовать gregexpr.

strrep("/", max(sapply(gregexpr("/*", input_string)
  , function(x) max(attributes(x)$match.length)))+1)
#[1] "///"

strrep("/", max(c(0, sapply(gregexpr("/+", input_string)
  , function(x) max(attributes(x)$match.length))))+1)
#[1] "///"
0 голосов
/ 08 октября 2019

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

Код

library(microbenchmark)
sep <- "/"

make_input <- function(max_occ, vec_len = 1000) {
   paste0("A", strrep(sep, sample(0:max_occ, vec_len, TRUE)))
}

set.seed(1)

no_occ   <- make_input(0)
typ_occ  <- make_input(1)
mid_occ  <- make_input(10)
high_occ <- make_input(100)

while_fun <- function(in_str) {
  my_sep <- sep
  while(any(grepl(my_sep, in_str, fixed = TRUE))) {
    my_sep <- paste0(my_sep, sep)
  }
  my_sep
}

greg_fun <- function(in_str) {
 strrep(sep, 
    max(sapply(gregexpr(paste0(sep, "+"), in_str), 
               purrr::attr_getter("match.length")), 0) + 1)
}

microbenchmark(no_occ_w   = while_fun(no_occ),
               no_occ_r   = greg_fun(no_occ),
               typ_occ_w  = while_fun(typ_occ),
               typ_occ_r  = greg_fun(typ_occ),
               mid_occ_w  = while_fun(mid_occ),
               mid_occ_r  = greg_fun(mid_occ),
               high_occ_w = while_fun(high_occ),
               high_occ_r = greg_fun(high_occ))

Результаты

Unit: microseconds
       expr    min      lq     mean  median      uq    max neval  cld
   no_occ_w   12.3   13.30   15.947   14.60   16.55   51.1   100 a   
   no_occ_r 1074.8 1184.90 1981.637 1253.45 1546.20 7037.9   100  b  
  typ_occ_w   33.8   36.00   42.842   38.55   41.45  229.2   100 a   
  typ_occ_r 1090.4 1192.15 2090.526 1283.80 1547.10 8490.7   100  b  
  mid_occ_w  277.9  283.35  336.466  288.30  309.45 3452.2   100 a   
  mid_occ_r 1161.6 1269.50 2204.213 1368.45 1789.20 7664.7   100  b  
 high_occ_w 3736.4 3852.95 4082.844 3962.30 4097.60 6658.3   100    d
 high_occ_r 1685.5 1776.15 2819.703 1868.10 4065.00 7960.9   100   c
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...