Skip to content

Commit ae7bbd8

Browse files
Merge pull request #11354 from dotty-staging/add-missing-IArray-operations
Add missing IArray operations
2 parents cb47acd + c265957 commit ae7bbd8

File tree

3 files changed

+638
-45
lines changed

3 files changed

+638
-45
lines changed

library/src/scala/IArray.scala

Lines changed: 196 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package scala
22
import reflect.ClassTag
33

4-
import scala.collection.immutable
4+
import scala.collection.{LazyZip2, SeqView, Searching, Stepper, StepperShape}
5+
import scala.collection.immutable.ArraySeq
6+
import scala.collection.mutable.{ArrayBuilder, Builder}
57

68
opaque type IArray[+T] = Array[_ <: T]
79

@@ -39,15 +41,10 @@ object IArray:
3941
extension (arr: IArray[Object]) def length: Int = arr.asInstanceOf[Array[Object]].length
4042
extension [T](arr: IArray[T]) def length: Int = arr.asInstanceOf[Array[T]].length
4143

42-
/** Returns this array concatenated with the given array. */
43-
extension [T](arr: IArray[T]) def ++ [U >: T: ClassTag](that: IArray[U]): IArray[U] =
44-
genericArrayOps(arr) ++ that
4544

4645
/** Tests whether this array contains a given value as an element. */
4746
extension [T](arr: IArray[T]) def contains(elem: T): Boolean =
48-
// `genericArrayOps(arr).contains(elem)` does not work because `elem` does not have type `arr.T`
49-
// but we can use `exists` instead, which is how `ArrayOps#contains` itself is implemented:
50-
genericArrayOps(arr).exists(_ == elem)
47+
genericArrayOps(arr).contains(elem.asInstanceOf)
5148

5249
/** Copy elements of this array to another array. */
5350
extension [T](arr: IArray[T]) def copyToArray[U >: T](xs: Array[U]): Int =
@@ -100,7 +97,7 @@ object IArray:
10097

10198
/** Flattens a two-dimensional array by concatenating all its rows
10299
* into a single array. */
103-
extension [T](arr: IArray[T]) def flatten[U: ClassTag](using T => Iterable[U]): IArray[U] =
100+
extension [T](arr: IArray[T]) def flatten[U](using asIterable: T => Iterable[U], ct: ClassTag[U]): IArray[U] =
104101
genericArrayOps(arr).flatten
105102

106103
/** Folds the elements of this array using the specified associative binary operator. */
@@ -236,10 +233,6 @@ object IArray:
236233
extension [T](arr: IArray[T]) def splitAt(n: Int): (IArray[T], IArray[T]) =
237234
genericArrayOps(arr).splitAt(n)
238235

239-
/** Tests whether this array starts with the given array. */
240-
extension [T](arr: IArray[T]) def startsWith[U >: T](that: IArray[U], offset: Int = 0): Boolean =
241-
genericArrayOps(arr).startsWith(that)
242-
243236
/** The rest of the array without its first element. */
244237
extension [T](arr: IArray[T]) def tail: IArray[T] =
245238
genericArrayOps(arr).tail
@@ -260,59 +253,127 @@ object IArray:
260253
extension [T](arr: IArray[T]) def toArray: Array[T] =
261254
arr.clone.asInstanceOf[Array[T]]
262255

