From f540f0350315c2af8779286f85084405cdbf0d76 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 6 Nov 2009 19:25:10 +0000 Subject: - Renames filterMap to partialMap - Renames GenericRange to NumericRange - Removes most @experimental annotations --- src/compiler/scala/tools/nsc/io/AbstractFile.scala | 2 +- src/compiler/scala/tools/nsc/io/Directory.scala | 6 +- src/compiler/scala/tools/nsc/io/Process.scala | 3 - .../tools/nsc/matching/ParallelMatching.scala | 2 +- src/library/scala/BigDecimal.scala | 12 +- src/library/scala/BigInt.scala | 2 +- src/library/scala/Function.scala | 25 --- src/library/scala/Numeric.scala | 2 +- src/library/scala/Option.scala | 2 +- src/library/scala/PartialFunction.scala | 7 +- src/library/scala/annotation/experimental.scala | 1 + src/library/scala/collection/Iterator.scala | 3 +- src/library/scala/collection/TraversableLike.scala | 6 +- .../scala/collection/immutable/GenericRange.scala | 223 --------------------- .../scala/collection/immutable/NumericRange.scala | 222 ++++++++++++++++++++ src/library/scala/collection/immutable/Range.scala | 16 +- .../collection/interfaces/TraversableMethods.scala | 2 +- src/library/scala/runtime/RichDouble.scala | 10 +- src/library/scala/runtime/RichLong.scala | 8 +- 19 files changed, 259 insertions(+), 295 deletions(-) delete mode 100644 src/library/scala/collection/immutable/GenericRange.scala create mode 100644 src/library/scala/collection/immutable/NumericRange.scala (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/io/AbstractFile.scala b/src/compiler/scala/tools/nsc/io/AbstractFile.scala index 51bfb741cb..9e40feeca9 100644 --- a/src/compiler/scala/tools/nsc/io/AbstractFile.scala +++ b/src/compiler/scala/tools/nsc/io/AbstractFile.scala @@ -58,7 +58,7 @@ object AbstractFile * @return ... */ def getURL(url: URL): AbstractFile = - Option(url) filterMap { case url: URL if isJarOrZip(url.getPath) => ZipArchive fromURL url } orNull + Option(url) partialMap { case url: URL if isJarOrZip(url.getPath) => ZipArchive fromURL url } orNull } /** diff --git a/src/compiler/scala/tools/nsc/io/Directory.scala b/src/compiler/scala/tools/nsc/io/Directory.scala index 139e433383..3a01277fef 100644 --- a/src/compiler/scala/tools/nsc/io/Directory.scala +++ b/src/compiler/scala/tools/nsc/io/Directory.scala @@ -44,8 +44,8 @@ class Directory(jfile: JFile) extends Path(jfile) case xs => xs.iterator map Path.apply } - def dirs: Iterator[Directory] = list filterMap { case x: Directory => x } - def files: Iterator[File] = list filterMap { case x: File => x } + def dirs: Iterator[Directory] = list partialMap { case x: Directory => x } + def files: Iterator[File] = list partialMap { case x: File => x } /** If optional depth argument is not given, will recurse * until it runs out of contents. @@ -59,7 +59,7 @@ class Directory(jfile: JFile) extends Path(jfile) * to the (optionally) given depth. */ def subdirs(depth: Int = 1): Iterator[Directory] = - deepList(depth) filterMap { case x: Directory => x } + deepList(depth) partialMap { case x: Directory => x } /** Deletes the directory recursively. Returns false on failure. * Use with caution! diff --git a/src/compiler/scala/tools/nsc/io/Process.scala b/src/compiler/scala/tools/nsc/io/Process.scala index e6626e0abe..5b22028741 100644 --- a/src/compiler/scala/tools/nsc/io/Process.scala +++ b/src/compiler/scala/tools/nsc/io/Process.scala @@ -33,13 +33,11 @@ import java.util.concurrent.LinkedBlockingQueue * @since 2.8 */ -@experimental object Process { lazy val javaVmArguments = java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments() lazy val runtime = Runtime.getRuntime() - @experimental private[Process] class ProcessBuilder(val pb: JProcessBuilder) { def this(cmd: String*) = this(new JProcessBuilder(cmd: _*)) @@ -105,7 +103,6 @@ object Process } import Process._ -@experimental class Process(processCreator: () => JProcess) extends Iterable[String] { lazy val process = processCreator() diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala index ba7eac7ea0..4bc8826908 100644 --- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala +++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala @@ -150,7 +150,7 @@ trait ParallelMatching extends ast.TreeDSL if (!scrut.isSimple) None else { val (_lits, others) = ps span isSwitchableConst - val lits = _lits filterMap { case x: LiteralPattern => x } + val lits = _lits partialMap { case x: LiteralPattern => x } condOpt(others) { case Nil => new PatternSwitch(scrut, lits, None) diff --git a/src/library/scala/BigDecimal.scala b/src/library/scala/BigDecimal.scala index fc874db1f2..b43e4b2a04 100644 --- a/src/library/scala/BigDecimal.scala +++ b/src/library/scala/BigDecimal.scala @@ -12,7 +12,7 @@ package scala import java.{ lang => jl } import java.math.{ MathContext, BigDecimal => BigDec } -import scala.collection.immutable.GenericRange +import scala.collection.immutable.NumericRange /** Conversions which present a consistent conversion interface * across all the numeric types. @@ -359,27 +359,27 @@ extends jl.Number with ScalaNumericConversions def toIntExact = bigDecimal.intValueExact def toLongExact = bigDecimal.longValueExact - /** Creates a partially constructed GenericRange[BigDecimal] in range + /** Creates a partially constructed NumericRange[BigDecimal] in range * [start;end), where start is the target BigDecimal. The step * must be supplied via the "by" method of the returned object in order * to receive the fully constructed range. For example: *
    * val partial = BigDecimal(1.0) to 2.0       // not usable yet
-   * val range = partial by 0.01                // now a GenericRange
+   * val range = partial by 0.01                // now a NumericRange
    * val range2 = BigDecimal(0) to 1.0 by 0.01  // all at once of course is fine too
    * 
* * @param end the end value of the range (exclusive) - * @return the partially constructed GenericRange + * @return the partially constructed NumericRange */ - def until(end: BigDecimal): Range.Partial[BigDecimal, GenericRange.Exclusive[BigDecimal]] = + def until(end: BigDecimal): Range.Partial[BigDecimal, NumericRange.Exclusive[BigDecimal]] = new Range.Partial(until(end, _)) /** Same as the one-argument until, but creates the range immediately. */ def until(end: BigDecimal, step: BigDecimal) = Range.BigDecimal(this, end, step) /** Like until, but inclusive of the end value. */ - def to(end: BigDecimal): Range.Partial[BigDecimal, GenericRange.Inclusive[BigDecimal]] = + def to(end: BigDecimal): Range.Partial[BigDecimal, NumericRange.Inclusive[BigDecimal]] = new Range.Partial(to(end, _)) /** Like until, but inclusive of the end value. */ diff --git a/src/library/scala/BigInt.scala b/src/library/scala/BigInt.scala index dd84371123..9ccd9663a7 100644 --- a/src/library/scala/BigInt.scala +++ b/src/library/scala/BigInt.scala @@ -339,7 +339,7 @@ class BigInt(val bigInteger: BigInteger) extends jl.Number with ScalaNumericConv */ def doubleValue = this.bigInteger.doubleValue - /** Create a GenericRange[BigInt] in range [start;end) + /** Create a NumericRange[BigInt] in range [start;end) * with the specified step, where start is the target BigInt. * * @param end the end value of the range (exclusive) diff --git a/src/library/scala/Function.scala b/src/library/scala/Function.scala index a76fa61039..4d4d21c1c6 100644 --- a/src/library/scala/Function.scala +++ b/src/library/scala/Function.scala @@ -20,31 +20,6 @@ import annotation.experimental */ object Function { - /** Given a starting value, the returned object can be repeatedly - * applied with Function1s and then the result retrieved with apply(). - * At each iteration the argument is checked for null before function - * application; if it is ever null, the result will be null. - * - *
-   *    case class Bop(next: Bop)
-   *    val x = Bop(Bop(Bop(null)))
-   *    ??(x)(_.next)()                         // returns Bop(Bop(null))
-   *    ??(x)(_.next)(_.next)()                 // returns Bop(null)
-   *    ??(x)(_.next)(_.next)(_.next)()         // returns null
-   *    ??(x)(_.next)(_.next)(_.next)(_.next)() // still returns null!
-   *  
- * - * @param x The starting value - * @return The ?? object, containing apply methods T => U and () => T - */ - @experimental - case class ??[T](x: T) { - def apply(): T = x - def apply[U >: Null](f: T => U): ??[U] = - if (x == null) ??[U](null) - else ??[U](f(x)) - } - /** Given a sequence of functions f1, ..., * fn, return the function f1 * andThen ... andThen fn. diff --git a/src/library/scala/Numeric.scala b/src/library/scala/Numeric.scala index 3048f9287d..fa71df6383 100644 --- a/src/library/scala/Numeric.scala +++ b/src/library/scala/Numeric.scala @@ -144,7 +144,7 @@ object Numeric { } // For Double and BigDecimal we offer implicit Fractional objects, but also one - // which acts like an Integral type, which is useful in GenericRange. + // which acts like an Integral type, which is useful in NumericRange. implicit object BigDecimalIsFractional extends BigDecimalIsFractional with Ordering.BigDecimalOrdering object BigDecimalAsIfIntegral extends BigDecimalAsIfIntegral with Ordering.BigDecimalOrdering diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala index cabf3cf74b..feacfd6acd 100644 --- a/src/library/scala/Option.scala +++ b/src/library/scala/Option.scala @@ -121,7 +121,7 @@ sealed abstract class Option[+A] extends Product { * @param pf the partial function. */ @experimental - def filterMap[B](pf: PartialFunction[Any, B]): Option[B] = + def partialMap[B](pf: PartialFunction[Any, B]): Option[B] = if (!isEmpty && pf.isDefinedAt(this.get)) Some(pf(this.get)) else None /** If the option is nonempty return it, diff --git a/src/library/scala/PartialFunction.scala b/src/library/scala/PartialFunction.scala index 19dbc0308c..0801917aed 100644 --- a/src/library/scala/PartialFunction.scala +++ b/src/library/scala/PartialFunction.scala @@ -61,7 +61,6 @@ trait PartialFunction[-A, +B] extends AnyRef with (A => B) { * @author Paul Phillips * @since 2.8 */ -@experimental object PartialFunction { /** Creates a Boolean test based on a value and a partial function. @@ -75,7 +74,7 @@ object PartialFunction def cond[T](x: T)(pf: PartialFunction[T, Boolean]): Boolean = (pf isDefinedAt x) && pf(x) - /** Transforms a PartialFunction[T,U] `pf' into Function1[T,Option[U]] `f' + /** Transforms a PartialFunction[T,U] `pf' into Function1[T, Option[U]] `f' * whose result is Some(x) if the argument is in pf's domain and None otherwise, * and applies it to the value `x'. In effect, it is a 'match' statement * which wraps all case results in Some(_) and adds 'case _ => None' to the end. @@ -86,8 +85,4 @@ object PartialFunction */ def condOpt[T,U](x: T)(pf: PartialFunction[T, U]): Option[U] = if (pf isDefinedAt x) Some(pf(x)) else None - - // If only getOrElse were a bit less unwieldy... - // def opt[T,U](x: T, default: U)(pf: PartialFunction[T, U]): U = - // opt(x)(pf) getOrElse default } diff --git a/src/library/scala/annotation/experimental.scala b/src/library/scala/annotation/experimental.scala index 9834ea4b21..43ce631e2d 100644 --- a/src/library/scala/annotation/experimental.scala +++ b/src/library/scala/annotation/experimental.scala @@ -13,6 +13,7 @@ package scala.annotation * * @since 2.8 */ +@experimental // and an experiment which may soon be ending final class experimental(message: String) extends StaticAnnotation { def this() = this("") } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index cb40bf0383..6617216e65 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -390,8 +390,7 @@ trait Iterator[+A] { self => * @param pf the partial function which filters and maps the iterator. * @return the new iterator. */ - @experimental - def filterMap[B](pf: PartialFunction[Any, B]): Iterator[B] = { + def partialMap[B](pf: PartialFunction[Any, B]): Iterator[B] = { val self = buffered new Iterator[B] { private def skip() = while (self.hasNext && !pf.isDefinedAt(self.head)) self.next() diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 2b0a938ca8..57f9840804 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -195,8 +195,7 @@ self => * @param pf the partial function which filters and maps the traversable. * @return the new traversable. */ - @experimental - def filterMap[B, That](pf: PartialFunction[Any, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { + def partialMap[B, That](pf: PartialFunction[Any, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) for (x <- this) if (pf.isDefinedAt(x)) b += pf(x) b.result @@ -730,9 +729,8 @@ self => */ def toStream: Stream[A] = toList.toStream - /** Returns a set with all unique elements in this traversable object. + /** Returns an immutable set with all unique elements in this traversable object. */ - @experimental def toSet[B >: A]: immutable.Set[B] = immutable.Set() ++ thisCollection /** Returns a string representation of this traversable object. The resulting string diff --git a/src/library/scala/collection/immutable/GenericRange.scala b/src/library/scala/collection/immutable/GenericRange.scala deleted file mode 100644 index b541b900a0..0000000000 --- a/src/library/scala/collection/immutable/GenericRange.scala +++ /dev/null @@ -1,223 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2006-2009, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -// $Id: GenericRange.scala 18987 2009-10-08 18:31:44Z odersky $ - -package scala.collection -package immutable - -import annotation.experimental -import mutable.{ Builder, ListBuffer } -import generic._ - -/**

- * GenericRange is a generified version of the - * Range class which works with arbitrary types. - * It must be supplied with an Integral implementation of the - * range type. - * - * Factories for likely types include Range.BigInt, Range.Long, - * and Range.BigDecimal. Range.Int exists for completeness, but - * the Int-based scala.Range should be more performant. - *

- *     val r1 = new Range(0, 100, 1)
- *     val veryBig = Math.MAX_INT.toLong + 1
- *     val r2 = Range.Long(veryBig, veryBig + 100, 1)
- *     assert(r1 sameElements r2.map(_ - veryBig))
- *  
- * - * @author Paul Phillips - * @version 2.8 - */ -@experimental -abstract class GenericRange[+T] - (val start: T, val end: T, val step: T, val isInclusive: Boolean) - (implicit num: Integral[T]) -extends IndexedSeq[T] -{ - import num._ - - private def fail(msg: String) = throw new UnsupportedOperationException(msg) - - if (step equiv zero) - fail("GenericRange step cannot be zero.") - - // todo? - we could lift the length restriction by implementing a range as a sequence of - // subranges and limiting the subranges to MAX_INT. There's no other way around it because - // the generics we inherit assume integer-based indexing (as well they should.) - // The second condition is making sure type T can meaningfully be compared to Math.MAX_INT. - if (genericLength > fromInt(Math.MAX_INT) && (Math.MAX_INT == toInt(fromInt(Math.MAX_INT)))) - fail("Implementation restricts ranges to Math.MAX_INT elements.") - - // inclusive/exclusiveness captured this way because we do not have any - // concept of a "unit", we can't just add an epsilon to an exclusive - // endpoint to make it inclusive (as can be done with the int-based Range.) - protected def limitTest[U >: T](x: U)(implicit unum: Integral[U]) = - !isEmpty && isInclusive && unum.equiv(x, end) - - protected def underlying = collection.immutable.IndexedSeq.empty[T] - - /** Create a new range with the start and end values of this range and - * a new step. - */ - def by[U >: T](newStep: U)(implicit unum: Integral[U]): GenericRange[U] = - copy(start, end, newStep) - - /** Create a copy of this range. - */ - def copy[U >: T](start: U, end: U, step: U)(implicit unum: Integral[U]): GenericRange[U] - - override def foreach[U](f: T => U) { - var i = start - if (step > zero) { - while (i < end) { - f(i) - i = i + step - } - } else { - while (i > end) { - f(i) - i = i + step - } - } - if (limitTest(i)) f(i) - } - - def genericLength: T = { - def lim = if (limitTest(end)) one else zero - - if ((start < end && step < zero) || (start > end && step > zero)) zero - else if (equiv(start, end)) lim - else { - val (steps, left) = (end - start) /% step - val last = if (!equiv(left, zero) || isInclusive) one else zero - - steps + last - } - } - - def length: Int = toInt(genericLength) - override def isEmpty = - if (step > zero) - if (isInclusive) end < start - else end <= start - else - if (isInclusive) end > start - else end >= start - - def apply(idx: Int): T = { - if (idx < 0 || idx >= length) throw new IndexOutOfBoundsException(idx.toString) - else start + (fromInt(idx) * step) - } - - // a well-typed contains method. - def containsTyped[U >: T](x: U)(implicit unum: Integral[U]): Boolean = { - import unum._ - def divides(d: U, by: U) = equiv(d % by, zero) - - limitTest(x) || ( - if (step > zero) - (start <= x) && (x < end) && divides(x - start, step) - else - (start >= x) && (x > end) && divides(start - x, step) - ) - } - - // Motivated by the desire for Double ranges with BigDecimal precision, - // we need some way to map a Range and get another Range. This can't be - // done in any fully general way because Ranges are not arbitrary - // sequences but step-valued, so we have a custom method only we can call - // which we promise to use responsibly. - // - // The point of it all is that - // - // 0.0 to 1.0 by 0.1 - // - // should result in - // - // GenericRange[Double](0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0) - // - // and not - // - // GenericRange[Double](0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9) - // - // or perhaps more importantly, - // - // (0.1 to 0.3 by 0.1 contains 0.3) == true - // - private[immutable] def mapRange[A](fm: T => A)(implicit unum: Integral[A]): GenericRange[A] = { - val self = this - - // XXX This may be incomplete. - new GenericRange[A](fm(start), fm(end), fm(step), isInclusive) { - def copy[A1 >: A](start: A1, end: A1, step: A1)(implicit unum: Integral[A1]): GenericRange[A1] = - if (isInclusive) GenericRange.inclusive(start, end, step) - else GenericRange(start, end, step) - - private val underlyingRange: GenericRange[T] = self - override def foreach[U](f: A => U) { underlyingRange foreach (x => f(fm(x))) } - override def isEmpty = underlyingRange.isEmpty - override def apply(idx: Int): A = fm(underlyingRange(idx)) - override def containsTyped[A1 >: A](el: A1)(implicit unum: Integral[A1]) = - underlyingRange exists (x => fm(x) == el) - } - } - - // The contains situation makes for some interesting code. - // I am not aware of any way to avoid a cast somewhere, because - // contains must take an Any. - override def contains(x: Any): Boolean = - try { - // if we don't verify that x == typedX, then a range - // of e.g. Longs will appear to contain an Int because - // the cast will perform the conversion. (As of this writing - // it is anticipated that in scala 2.8, 5L != 5 although - // this is not yet implemented.) - val typedX = x.asInstanceOf[T] - containsTyped(typedX) && (x == typedX) - } - catch { case _: ClassCastException => super.contains(x) } - - override lazy val hashCode = super.hashCode() - override def equals(other: Any) = other match { - case x: GenericRange[_] => (length == x.length) && (length match { - case 0 => true - case 1 => x.start == start - case n => x.start == start && x.step == step - }) - case _ => super.equals(other) - } - override def toString() = { - val endStr = if (length > Range.MAX_PRINT) ", ... )" else ")" - take(Range.MAX_PRINT).mkString("GenericRange(", ", ", endStr) - } -} - -object GenericRange { - class Inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) - extends GenericRange(start, end, step, true) { - def copy[U >: T](start: U, end: U, step: U)(implicit unum: Integral[U]): Inclusive[U] = - GenericRange.inclusive(start, end, step) - - def exclusive: Exclusive[T] = GenericRange(start, end, step) - } - - class Exclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) - extends GenericRange(start, end, step, false) { - def copy[U >: T](start: U, end: U, step: U)(implicit unum: Integral[U]): Exclusive[U] = - GenericRange(start, end, step) - - def inclusive: Inclusive[T] = GenericRange.inclusive(start, end, step) - } - - def apply[T](start: T, end: T, step: T)(implicit num: Integral[T]): Exclusive[T] = - new Exclusive(start, end, step) - def inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]): Inclusive[T] = - new Inclusive(start, end, step) -} - diff --git a/src/library/scala/collection/immutable/NumericRange.scala b/src/library/scala/collection/immutable/NumericRange.scala new file mode 100644 index 0000000000..9d47cc13c1 --- /dev/null +++ b/src/library/scala/collection/immutable/NumericRange.scala @@ -0,0 +1,222 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2006-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: NumericRange.scala 18987 2009-10-08 18:31:44Z odersky $ + +package scala.collection +package immutable + +import annotation.experimental +import mutable.{ Builder, ListBuffer } +import generic._ + +/**

+ * NumericRange is a more generic version of the + * Range class which works with arbitrary types. + * It must be supplied with an Integral implementation of the + * range type. + * + * Factories for likely types include Range.BigInt, Range.Long, + * and Range.BigDecimal. Range.Int exists for completeness, but + * the Int-based scala.Range should be more performant. + *

+ *     val r1 = new Range(0, 100, 1)
+ *     val veryBig = Math.MAX_INT.toLong + 1
+ *     val r2 = Range.Long(veryBig, veryBig + 100, 1)
+ *     assert(r1 sameElements r2.map(_ - veryBig))
+ *  
+ * + * @author Paul Phillips + * @version 2.8 + */ +abstract class NumericRange[+T] + (val start: T, val end: T, val step: T, val isInclusive: Boolean) + (implicit num: Integral[T]) +extends IndexedSeq[T] +{ + import num._ + + private def fail(msg: String) = throw new UnsupportedOperationException(msg) + + if (step equiv zero) + fail("NumericRange step cannot be zero.") + + // todo? - we could lift the length restriction by implementing a range as a sequence of + // subranges and limiting the subranges to MAX_INT. There's no other way around it because + // the generics we inherit assume integer-based indexing (as well they should.) + // The second condition is making sure type T can meaningfully be compared to Math.MAX_INT. + if (genericLength > fromInt(Math.MAX_INT) && (Math.MAX_INT == toInt(fromInt(Math.MAX_INT)))) + fail("Implementation restricts ranges to Math.MAX_INT elements.") + + // inclusive/exclusiveness captured this way because we do not have any + // concept of a "unit", we can't just add an epsilon to an exclusive + // endpoint to make it inclusive (as can be done with the int-based Range.) + protected def limitTest[U >: T](x: U)(implicit unum: Integral[U]) = + !isEmpty && isInclusive && unum.equiv(x, end) + + protected def underlying = collection.immutable.IndexedSeq.empty[T] + + /** Create a new range with the start and end values of this range and + * a new step. + */ + def by[U >: T](newStep: U)(implicit unum: Integral[U]): NumericRange[U] = + copy(start, end, newStep) + + /** Create a copy of this range. + */ + def copy[U >: T](start: U, end: U, step: U)(implicit unum: Integral[U]): NumericRange[U] + + override def foreach[U](f: T => U) { + var i = start + if (step > zero) { + while (i < end) { + f(i) + i = i + step + } + } else { + while (i > end) { + f(i) + i = i + step + } + } + if (limitTest(i)) f(i) + } + + def genericLength: T = { + def lim = if (limitTest(end)) one else zero + + if ((start < end && step < zero) || (start > end && step > zero)) zero + else if (equiv(start, end)) lim + else { + val (steps, left) = (end - start) /% step + val last = if (!equiv(left, zero) || isInclusive) one else zero + + steps + last + } + } + + def length: Int = toInt(genericLength) + override def isEmpty = + if (step > zero) + if (isInclusive) end < start + else end <= start + else + if (isInclusive) end > start + else end >= start + + def apply(idx: Int): T = { + if (idx < 0 || idx >= length) throw new IndexOutOfBoundsException(idx.toString) + else start + (fromInt(idx) * step) + } + + // a well-typed contains method. + def containsTyped[U >: T](x: U)(implicit unum: Integral[U]): Boolean = { + import unum._ + def divides(d: U, by: U) = equiv(d % by, zero) + + limitTest(x) || ( + if (step > zero) + (start <= x) && (x < end) && divides(x - start, step) + else + (start >= x) && (x > end) && divides(start - x, step) + ) + } + + // Motivated by the desire for Double ranges with BigDecimal precision, + // we need some way to map a Range and get another Range. This can't be + // done in any fully general way because Ranges are not arbitrary + // sequences but step-valued, so we have a custom method only we can call + // which we promise to use responsibly. + // + // The point of it all is that + // + // 0.0 to 1.0 by 0.1 + // + // should result in + // + // NumericRange[Double](0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0) + // + // and not + // + // NumericRange[Double](0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9) + // + // or perhaps more importantly, + // + // (0.1 to 0.3 by 0.1 contains 0.3) == true + // + private[immutable] def mapRange[A](fm: T => A)(implicit unum: Integral[A]): NumericRange[A] = { + val self = this + + // XXX This may be incomplete. + new NumericRange[A](fm(start), fm(end), fm(step), isInclusive) { + def copy[A1 >: A](start: A1, end: A1, step: A1)(implicit unum: Integral[A1]): NumericRange[A1] = + if (isInclusive) NumericRange.inclusive(start, end, step) + else NumericRange(start, end, step) + + private val underlyingRange: NumericRange[T] = self + override def foreach[U](f: A => U) { underlyingRange foreach (x => f(fm(x))) } + override def isEmpty = underlyingRange.isEmpty + override def apply(idx: Int): A = fm(underlyingRange(idx)) + override def containsTyped[A1 >: A](el: A1)(implicit unum: Integral[A1]) = + underlyingRange exists (x => fm(x) == el) + } + } + + // The contains situation makes for some interesting code. + // I am not aware of any way to avoid a cast somewhere, because + // contains must take an Any. + override def contains(x: Any): Boolean = + try { + // if we don't verify that x == typedX, then a range + // of e.g. Longs will appear to contain an Int because + // the cast will perform the conversion. (As of this writing + // it is anticipated that in scala 2.8, 5L != 5 although + // this is not yet implemented.) + val typedX = x.asInstanceOf[T] + containsTyped(typedX) && (x == typedX) + } + catch { case _: ClassCastException => super.contains(x) } + + override lazy val hashCode = super.hashCode() + override def equals(other: Any) = other match { + case x: NumericRange[_] => (length == x.length) && (length match { + case 0 => true + case 1 => x.start == start + case n => x.start == start && x.step == step + }) + case _ => super.equals(other) + } + override def toString() = { + val endStr = if (length > Range.MAX_PRINT) ", ... )" else ")" + take(Range.MAX_PRINT).mkString("NumericRange(", ", ", endStr) + } +} + +object NumericRange { + class Inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) + extends NumericRange(start, end, step, true) { + def copy[U >: T](start: U, end: U, step: U)(implicit unum: Integral[U]): Inclusive[U] = + NumericRange.inclusive(start, end, step) + + def exclusive: Exclusive[T] = NumericRange(start, end, step) + } + + class Exclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]) + extends NumericRange(start, end, step, false) { + def copy[U >: T](start: U, end: U, step: U)(implicit unum: Integral[U]): Exclusive[U] = + NumericRange(start, end, step) + + def inclusive: Inclusive[T] = NumericRange.inclusive(start, end, step) + } + + def apply[T](start: T, end: T, step: T)(implicit num: Integral[T]): Exclusive[T] = + new Exclusive(start, end, step) + def inclusive[T](start: T, end: T, step: T)(implicit num: Integral[T]): Inclusive[T] = + new Inclusive(start, end, step) +} + diff --git a/src/library/scala/collection/immutable/Range.scala b/src/library/scala/collection/immutable/Range.scala index 18b457e090..d9a7725d1a 100644 --- a/src/library/scala/collection/immutable/Range.scala +++ b/src/library/scala/collection/immutable/Range.scala @@ -172,13 +172,13 @@ object Range { // BigInt and Long are straightforward generic ranges. object BigInt { - def apply(start: BigInt, end: BigInt, step: BigInt) = GenericRange(start, end, step) - def inclusive(start: BigInt, end: BigInt, step: BigInt) = GenericRange.inclusive(start, end, step) + def apply(start: BigInt, end: BigInt, step: BigInt) = NumericRange(start, end, step) + def inclusive(start: BigInt, end: BigInt, step: BigInt) = NumericRange.inclusive(start, end, step) } object Long { - def apply(start: Long, end: Long, step: Long) = GenericRange(start, end, step) - def inclusive(start: Long, end: Long, step: Long) = GenericRange.inclusive(start, end, step) + def apply(start: Long, end: Long, step: Long) = NumericRange(start, end, step) + def inclusive(start: Long, end: Long, step: Long) = NumericRange.inclusive(start, end, step) } // BigDecimal uses an alternative implementation of Numeric in which @@ -190,9 +190,9 @@ object Range { implicit val bigDecAsIntegral = scala.Numeric.BigDecimalAsIfIntegral def apply(start: BigDecimal, end: BigDecimal, step: BigDecimal) = - GenericRange(start, end, step) + NumericRange(start, end, step) def inclusive(start: BigDecimal, end: BigDecimal, step: BigDecimal) = - GenericRange.inclusive(start, end, step) + NumericRange.inclusive(start, end, step) } // Double works by using a BigDecimal under the hood for precise @@ -224,7 +224,7 @@ object Range { // indefinitely, for performance and because the compiler seems to bootstrap // off it and won't do so with our parameterized version without modifications. object Int { - def apply(start: Int, end: Int, step: Int) = GenericRange(start, end, step) - def inclusive(start: Int, end: Int, step: Int) = GenericRange.inclusive(start, end, step) + def apply(start: Int, end: Int, step: Int) = NumericRange(start, end, step) + def inclusive(start: Int, end: Int, step: Int) = NumericRange.inclusive(start, end, step) } } diff --git a/src/library/scala/collection/interfaces/TraversableMethods.scala b/src/library/scala/collection/interfaces/TraversableMethods.scala index 25a1f6dc49..c42ec00f0e 100644 --- a/src/library/scala/collection/interfaces/TraversableMethods.scala +++ b/src/library/scala/collection/interfaces/TraversableMethods.scala @@ -24,7 +24,7 @@ trait TraversableMethods[+A, +This <: TraversableLike[A, This] with Traversable[ // maps/iteration def flatMap[B, That](f: A => Traversable[B])(implicit bf: CanBuildFrom[This, B, That]): That def map[B, That](f: A => B)(implicit bf: CanBuildFrom[This, B, That]): That - def filterMap[B, That](pf: PartialFunction[Any, B])(implicit bf: CanBuildFrom[This, B, That]): That + def partialMap[B, That](pf: PartialFunction[Any, B])(implicit bf: CanBuildFrom[This, B, That]): That // new collections def ++[B >: A, That](that: Iterator[B])(implicit bf: CanBuildFrom[This, B, That]): That diff --git a/src/library/scala/runtime/RichDouble.scala b/src/library/scala/runtime/RichDouble.scala index 6e5c33bcb5..d30cd899a2 100644 --- a/src/library/scala/runtime/RichDouble.scala +++ b/src/library/scala/runtime/RichDouble.scala @@ -11,7 +11,7 @@ package scala.runtime -import scala.collection.immutable.{Range, GenericRange} +import scala.collection.immutable.{Range, NumericRange} final class RichDouble(x: Double) extends Proxy with Ordered[Double] { // Proxy.self @@ -28,19 +28,19 @@ final class RichDouble(x: Double) extends Proxy with Ordered[Double] { def floor: Double = Math.floor(x) /** See BigDecimal.until. */ - def until(end: Double): Range.Partial[Double, GenericRange[Double]] = + def until(end: Double): Range.Partial[Double, NumericRange[Double]] = new Range.Partial(until(end, _)) /** See BigDecimal.until. */ - def until(end: Double, step: Double): GenericRange[Double] = + def until(end: Double, step: Double): NumericRange[Double] = Range.Double(x, end, step) /** See BigDecimal.to. */ - def to(end: Double): Range.Partial[Double, GenericRange[Double]] = + def to(end: Double): Range.Partial[Double, NumericRange[Double]] = new Range.Partial(to(end, _)) /** See BigDecimal.to. */ - def to(end: Double, step: Double): GenericRange[Double] = + def to(end: Double, step: Double): NumericRange[Double] = Range.Double.inclusive(x, end, step) /** Converts an angle measured in degrees to an approximately equivalent diff --git a/src/library/scala/runtime/RichLong.scala b/src/library/scala/runtime/RichLong.scala index 8cdd21d5dc..7e8a5d71ba 100644 --- a/src/library/scala/runtime/RichLong.scala +++ b/src/library/scala/runtime/RichLong.scala @@ -11,7 +11,7 @@ package scala.runtime -import scala.collection.immutable.{Range, GenericRange} +import scala.collection.immutable.{Range, NumericRange} final class RichLong(x: Long) extends Proxy with Ordered[Long] { @@ -21,18 +21,18 @@ final class RichLong(x: Long) extends Proxy with Ordered[Long] { // Ordered[Long].compare def compare(y: Long): Int = if (x < y) -1 else if (x > y) 1 else 0 - /** Create a GenericRange[Long] in range [start;end) + /** Create a NumericRange[Long] in range [start;end) * with the specified step, where start is the target Long. * * @param end the end value of the range (exclusive) * @param step the distance between elements (defaults to 1) * @return the range */ - def until(end: Long, step: Long = 1L): GenericRange.Exclusive[Long] = Range.Long(x, end, step) + def until(end: Long, step: Long = 1L): NumericRange.Exclusive[Long] = Range.Long(x, end, step) /** Like until, but inclusive of the end value. */ - def to(end: Long, step: Long = 1L): GenericRange.Inclusive[Long] = Range.Long.inclusive(x, end, step) + def to(end: Long, step: Long = 1L): NumericRange.Inclusive[Long] = Range.Long.inclusive(x, end, step) def min(y: Long): Long = if (x < y) x else y def max(y: Long): Long = if (x > y) x else y -- cgit v1.2.3