简体   繁体   English

将第n次出现替换为反向

[英]Replace nth occurrence to its reverse

Scala provides out of the box methods to work either with first or with all occurrences of the pattern. Scala提供了开箱即用的方法,可以使用模式的第一个或所有模式。

What is the best (or canonical way) to replace only nth occurrence? 仅替换第n次出现的最佳(或规范方式)是什么?

I can think of a couple of solutions but I do not really like any of them. 我可以想到几个解决方案,但我真的不喜欢它们中的任何一个。

First one uses mutable var to track occurrences. 第一个使用mutable var来跟踪事件。

  def f1(str: String, pattern: String, occurrence: Int) = {
    pattern.r.replaceAllIn(str, {var c = 0
      m: Match => {
      c = c + 1
      if (c == occurrence) m.group(1).reverse else m.group(1)
      }
    })
  }

  println(f1("aaa bbb123, ccc456, ddd789, qqq1010 206z", """(\d+)""", 3))

Second one finds all matches, picks up the required and applies patch method for the String. 第二个找到所有匹配,获取所需的匹配并为String应用补丁方法。

  def f2(str: String, pattern: String, occurrence: Int) = {
    val m = pattern.r.findAllMatchIn(str).toList.lift(occurrence-1)
    m match {
      case Some(m) => str.patch(m.start(1), m.group(1).reverse, m.group(1).length)
      case None => str
    }
  }

  println(f2("aaa bbb123, ccc456, ddd789, qqq1010 206z", """(\d+)""", 3))

Is there more concise/preferable or better way? 是否有更简洁/更好或更好的方式?

Update 更新

Yet another approach with zipAll. zipAll的另一种方法。

  def f5(str: String, pattern: String, occurrence: Int) = {
    val m = pattern.r.findAllIn(str).toArray
    val x = str.split(pattern)
    if (x.size>occurrence) m(occurrence-1) = m(occurrence-1).reverse
    x.zipAll(m, "", "").flatMap(t => List(t._1, t._2)).mkString
  }

Results of the performance test for functions f1...f5 with 1 000 000 executions and below function to measure elapsed time 功能f1 ... f5的性能测试结果,执行1 000 000次以下,测量经过时间的功能

  def time[R](block: => R): R = {
    val t0 = System.nanoTime()
    val result = block    // call-by-name
    val t1 = System.nanoTime()
    println("Elapsed time: " + (t1 - t0) + "ns")
    result
  }

Elapsed time: 6352446800ns
Elapsed time: 4832129400ns
Elapsed time: 3153650800ns
Elapsed time: 3501623300ns
Elapsed time: 6276521500ns

f3 seems to be the best (which is expected). f3似乎是最好的(预期)。

I think your 2nd approach is a good one but I wouldn't bother with the List manipulations. 我认为你的第二种方法是一个很好的方法但我不打扰List操作。

def f3(str: String, pattern: String, occurrence: Int) = {
  val mi = pattern.r.findAllMatchIn(str).drop(occurrence - 1)
  if (mi.hasNext) {
    val m = mi.next()
    val s = m.group(0)
    str.patch(m.start, s.reverse, s.length)
  } else str
}

update : You could also try this slight modification. 更新 :您也可以尝试这种轻微的修改。

def f4(str: String, pattern: String, occurrence: Int) =
  util.Try{pattern.r.findAllMatchIn(str).drop(occurrence - 1).next()
  }.fold(_=>str, m=>str.patch(m.start, m.group(0).reverse, m.group(0).length))


f4("aaa bbb123, ccc456, ddd789, qqq1010 206z", "\\d+", 3)

It's a bit more concise (a single line) and somewhat safer (won't throw if pattern can't be compiled to a regex), but I'm not sure it's actually preferable. 它更简洁(单行)并且更安全(如果pattern无法编译为正则表达式则不会抛出),但我不确定它实际上更可取。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM