在Char迭代器中查找字符串

时间:2015-12-18 09:28:40

标签: scala

我有一个用例,我需要从Char的迭代器返回一个String到分隔符String(如果找到)。

合同:

  • 如果迭代器耗尽(仅在开头),则返回None
  • 如果找到分隔符String,则返回之前的所有字符(空字符串很好),分隔符将被删除
  • else返回剩余的字符
  • 不要急切地耗尽迭代器!

我确实有这个有效的解决方案,但感觉就像Java(我来自哪里)

dat <- data.frame(id1=c(-1,-1), id2=c(-1,-2), var1=c(0,9), var2=c(-33,10), var3=c(5,-1))
dat[,-c(1,2)] <- matrix(pmax(unlist(dat[,-c(1,2)]),0), nrow=nrow(dat))

有没有办法以更实用的方式完成此操作(理想情况下不更改方法签名)?

更新:以下是我测试的方法

class MyClass(str: String) {
  def nextString(iterator: Iterator[Char]): Option[String] = {
    val sb = new StringBuilder
    if(!iterator.hasNext) return None
    while (iterator.hasNext) {
      sb.append(iterator.next())
      if (sb.endsWith(str)) return Some(sb.stripSuffix(str))
    }
    Some(sb.toString())
  }
}

输出:

val desmurfer = new MyClass("_smurf_")
val iterator: Iterator[Char] = "Scala_smurf_is_smurf_great_smurf__smurf_".iterator
println(desmurfer.nextString(iterator))
println(desmurfer.nextString(iterator))
println(desmurfer.nextString(iterator))
println(desmurfer.nextString(iterator))
println(desmurfer.nextString(iterator))
println
println(desmurfer.nextString("FooBarBaz".iterator))
println(desmurfer.nextString("".iterator))

6 个答案:

答案 0 :(得分:3)

这个怎么样:

scala> def nextString(itr: Iterator[Char], sep: String): Option[String] = {
     |    def next(res: String): String =
     |      if(res endsWith sep) res dropRight sep.size else if(itr.hasNext) next(res:+itr.next) else res
     |   if(itr.hasNext) Some(next("")) else None
     | }
nextString: (itr: Iterator[Char], sep: String)Option[String]

scala> val iterator: Iterator[Char] = "Scala_smurf_is_smurf_great".iterator
iterator: Iterator[Char] = non-empty iterator

scala> println(nextString(iterator, "_smurf_"))
Some(Scala)

scala> println(nextString(iterator, "_smurf_"))
Some(is)

scala> println(nextString(iterator, "_smurf_"))
Some(great)

scala> println(nextString(iterator, "_smurf_"))
None

scala> println(nextString("FooBarBaz".iterator, "_smurf_"))
Some(FooBarBaz)

答案 1 :(得分:3)

这个怎么样?

def nextString(iterator: Iterator[Char]): Option[String] = {
    val t = iterator.toStream

    val index = t.indexOfSlice(s)
    if(t.isEmpty) None
    else if(index == -1) Some(t.mkString)
    else Some(t.slice(0,index).mkString)
  }

它通过了这个测试:

val desmurfer = new MyClass("_smurf_")
val iterator: Iterator[Char] = "Scala_smurf_is_smurf_great_smurf__smurf_".iterator
assert(desmurfer.nextString(iterator) == Some("Scala"))
assert(desmurfer.nextString(iterator) == Some("is"))
assert(desmurfer.nextString(iterator) == Some("great"))
assert(desmurfer.nextString(iterator) == Some(""))
assert(desmurfer.nextString(iterator) == None)

assert(desmurfer.nextString("FooBarBaz".iterator) == Some("FooBarBaz"))
assert(desmurfer.nextString("".iterator) == None)

已更新:已删除“index == -1&amp;&amp;”从第一个“如果条件条款”。

答案 2 :(得分:2)

这似乎正在做你想要的。 @Eastsun回答激励我

