From 3d64deeb687452cb7e8e53b5a172e7bc7c50bf1b Mon Sep 17 00:00:00 2001 From: Rex Kerr Date: Thu, 29 May 2014 08:04:14 -0700 Subject: SI-8474 Inconsistent behavior of patch method Changed Iterator to be consistent with other collections. Also fixed SeqViewLike to validate/constrain inputs. No specific tests; quasi-comprehensive collection tests will cover this later. --- src/library/scala/collection/Iterator.scala | 40 +++++++++++++++++++------- src/library/scala/collection/SeqViewLike.scala | 23 +++++++++++---- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index f6f46e158f..660cc5a42a 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -1088,6 +1088,9 @@ trait Iterator[+A] extends TraversableOnce[A] { } /** Returns this iterator with patched values. + * Patching at negative indices is the same as patching starting at 0. + * Patching at indices at or larger than the length of the original iterator appends the patch to the end. + * If more values are replaced than actually exist, the excess is ignored. * * @param from The start index from which to patch * @param patchElems The iterator of patch values @@ -1096,18 +1099,33 @@ trait Iterator[+A] extends TraversableOnce[A] { */ def patch[B >: A](from: Int, patchElems: Iterator[B], replaced: Int): Iterator[B] = new AbstractIterator[B] { private var origElems = self - private var i = 0 - def hasNext: Boolean = - if (i < from) origElems.hasNext - else patchElems.hasNext || origElems.hasNext + private var i = (if (from > 0) from else 0) // Counts down, switch to patch on 0, -1 means use patch first + def hasNext: Boolean = { + if (i == 0) { + origElems = origElems drop replaced + i = -1 + } + origElems.hasNext || patchElems.hasNext + } def next(): B = { - // We have to do this *first* just in case from = 0. - if (i == from) origElems = origElems drop replaced - val result: B = - if (i < from || !patchElems.hasNext) origElems.next() - else patchElems.next() - i += 1 - result + if (i == 0) { + origElems = origElems drop replaced + i = -1 + } + if (i < 0) { + if (patchElems.hasNext) patchElems.next() + else origElems.next() + } + else { + if (origElems.hasNext) { + i -= 1 + origElems.next() + } + else { + i = -1 + patchElems.next() + } + } } } diff --git a/src/library/scala/collection/SeqViewLike.scala b/src/library/scala/collection/SeqViewLike.scala index 5e31ac4a53..e719f19c78 100644 --- a/src/library/scala/collection/SeqViewLike.scala +++ b/src/library/scala/collection/SeqViewLike.scala @@ -154,17 +154,27 @@ trait SeqViewLike[+A, } } + // Note--for this to work, must ensure 0 <= from and 0 <= replaced + // Must also take care to allow patching inside an infinite stream + // (patching in an infinite stream is not okay) trait Patched[B >: A] extends Transformed[B] { protected[this] val from: Int protected[this] val patch: GenSeq[B] protected[this] val replaced: Int private lazy val plen = patch.length override def iterator: Iterator[B] = self.iterator patch (from, patch.iterator, replaced) - def length: Int = self.length + plen - replaced - def apply(idx: Int): B = - if (idx < from) self.apply(idx) - else if (idx < from + plen) patch.apply(idx - from) + def length: Int = { + val len = self.length + val pre = math.min(from, len) + val post = math.max(0, len - pre - replaced) + pre + plen + post + } + def apply(idx: Int): B = { + val actualFrom = if (self.lengthCompare(from) < 0) self.length else from + if (idx < actualFrom) self.apply(idx) + else if (idx < actualFrom + plen) patch.apply(idx - actualFrom) else self.apply(idx - plen + replaced) + } final override protected[this] def viewIdentifier = "P" } @@ -210,7 +220,10 @@ trait SeqViewLike[+A, override def reverse: This = newReversed.asInstanceOf[This] override def patch[B >: A, That](from: Int, patch: GenSeq[B], replaced: Int)(implicit bf: CanBuildFrom[This, B, That]): That = { - newPatched(from, patch, replaced).asInstanceOf[That] + // Be careful to not evaluate the entire sequence! Patch should work (slowly, perhaps) on infinite streams. + val nonNegFrom = math.max(0,from) + val nonNegRep = math.max(0,replaced) + newPatched(nonNegFrom, patch, nonNegRep).asInstanceOf[That] // was: val b = bf(repr) // if (b.isInstanceOf[NoBuilder[_]]) newPatched(from, patch, replaced).asInstanceOf[That] // else super.patch[B, That](from, patch, replaced)(bf) -- cgit v1.2.3