解决方案 1
stringi 包函数的一个小包装器,用于输入 OP 所需的内容。
inject <- function(string, index, replacement){
stringi::stri_sub_replace_all(string, from = index,
to = index-1,
replacement = replacement)
}
text <- "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
idx_list <- c(5, 16, 30, 50)
word_list <- c("AAA", "BBB", "CCC", "DDD")
inject(text, idx_list, word_list)
#> [1] "LoreAAAm ipsum dolBBBor sit amet, cCCConsectetur adipiscinDDDg elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
解决方案 2
Zach Foster's 答案的改编,浓缩为单个函数
inject_two <- function(string, inject, index) {
inject <- inject[order(index)]
index <- sort(index)
# expand string
split <- substr(rep(string, length(index) + 1),
start = c(1, index),
stop = c(index - 1, nchar(string))
)
ord1 <- 2 * (1:length(split)) - 1
ord2 <- 2 * (1:length(inject))
paste(c(split, inject)[order(c(ord1, ord2))], collapse = "")
}
inject_two(text, word_list, idx_list)
#> [1] "LoreAAAm ipsum dolBBBor sit amet, cCCConsectetur adipiscinDDDg elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
基准
evans <- function(string, index, replacement){
ord <- order(-index)
Reduce(function(S, R) {
paste0(substring(S, 1, R[[1]]-1), R[[2]], substring(S, R[[1]], nchar(S)))
}, Map(list, index[ord], replacement[ord]), string)
}
ggplot2::autoplot(microbecnhmark::microbenchmark(
inject(text, idx_list, word_list),
inject_two(text, idx_list, word_list),
evans(text, idx_list, word_list),
times = 1000
))
对于少量插入,所有解决方案的表现都相似。如果我们执行许多(这里 N = 410 000)插入怎么办?
text_large = paste0(rep(text, 10000), collapse = "")
idx_list_large = seq(1, nchar(text_large), by = 3)
word_list_large = sample(LETTERS, size = length(idx_list_large), replace = T)
bench::mark(
inject = inject(text_large, idx_list_large, word_list_large),
inject_two = inject_two(text_large, idx_list_large, word_list_large),
iterations = 50
)[,c(1,3,5,7)]
# A tibble: 2 x 4
expression median mem_alloc n_itr
<bch:expr> <bch:tm> <bch:byt> <int>
1 inject 32.2ms 9.38MB 50
2 inject_two 157.4ms 65.69MB 50
出人意料,基于 C++ 的 stringi 在速度和内存方面都占据优势。注意 evans 被省略,因为在测试时 1 次通过需要多分钟。