Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
String 如何从迭代器中删除重复的元素?_String_List_Scala_Collections_Functional Programming - Fatal编程技术网

String 如何从迭代器中删除重复的元素?

String 如何从迭代器中删除重复的元素?,string,list,scala,collections,functional-programming,String,List,Scala,Collections,Functional Programming,这是我上一次的后续行动。 如何编写一个函数来过滤给定迭代器中相邻的重复项 def remove[A](it: Iterator[A]): Iterator[A] = ??? remove("aaabccbbad".iterator).toList.map(_.mkString) // "abcbad" 另外,当整个输入不适合内存时,该功能应能工作。这就是函数使用迭代器的原因。您可以使用以下内容: "aaabccbbad" .map(ch => s"${ch}") .red

这是我上一次的后续行动。
如何编写一个函数来过滤给定迭代器中相邻的重复项

def remove[A](it: Iterator[A]): Iterator[A] = ???
remove("aaabccbbad".iterator).toList.map(_.mkString) // "abcbad"

另外,当整个输入不适合内存时,该功能应能工作。这就是函数使用迭代器的原因。

您可以使用以下内容:

"aaabccbbad"
    .map(ch => s"${ch}")
    .reduce((s1, s2) => if(s1.takeRight(1) == s2) s1 else s1 + s2)
这导致

res0:String=abcbad

首先,为了方便起见,我在弦上施法。然后我将我已经拥有的最后一个字符与连续字符进行比较,如果它们不同,我将添加它

更一般地说,它可能是这样的:

stream.map(el => ListBuffer.empty.addOne(el))
    .reduce((lb1, lb2) => if(lb1.last == lb2.last) lb1 else lb1.addAll(lb2))
    .toList
def remove[A](it: Iterator[A]): Iterator[A] = split(it).flatMap(_.headOption)

有点太低了。但这确保了它只在需要时使用元素

def remove[A](it: Iterator[A]): Iterator[A] =
  new Iterator[A] {
    private[this] var current: Option[A] = None

    override def hasNext: Boolean =
      it.hasNext || (current ne None)

    override def next(): A = {
      @annotation.tailrec
      def loop(): A = (it.nextOption(), current) match {
        case (Some(a), Some(c)) if (a == c) =>
          loop()

        case (sa @ Some(a), Some(c)) =>
          current = sa
          c

        case (sa @ Some(a), None) =>
          current = sa
          loop()

        case (None, Some(c)) =>
          current = None
          c

        case (None, None) =>
          Iterator.empty[A].next()
      }

      loop()
    }
  }

大致与上面相同,但改用
展开

def remove[A](it: Iterator[A]): Iterator[A] = {
  type State = (Option[A], Option[A]) // value -> current

  def process(state: State): Option[State] = state match {
    case (Some(a), sc @ Some(c)) if (a == c) =>
      Some(None -> sc)

    case (sa @ Some(a), sc @ Some(c)) =>
      Some(sc -> sa)

    case (sa @ Some(a), None) =>
      Some(None -> sa)

    case (None, sc @ Some(c)) =>
      Some(sc -> None)

    case (None, None) =>
      None
  }

  Iterator.unfold(it.nextOption() -> Option.empty[A]) { state =>
    process(state).map {
      case (value, current) =>
        (value -> (it.nextOption() -> current))
    }
  } collect {
    case Some(a) => a
  }
}


(使用
null
而不是选项可以提高它们的效率,但它需要对原语进行特殊处理)

我会将输入迭代器转换为重复列表的迭代器,然后将每个列表映射到其头部。为了做到这一点,我将使用前面问题中的两个函数:

  • 函数
    splitDupes:Iterator[A]=>(List[A],Iterator[A])
    (建议)拆分重复的前缀,并返回一对前缀和其余前缀

  • 函数
    split:Iterator[A]=>Iterator[List[A]
    (建议)使用
    splitDupes
    将给定迭代器转换为重复列表迭代器

非常感谢您的建议。使用它们,我可以像这样实现
删除

stream.map(el => ListBuffer.empty.addOne(el))
    .reduce((lb1, lb2) => if(lb1.last == lb2.last) lb1 else lb1.addAll(lb2))
    .toList
def remove[A](it: Iterator[A]): Iterator[A] = split(it).flatMap(_.headOption)
请参见下面的
splitdupe
split
实现,仅供参考:

def splitDupes[A](it: Iterator[A]): (List[A], Iterator[A]) = {
  if (it.isEmpty) {
    (Nil, Iterator.empty)
  } else {
    val head = it.next()
    val (dupes, rest) = it.span(_ == head)
    (head +: dupes.toList, rest)
  }
}

def split[A](it: Iterator[A]): Iterator[List[A]] = {
  Iterator.iterate(splitDupes(it))(x => splitDupes(x._2)).map(_._1).takeWhile(_.nonEmpty)
}

我认为尾部递归更容易:

def remove[A](it: Iterator[A]): Iterator[A] = {
  def removeLoop(result: Iterator[A], remaining: Iterator[A]): Iterator[A] = {
    if(remaining.isEmpty) {
      result
    } else {
      val e = remaining.next();
      removeLoop(result ++ Iterator(e), remaining.dropWhile(a => a == e))
    }  
  }

  removeLoop(Iterator.empty[A], it)
}

remove("aaabccbbad".iterator).toList.map(_.toString).mkString // "abcbad"

编辑:根据评论,上面的实现并不懒惰

可能的延迟实现如下所示:

def remove[A](it: Iterator[A]): Iterator[A] = new AbstractIterator[A] {
  var lastElement: A = _

  def hasNext: Boolean = it.hasNext

  def next(): A = {
    @scala.annotation.tailrec
    def nextLoop(lastElement: A, it: Iterator[A]): A = {
      val temp = it.next
      if(lastElement == temp)
        nextLoop(lastElement, it)
      else
        temp
    }
    lastElement = nextLoop(lastElement, it)
    lastElement
  }
}

remove("aaabccbbad".iterator).take(2).foreach(print) // "ab"
remove("aaabccbbad".iterator).foreach(print) // "abcbad"

谢谢如果所有的输入都不在内存中,它会起作用吗?我更新了问题以提及内存条件。@Michael考虑到scala的流惰性,我会说是的,但我不是100%确定。我没有看到任何流。我错过什么了吗?@Michael你说得对,我没提过。您可以在
懒散列表
上使用此链,然后将惰性地计算连续元素。谢谢。不过,我希望避免实现
hasNext
next
。@Michael我建议您改为查看流媒体库。像
fs2
&
AkkaStreams
。谢谢,但我正在寻找一个没有3d派对库的解决方案。@Michael,你要求的东西太多了。使用高级combinator的高效惰性流不容易实现,因此存在提供此功能的强大库。@Michael我刚刚用更高级的备选方案编辑了我的问题。我担心这不会是惰性的。我认为解决方案是惰性的。迭代器不会转换为任何其他(非惰性)集合,因此它不会一次加载内存中的所有内容。使用的所有操作都来自迭代器类本身(主要生成新的迭代器),next()使元素可用。不过我还没有测试过。你有什么特别的理由认为它不会懒惰吗?我试过类似的方法,但它并不懒惰。我将检查此解决方案…我在
val e=remaining.next()之后添加了
println(e)
。当我运行
remove(“aaabccbbad.iterator)
时,它会打印“abcbad”的所有字符。这不是我所说的“懒惰”。我遗漏了什么吗?你说得对。实现并不是懒惰的。我又提出了一个解决方案,让它变得懒惰。