Scala 在字符迭代器中查找字符串

Scala 在字符迭代器中查找字符串,scala,Scala,我有一个用例,需要从Char迭代器返回一个字符串到一个分隔符字符串(如果找到) 合同: 如果迭代器耗尽(仅在开始时),则返回None 如果找到分隔符字符串,则返回它之前的所有字符(空字符串可以),分隔符将被删除 否则返回剩余的字符 不要急于耗尽迭代器 我确实有这个可行的解决方案,但感觉像Java(我来自Java) 有没有一种方法可以更有效地实现这一点(理想情况下不改变方法签名) 更新:下面是我如何测试它的 val desmurfer = new MyClass("_smurf_") val

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

合同:

  • 如果迭代器耗尽(仅在开始时),则返回None
  • 如果找到分隔符字符串,则返回它之前的所有字符(空字符串可以),分隔符将被删除
  • 否则返回剩余的字符
  • 不要急于耗尽迭代器
我确实有这个可行的解决方案,但感觉像Java(我来自Java)

有没有一种方法可以更有效地实现这一点(理想情况下不改变方法签名)

更新:下面是我如何测试它的

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))
输出:

Some(Scala)
Some(is)
Some(great)
Some()
None

Some(FooBarBaz)
None

更新:这在不将迭代器转换为字符串的情况下工作

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})
}
这个怎么样:

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)

这似乎是在做你想做的事@东森的回答激励了我

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内容

另外,我不知道如何匹配迭代器的第一个元素,所以我使用了
iterator.find(x=>true)
,这很难看。对不起

p.p.S.一点解释。我重新构建收集的
以填充您正在搜索的元素。我还使用last
str.size
-元素构建
queue
。然后每次我都会检查这个队列。这可能不是做这些事情最有效的方法。如果您想要更多,可以使用Aho–Corasick算法或类似算法

p.p.p.S.我使用
迭代器作为状态,这可能不是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

这里有一个我只是因为它有点扭曲才发布的:)我不建议实际使用它:

  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)

    }
  }
类MyClass2(str:String){
val sepLength=str.length
def nextString(迭代器:迭代器[Char]):选项[String]={
如果(!iterator.hasNext)返回None
val sit=迭代器滑动(sepLength)
val prefix=sit.takeWhile(uu.mkString!=str).toList
val prefixString=prefix.toList.map(u.head.mkString)
if(prefix.head.length
其思想是,通过在我们的底层迭代器上调用
slide()
,我们可以得到一个序列,如果它存在,其中一个就是我们的定界符。因此,我们可以使用
takeWhile
查找分隔符。然后,在分隔符之前的每个滑动字符串的第一个字符就是我们跳过的字符串。就像我说的,扭曲了

我真的很想定义
滑动
,这样它就可以生成长度
n
的所有子序列,并且在长度
n-1
的末尾,对于这个特定用例,
n-2
1
,但是它没有,最后可怕的if语句正在处理各种情况


它通过了测试用例:)

一位同事提供了这个答案的材料,这是他最初的方法和我这边的一些润色的混合。谢谢你,埃文斯! 然后另一位同事也添加了一些输入。谢谢Ako:-)

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

这个方法怎么样

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)

更新:从第一个“if condition子句”中删除了“index==-1&&”。

您期望的样本输出是什么?@S.Karthik添加了样本输出请检查答案请检查答案如果允许我转换为字符串,答案将显而易见。我的限制条件是,我应该只根据需要使用尽可能多的字符(将该字符添加到合同中),因为我错过了该部分。我已经更新了我的答案,在不将迭代器转换为String
foldLeft
的情况下,仍然会消耗整个迭代器。是的,最好的解决方案显然是使用递归函数。Eastsun的答案似乎很完美。这很好,但我想,
:+
应该是O(n),并且在大量调用时也会出现内存问题。请看我的答案(虽然要大得多)作为我的改进尝试。因为简单,你的答案可能会更好。到目前为止,我最喜欢这个。Had也研究了.slideing,但后来拒绝了它,因为它太复杂了。在一些边缘情况下,分隔符是基础迭代器中的最后一项或第一项。在那种情况下会搞砸的。我以后可能会去修那个。您可能需要更多的测试用例…您应该将其本身转化为迭代器<代码>类MyIterator(迭代器:迭代器[Char])扩展迭代器{def hasNext()=迭代器.hasNext;def next()={}
确实很好。只是好奇:为什么要测试
index
两次?同时
index!=-1
是否可能
t.isEmpty
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)