Skip to content

Commit fabcb58

Browse files
committed
Add missing IArray operations
1 parent 5b46900 commit fabcb58

File tree

3 files changed

+558
-22
lines changed

3 files changed

+558
-22
lines changed

library/src/scala/IArray.scala

Lines changed: 198 additions & 22 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
/** An immutable array. An `IArray[T]` has the same representation as an `Array[T]`,
78
* but it cannot be updated. Unlike regular arrays, immutable arrays are covariant.
@@ -41,9 +42,6 @@ object opaques:
4142
extension (arr: IArray[Object]) def length: Int = arr.asInstanceOf[Array[Object]].length
4243
extension [T](arr: IArray[T]) def length: Int = arr.asInstanceOf[Array[T]].length
4344

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

4846
/** Tests whether this array contains a given value as an element. */
4947
extension [T](arr: IArray[T]) def contains(elem: T): Boolean =
@@ -102,7 +100,7 @@ object opaques:
102100

103101
/** Flattens a two-dimensional array by concatenating all its rows
104102
* into a single array. */
105-
extension [T](arr: IArray[T]) def flatten[U: ClassTag](using T => Iterable[U]): IArray[U] =
103+
extension [T](arr: IArray[T]) def flatten[U: ClassTag](using asIterable: T => Iterable[U]): IArray[U] =
106104
genericArrayOps(arr).flatten
107105

108106
/** Folds the elements of this array using the specified associative binary operator. */
@@ -187,7 +185,7 @@ object opaques:
187185
extension [T](arr: IArray[T]) def nonEmpty: Boolean =
188186
genericArrayOps(arr).nonEmpty
189187

190-
/** A pair of, first, all elements that satisfy predicate `p` and, second, all elements that do not. */
188+
/** T pair of, first, all elements that satisfy predicate `p` and, second, all elements that do not. */
191189
extension [T](arr: IArray[T]) def partition(p: T => Boolean): (IArray[T], IArray[T]) =
192190
genericArrayOps(arr).partition(p)
193191

@@ -238,10 +236,6 @@ object opaques:
238236
extension [T](arr: IArray[T]) def splitAt(n: Int): (IArray[T], IArray[T]) =
239237
genericArrayOps(arr).splitAt(n)
240238

241-
/** Tests whether this array starts with the given array. */
242-
extension [T](arr: IArray[T]) def startsWith[U >: T](that: IArray[U], offset: Int = 0): Boolean =
243-
genericArrayOps(arr).startsWith(that)
244-
245239
/** The rest of the array without its first element. */
246240
extension [T](arr: IArray[T]) def tail: IArray[T] =
247241
genericArrayOps(arr).tail
@@ -262,15 +256,134 @@ object opaques:
262256
extension [T](arr: IArray[T]) def toArray: Array[T] =
263257
arr.clone.asInstanceOf[Array[T]]
264258

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

275388
/** Conversion from IArray to immutable.ArraySeq */
276389
extension [T](arr: IArray[T]) def toSeq: immutable.ArraySeq[T] =
@@ -320,7 +433,7 @@ end opaques
320433

321434
type IArray[+T] = opaques.IArray[T]
322435

323-
object IArray {
436+
object IArray:
324437
import opaques.Sub
325438
import opaques.Sup
326439

@@ -385,7 +498,7 @@ object IArray {
385498
// `Array.concat` should arguably take in a `Seq[Array[_ <: T]]`,
386499
// but since it currently takes a `Seq[Array[T]]` we have to perform a cast,
387500
// knowing tacitly that `concat` is not going to do the wrong thing.
388-
Array.concat[T](xss.asInstanceOf[Seq[Array[T]]]: _*)
501+
Array.concat[T](xss.asInstanceOf[immutable.Seq[Array[T]]]: _*)
389502

390503
/** Returns an immutable array that contains the results of some element computation a number
391504
* of times. Each element is determined by a separate computation.
@@ -535,4 +648,67 @@ object IArray {
535648
// The double type ascription is currently needed,
536649
// for some reason (see: https://scastie.scala-lang.org/sSsmOhKxSKym405MgNRKqQ)
537650
Array.unapplySeq((x: Sup[T]): Array[_ <: T])
538-
}
651+
652+
// TODO where should this class be defined? The WithFilter for Array is in ArrayOps.
653+
/** T lazy filtered array. No filtering is applied until one of `foreach`, `map` or `flatMap` is called. */
654+
class WithFilter[T](p: T => Boolean, xs: IArray[T]):
655+
656+
/** Apply `f` to each element for its side effects.
657+
* Note: [U] parameter needed to help scalac's type inference.
658+
*/
659+
def foreach[U](f: T => U): Unit = {
660+
val len = xs.length
661+
var i = 0
662+
while(i < len) {
663+
val x = xs(i)
664+
if(p(x)) f(x)
665+
i += 1
666+
}
667+
}
668+
669+
/** Builds a new array by applying a function to all elements of this array.
670+
*
671+
* @param f the function to apply to each element.
672+
* @tparam U the element type of the returned array.
673+
* @return a new array resulting from applying the given function
674+
* `f` to each element of this array and collecting the results.
675+
*/
676+
def map[U: ClassTag](f: T => U): IArray[U] = {
677+
val b = mutable.ArrayBuilder.make[U]
678+
var i = 0
679+
while (i < xs.length) {
680+
val x = xs(i)
681+
if(p(x)) b += f(x)
682+
i = i + 1
683+
}
684+
b.result()
685+
}
686+
687+
/** Builds a new array by applying a function to all elements of this array
688+
* and using the elements of the resulting collections.
689+
*
690+
* @param f the function to apply to each element.
691+
* @tparam U the element type of the returned array.
692+
* @return a new array resulting from applying the given collection-valued function
693+
* `f` to each element of this array and concatenating the results.
694+
*/
695+
def flatMap[U: ClassTag](f: T => IterableOnce[U]): IArray[U] = {
696+
val b = mutable.ArrayBuilder.make[U]
697+
var i = 0
698+
while(i < xs.length) {
699+
val x = xs(i)
700+
if(p(x)) b ++= f(xs(i))
701+
i += 1
702+
}
703+
b.result()
704+
}
705+
706+
def flatMap[BS, U](f: T => BS)(using asIterable: BS => Iterable[U], m: ClassTag[U]): IArray[U] =
707+
flatMap[U](x => asIterable(f(x)))
708+
709+
/** Creates a new non-strict filter which combines this filter with the given predicate. */
710+
def withFilter(q: T => Boolean): WithFilter[T] = new WithFilter[T](a => p(a) && q(a), xs)
711+
712+
end WithFilter
713+
714+
end IArray

0 commit comments

Comments
 (0)