Skip to content

Commit 001b436

Browse files
committed
Add missing IArray operations
1 parent 6a7681f commit 001b436

File tree

4 files changed

+1147
-31
lines changed

4 files changed

+1147
-31
lines changed

library/src/scala/IArray.scala

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

4-
import scala.collection.immutable
4+
import scala.collection._
5+
import scala.collection.mutable.Buffer
56

67
opaque type IArray[+T] = Array[_ <: T]
78

@@ -39,15 +40,10 @@ object IArray:
3940
extension (arr: IArray[Object]) def length: Int = arr.asInstanceOf[Array[Object]].length
4041
extension [T](arr: IArray[T]) def length: Int = arr.asInstanceOf[Array[T]].length
4142

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
4543

4644
/** Tests whether this array contains a given value as an element. */
4745
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)
46+
genericArrayOps(arr).contains(elem.asInstanceOf)
5147

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

10197
/** Flattens a two-dimensional array by concatenating all its rows
10298
* into a single array. */
103-
extension [T](arr: IArray[T]) def flatten[U: ClassTag](using T => Iterable[U]): IArray[U] =
99+
extension [T](arr: IArray[T]) def flatten[U](using asIterable: T => Iterable[U], ct: ClassTag[U]): IArray[U] =
104100
genericArrayOps(arr).flatten
105101

106102
/** Folds the elements of this array using the specified associative binary operator. */
@@ -135,9 +131,6 @@ object IArray:
135131

136132
/** Finds index of first occurrence of some value in this array after or at some start index. */
137133
extension [T](arr: IArray[T]) def indexOf(elem: T, from: Int = 0): Int =
138-
// `asInstanceOf` needed because `elem` does not have type `arr.T`
139-
// We could use `arr.iterator.indexOf(elem, from)` or `arr.indexWhere(_ == elem, from)`
140-
// but these would incur some overhead.
141134
genericArrayOps(arr).indexOf(elem.asInstanceOf, from)
142135

143136
/** Finds index of the first element satisfying some predicate after or at some start index. */
@@ -170,7 +163,6 @@ object IArray:
170163

171164
/** Finds index of last occurrence of some value in this array before or at a given end index. */
172165
extension [T](arr: IArray[T]) def lastIndexOf(elem: T, end: Int = arr.length - 1): Int =
173-
// see: same issue in `indexOf`
174166
genericArrayOps(arr).lastIndexOf(elem.asInstanceOf, end)
175167

