【问题标题】:Return all the indexes of a particular substring返回特定子字符串的所有索引
【发布时间】:2014-07-21 20:57:05
【问题描述】:

是否有 Scala 库 API 方法(如果没有,则为惯用方式)来获取较大字符串(源)中子字符串(目标)的所有索引的列表?我试图浏览 ScalaDoc,但找不到任何明显的东西。有这么多方法可以做这么多有用的事情,我猜我只是没有提交正确的搜索词。

例如,如果我有一个源字符串“name:Yo,name:Jim,name:name,name:bozo”,而我使用目标字符串“name:”,我想返回一个 List List(0, 8, 17, 27) 的 [Int]。

这是我解决问题的快速技巧:

def indexesOf(source: String, target: String, index: Int = 0, withinOverlaps: Boolean = false): List[Int] = {
    def recursive(index: Int, accumulator: List[Int]): List[Int] = {
      if (!(index < source.size)) accumulator
      else {
        val position = source.indexOf(target, index)
        if (position == -1) accumulator
        else {
          recursive(position + (if (withinOverlaps) 1 else target.size), position :: accumulator)
        }
      }
    }

    if (target.size <= source.size) {
      if (!source.equals(target)) {
        recursive(0, Nil).reverse
      }
      else List(0)
    }
    else Nil
  }

您能给我的任何指导都将其替换为适当的标准库入口点,我们将不胜感激。

2019 年 6 月 16 日更新:

进一步收紧代码:

  def indexesOf(source: String, target: String, index: Int = 0, withinOverlaps: Boolean = false): List[Int] = {
    def recursive(indexTarget: Int = index, accumulator: List[Int] = Nil): List[Int] = {
      val position = source.indexOf(target, indexTarget)
      if (position == -1)
        accumulator
      else
        recursive(position + (if (withinOverlaps) 1 else target.size), position :: accumulator)
    }
    recursive().reverse
  }

2014 年 7 月 22 日更新:

受 Siddhartha Dutta 的回答启发,我收紧了我的代码。现在看起来像这样:

  def indexesOf(source: String, target: String, index: Int = 0, withinOverlaps: Boolean = false): List[Int] = {
    @tailrec def recursive(indexTarget: Int, accumulator: List[Int]): List[Int] = {
      val position = source.indexOf(target, indexTarget)
      if (position == -1) accumulator
      else
        recursive(position + (if (withinOverlaps) 1 else target.size), position :: accumulator)
    }
    recursive(index, Nil).reverse
  }

另外,如果我有一个源字符串“aaaaaaaa”并且我使用一个目标字符串“aa”,我希望默认返回 List(0, 2, 4, 6) 的 List[Int]它跳过从找到的子字符串内部开始的搜索。可以通过为 insideOverlaps 参数传递“true”来覆盖默认值,在“aaaaaaaa”/“aa”的情况下,该参数将返回 List(0, 1, 2, 3, 4, 5, 6)。

【问题讨论】:

  • 不,没有“[标准]方法”。此外,由于这是工作代码,它可能更适合代码审查。
  • @chaotic3quilibrium 任何方式你可以 BSD 许可该方法,这样如果我复制/改编它,老板就不会生我的气? :)
  • @ericpeters 我的理解是,在 StackOverflow 上发布的任何 sn-ps 代码都可能被假定为本质上是公共领域;即不受任何许可限制的限制,限制您将 sn-p 剪切/粘贴/修改/自定义到您需要的任何上下文的能力。
  • @chaotic3quilibrium 这是一个有趣的灰色地带,从技术上讲,他们直到 3 月才成为 MIT-ish 归因 (meta.stackexchange.com/questions/271080/…)
  • 您应该更新答案以使用 Vector(并附加而不是 prepend)而不是 List 以避免最后的额外 .reverse 。可能也有助于返回类型 IndexedSeq[Int]

标签: string scala indexof


【解决方案1】:

我总是倾向于使用诸如此类问题的正则表达式技巧包。我不会说它是正确,但它的代码少得多。 :)

val r = "\\Qname\\E".r
val ex = "name:Yo,name:Jim,name:name,name:bozo"

val is = r.findAllMatchIn(ex).map(_.start).toList

在这种情况下,引号 \\Q\\E 不是必需的,但如果您要查找的字符串有任何特殊字符,那么它就是。

【讨论】:

  • 非常好。在编写代码 Scala 之前,我花了不到两分钟的时间评估正则表达式方法。很高兴有不止一种方法来给字符串搜索猫换皮。
  • 顺便说一句,如果您想使用纯正则表达式(作为来自其他来源的未转义复制/粘贴),也可以将第一行更改为 """\Qname\E""".r . Scala 中的三引号选项很棒!
【解决方案2】:

获取所有索引的小代码
调用下面的方法为 getAllIndexes(source, target)

def getAllIndexes(source: String, target: String, index: Int = 0): List[Int] = {
        val targetIndex = source.indexOf(target, index)
        if(targetIndex != -1)
          List(targetIndex) ++ getAllIndexes(source, target, targetIndex+1)
        else
          List()
      }

【讨论】:

  • 这似乎以相反的顺序返回列表,即 List(27, 17, 8, 0),对吗?此外,您可以优化这两个 if 路径。第一个将“List(targetIndex) ++ get...”替换为“targetIndex :: get...”。第二个用“Nil”替换“List()”。
  • No 该方法根据索引按升序返回列表,即 List(0,8,17,27)。优化是正确的。
  • 我刚刚尝试了您的调用,在添加 @tailrec 注释后,我收到一个编译器错误,指出它不是尾递归(使用 ++ 或 ::)。但是,您较小的代码启发了我,因此我提供了更新以显示我的代码已收紧。我还添加了另一个测试用例(“aaaaaaaa”、“aa”示例)来展示可选的 insideOverlaps 参数的好处。
  • 是的,该方法不是尾递归的,因为它首先调用自己然后添加结果。
  • 啊。我有一个字符串,我得到了数百到数千次点击,并且无法承受破坏调用堆栈的代价。
猜你喜欢
  • 2013-10-02
  • 2023-04-04
  • 2021-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多