263-
/** Converts an array of pairs into an array of first elements and an array of second elements. */
264-
extension [U: ClassTag, V: ClassTag](arr: IArray[(U, V)]) def unzip: (IArray[U], IArray[V]) =
265-
genericArrayOps(arr).unzip
266-
267-
/** Returns an array formed from this array and another iterable collection
268-
* by combining corresponding elements in pairs.
269-
* If one of the two collections is longer than the other, its remaining elements are ignored. */
270-
extension [T](arr: IArray[T]) def zip[U](that: IArray[U]): IArray[(T, U)] =
271-
genericArrayOps(arr).zip(that)
256+
extension [T](arr: IArray[T])
257+
def ++[U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr) ++ suffix.toSeq
258+
def ++[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr) ++ suffix
259+
def :+ [U >: T: ClassTag](x: U): IArray[U] = genericArrayOps(arr) :+ x
260+
def :++ [U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr) :++ suffix
261+
def :++ [U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr) :++ suffix
262+
def appended[U >: T: ClassTag](x: U): IArray[U] = genericArrayOps(arr).appended(x)
263+
def appendedAll[U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr).appendedAll(suffix)
264+
def appendedAll[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr).appendedAll(suffix)
265+
def collect[U: ClassTag](pf: PartialFunction[T, U]): IArray[U] = genericArrayOps(arr).collect(pf)
266+
def collectFirst[U](f: PartialFunction[T, U]): Option[U] = genericArrayOps(arr).collectFirst(f)
267+
def combinations(n: Int): Iterator[IArray[T]] = genericArrayOps(arr).combinations(n)
268+
def concat[U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr).concat(suffix)
269+
def concat[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr).concat(suffix)
270+
def diff[U >: T](that: IArray[U]): IArray[T] = genericArrayOps(arr).diff(that.toSeq)
271+
def diff[U >: T](that: Seq[U]): IArray[T] = genericArrayOps(arr).diff(that)
272+
def distinct: IArray[T] = genericArrayOps(arr).distinct
273+
def distinctBy[U](f: T => U): IArray[T] = genericArrayOps(arr).distinctBy(f)
274+
def startsWith[U >: T](that: IArray[U]): Boolean = genericArrayOps(arr).startsWith(that, 0)
275+
def startsWith[U >: T](that: IArray[U], offset: Int): Boolean = genericArrayOps(arr).startsWith(that, offset)
276+
def startsWith[U >: T](that: IterableOnce[U]): Boolean = genericArrayOps(arr).startsWith(that, 0)
277+
def startsWith[U >: T](that: IterableOnce[U], offset: Int): Boolean = genericArrayOps(arr).startsWith(that, offset)
278+
def endsWith[U >: T](that: IArray[U]): Boolean = genericArrayOps(arr).endsWith(that)
279+
def endsWith[U >: T](that: Iterable[U]): Boolean = genericArrayOps(arr).endsWith(that)
280+
def groupBy[K](f: T => K): Map[K, IArray[T]] = genericArrayOps(arr).groupBy(f)
281+
def groupMap[K, U: ClassTag](key: T => K)(f: T => U): Map[K, IArray[U]] = genericArrayOps(arr).groupMap(key)(f)
282+
def grouped(size: Int): Iterator[IArray[T]] = genericArrayOps(arr).grouped(size)
283+
def inits: Iterator[IArray[T]] = genericArrayOps(arr).inits
284+
def intersect[U >: T](that: IArray[U]): IArray[T] = genericArrayOps(arr).intersect(that)
285+
def intersect[U >: T](that: Seq[U]): IArray[T] = genericArrayOps(arr).intersect(that)
286+
def lazyZip[U](that: IArray[U]): LazyZip2[T, U, IArray[T]] = genericArrayOps(arr).lazyZip[U](that).asInstanceOf[LazyZip2[T, U, IArray[T]]]
287+
def lazyZip[U](that: Iterable[U]): LazyZip2[T, U, IArray[T]] = genericArrayOps(arr).lazyZip[U](that).asInstanceOf[LazyZip2[T, U, IArray[T]]]
288+
def lengthCompare(len: Int): Int = genericArrayOps(arr).lengthCompare(len)
289+
def padTo[U >: T: ClassTag](len: Int, elem: U): IArray[U] = genericArrayOps(arr).padTo(len, elem)
290+
def partitionMap[T1: ClassTag, T2: ClassTag](f: T => Either[T1, T2]): (IArray[T1], IArray[T2]) = genericArrayOps(arr).partitionMap(f)
291+
def patch[U >: T: ClassTag](from: Int, other: IterableOnce[U], replaced: Int): IArray[U] = genericArrayOps(arr).patch(from, other, replaced)
292+
def permutations: Iterator[IArray[T]] = genericArrayOps(arr).permutations
293+
def prepended[U >: T: ClassTag](x: U): IArray[U] = genericArrayOps(arr).prepended(x)
294+
def prependedAll[U >: T: ClassTag](prefix: IterableOnce[U]): IArray[U] = genericArrayOps(arr).prependedAll(prefix)
295+
def reverseIterator: Iterator[T] = genericArrayOps(arr).reverseIterator
296+
def search[U >: T](elem: U)(using Ordering[U]): Searching.SearchResult = arr.toSeq.search(elem)
297+
def search[U >: T](elem: U, from: Int, to: Int)(using Ordering[U]): Searching.SearchResult = arr.toSeq.search(elem, from, to)
298+
def sizeCompare(that: IArray[Any]): Int = arr.toSeq.sizeCompare(that)
299+
def sizeCompare(that: Iterable[_]): Int = arr.toSeq.sizeCompare(that)
300+
def sizeCompare(otherSize: Int): Int = genericArrayOps(arr).sizeCompare(otherSize)
301+
def sliding(size: Int, step: Int = 1): Iterator[IArray[T]] = genericArrayOps(arr).sliding(size, step)
302+
def stepper[S <: Stepper[_]](using StepperShape[T, S]): S = genericArrayOps(arr).stepper[S]
303+
def tails: Iterator[IArray[T]] = genericArrayOps(arr).tails
304+
def tapEach[U](f: (T) => U): IArray[T] =
305+
arr.toSeq.foreach(f)
306+
arr
307+
def transpose[U](implicit asArray: T => IArray[U]): IArray[IArray[U]] =
308+
genericArrayOps(arr).transpose(using asArray.asInstanceOf[T => Array[U]])
309+
def unzip[T1, T2](using asPair: T => (T1, T2), ct1: ClassTag[T1], ct2: ClassTag[T2]): (IArray[T1], IArray[T2]) = genericArrayOps(arr).unzip
310+
def unzip3[T1, T2, T3](using asTriple: T => (T1, T2, T3), ct1: ClassTag[T1], ct2: ClassTag[T2], ct3: ClassTag[T3]): (IArray[T1], IArray[T2], IArray[T3]) = genericArrayOps(arr).unzip3
311+
def updated[U >: T: ClassTag](index: Int, elem: U): IArray[U] = genericArrayOps(arr).updated(index, elem)
312+
def view: SeqView[T] = genericArrayOps(arr).view
313+
def withFilter(p: T => Boolean): WithFilter[T] = new WithFilter(p, arr)
314+
def zip[U](that: IArray[U]): IArray[(T, U)] = genericArrayOps(arr).zip(that)
315+
def zip[U](that: IterableOnce[U]): IArray[(T, U)] = genericArrayOps(arr).zip(that)
316+
def zipAll[T1 >: T, U](that: IArray[U], thisElem: T1, thatElem: U): IArray[(T1, U)] = genericArrayOps(arr).zipAll(that, thisElem, thatElem)
317+
def zipAll[T1 >: T, U](that: Iterable[U], thisElem: T1, thatElem: U): IArray[(T1, U)] = genericArrayOps(arr).zipAll(that, thisElem, thatElem)
318+
def zipWithIndex: IArray[(T, Int)] = genericArrayOps(arr).zipWithIndex
319+
320+
extension [T, U >: T: ClassTag](prefix: IterableOnce[T])
321+
def ++:(arr: IArray[U]): IArray[U] = genericArrayOps(arr).prependedAll(prefix)
322+
323+
extension [T, U >: T: ClassTag](prefix: IArray[T])
324+
def ++:(arr: IArray[U]): IArray[U] = genericArrayOps(arr).prependedAll(prefix)
325+
326+
extension [T, U >: T: ClassTag](x: T)
327+
def +:(arr: IArray[U]): IArray[U] = genericArrayOps(arr).prepended(x)
272328