176168
/** Finds index of last element satisfying some predicate before or at given end index. */
@@ -236,10 +228,6 @@ object IArray:
236228
extension [T](arr: IArray[T]) def splitAt(n: Int): (IArray[T], IArray[T]) =
237229
genericArrayOps(arr).splitAt(n)
238230

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-
243231
/** The rest of the array without its first element. */
244232
extension [T](arr: IArray[T]) def tail: IArray[T] =
245233
genericArrayOps(arr).tail
@@ -260,15 +248,133 @@ object IArray:
260248
extension [T](arr: IArray[T]) def toArray: Array[T] =
261249
arr.clone.asInstanceOf[Array[T]]
262250

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)
251+
extension [T](arr: IArray[T])
252+
def ++[U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr) ++ suffix.toSeq
253+
def ++[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr) ++ suffix
254+
def :+ [U >: T: ClassTag](x: U): IArray[U] = genericArrayOps(arr) :+ x
255+
def :++ [U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr) :++ suffix
256+
def :++ [U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr) :++ suffix
257+
def addString(b: mutable.StringBuilder): mutable.StringBuilder = arr.toSeq.addString(b)
258+
def addString(b: mutable.StringBuilder, sep: String): mutable.StringBuilder = arr.toSeq.addString(b, sep)
259+
def addString(b: mutable.StringBuilder, start: String, sep: String, end: String): mutable.StringBuilder = arr.toSeq.addString(b, start, sep, end)
260+
def appended[U >: T: ClassTag](x: U): IArray[U] = genericArrayOps(arr).appended(x)
261+
def appendedAll[U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr).appendedAll(suffix)
262+
def appendedAll[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr).appendedAll(suffix)
263+
def collect[U: ClassTag](pf: PartialFunction[T, U]): IArray[U] = genericArrayOps(arr).collect(pf)
264+
def collectFirst[U](f: PartialFunction[T, U]): Option[U] = genericArrayOps(arr).collectFirst(f)
265+
def combinations(n: Int): Iterator[IArray[T]] = genericArrayOps(arr).combinations(n)
266+
def concat[U >: T: ClassTag](suffix: IArray[U]): IArray[U] = genericArrayOps(arr).concat(suffix)
267+
def concat[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U] = genericArrayOps(arr).concat(suffix)
268+
def containsSlice[U](that: IArray[U]): Boolean = arr.toSeq.containsSlice(that.toSeq)
269+
def containsSlice[U](that: Seq[U]): Boolean = arr.toSeq.containsSlice(that)
270+
def corresponds[U](that: IArray[U])(p: (T, U) => Boolean): Boolean = arr.toSeq.corresponds(that.toSeq)(p)
271+
def corresponds[U](that: IterableOnce[U])(p: (T, U) => Boolean): Boolean = arr.toSeq.corresponds(that)(p)
272+
def diff[U >: T](that: IArray[U]): IArray[T] = genericArrayOps(arr).diff(that.toSeq)
273+
def diff[U >: T](that: Seq[U]): IArray[T] = genericArrayOps(arr).diff(that)
274+
def distinct: IArray[T] = genericArrayOps(arr).distinct
275+
def distinctBy[U](f: T => U): IArray[T] = genericArrayOps(arr).distinctBy(f)
276+
def empty: immutable.ArraySeq[T] = arr.toSeq.empty
277+
def startsWith[U >: T](that: IArray[U]): Boolean = genericArrayOps(arr).startsWith(that, 0)
278+
def startsWith[U >: T](that: IArray[U], offset: Int): Boolean = genericArrayOps(arr).startsWith(that, offset)
279+
def startsWith[U >: T](that: IterableOnce[U]): Boolean = genericArrayOps(arr).startsWith(that, 0)
280+
def startsWith[U >: T](that: IterableOnce[U], offset: Int): Boolean = genericArrayOps(arr).startsWith(that, offset)
281+
def endsWith[U >: T](that: IArray[U]): Boolean = genericArrayOps(arr).endsWith(that)
282+
def endsWith[U >: T](that: Iterable[U]): Boolean = genericArrayOps(arr).endsWith(that)
283+
def findLast(p: T => Boolean): Option[T] = arr.toSeq.findLast(p)
284+
def groupBy[K](f: T => K): immutable.Map[K, IArray[T]] = genericArrayOps(arr).groupBy(f)
285+
def groupMap[K, U: ClassTag](key: T => K)(f: T => U): immutable.Map[K, IArray[U]] = genericArrayOps(arr).groupMap(key)(f)
286+
def groupMapReduce[K, U](key: (T) => K)(f: (T) => U)(reduce: (U, U) => U): immutable.Map[K, U] = arr.toSeq.groupMapReduce(key)(f)(reduce)
287+
def grouped(size: Int): Iterator[IArray[T]] = genericArrayOps(arr).grouped(size)
288+
def indexOfSlice[U >: T](that: IArray[U]): Int = arr.toSeq.indexOfSlice(that)
289+
def indexOfSlice[U >: T](that: Seq[U]): Int = arr.toSeq.indexOfSlice(that)
290+
def indexOfSlice[U >: T](that: IArray[U], from: Int): Int = arr.toSeq.indexOfSlice(that, from)
291+
def indexOfSlice[U >: T](that: Seq[U], from: Int): Int = arr.toSeq.indexOfSlice(that, from)
292+
def inits: Iterator[IArray[T]] = genericArrayOps(arr).inits
293+
def intersect[U >: T](that: IArray[U]): IArray[T] = genericArrayOps(arr).intersect(that)
294+
def intersect[U >: T](that: Seq[U]): IArray[T] = genericArrayOps(arr).intersect(that)
295+
def isTraversableAgain: Boolean = arr.toSeq.isTraversableAgain
296+
def knownSize: Int = arr.length
297+
def lastIndexOfSlice[U >: T](that: IArray[U]): Int = arr.toSeq.lastIndexOfSlice(that)
298+
def lastIndexOfSlice[U >: T](that: Seq[U]): Int = arr.toSeq.lastIndexOfSlice(that)
299+
def lastIndexOfSlice[U >: T](that: IArray[U], end: Int): Int = arr.toSeq.lastIndexOfSlice(that, end)
300+
def lastIndexOfSlice[U >: T](that: Seq[U], end: Int): Int = arr.toSeq.lastIndexOfSlice(that, end)
301+
def lazyZip[U](that: IArray[U]): LazyZip2[T, U, IArray[T]] = genericArrayOps(arr).lazyZip[U](that).asInstanceOf[LazyZip2[T, U, IArray[T]]]
302+
def lazyZip[U](that: Iterable[U]): LazyZip2[T, U, IArray[T]] = genericArrayOps(arr).lazyZip[U](that).asInstanceOf[LazyZip2[T, U, IArray[T]]]
303+
def lengthCompare(len: Int): Int = genericArrayOps(arr).lengthCompare(len)
304+
def lengthIs: IterableOps.SizeCompareOps = arr.toSeq.lengthIs
305+
def max[U >: T](using math.Ordering[U]): T = arr.toSeq.max[U]
306+
def maxBy[U](f: T => U)(using math.Ordering[U]): T = arr.toSeq.maxBy(f)
307+
def maxByOption[U](f: T => U)(using math.Ordering[U]): Option[T] = arr.toSeq.maxByOption(f)
308+
def maxOption[U >: T](using math.Ordering[U]): Option[U] = arr.toSeq.maxOption[U]
309+
def min[U >: T](using math.Ordering[U]): T = arr.toSeq.min[U]
310+
def minBy[U](f: T => U)(using math.Ordering[U]): T = arr.toSeq.minBy(f)
311+
def minByOption[U](f: T => U)(using math.Ordering[U]): Option[T] = arr.toSeq.minByOption(f)
312+
def minOption[U >: T](using math.Ordering[U]): Option[U] = arr.toSeq.minOption[U]
313+
def mkString: String = arr.toSeq.mkString
314+
def mkString(sep: String): String = arr.toSeq.mkString(sep)
315+
def mkString(start: String, sep: String, end: String): String = arr.toSeq.mkString(start, sep, end)
316+
def padTo[U >: T: ClassTag](len: Int, elem: U): IArray[U] = genericArrayOps(arr).padTo(len, elem)
317+
def partitionMap[T1: ClassTag, T2: ClassTag](f: T => Either[T1, T2]): (IArray[T1], IArray[T2]) = genericArrayOps(arr).partitionMap(f)
318+
def patch[U >: T: ClassTag](from: Int, other: IterableOnce[U], replaced: Int): IArray[U] = genericArrayOps(arr).patch(from, other, replaced)
319+
def permutations: Iterator[IArray[T]] = genericArrayOps(arr).permutations
320+
def prepended[U >: T: ClassTag](x: U): IArray[U] = genericArrayOps(arr).prepended(x)
321+
def prependedAll[U >: T: ClassTag](prefix: IterableOnce[U]): IArray[U] = genericArrayOps(arr).prependedAll(prefix)
322+
def product[U >: T](using math.Numeric[U]): U = arr.toSeq.product[U]
323+
def reduce[U >: T](op: (U, U) => U): U = arr.toSeq.reduce(op)
324+
def reduceLeft[U >: T](op: (U, T) => U): U = arr.toSeq.reduceLeft(op)
325+
def reduceRight[U >: T](op: (T, U) => U): U = arr.toSeq.reduceRight(op)
326+
def reverseIterator: Iterator[T] = genericArrayOps(arr).reverseIterator
327+
def sameElements[U >: T](that: IArray[U]): Boolean = arr.toSeq.sameElements(that)
328+
def sameElements[U >: T](that: IterableOnce[U]): Boolean = arr.toSeq.sameElements(that)
329+
def search[U >: T](elem: U)(using Ordering[U]): Searching.SearchResult = arr.toSeq.search(elem)
330+
def search[U >: T](elem: U, from: Int, to: Int)(using Ordering[U]): Searching.SearchResult = arr.toSeq.search(elem, from, to)
331+
def segmentLength(p: (T) => Boolean, from: Int): Int = arr.toSeq.segmentLength(p, from)
332+
def segmentLength(p: (T) => Boolean): Int = arr.toSeq.segmentLength(p)
333+
def sizeCompare(that: IArray[Any]): Int = arr.toSeq.sizeCompare(that)
334+
def sizeCompare(that: Iterable[_]): Int = arr.toSeq.sizeCompare(that)
335+
def sizeCompare(otherSize: Int): Int = genericArrayOps(arr).sizeCompare(otherSize)
336+
def sizeIs: IterableOps.SizeCompareOps = arr.toSeq.sizeIs
337+
def sliding(size: Int, step: Int = 1): Iterator[IArray[T]] = genericArrayOps(arr).sliding(size, step)
338+
def stepper[S <: Stepper[_]](using StepperShape[T, S]): S = genericArrayOps(arr).stepper[S]
339+
def sum[U >: T](using math.Numeric[U]): U = arr.toSeq.sum[U]
340+
def tails: Iterator[IArray[T]] = genericArrayOps(arr).tails
341+
def tapEach[U](f: (T) => U): IArray[T] =
342+
arr.toSeq.foreach(f)
343+
arr
344+
def to[C1](factory: Factory[T, C1]): C1 = arr.toSeq.to(factory)
345+
def toBuffer[U >: T]: Buffer[U] = arr.toSeq.toBuffer[U]
346+
def toIndexedSeq: immutable.IndexedSeq[T] = arr.toSeq.toIndexedSeq
347+
def toIterable: Iterable[T] = arr.toSeq.toIterable
348+
def toList: List[T] = arr.toSeq.toList
349+
def toSet: Set[T] = arr.toSeq.toSet
350+
def toVector: Vector[T] = arr.toSeq.toVector
351+
def unzip[T1, T2](using asPair: T => (T1, T2), ct1: ClassTag[T1], ct2: ClassTag[T2]): (IArray[T1], IArray[T2]) = genericArrayOps(arr).unzip
352+
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
353+
def updated[U >: T: ClassTag](index: Int, elem: U): IArray[U] = genericArrayOps(arr).updated(index, elem)
354+
def view: SeqView[T] = genericArrayOps(arr).view
355+
def withFilter(p: T => Boolean): WithFilter[T] = new WithFilter(p, arr)
356+
def zip[U](that: IArray[U]): IArray[(T, U)] = genericArrayOps(arr).zip(that)
357+
def zip[U](that: IterableOnce[U]): IArray[(T, U)] = genericArrayOps(arr).zip(that)
358+
def zipAll[T1 >: T, U](that: IArray[U], thisElem: T1, thatElem: U): IArray[(T1, U)] = genericArrayOps(arr).zipAll(that, thisElem, thatElem)
359+
def zipAll[T1 >: T, U](that: Iterable[U], thisElem: T1, thatElem: U): IArray[(T1, U)] = genericArrayOps(arr).zipAll(that, thisElem, thatElem)
360+
def zipWithIndex: IArray[(T, Int)] = genericArrayOps(arr).zipWithIndex
361+
end extension
362+
363+
extension [T](arr: IArray[T])
364+
def transpose[U](implicit asArray: T => IArray[U]): IArray[IArray[U]] =
365+
genericArrayOps(arr).transpose(using asArray.asInstanceOf[T => Array[U]])
366+
367+
extension [T, U >: T: ClassTag](prefix: IterableOnce[T])
368+
def ++:(arr: IArray[U]): IArray[U] = genericArrayOps(arr).prependedAll(prefix)
369+
370+
extension [T, U >: T: ClassTag](prefix: IArray[T])
371+
def ++:(arr: IArray[U]): IArray[U] = genericArrayOps(arr).prependedAll(prefix)
372+
373+
extension [T, U >: T: ClassTag](x: T)
374+
def +:(arr: IArray[U]): IArray[U] = genericArrayOps(arr).prepended(x)
375+
376+
extension [T1, T2](arr: IArray[(T1, T2)])
377+
def toMap: Map[T1, T2] = arr.toSeq.toMap
272378

