如何用模式字符串屏蔽字符串的子序列

How to mask subsequences of string with a pattern string

我有一个主字符串,如下所示:

my_main <- "ABCDEFGHIJ"

我想做的是在每个位置用另一个模式字符串顺序屏蔽:

my_pattern <- "x*x" # the length could be varied from 1 up to length of my_main

每个与 * 重叠的字符将被保留,其他将被替换为 x

最终结果是包含以下内容的字符串向量:

xBxDEFGHIJ
AxCxEFGHIJ
ABxDxFGHIJ
ABCxExGHIJ
ABCDxFxHIJ
ABCDExGxIJ
ABCDEFxHxJ
ABCDEFGxIx

下一个如果模式是

my_pattern <- "xx**x" 

结果将是:

xxCDxFGHIJ
AxxDExGHIJ
ABxxEFxHIJ
ABCxxFGxIJ
ABCDxxGHxJ
ABCDExxHIx

我怎样才能做到这一点?

这可能有点 over-complicated,但这是一个开始:

我将重用 中的 Reduce_frame

Reduce_frame <- function(data, expr, init) {
  expr <- substitute(expr)
  out <- rep(init[1][NA], nrow(data))
  for (rn in seq_len(nrow(data))) {
    out[rn] <- init <- eval(expr, envir = data[rn,])
  }
  out
}

从这里开始,让我们将模式分成一个框架(为了便于访问,如果没有别的):

repl <- subset(
  data.frame(p = strsplit(my_pattern, "")[[1]], i = seq_len(nchar(my_pattern))),
  p != "*")
repl
#   p i
# 1 x 1
# 3 x 3

从这里开始,我们可以一次使用:

tail(Reduce_frame(repl, `substring<-`(init, i, i, p), init = my_main), 1)
# [1] "xBxDEFGHIJ"

这意味着我们可以相当轻松地进行迭代:

sapply(c(0, seq_len(nchar(my_main) - nchar(my_pattern))), function(offset) {
  tail(Reduce_frame(transform(repl, i = i + offset),
                    `substring<-`(init, i, i, p), init = my_main), 1)
})
# [1] "xBxDEFGHIJ" "AxCxEFGHIJ" "ABxDxFGHIJ" "ABCxExGHIJ" "ABCDxFxHIJ" "ABCDExGxIJ" "ABCDEFxHxJ" "ABCDEFGxIx"

要使用第二个模式,

my_pattern <- "xx**x" 
repl <- transform(...) # from above
## the rest of this code is unchanged from above
sapply(c(0, seq_len(nchar(my_main) - nchar(my_pattern))), function(offset) {
  tail(Reduce_frame(transform(repl, i = i + offset),
                    `substring<-`(init, i, i, p), init = my_main), 1)
})
# [1] "xxCDxFGHIJ" "AxxDExGHIJ" "ABxxEFxHIJ" "ABCxxFGxIJ" "ABCDxxGHxJ" "ABCDExxHIx"

所以这很容易实现功能化:

Reduce_frame <- ... # defined above
func <- function(S, pattern) {
  stopifnot(nchar(S) >= nchar(pattern))
  repl <- subset(
    data.frame(p = strsplit(pattern, "")[[1]], i = seq_len(nchar(pattern))),
    p != "*")
  sapply(c(0, seq_len(nchar(S) - nchar(pattern))), function(offset) {
    tail(Reduce_frame(transform(repl, i = i + offset),
                      `substring<-`(init, i, i, p), init = S), 1)
  })
}

func("ABCDEFGHIJ", "x*x")
# [1] "xBxDEFGHIJ" "AxCxEFGHIJ" "ABxDxFGHIJ" "ABCxExGHIJ" "ABCDxFxHIJ" "ABCDExGxIJ" "ABCDEFxHxJ" "ABCDEFGxIx"
func("ABCDEFGHIJ", "xx**x")
# [1] "xxCDxFGHIJ" "AxxDExGHIJ" "ABxxEFxHIJ" "ABCxxFGxIJ" "ABCDxxGHxJ" "ABCDExxHIx"

这是使用 strsplitgreplpaste 的一种方法。