273329
/** Conversion from IArray to immutable.ArraySeq */
274-
extension [T](arr: IArray[T]) def toSeq: immutable.ArraySeq[T] =
275-
immutable.ArraySeq.unsafeWrapArray(arr.asInstanceOf[Array[T]])
330+
implicit def genericWrapArray[T](arr: IArray[T]): ArraySeq[T] =
331+
if arr eq null then null else ArraySeq.unsafeWrapArray(arr)
276332

277333
/** Conversion from IArray to immutable.ArraySeq */
278-
extension [T <: AnyRef](arr: IArray[T]) def toSeq: immutable.ArraySeq[T] =
279-
immutable.ArraySeq.ofRef(arr.asInstanceOf[Array[T]])
334+
implicit def wrapRefArray[T <: AnyRef](arr: IArray[T]): ArraySeq.ofRef[T] =
335+
// Since the JVM thinks arrays are covariant, one 0-length Array[AnyRef]
336+
// is as good as another for all T <: AnyRef. Instead of creating 100,000,000
337+
// unique ones by way of this implicit, let's share one.
338+
if (arr eq null) null
339+
else if (arr.length == 0) ArraySeq.empty[AnyRef].asInstanceOf[ArraySeq.ofRef[T]]
340+
else ArraySeq.ofRef(arr.asInstanceOf[Array[T]])
280341

281342
/** Conversion from IArray to immutable.ArraySeq */
282-
extension (arr: IArray[Int]) def toSeq: immutable.ArraySeq[Int] =
283-
immutable.ArraySeq.ofInt(arr.asInstanceOf[Array[Int]])
343+
implicit def wrapIntArray(arr: IArray[Int]): ArraySeq.ofInt =
344+
if (arr ne null) new ArraySeq.ofInt(arr.asInstanceOf[Array[Int]]) else null
284345