273379
/** Conversion from IArray to immutable.ArraySeq */
274380
extension [T](arr: IArray[T]) def toSeq: immutable.ArraySeq[T] =
@@ -363,16 +469,32 @@ object IArray:
363469
/** An immutable array with given elements. */
364470
def apply(x: Unit, xs: Unit*): IArray[Unit] = Array(x, xs: _*)
365471

472+
/** Build an array from the iterable collection.
473+
*
474+
* {{{
475+
* scala> val a = Array.from(Seq(1, 5))
476+
* val a: Array[Int] = Array(1, 5)
477+
*
478+
* scala> val b = Array.from(Range(1, 5))
479+
* val b: Array[Int] = Array(1, 2, 3, 4)
480+
* }}}
481+
*
482+
* @param it the iterable collection
483+
* @return an array consisting of elements of the iterable collection
484+
*/
485+
def from[A : ClassTag](it: IterableOnce[A]): Array[A] =
486+
Array.from(it)
487+
488+
def newBuilder[T](using t: ClassTag[T]): scala.collection.immutable.iarray.IArrayBuilder[T] =
489+
scala.collection.immutable.iarray.IArrayBuilder.make[T]
490+
366491
/** Concatenates all arrays into a single immutable array.
367492
*
368493
* @param xss the given immutable arrays
369494
* @return the array created from concatenating `xss`
370495
*/
371496
def concat[T: ClassTag](xss: IArray[T]*): IArray[T] =
372-
// `Array.concat` should arguably take in a `Seq[Array[_ <: T]]`,
373-
// but since it currently takes a `Seq[Array[T]]` we have to perform a cast,
374-
// knowing tacitly that `concat` is not going to do the wrong thing.
375-
Array.concat[T](xss.asInstanceOf[Seq[Array[T]]]: _*)
497+
Array.concat[T](xss.asInstanceOf[immutable.Seq[Array[T]]]: _*)
376498