f <- \(mm, mp) {
    m <- el(strsplit(mm, ''))
    p <- el(strsplit(mp, '')) 
    i <- which(!grepl(p, pattern='\*'))
    vapply(c(0L, seq_len(length(m) - max(i))), \(j) {
        m[i + j] <- p[i]
        paste(m, collapse='')
    }, vector('character', 1L))
}


f('ABCDEFGHIJ', 'x*x')
# [1] "xBxDEFGHIJ" "AxCxEFGHIJ" "ABxDxFGHIJ" "ABCxExGHIJ" "ABCDxFxHIJ"
# [6] "ABCDExGxIJ" "ABCDEFxHxJ" "ABCDEFGxIx"
f('ABCDEFGHIJ', 'x**x')
# [1] "xBCxEFGHIJ" "AxCDxFGHIJ" "ABxDExGHIJ" "ABCxEFxHIJ" "ABCDxFGxIJ"
# [6] "ABCDExGHxJ" "ABCDEFxHIx"
f('ABCDEFGHIJ', 'xx**x')
# [1] "xxCDxFGHIJ" "AxxDExGHIJ" "ABxxEFxHIJ" "ABCxxFGxIJ" "ABCDxxGHxJ"
# [6] "ABCDExxHIx"

f('ABCDEFGHIJ', 'kk**krr')
# [1] "kkCDkrrHIJ" "AkkDEkrrIJ" "ABkkEFkrrJ" "ABCkkFGkrr"
f('ABCDEFGHIJ', 'kk**kr*r')
# [1] "kkCDkrGrIJ" "AkkDEkrHrJ" "ABkkEFkrIr"

这是一种与 r2evans 的答案相同的方法,但依赖于一些 stringr 函数,这些函数应该比基本等效函数更有效:

library(stringr)

f <- function(main, r_pattern) {
  shift <-  nchar(main) - nchar(r_pattern) + 1
  idx <- as.data.frame(str_locate_all(r_pattern, "[^*]+")[[1]])
  x_pattern <- str_split(r_pattern, "\*+")[[1]]
  
  Reduce(
    function(x, y)
      `str_sub<-`(
        x,
        seq(idx$start[y], length.out = shift),
        seq(idx$end[y], length.out = shift),
        omit_na = FALSE,
        x_pattern[y]
      ),
    seq(nrow(idx)),
    init = main
  )
}

f("ABCDEFGHIJ", "x*x")
[1] "xBxDEFGHIJ" "AxCxEFGHIJ" "ABxDxFGHIJ" "ABCxExGHIJ" "ABCDxFxHIJ" "ABCDExGxIJ" "ABCDEFxHxJ" "ABCDEFGxIx"

f("ABCDEFGHIJ", "xx**x")
[1] "xxCDxFGHIJ" "AxxDExGHIJ" "ABxxEFxHIJ" "ABCxxFGxIJ" "ABCDxxGHxJ" "ABCDExxHIx"

# Edit after OP comment:
f(my_main, "KK**KRR")
[1] "KKCDKRRHIJ" "AKKDEKRRIJ" "ABKKEFKRRJ" "ABCKKFGKRR"

这是生成所需结果的 Ruby 代码。我在 reader 希望将其转换为 R 的情况下展示它,当然可能需要修改。

即使你不知道Ruby,你也应该能够阅读代码,只要你理解:

  • 'abc'.size returns 3;
  • 0..8是介于08之间的整数范围;
  • 'abc' << 'd' returns 'abcd';
  • 7.modulo(3) returns 1;
  • 'abcd'[2] returns 'c', 2 为索引;和
  • s == 'x' ? 'x' : my_main[j] 表示,“如果字符串 s(将是 'x''*')等于 'x' return 'x', 否则 return my_main 的字符在索引 j.

Ruby 代码(对通常编写的代码进行了一些简化)如下。

def doit(my_main, my_pattern)
  msz = my_main.size
  psz = my_pattern.size
  (0..msz-psz).map do |i|
    s = ''
    (0..msz-1).each do |j|
      s << (my_pattern[(j-i).modulo(msz)] == 'x' ? 'x' : my_main[j])
    end
    s
  end
end