285346
/** Conversion from IArray to immutable.ArraySeq */
286-
extension (arr: IArray[Double]) def toSeq: immutable.ArraySeq[Double] =
287-
immutable.ArraySeq.ofDouble(arr.asInstanceOf[Array[Double]])
347+
implicit def wrapDoubleIArray(arr: IArray[Double]): ArraySeq.ofDouble =
348+
if (arr ne null) new ArraySeq.ofDouble(arr.asInstanceOf[Array[Double]]) else null
288349

289350
/** Conversion from IArray to immutable.ArraySeq */
290-
extension (arr: IArray[Long]) def toSeq: immutable.ArraySeq[Long] =
291-
immutable.ArraySeq.ofLong(arr.asInstanceOf[Array[Long]])
351+
implicit def wrapLongIArray(arr: IArray[Long]): ArraySeq.ofLong =
352+
if (arr ne null) new ArraySeq.ofLong(arr.asInstanceOf[Array[Long]]) else null
292353

293354
/** Conversion from IArray to immutable.ArraySeq */
294-
extension (arr: IArray[Float]) def toSeq: immutable.ArraySeq[Float] =
295-
immutable.ArraySeq.ofFloat(arr.asInstanceOf[Array[Float]])
355+
implicit def wrapFloatIArray(arr: IArray[Float]): ArraySeq.ofFloat =
356+
if (arr ne null) new ArraySeq.ofFloat(arr.asInstanceOf[Array[Float]]) else null
296357

297358
/** Conversion from IArray to immutable.ArraySeq */
298-
extension (arr: IArray[Char]) def toSeq: immutable.ArraySeq[Char] =
299-
immutable.ArraySeq.ofChar(arr.asInstanceOf[Array[Char]])
359+
implicit def wrapCharIArray(arr: IArray[Char]): ArraySeq.ofChar =
360+
if (arr ne null) new ArraySeq.ofChar(arr.asInstanceOf[Array[Char]]) else null
300361

301362
/** Conversion from IArray to immutable.ArraySeq */
302-
extension (arr: IArray[Byte]) def toSeq: immutable.ArraySeq[Byte] =
303-
immutable.ArraySeq.ofByte(arr.asInstanceOf[Array[Byte]])
363+
implicit def wrapByteIArray(arr: IArray[Byte]): ArraySeq.ofByte =
364+
if (arr ne null) new ArraySeq.ofByte(arr.asInstanceOf[Array[Byte]]) else null
304365

305366
/** Conversion from IArray to immutable.ArraySeq */
306-
extension (arr: IArray[Short]) def toSeq: immutable.ArraySeq[Short] =
307-
immutable.ArraySeq.ofShort(arr.asInstanceOf[Array[Short]])
367+
implicit def wrapShortIArray(arr: IArray[Short]): ArraySeq.ofShort =
368+
if (arr ne null) new ArraySeq.ofShort(arr.asInstanceOf[Array[Short]]) else null
308369

309370
/** Conversion from IArray to immutable.ArraySeq */
310-
extension (arr: IArray[Boolean]) def toSeq: immutable.ArraySeq[Boolean] =
311-
immutable.ArraySeq.ofBoolean(arr.asInstanceOf[Array[Boolean]])
371+
implicit def wrapBooleanIArray(arr: IArray[Boolean]): ArraySeq.ofBoolean =
372+
if (arr ne null) new ArraySeq.ofBoolean(arr.asInstanceOf[Array[Boolean]]) else null
312373

313374
/** Conversion from IArray to immutable.ArraySeq */
314-
extension (arr: IArray[Unit]) def toSeq: immutable.ArraySeq[Unit] =
315-
immutable.ArraySeq.ofUnit(arr.asInstanceOf[Array[Unit]])
375+
implicit def wrapUnitIArray(arr: IArray[Unit]): ArraySeq.ofUnit =
376+
if (arr ne null) new ArraySeq.ofUnit(arr.asInstanceOf[Array[Unit]]) else null
316377

317378
/** Convert an array into an immutable array without copying, the original array
318379
* must _not_ be mutated after this or the guaranteed immutablity of IArray will
@@ -363,6 +424,25 @@ object IArray:
363424
/** An immutable array with given elements. */
364425
def apply(x: Unit, xs: Unit*): IArray[Unit] = Array(x, xs: _*)
365426