377499
/** Returns an immutable array that contains the results of some element computation a number
378500
* of times. Each element is determined by a separate computation.
@@ -391,7 +513,6 @@ object IArray:
391513
* @param elem the element computation
392514
*/
393515
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:
395516
Array.fill(n1, n2)(elem)
396517

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

636+
/** Compare two arrays per element.
637+
*
638+
* A more efficient version of `xs.sameElements(ys)`.
639+
*
640+
* @param xs an array of AnyRef
641+
* @param ys an array of AnyRef
642+
* @return true if corresponding elements are equal
643+
*/
644+
def equals(xs: IArray[AnyRef], ys: IArray[AnyRef]): Boolean =
645+
Array.equals(xs.asInstanceOf[Array[AnyRef]], ys.asInstanceOf[Array[AnyRef]])
646+
515647
/** Returns a decomposition of the array into a sequence. This supports
516648
* a pattern match like `{ case IArray(x,y,z) => println('3 elements')}`.
517649
*
@@ -521,4 +653,66 @@ object IArray:
521653
def unapplySeq[T](x: IArray[T]): Array.UnapplySeqWrapper[_ <: T] =
522654
Array.unapplySeq(x)
523655

524-
end IArray
656+
/** A lazy filtered array. No filtering is applied until one of `foreach`, `map` or `flatMap` is called. */
657+
class WithFilter[T](p: T => Boolean, xs: IArray[T]):
658+
659+
/** Apply `f` to each element for its side effects.
660+
* Note: [U] parameter needed to help scalac's type inference.
661+
*/
662+
def foreach[U](f: T => U): Unit = {
663+
val len = xs.length
664+
var i = 0
665+
while(i < len) {
666+
val x = xs(i)
667+
if(p(x)) f(x)
668+
i += 1
669+
}
670+
}
671+
672+
/** Builds a new array by applying a function to all elements of this array.
673+
*
674+
* @param f the function to apply to each element.
675+
* @tparam U the element type of the returned array.
676+
* @return a new array resulting from applying the given function
677+
* `f` to each element of this array and collecting the results.
678+
*/
679+
def map[U: ClassTag](f: T => U): IArray[U] = {
680+
val b = scala.collection.immutable.iarray.IArrayBuilder.make[U]
681+
var i = 0
682+
while (i < xs.length) {
683+
val x = xs(i)
684+
if(p(x)) b += f(x)
685+
i = i + 1
686+
}
687+
b.result()
688+
}
689+
690+
/** Builds a new array by applying a function to all elements of this array
691+
* and using the elements of the resulting collections.
692+
*
693+
* @param f the function to apply to each element.
694+
* @tparam U the element type of the returned array.
695+
* @return a new array resulting from applying the given collection-valued function
696+
* `f` to each element of this array and concatenating the results.
697+
*/
698+
def flatMap[U: ClassTag](f: T => IterableOnce[U]): IArray[U] = {
699+
val b = scala.collection.immutable.iarray.IArrayBuilder.make[U]
700+
var i = 0
701+
while(i < xs.length) {
702+
val x = xs(i)
703+
if(p(x)) b ++= f(xs(i))
704+
i += 1
705+
}
706+
b.result()
707+
}
708+
709+
def flatMap[BS, U](f: T => BS)(using asIterable: BS => Iterable[U], m: ClassTag[U]): IArray[U] =
710+
flatMap[U](x => asIterable(f(x)))
711+
712+
/** Creates a new non-strict filter which combines this filter with the given predicate. */
713+
def withFilter(q: T => Boolean): WithFilter[T] = new WithFilter[T](a => p(a) && q(a), xs)
714+
715+
end WithFilter
716+
717+
718+
end IArray

0 commit comments

Comments
 (0)