val str = "hello"

  def nextString2(iterator: Iterator[Char]): Option[String] = {
    val maxSize = str.size
    @tailrec
    def inner(collected: List[Char], queue: Queue[Char]): Option[List[Char]] =
      if (queue.size == maxSize && queue.sameElements(str))
        Some(collected.reverse.dropRight(maxSize))
      else
        iterator.find(x => true) match {
          case Some(el) => inner(el :: collected, if (queue.size == maxSize) queue.dequeue._2.enqueue(el) else queue.enqueue(el))
          case None => Some(collected.reverse)
        }

    if (iterator.hasNext)
      inner(Nil, Queue.empty).map(_.mkString)
    else
      None
  }

  test(nextString2(Nil.iterator)) === None
  test(nextString2("".iterator)) === None
  test(nextString2("asd".iterator)) === Some("asd")
  test(nextString2("asfhello".iterator)) === Some("asf")
  test(nextString2("asehelloasdasd".iterator)) === Some("ase")

但老实说,我认为它太复杂而无法使用。有时你必须在scala中使用非FP内容才能提高性能。

P.S。我不知道如何在它的第一个元素上匹配迭代器,所以我使用了丑陋的iterator.find(x => true)。遗憾。

P.P.S。一点解释。我经常积累collected来填充你要搜索的元素。我还使用最后queue个元素构建str.size。然后我每次只检查此队列str。这可能不是最有效的方法。如果你想要更多,可以使用Aho-Corasick算法或模拟算法。

P.P.P.S。我使用iterator作为状态,这可能不是FP方式

P.P.P.P.S。你也测试了通行证:

  val desmurfer = new MyClass("_smurf_")
  val iterator: Iterator[Char] = "Scala_smurf_is_smurf_great".iterator
  test(desmurfer.nextString2(iterator)) === Some("Scala")
  test(desmurfer.nextString2(iterator)) === Some("is")
  test(desmurfer.nextString2(iterator)) === Some("great")
  test(desmurfer.nextString2(iterator)) === None
  println()
  test(desmurfer.nextString2("FooBarBaz".iterator)) === Some("FooBarBaz")
  test(desmurfer.nextString2("".iterator)) === None

答案 3 :(得分:2)

这是我发布的一篇文章,因为它有点扭曲:)我不建议实际使用它:

  class MyClass2(str: String) {
    val sepLength = str.length
    def nextString(iterator: Iterator[Char]): Option[String] = {
      if (!iterator.hasNext) return None

      val sit = iterator.sliding(sepLength)
      val prefix = sit.takeWhile(_.mkString != str).toList

      val prefixString = prefix.toList.map(_.head).mkString
      if (prefix.head.length < sepLength) Some(prefix.head.mkString)
      else if (!iterator.hasNext) Some(prefix.head.mkString + prefix.last.mkString)
      else Some(prefixString)

    }
  }

这个想法是通过在我们的底层迭代器上调用sliding(),我们可以得到一个序列,其中一个序列将是我们的分隔符,如果它存在的话。因此我们可以使用takeWhile来查找分隔符。然后我们的分隔符之前的每个滑动字符串的第一个字符是我们跳过的字符串。正如我所说,扭曲了。

我真的希望sliding被定义,以便它生成长度为n的所有子序列,并且在长度n-1n-2的末尾序列。 ... 1对于这个特定的用例,但它没有,而最后可怕的if语句正在处理各种情况。

通过测试用例:)

答案 4 :(得分:0)

更新:无需将迭代器转换为String

即可
def nextString(iterator: Iterator[Char]): Option[String] = {
  if (iterator.isEmpty) None
  else Some(iterator.foldLeft("") { (result, currentChar) => if (res.endsWith(str)) result else result + currentChar})
}

答案 5 :(得分:0)

一位同事提供了这个答案的材料,这是他原来的方法和我身边的一些抛光之间的混合物。谢谢,埃文斯! 然后另一位同事也加入了一些意见。谢谢Ako: - )

class MyClass(str: String) {
  def nextString(iterator: Iterator[Char]): Option[String] = {

    def nextString(iterator: Iterator[Char], sb: StringBuilder): Option[String] = {
      if (!iterator.hasNext || sb.endsWith(str)) {
        Some(sb.stripSuffix(str))
      } else {
        nextString(iterator, sb.append(iterator.next()))
      }
    }

    if (!iterator.hasNext) None
    else nextString(iterator, new StringBuilder)
  }
}

到目前为止,我最喜欢这种方法,所以我会在两天内接受它,除非那时有更好的答案。