427+
/** Build an array from the iterable collection.
428+
*
429+
* {{{
430+
* scala> val a = Array.from(Seq(1, 5))
431+
* val a: Array[Int] = Array(1, 5)
432+
*
433+
* scala> val b = Array.from(Range(1, 5))
434+
* val b: Array[Int] = Array(1, 2, 3, 4)
435+
* }}}
436+
*
437+
* @param it the iterable collection
438+
* @return an array consisting of elements of the iterable collection
439+
*/
440+
def from[A : ClassTag](it: IterableOnce[A]): Array[A] =
441+
Array.from(it)
442+
443+
def newBuilder[T](using t: ClassTag[T]): Builder[T, IArray[T]] =
444+
ArrayBuilder.make[T].mapResult(IArray.unsafeFromArray)
445+
366446
/** Concatenates all arrays into a single immutable array.
367447
*
368448
* @param xss the given immutable arrays
@@ -391,7 +471,6 @@ object IArray:
391471
* @param elem the element computation
392472
*/
393473
def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): IArray[IArray[T]] =
394-
// We cannot avoid a cast here as Array.fill creates inner arrays out of our control:
395474
Array.fill(n1, n2)(elem)
396475

397476
/** Returns a three-dimensional immutable array that contains the results of some element computation a number
@@ -512,6 +591,17 @@ object IArray:
512591
*/
513592
def iterate[T: ClassTag](start: T, len: Int)(f: T => T): IArray[T] = Array.iterate(start, len)(f)
514593

594+
/** Compare two arrays per element.
595+
*
596+
* A more efficient version of `xs.sameElements(ys)`.
597+
*
598+
* @param xs an array of AnyRef
599+
* @param ys an array of AnyRef
600+
* @return true if corresponding elements are equal
601+
*/
602+
def equals(xs: IArray[AnyRef], ys: IArray[AnyRef]): Boolean =
603+
Array.equals(xs.asInstanceOf[Array[AnyRef]], ys.asInstanceOf[Array[AnyRef]])
604+
515605
/** Returns a decomposition of the array into a sequence. This supports
516606
* a pattern match like `{ case IArray(x,y,z) => println('3 elements')}`.
517607
*
@@ -521,4 +611,65 @@ object IArray:
521611
def unapplySeq[T](x: IArray[T]): Array.UnapplySeqWrapper[_ <: T] =
522612
Array.unapplySeq(x)
523613

524-
end IArray
614+
/** A lazy filtered array. No filtering is applied until one of `foreach`, `map` or `flatMap` is called. */
615+
class WithFilter[T](p: T => Boolean, xs: IArray[T]):
616+
617+
/** Apply `f` to each element for its side effects.
618+
* Note: [U] parameter needed to help scalac's type inference.
619+
*/
620+
def foreach[U](f: T => U): Unit = {
621+
val len = xs.length
622+
var i = 0
623+
while(i < len) {
624+
val x = xs(i)
625+
if(p(x)) f(x)
626+
i += 1
627+
}
628+
}
629+
630+
/** Builds a new array by applying a function to all elements of this array.
631+
*
632+
* @param f the function to apply to each element.
633+
* @tparam U the element type of the returned array.
634+
* @return a new array resulting from applying the given function
635+
* `f` to each element of this array and collecting the results.
636+
*/
637+
def map[U: ClassTag](f: T => U): IArray[U] = {
638+
val b = IArray.newBuilder[U]
639+
var i = 0
640+
while (i < xs.length) {
641+
val x = xs(i)
642+
if(p(x)) b += f(x)
643+
i = i + 1
644+
}
645+
b.result()
646+
}
647+
648+
/** Builds a new array by applying a function to all elements of this array
649+
* and using the elements of the resulting collections.
650+
*
651+
* @param f the function to apply to each element.
652+
* @tparam U the element type of the returned array.
653+
* @return a new array resulting from applying the given collection-valued function
654+
* `f` to each element of this array and concatenating the results.
655+
*/
656+
def flatMap[U: ClassTag](f: T => IterableOnce[U]): IArray[U] = {
657+
val b = IArray.newBuilder[U]
658+
var i = 0
659+
while(i < xs.length) {
660+
val x = xs(i)
661+
if(p(x)) b ++= f(xs(i))
662+
i += 1
663+
}
664+
b.result()
665+
}
666+
667+
def flatMap[BS, U](f: T => BS)(using asIterable: BS => Iterable[U], m: ClassTag[U]): IArray[U] =
668+
flatMap[U](x => asIterable(f(x)))
669+
670+
/** Creates a new non-strict filter which combines this filter with the given predicate. */
671+
def withFilter(q: T => Boolean): WithFilter[T] = new WithFilter[T](a => p(a) && q(a), xs)
672+
673+
end WithFilter
674+
675+
end IArray

0 commit comments

Comments
 (0)