@@ -27,15 +27,21 @@ to choose `Seq` because our collection can contain duplicates and
27
27
iteration order is determined by insertion order. However, some
28
28
[ properties of ` Seq ` ] ( /overviews/collections/seqs.html ) are not satisfied:
29
29
30
+ {% tabs notCapped_1 %}
31
+ {% tab 'Scala 2 and 3' for=notCapped_1 %}
30
32
~~~ scala
31
33
(xs ++ ys).size == xs.size + ys.size
32
34
~~~
35
+ {% endtab %}
36
+ {% endtabs %}
33
37
34
38
Consequently, the only sensible choice as a base collection type
35
39
is ` collection.immutable.Iterable ` .
36
40
37
41
### First version of ` Capped ` class ###
38
42
43
+ {% tabs capped1_1 class=tabs-scala-version %}
44
+ {% tab 'Scala 2' for=capped1_1 %}
39
45
~~~ scala
40
46
import scala .collection ._
41
47
@@ -72,11 +78,54 @@ class Capped1[A] private (val capacity: Int, val length: Int, offset: Int, elems
72
78
elem
73
79
}
74
80
}
75
-
81
+
76
82
override def className = " Capped1"
77
83
78
84
}
79
85
~~~
86
+ {% endtab %}
87
+ {% tab 'Scala 3' for=capped1_1 %}
88
+ ~~~ scala
89
+ import scala .collection .*
90
+
91
+ class Capped1 [A ] private (val capacity : Int , val length : Int , offset : Int , elems : Array [Any ])
92
+ extends immutable.Iterable [A ]:
93
+ self =>
94
+
95
+ def this (capacity : Int ) =
96
+ this (capacity, length = 0 , offset = 0 , elems = Array .ofDim(capacity))
97
+
98
+ def appended [B >: A ](elem : B ): Capped1 [B ] =
99
+ val newElems = Array .ofDim[Any ](capacity)
100
+ Array .copy(elems, 0 , newElems, 0 , capacity)
101
+ val (newOffset, newLength) =
102
+ if length == capacity then
103
+ newElems(offset) = elem
104
+ ((offset + 1 ) % capacity, length)
105
+ else
106
+ newElems(length) = elem
107
+ (offset, length + 1 )
108
+ Capped1 [B ](capacity, newLength, newOffset, newElems)
109
+ end appended
110
+
111
+ inline def :+ [B >: A ](elem : B ): Capped1 [B ] = appended(elem)
112
+
113
+ def apply (i : Int ): A = elems((i + offset) % capacity).asInstanceOf [A ]
114
+
115
+ def iterator : Iterator [A ] = new AbstractIterator [A ]:
116
+ private var current = 0
117
+ def hasNext = current < self.length
118
+ def next (): A =
119
+ val elem = self(current)
120
+ current += 1
121
+ elem
122
+ end iterator
123
+
124
+ override def className = " Capped1"
125
+ end Capped1
126
+ ~~~
127
+ {% endtab %}
128
+ {% endtabs %}
80
129
81
130
The above listing presents the first version of our capped collection
82
131
implementation. It will be refined later. The class ` Capped1 ` has a
@@ -100,33 +149,37 @@ the `offset`.
100
149
These two methods, ` appended ` and ` apply ` , implement the specific
101
150
behavior of the ` Capped1 ` collection type. In addition to them, we have
102
151
to implement ` iterator ` to make the generic collection operations
103
- (such as ` foldLeft ` , ` count ` , etc.) work on ` Capped ` collections.
152
+ (such as ` foldLeft ` , ` count ` , etc.) work on ` Capped1 ` collections.
104
153
Here we implement it by using indexed access.
105
154
106
155
Last, we override ` className ` to return the name of the collection,
107
- “Capped1”. This name is used by the ` toString ` operation.
156
+ ` “Capped1” ` . This name is used by the ` toString ` operation.
108
157
109
158
Here are some interactions with the ` Capped1 ` collection:
110
159
160
+ {% tabs capped1_2 %}
161
+ {% tab 'Scala 2 and 3' for=capped1_2 %}
111
162
~~~ scala
112
- scala> new Capped1 (capacity = 4 )
113
- res0 : Capped1 [Nothing ] = Capped1 ()
163
+ scala> val c0 = new Capped1 (capacity = 4 )
164
+ val c0 : Capped1 [Nothing ] = Capped1 ()
114
165
115
- scala> res0 :+ 1 :+ 2 :+ 3
116
- res1 : Capped1 [Int ] = Capped1 (1 , 2 , 3 )
166
+ scala> val c1 = c0 :+ 1 :+ 2 :+ 3
167
+ val c1 : Capped1 [Int ] = Capped1 (1 , 2 , 3 )
117
168
118
- scala> res1 .length
119
- res2: Int = 3
169
+ scala> c1 .length
170
+ val res2 : Int = 3
120
171
121
- scala> res1 .lastOption
122
- res3: Option [Int ] = Some (3 )
172
+ scala> c1 .lastOption
173
+ val res3 : Option [Int ] = Some (3 )
123
174
124
- scala> res1 :+ 4 :+ 5 :+ 6
125
- res4 : Capped1 [Int ] = Capped1 (3 , 4 , 5 , 6 )
175
+ scala> val c2 = c1 :+ 4 :+ 5 :+ 6
176
+ val c2 : Capped1 [Int ] = Capped1 (3 , 4 , 5 , 6 )
126
177
127
- scala> res4 .take(3 )
128
- res5 : collection.immutable.Iterable [Int ] = List (3 , 4 , 5 )
178
+ scala> val c3 = c2 .take(3 )
179
+ val c3 : collection.immutable.Iterable [Int ] = List (3 , 4 , 5 )
129
180
~~~
181
+ {% endtab %}
182
+ {% endtabs %}
130
183
131
184
You can see that if we try to grow the collection with more than four
132
185
elements, the first elements are dropped (see ` res4 ` ). The operations
@@ -144,7 +197,13 @@ question should be what needs to be done to change them? One way to do
144
197
this would be to override the ` take ` method in class ` Capped1 ` , maybe like
145
198
this:
146
199
147
- def take(count: Int): Capped1 = …
200
+ {% tabs take_signature %}
201
+ {% tab 'Scala 2 and 3' for=take_signature %}
202
+ ``` scala
203
+ def take (count : Int ): Capped1 = …
204
+ ```
205
+ {% endtab %}
206
+ {% endtabs %}
148
207
149
208
This would do the job for ` take ` . But what about ` drop ` , or ` filter ` , or
150
209
` init ` ? In fact there are over fifty methods on collections that return
@@ -155,6 +214,8 @@ effect, as shown in the next section.
155
214
156
215
### Second version of ` Capped ` class ###
157
216
217
+ {% tabs capped2_1 class=tabs-scala-version %}
218
+ {% tab 'Scala 2' for=capped2_1 %}
158
219
~~~ scala
159
220
import scala .collection ._
160
221
@@ -191,6 +252,44 @@ class Capped2Factory(capacity: Int) extends IterableFactory[Capped2] {
191
252
}
192
253
}
193
254
~~~
255
+ {% endtab %}
256
+ {% tab 'Scala 3' for=capped2_1 %}
257
+ ~~~ scala
258
+ class Capped2 [A ] private (val capacity : Int , val length : Int , offset : Int , elems : Array [Any ])
259
+ extends immutable.Iterable [A ],
260
+ IterableOps [A , Capped2 , Capped2 [A ]]:
261
+ self =>
262
+
263
+ def this (capacity : Int ) = // as before
264
+
265
+ def appended [B >: A ](elem : B ): Capped2 [B ] = // as before
266
+ inline def :+ [B >: A ](elem : B ): Capped2 [B ] = // as before
267
+ def apply (i : Int ): A = // as before
268
+
269
+ def iterator : Iterator [A ] = // as before
270
+
271
+ override def className = " Capped2"
272
+ override val iterableFactory : IterableFactory [Capped2 ] = Capped2Factory (capacity)
273
+ override protected def fromSpecific (coll : IterableOnce [A ]): Capped2 [A ] = iterableFactory.from(coll)
274
+ override protected def newSpecificBuilder : mutable.Builder [A , Capped2 [A ]] = iterableFactory.newBuilder
275
+ override def empty : Capped2 [A ] = iterableFactory.empty
276
+ end Capped2
277
+
278
+ class Capped2Factory (capacity : Int ) extends IterableFactory [Capped2 ]:
279
+
280
+ def from [A ](source : IterableOnce [A ]): Capped2 [A ] =
281
+ (newBuilder[A ] ++= source).result()
282
+
283
+ def empty [A ]: Capped2 [A ] = Capped2 [A ](capacity)
284
+
285
+ def newBuilder [A ]: mutable.Builder [A , Capped2 [A ]] =
286
+ new mutable.ImmutableBuilder [A , Capped2 [A ]](empty):
287
+ def addOne (elem : A ): this .type =
288
+ elems = elems :+ elem; this
289
+ end Capped2Factory
290
+ ~~~
291
+ {% endtab %}
292
+ {% endtabs %}
194
293
195
294
The Capped class needs to inherit not only from ` Iterable ` , but also
196
295
from its implementation trait ` IterableOps ` . This is shown in the
@@ -229,31 +328,35 @@ With the refined implementation of the [`Capped2` class](#second-version-of-capp
229
328
the transformation operations work now as expected, and the
230
329
` Capped2Factory ` class provides seamless conversions from other collections:
231
330
331
+ {% tabs capped2_2 %}
332
+ {% tab 'Scala 2 and 3' for=capped2_2 %}
232
333
~~~ scala
233
334
scala> object Capped extends Capped2Factory (capacity = 4 )
234
335
defined object Capped
235
336
236
337
scala> Capped (1 , 2 , 3 )
237
- res0: Capped2 [Int ] = Capped2 (1 , 2 , 3 )
338
+ val res0 : Capped2 [Int ] = Capped2 (1 , 2 , 3 )
238
339
239
340
scala> res0.take(2 )
240
- res1: Capped2 [Int ] = Capped2 (1 , 2 )
341
+ val res1 : Capped2 [Int ] = Capped2 (1 , 2 )
241
342
242
343
scala> res0.filter(x => x % 2 == 1 )
243
- res2: Capped2 [Int ] = Capped2 (1 , 3 )
344
+ val res2 : Capped2 [Int ] = Capped2 (1 , 3 )
244
345
245
346
scala> res0.map(x => x * x)
246
- res3: Capped2 [Int ] = Capped2 (1 , 4 , 9 )
347
+ val res3 : Capped2 [Int ] = Capped2 (1 , 4 , 9 )
247
348
248
349
scala> List (1 , 2 , 3 , 4 , 5 ).to(Capped )
249
- res4: Capped2 [Int ] = Capped2 (2 , 3 , 4 , 5 )
350
+ val res4 : Capped2 [Int ] = Capped2 (2 , 3 , 4 , 5 )
250
351
~~~
352
+ {% endtab %}
353
+ {% endtabs %}
251
354
252
355
This implementation now behaves correctly, but we can still improve
253
356
a few things:
254
357
255
358
- since our collection is strict, we can take advantage
256
- of the better performance offered by
359
+ of the better performance offered by
257
360
strict implementations of transformation operations,
258
361
- since our ` fromSpecific ` , ` newSpecificBuilder ` and ` empty `
259
362
operation just forward to the ` iterableFactory ` member,
@@ -262,6 +365,8 @@ a few things:
262
365
263
366
### Final version of ` Capped ` class ###
264
367
368
+ {% tabs capped_1 class=tabs-scala-version %}
369
+ {% tab 'Scala 2' for=capped_1 %}
265
370
~~~ scala
266
371
import scala .collection ._
267
372
@@ -324,6 +429,69 @@ class CappedFactory(capacity: Int) extends IterableFactory[Capped] {
324
429
325
430
}
326
431
~~~
432
+ {% endtab %}
433
+ {% tab 'Scala 3' for=capped_1 %}
434
+ ~~~ scala
435
+ import scala .collection .*
436
+
437
+ final class Capped [A ] private (val capacity : Int , val length : Int , offset : Int , elems : Array [Any ])
438
+ extends immutable.Iterable [A ],
439
+ IterableOps [A , Capped , Capped [A ]],
440
+ IterableFactoryDefaults [A , Capped ],
441
+ StrictOptimizedIterableOps [A , Capped , Capped [A ]]:
442
+ self =>
443
+
444
+ def this (capacity : Int ) =
445
+ this (capacity, length = 0 , offset = 0 , elems = Array .ofDim(capacity))
446
+
447
+ def appended [B >: A ](elem : B ): Capped [B ] =
448
+ val newElems = Array .ofDim[Any ](capacity)
449
+ Array .copy(elems, 0 , newElems, 0 , capacity)
450
+ val (newOffset, newLength) =
451
+ if length == capacity then
452
+ newElems(offset) = elem
453
+ ((offset + 1 ) % capacity, length)
454
+ else
455
+ newElems(length) = elem
456
+ (offset, length + 1 )
457
+ Capped [B ](capacity, newLength, newOffset, newElems)
458
+ end appended
459
+
460
+ inline def :+ [B >: A ](elem : B ): Capped [B ] = appended(elem)
461
+
462
+ def apply (i : Int ): A = elems((i + offset) % capacity).asInstanceOf [A ]
463
+
464
+ def iterator : Iterator [A ] = view.iterator
465
+
466
+ override def view : IndexedSeqView [A ] = new IndexedSeqView [A ]:
467
+ def length : Int = self.length
468
+ def apply (i : Int ): A = self(i)
469
+
470
+ override def knownSize : Int = length
471
+
472
+ override def className = " Capped"
473
+
474
+ override val iterableFactory : IterableFactory [Capped ] = new CappedFactory (capacity)
475
+
476
+ end Capped
477
+
478
+ class CappedFactory (capacity : Int ) extends IterableFactory [Capped ]:
479
+
480
+ def from [A ](source : IterableOnce [A ]): Capped [A ] =
481
+ source match
482
+ case capped : Capped [? ] if capped.capacity == capacity => capped.asInstanceOf [Capped [A ]]
483
+ case _ => (newBuilder[A ] ++= source).result()
484
+
485
+ def empty [A ]: Capped [A ] = Capped [A ](capacity)
486
+
487
+ def newBuilder [A ]: mutable.Builder [A , Capped [A ]] =
488
+ new mutable.ImmutableBuilder [A , Capped [A ]](empty):
489
+ def addOne (elem : A ): this .type = { elems = elems :+ elem; this }
490
+
491
+ end CappedFactory
492
+ ~~~
493
+ {% endtab %}
494
+ {% endtabs %}
327
495
328
496
That is it. The final [ ` Capped ` class] ( #final-version-of-capped-class ) :
329
497
@@ -345,33 +513,58 @@ methods (such as `iterator` in our case), if any.
345
513
346
514
## RNA sequences ##
347
515
348
- To start with the second example, we define the four RNA Bases:
349
-
350
- abstract class Base
351
- case object A extends Base
352
- case object U extends Base
353
- case object G extends Base
354
- case object C extends Base
516
+ To start with the second example, say you want to create a new immutable sequence type for RNA strands.
517
+ These are sequences of bases A (adenine), U (uracil), G (guanine), and C
518
+ (cytosine). The definitions for bases are set up as shown in the
519
+ listing of RNA bases below:
355
520
356
- object Base {
357
- val fromInt: Int => Base = Array(A, U, G, C)
358
- val toInt: Base => Int = Map(A -> 0, U -> 1, G -> 2, C -> 3)
359
- }
360
-
361
- Say you want to create a new immutable sequence type for RNA strands, which are
362
- sequences of bases A (adenine), U (uracil), G (guanine), and C
363
- (cytosine). The definitions for bases are easily set up as shown in the
364
- listing of RNA bases above.
521
+ {% tabs Base_1 class=tabs-scala-version %}
522
+ {% tab 'Scala 2' for=Base_1 %}
523
+ ~~~ scala
524
+ abstract class Base
525
+ case object A extends Base
526
+ case object U extends Base
527
+ case object G extends Base
528
+ case object C extends Base
529
+
530
+ object Base {
531
+ val fromInt : Int => Base = Array (A , U , G , C )
532
+ val toInt : Base => Int = Map (A -> 0 , U -> 1 , G -> 2 , C -> 3 )
533
+ }
534
+ ~~~
365
535
366
536
Every base is defined as a case object that inherits from a common
367
537
abstract class ` Base ` . The ` Base ` class has a companion object that
368
538
defines two functions that map between bases and the integers 0 to 3.
369
- You can see in the examples two different ways to use collections
539
+
540
+ You can see in the above example two different ways to use collections
370
541
to implement these functions. The ` toInt ` function is implemented as a
371
542
` Map ` from ` Base ` values to integers. The reverse function, ` fromInt ` , is
372
543
implemented as an array. This makes use of the fact that both maps and
373
544
arrays * are* functions because they inherit from the ` Function1 ` trait.
374
545
546
+ {% endtab %}
547
+ {% tab 'Scala 3' for=Base_1 %}
548
+ ~~~ scala
549
+ enum Base :
550
+ case A , U , G , C
551
+
552
+ object Base :
553
+ val fromInt : Int => Base = values
554
+ val toInt : Base => Int = _.ordinal
555
+ ~~~
556
+
557
+ Every base is defined as a case of the ` Base ` enum. ` Base ` has a companion object
558
+ that defines two functions that map between bases and the integers 0 to 3.
559
+
560
+ The ` toInt ` function is implemented by delegating to the ` ordinal ` method defined on ` Base ` ,
561
+ which is automatically defined because ` Base ` is an enum. Each enum case will have a unique ` ordinal ` value.
562
+ The reverse function, ` fromInt ` , is implemented as an array. This makes use of the fact that
563
+ arrays * are* functions because they inherit from the ` Function1 ` trait.
564
+
565
+ {% endtab %}
566
+ {% endtabs %}
567
+
375
568
The next task is to define a class for strands of RNA. Conceptually, a
376
569
strand of RNA is simply a ` Seq[Base] ` . However, RNA strands can get
377
570
quite long, so it makes sense to invest some work in a compact
@@ -383,51 +576,104 @@ representation.
383
576
384
577
### First version of RNA strands class ###
385
578
386
- import collection.mutable
387
- import collection.immutable.{ IndexedSeq, IndexedSeqOps }
579
+ {% tabs RNA1_1 class=tabs-scala-version %}
580
+ {% tab 'Scala 2' for=RNA1_1 %}
581
+ ~~~ scala
582
+ import collection .mutable
583
+ import collection .immutable .{ IndexedSeq , IndexedSeqOps }
388
584
389
- final class RNA1 private (
390
- val groups: Array[Int],
391
- val length: Int
392
- ) extends IndexedSeq[Base]
393
- with IndexedSeqOps[Base, IndexedSeq, RNA1] {
585
+ final class RNA1 private (
586
+ val groups : Array [Int ],
587
+ val length : Int
588
+ ) extends IndexedSeq [Base ]
589
+ with IndexedSeqOps [Base , IndexedSeq , RNA1 ] {
394
590
395
- import RNA1._
591
+ import RNA1 ._
396
592
397
- def apply(idx: Int): Base = {
398
- if (idx < 0 || length <= idx)
399
- throw new IndexOutOfBoundsException
400
- Base.fromInt(groups(idx / N) >> (idx % N * S) & M)
401
- }
593
+ def apply (idx : Int ): Base = {
594
+ if (idx < 0 || length <= idx)
595
+ throw new IndexOutOfBoundsException
596
+ Base .fromInt(groups(idx / N ) >> (idx % N * S ) & M )
597
+ }
402
598
403
- override protected def fromSpecific(coll: IterableOnce[Base]): RNA1 =
404
- fromSeq(coll.iterator.toSeq)
405
- override protected def newSpecificBuilder: mutable.Builder[Base, RNA1] =
406
- iterableFactory.newBuilder[Base].mapResult(fromSeq)
407
- override def empty: RNA1 = fromSeq(Seq.empty)
408
- override def className = "RNA1"
409
- }
599
+ override protected def fromSpecific (coll : IterableOnce [Base ]): RNA1 =
600
+ fromSeq(coll.iterator.toSeq)
601
+ override protected def newSpecificBuilder : mutable.Builder [Base , RNA1 ] =
602
+ iterableFactory.newBuilder[Base ].mapResult(fromSeq)
603
+ override def empty : RNA1 = fromSeq(Seq .empty)
604
+ override def className = " RNA1"
605
+ }
410
606
411
- object RNA1 {
607
+ object RNA1 {
412
608
413
- // Number of bits necessary to represent group
414
- private val S = 2
609
+ // Number of bits necessary to represent group
610
+ private val S = 2
415
611
416
- // Number of groups that fit in an Int
417
- private val N = 32 / S
612
+ // Number of groups that fit in an Int
613
+ private val N = 32 / S
418
614
419
- // Bitmask to isolate a group
420
- private val M = (1 << S) - 1
615
+ // Bitmask to isolate a group
616
+ private val M = (1 << S ) - 1
421
617
422
- def fromSeq(buf: collection.Seq[Base]): RNA1 = {
423
- val groups = new Array[Int]((buf.length + N - 1) / N)
424
- for (i <- 0 until buf.length)
425
- groups(i / N) |= Base.toInt(buf(i)) << (i % N * S)
426
- new RNA1(groups, buf.length)
427
- }
618
+ def fromSeq (buf : collection.Seq [Base ]): RNA1 = {
619
+ val groups = new Array [Int ]((buf.length + N - 1 ) / N )
620
+ for (i <- 0 until buf.length)
621
+ groups(i / N ) |= Base .toInt(buf(i)) << (i % N * S )
622
+ new RNA1 (groups, buf.length)
623
+ }
428
624
429
- def apply(bases: Base*) = fromSeq(bases)
430
- }
625
+ def apply (bases : Base * ) = fromSeq(bases)
626
+ }
627
+ ~~~
628
+ {% endtab %}
629
+ {% tab 'Scala 3' for=RNA1_1 %}
630
+ ~~~ scala
631
+ import collection .mutable
632
+ import collection .immutable .{ IndexedSeq , IndexedSeqOps }
633
+
634
+ final class RNA1 private
635
+ ( val groups : Array [Int ],
636
+ val length : Int
637
+ ) extends IndexedSeq [Base ],
638
+ IndexedSeqOps [Base , IndexedSeq , RNA1 ]:
639
+
640
+ import RNA1 .*
641
+
642
+ def apply (idx : Int ): Base =
643
+ if idx < 0 || length <= idx then
644
+ throw IndexOutOfBoundsException ()
645
+ Base .fromInt(groups(idx / N ) >> (idx % N * S ) & M )
646
+
647
+ override protected def fromSpecific (coll : IterableOnce [Base ]): RNA1 =
648
+ fromSeq(coll.iterator.toSeq)
649
+ override protected def newSpecificBuilder : mutable.Builder [Base , RNA1 ] =
650
+ iterableFactory.newBuilder[Base ].mapResult(fromSeq)
651
+ override def empty : RNA1 = fromSeq(Seq .empty)
652
+ override def className = " RNA1"
653
+ end RNA1
654
+
655
+ object RNA1 :
656
+
657
+ // Number of bits necessary to represent group
658
+ private val S = 2
659
+
660
+ // Number of groups that fit in an Int
661
+ private val N = 32 / S
662
+
663
+ // Bitmask to isolate a group
664
+ private val M = (1 << S ) - 1
665
+
666
+ def fromSeq (buf : collection.Seq [Base ]): RNA1 =
667
+ val groups = new Array [Int ]((buf.length + N - 1 ) / N )
668
+ for i <- 0 until buf.length do
669
+ groups(i / N ) |= Base .toInt(buf(i)) << (i % N * S )
670
+ new RNA1 (groups, buf.length)
671
+
672
+ def apply (bases : Base * ) = fromSeq(bases)
673
+ end RNA1
674
+ ~~~
675
+ {% endtab %}
676
+ {% endtabs %}
431
677
432
678
The [ RNA strands class listing] ( #first-version-of-rna-strands-class ) above
433
679
presents the first version of this
@@ -484,14 +730,22 @@ in the `RNA1` object. It takes a variable number of `Base` arguments and
484
730
simply forwards them as a sequence to ` fromSeq ` . Here are the two
485
731
creation schemes in action:
486
732
487
- scala> val xs = List(A, G, U, A)
488
- xs: List[Base] = List(A, G, U, A)
733
+ {% tabs RNA1_2 %}
734
+ {% tab 'Scala 2 and 3' for=RNA1_2 %}
735
+
736
+ ``` scala
737
+ scala> val xs = List (A , G , U , A )
738
+ val xs : List [Base ] = List (A , G , U , A )
739
+
740
+ scala> RNA1 .fromSeq(xs)
741
+ val res1 : RNA1 = RNA1 (A , G , U , A )
489
742
490
- scala> RNA1.fromSeq(xs)
491
- res1: RNA1 = RNA1(A, G, U, A)
743
+ scala> val rna1 = RNA1 (A , U , G , G , C )
744
+ val rna1 : RNA1 = RNA1 (A , U , G , G , C )
745
+ ```
492
746
493
- scala> val rna1 = RNA1(A, U, G, G, C)
494
- rna1: RNA1 = RNA1(A, U, G, G, C)
747
+ {% endtab %}
748
+ {% endtabs %}
495
749
496
750
Also note that the type parameters of the ` IndexedSeqOps ` trait that
497
751
we inherit from are: ` Base ` , ` IndexedSeq ` and ` RNA1 ` . The first one
@@ -507,11 +761,19 @@ third one is `RNA1`. This means that operations like `map` or
507
761
508
762
Here is an example showing the usage of ` take ` and ` filter ` :
509
763
510
- scala> rna1.take(3)
511
- res5: RNA1 = RNA1(A, U, G)
764
+ {% tabs RNA1_3 %}
765
+ {% tab 'Scala 2 and 3' for=RNA1_3 %}
766
+
767
+ ``` scala
768
+ scala> val rna1_2 = rna1.take(3 )
769
+ val rna1_2 : RNA1 = RNA1 (A , U , G )
770
+
771
+ scala> val rna1_3 = rna1.filter(_ != U )
772
+ val rna1_3 : RNA1 = RNA1 (A , G , G , C )
773
+ ```
512
774
513
- scala> rna1.filter(_ != U)
514
- res6: RNA1 = RNA1(A, G, G, C)
775
+ {% endtab %}
776
+ {% endtabs %}
515
777
516
778
### Dealing with map and friends ###
517
779
@@ -523,41 +785,65 @@ methods be adapted to RNA strands? The desired behavior would be to get
523
785
back an RNA strand when mapping bases to bases or appending two RNA strands
524
786
with ` ++ ` :
525
787
526
- scala> val rna = RNA(A, U, G, G, C)
527
- rna: RNA = RNA(A, U, G, G, C)
788
+ {% tabs RNA1_4 %}
789
+ {% tab 'Scala 2 and 3' for=RNA1_4 %}
528
790
529
- scala> rna map { case A => U case b => b }
530
- res7: RNA = RNA(U, U, G, G, C)
791
+ ``` scala
792
+ scala> val rna = RNA (A , U , G , G , C )
793
+ val rna : RNA = RNA (A , U , G , G , C )
531
794
532
- scala> rna ++ rna
533
- res8: RNA = RNA(A, U, G, G, C, A, U, G, G, C)
795
+ scala> rna.map { case A => U case b => b }
796
+ val res7 : RNA = RNA (U , U , G , G , C )
797
+
798
+ scala> rna ++ rna
799
+ val res8 : RNA = RNA (A , U , G , G , C , A , U , G , G , C )
800
+ ```
801
+
802
+ {% endtab %}
803
+ {% endtabs %}
534
804
535
805
On the other hand, mapping bases to some other type over an RNA strand
536
806
cannot yield another RNA strand because the new elements have the
537
807
wrong type. It has to yield a sequence instead. In the same vein
538
808
appending elements that are not of type ` Base ` to an RNA strand can
539
809
yield a general sequence, but it cannot yield another RNA strand.
540
810
541
- scala> rna map Base.toInt
542
- res2: IndexedSeq[Int] = Vector(0, 1, 2, 2, 3)
811
+ {% tabs RNA1_5 %}
812
+ {% tab 'Scala 2 and 3' for=RNA1_5 %}
813
+
814
+ ``` scala
815
+ scala> rna.map(Base .toInt)
816
+ val res2 : IndexedSeq [Int ] = Vector (0 , 1 , 2 , 2 , 3 )
817
+
818
+ scala> rna ++ List (" missing" , " data" )
819
+ val res3 : IndexedSeq [java.lang.Object ] =
820
+ Vector (A , U , G , G , C , missing, data)
821
+ ```
543
822
544
- scala> rna ++ List("missing", "data")
545
- res3: IndexedSeq[java.lang.Object] =
546
- Vector(A, U, G, G, C, missing, data)
823
+ {% endtab %}
824
+ {% endtabs %}
547
825
548
826
This is what you'd expect in the ideal case. But this is not what the
549
827
[ ` RNA1 ` class] ( #first-version-of-rna-strands-class ) provides. In fact, all
550
828
examples will return instances of ` Vector ` , not just the last two. If you run
551
829
the first three commands above with instances of this class you obtain:
552
830
553
- scala> val rna1 = RNA1(A, U, G, G, C)
554
- rna1: RNA1 = RNA1(A, U, G, G, C)
831
+ {% tabs RNA1_6 %}
832
+ {% tab 'Scala 2 and 3' for=RNA1_6 %}
555
833
556
- scala> rna1 map { case A => U case b => b }
557
- res0: IndexedSeq[Base] = Vector(U, U, G, G, C)
834
+ ``` scala
835
+ scala> val rna1 = RNA1 (A , U , G , G , C )
836
+ val rna1 : RNA1 = RNA1 (A , U , G , G , C )
558
837
559
- scala> rna1 ++ rna1
560
- res1: IndexedSeq[Base] = Vector(A, U, G, G, C, A, U, G, G, C)
838
+ scala> rna1.map { case A => U case b => b }
839
+ val res0 : IndexedSeq [Base ] = Vector (U , U , G , G , C )
840
+
841
+ scala> rna1 ++ rna1
842
+ val res1 : IndexedSeq [Base ] = Vector (A , U , G , G , C , A , U , G , G , C )
843
+ ```
844
+
845
+ {% endtab %}
846
+ {% endtabs %}
561
847
562
848
So the result of ` map ` and ` ++ ` is never an RNA strand, even if the
563
849
element type of the generated collection is ` Base ` . To see how to do
@@ -566,7 +852,13 @@ method (or of `++`, which has a similar signature). The `map` method is
566
852
originally defined in class ` scala.collection.IterableOps ` with the
567
853
following signature:
568
854
569
- def map[B](f: A => B): CC[B]
855
+ {% tabs map_signature %}
856
+ {% tab 'Scala 2 and 3' for=map_signature %}
857
+ ``` scala
858
+ def map [B ](f : A => B ): CC [B ]
859
+ ```
860
+ {% endtab %}
861
+ {% endtabs %}
570
862
571
863
Here ` A ` is the type of elements of the collection, and ` CC ` is the type
572
864
constructor passed as a second parameter to the ` IterableOps ` trait.
@@ -576,38 +868,84 @@ this is why we always get a `Vector` as a result.
576
868
577
869
### Second version of RNA strands class ###
578
870
579
- import scala.collection.{ View, mutable }
580
- import scala.collection.immutable.{ IndexedSeq, IndexedSeqOps }
581
-
582
- final class RNA2 private (val groups: Array[Int], val length: Int)
583
- extends IndexedSeq[Base] with IndexedSeqOps[Base, IndexedSeq, RNA2] {
584
-
585
- import RNA2._
586
-
587
- def apply(idx: Int): Base = // as before
588
- override protected def fromSpecific(coll: IterableOnce[Base]): RNA2 = // as before
589
- override protected def newSpecificBuilder: mutable.Builder[Base, RNA2] = // as before
590
-
591
- // Overloading of `appended`, `prepended`, `appendedAll`,
592
- // `prependedAll`, `map`, `flatMap` and `concat` to return an `RNA2`
593
- // when possible
594
- def concat(suffix: IterableOnce[Base]): RNA2 =
595
- fromSpecific(iterator ++ suffix.iterator)
596
- // symbolic alias for `concat`
597
- @inline final def ++ (suffix: IterableOnce[Base]): RNA2 = concat(suffix)
598
- def appended(base: Base): RNA2 =
599
- fromSpecific(new View.Appended(this, base))
600
- def appendedAll(suffix: IterableOnce[Base]): RNA2 =
601
- concat(suffix)
602
- def prepended(base: Base): RNA2 =
603
- fromSpecific(new View.Prepended(base, this))
604
- def prependedAll(prefix: IterableOnce[Base]): RNA2 =
605
- fromSpecific(prefix.iterator ++ iterator)
606
- def map(f: Base => Base): RNA2 =
607
- fromSpecific(new View.Map(this, f))
608
- def flatMap(f: Base => IterableOnce[Base]): RNA2 =
609
- fromSpecific(new View.FlatMap(this, f))
610
- }
871
+ {% tabs RNA2_1 class=tabs-scala-version %}
872
+ {% tab 'Scala 2' for=RNA2_1 %}
873
+ ~~~ scala
874
+ import scala .collection .{ View , mutable }
875
+ import scala .collection .immutable .{ IndexedSeq , IndexedSeqOps }
876
+
877
+ final class RNA2 private (val groups : Array [Int ], val length : Int )
878
+ extends IndexedSeq [Base ] with IndexedSeqOps [Base , IndexedSeq , RNA2 ] {
879
+
880
+ import RNA2 ._
881
+
882
+ def apply (idx : Int ): Base = // as before
883
+ override protected def fromSpecific (coll : IterableOnce [Base ]): RNA2 = // as before
884
+ override protected def newSpecificBuilder : mutable.Builder [Base , RNA2 ] = // as before
885
+ override def empty : RNA2 = // as before
886
+ override def className = " RNA2"
887
+
888
+ // Overloading of `appended`, `prepended`, `appendedAll`,
889
+ // `prependedAll`, `map`, `flatMap` and `concat` to return an `RNA2`
890
+ // when possible
891
+ def concat (suffix : IterableOnce [Base ]): RNA2 =
892
+ fromSpecific(iterator ++ suffix.iterator)
893
+ // symbolic alias for `concat`
894
+ @ inline final def ++ (suffix : IterableOnce [Base ]): RNA2 = concat(suffix)
895
+ def appended (base : Base ): RNA2 =
896
+ fromSpecific(new View .Appended (this , base))
897
+ def appendedAll (suffix : IterableOnce [Base ]): RNA2 =
898
+ concat(suffix)
899
+ def prepended (base : Base ): RNA2 =
900
+ fromSpecific(new View .Prepended (base, this ))
901
+ def prependedAll (prefix : IterableOnce [Base ]): RNA2 =
902
+ fromSpecific(prefix.iterator ++ iterator)
903
+ def map (f : Base => Base ): RNA2 =
904
+ fromSpecific(new View .Map (this , f))
905
+ def flatMap (f : Base => IterableOnce [Base ]): RNA2 =
906
+ fromSpecific(new View .FlatMap (this , f))
907
+ }
908
+ ~~~
909
+ {% endtab %}
910
+ {% tab 'Scala 3' for=RNA2_1 %}
911
+ ~~~ scala
912
+ import scala .collection .{ View , mutable }
913
+ import scala .collection .immutable .{ IndexedSeq , IndexedSeqOps }
914
+
915
+ final class RNA2 private (val groups : Array [Int ], val length : Int )
916
+ extends IndexedSeq [Base ], IndexedSeqOps [Base , IndexedSeq , RNA2 ]:
917
+
918
+ import RNA2 .*
919
+
920
+ def apply (idx : Int ): Base = // as before
921
+ override protected def fromSpecific (coll : IterableOnce [Base ]): RNA2 = // as before
922
+ override protected def newSpecificBuilder : mutable.Builder [Base , RNA2 ] = // as before
923
+ override def empty : RNA2 = // as before
924
+ override def className = " RNA2"
925
+
926
+ // Overloading of `appended`, `prepended`, `appendedAll`,
927
+ // `prependedAll`, `map`, `flatMap` and `concat` to return an `RNA2`
928
+ // when possible
929
+ def concat (suffix : IterableOnce [Base ]): RNA2 =
930
+ fromSpecific(iterator ++ suffix.iterator)
931
+ // symbolic alias for `concat`
932
+ inline final def ++ (suffix : IterableOnce [Base ]): RNA2 = concat(suffix)
933
+ def appended (base : Base ): RNA2 =
934
+ fromSpecific(View .Appended (this , base))
935
+ def appendedAll (suffix : IterableOnce [Base ]): RNA2 =
936
+ concat(suffix)
937
+ def prepended (base : Base ): RNA2 =
938
+ fromSpecific(View .Prepended (base, this ))
939
+ def prependedAll (prefix : IterableOnce [Base ]): RNA2 =
940
+ fromSpecific(prefix.iterator ++ iterator)
941
+ def map (f : Base => Base ): RNA2 =
942
+ fromSpecific(View .Map (this , f))
943
+ def flatMap (f : Base => IterableOnce [Base ]): RNA2 =
944
+ fromSpecific(View .FlatMap (this , f))
945
+ end RNA2
946
+ ~~~
947
+ {% endtab %}
948
+ {% endtabs %}
611
949
612
950
To address this shortcoming, you need to overload the methods that
613
951
return an ` IndexedSeq[B] ` for the case where ` B ` is known to be ` Base ` ,
@@ -622,19 +960,40 @@ collection is strict, we could take advantage of the better performance offered
622
960
in transformation operations.
623
961
Also, if we try to convert an ` Iterable[Base] ` into an ` RNA2 ` it fails:
624
962
625
- ~~~
963
+ {% tabs RNA2_2 class=tabs-scala-version %}
964
+ {% tab 'Scala 2' for=RNA2_2 %}
965
+ ~~~ scala
626
966
scala> val bases : Iterable [Base ] = List (A , U , C , C )
627
- bases: Iterable[Base] = List(A, U, C, C)
967
+ val bases : Iterable [Base ] = List (A , U , C , C )
628
968
629
969
scala> bases.to(RNA2 )
630
970
^
631
971
error : type mismatch ;
632
972
found : RNA2 .type
633
973
required : scala.collection.Factory [Base ,? ]
634
974
~~~
975
+ {% endtab %}
976
+ {% tab 'Scala 3' for=RNA2_2 %}
977
+ ~~~ scala
978
+ scala> val bases : Iterable [Base ] = List (A , U , C , C )
979
+ val bases : Iterable [Base ] = List (A , U , C , C )
980
+
981
+ scala> bases.to(RNA2 )
982
+ -- [E007 ] Type Mismatch Error : -------------------------------------------------
983
+ 1 | bases.to(RNA2 )
984
+ | ^^^^
985
+ | Found : RNA2 .type
986
+ | Required : scala.collection.Factory [Base , Any ]
987
+ |
988
+ | longer explanation available when compiling with `-explain`
989
+ ~~~
990
+ {% endtab %}
991
+ {% endtabs %}
635
992
636
993
### Final version of RNA strands class ###
637
994
995
+ {% tabs RNA_1 class=tabs-scala-version %}
996
+ {% tab 'Scala 2' for=RNA_1 %}
638
997
~~~ scala
639
998
import scala .collection .{ AbstractIterator , SpecificIterableFactory , StrictOptimizedSeqOps , View , mutable }
640
999
import scala .collection .immutable .{ IndexedSeq , IndexedSeqOps }
@@ -723,6 +1082,94 @@ object RNA extends SpecificIterableFactory[Base, RNA] {
723
1082
}
724
1083
}
725
1084
~~~
1085
+ {% endtab %}
1086
+ {% tab 'Scala 3' for=RNA_1 %}
1087
+ ~~~ scala
1088
+ import scala .collection .{ AbstractIterator , SpecificIterableFactory , StrictOptimizedSeqOps , View , mutable }
1089
+ import scala .collection .immutable .{ IndexedSeq , IndexedSeqOps }
1090
+
1091
+ final class RNA private
1092
+ ( val groups : Array [Int ],
1093
+ val length : Int
1094
+ ) extends IndexedSeq [Base ],
1095
+ IndexedSeqOps [Base , IndexedSeq , RNA ],
1096
+ StrictOptimizedSeqOps [Base , IndexedSeq , RNA ]:
1097
+ rna =>
1098
+
1099
+ import RNA .*
1100
+
1101
+ // Mandatory implementation of `apply` in `IndexedSeqOps`
1102
+ def apply (idx : Int ): Base =
1103
+ if idx < 0 || length <= idx then
1104
+ throw new IndexOutOfBoundsException
1105
+ Base .fromInt(groups(idx / N ) >> (idx % N * S ) & M )
1106
+
1107
+ // Mandatory overrides of `fromSpecific`, `newSpecificBuilder`,
1108
+ // and `empty`, from `IterableOps`
1109
+ override protected def fromSpecific (coll : IterableOnce [Base ]): RNA =
1110
+ RNA .fromSpecific(coll)
1111
+ override protected def newSpecificBuilder : mutable.Builder [Base , RNA ] =
1112
+ RNA .newBuilder
1113
+ override def empty : RNA = RNA .empty
1114
+
1115
+ // Overloading of `appended`, `prepended`, `appendedAll`, `prependedAll`,
1116
+ // `map`, `flatMap` and `concat` to return an `RNA` when possible
1117
+ def concat (suffix : IterableOnce [Base ]): RNA =
1118
+ strictOptimizedConcat(suffix, newSpecificBuilder)
1119
+ inline final def ++ (suffix : IterableOnce [Base ]): RNA = concat(suffix)
1120
+ def appended (base : Base ): RNA =
1121
+ (newSpecificBuilder ++= this += base).result()
1122
+ def appendedAll (suffix : Iterable [Base ]): RNA =
1123
+ strictOptimizedConcat(suffix, newSpecificBuilder)
1124
+ def prepended (base : Base ): RNA =
1125
+ (newSpecificBuilder += base ++= this ).result()
1126
+ def prependedAll (prefix : Iterable [Base ]): RNA =
1127
+ (newSpecificBuilder ++= prefix ++= this ).result()
1128
+ def map (f : Base => Base ): RNA =
1129
+ strictOptimizedMap(newSpecificBuilder, f)
1130
+ def flatMap (f : Base => IterableOnce [Base ]): RNA =
1131
+ strictOptimizedFlatMap(newSpecificBuilder, f)
1132
+
1133
+ // Optional re-implementation of iterator,
1134
+ // to make it more efficient.
1135
+ override def iterator : Iterator [Base ] = new AbstractIterator [Base ]:
1136
+ private var i = 0
1137
+ private var b = 0
1138
+ def hasNext : Boolean = i < rna.length
1139
+ def next (): Base =
1140
+ b = if i % N == 0 then groups(i / N ) else b >>> S
1141
+ i += 1
1142
+ Base .fromInt(b & M )
1143
+
1144
+ override def className = " RNA"
1145
+ end RNA
1146
+
1147
+ object RNA extends SpecificIterableFactory [Base , RNA ]:
1148
+
1149
+ private val S = 2 // number of bits in group
1150
+ private val M = (1 << S ) - 1 // bitmask to isolate a group
1151
+ private val N = 32 / S // number of groups in an Int
1152
+
1153
+ def fromSeq (buf : collection.Seq [Base ]): RNA =
1154
+ val groups = new Array [Int ]((buf.length + N - 1 ) / N )
1155
+ for i <- 0 until buf.length do
1156
+ groups(i / N ) |= Base .toInt(buf(i)) << (i % N * S )
1157
+ new RNA (groups, buf.length)
1158
+
1159
+ // Mandatory factory methods: `empty`, `newBuilder`
1160
+ // and `fromSpecific`
1161
+ def empty : RNA = fromSeq(Seq .empty)
1162
+
1163
+ def newBuilder : mutable.Builder [Base , RNA ] =
1164
+ mutable.ArrayBuffer .newBuilder[Base ].mapResult(fromSeq)
1165
+
1166
+ def fromSpecific (it : IterableOnce [Base ]): RNA = it match
1167
+ case seq : collection.Seq [Base ] => fromSeq(seq)
1168
+ case _ => fromSeq(mutable.ArrayBuffer .from(it))
1169
+ end RNA
1170
+ ~~~
1171
+ {% endtab %}
1172
+ {% endtabs %}
726
1173
727
1174
The final [ ` RNA ` class] ( #final-version-of-rna-strands-class ) :
728
1175
@@ -793,17 +1240,35 @@ of a map that's implemented as a Patricia trie. We call the map a
793
1240
selects a submap of all keys starting with a given prefix. We'll first
794
1241
define a prefix map with the keys shown in the running example:
795
1242
796
- scala> val m = PrefixMap("abc" -> 0, "abd" -> 1, "al" -> 2,
797
- "all" -> 3, "xy" -> 4)
798
- m: PrefixMap[Int] = PrefixMap((abc,0), (abd,1), (al,2), (all,3), (xy,4))
1243
+ {% tabs prefixMap_1 %}
1244
+ {% tab 'Scala 2 and 3' for=prefixMap_1 %}
1245
+
1246
+ ``` scala
1247
+ scala> val m = PrefixMap (" abc" -> 0 , " abd" -> 1 , " al" -> 2 ,
1248
+ " all" -> 3 , " xy" -> 4 )
1249
+ val m : PrefixMap [Int ] = PrefixMap ((abc,0 ), (abd,1 ), (al,2 ), (all,3 ), (xy,4 ))
1250
+ ```
1251
+
1252
+ {% endtab %}
1253
+ {% endtabs %}
799
1254
800
1255
Then calling ` withPrefix ` on ` m ` will yield another prefix map:
801
1256
802
- scala> m withPrefix "a"
803
- res14: PrefixMap[Int] = PrefixMap((bc,0), (bd,1), (l,2), (ll,3))
1257
+ {% tabs prefixMap_2 %}
1258
+ {% tab 'Scala 2 and 3' for=prefixMap_2 %}
1259
+
1260
+ ``` scala
1261
+ scala> m.withPrefix(" a" )
1262
+ val res14 : PrefixMap [Int ] = PrefixMap ((bc,0 ), (bd,1 ), (l,2 ), (ll,3 ))
1263
+ ```
1264
+
1265
+ {% endtab %}
1266
+ {% endtabs %}
804
1267
805
1268
### Patricia trie implementation ###
806
1269
1270
+ {% tabs prefixMap_3 class=tabs-scala-version %}
1271
+ {% tab 'Scala 2' for=prefixMap_3 %}
807
1272
~~~ scala
808
1273
import scala .collection ._
809
1274
import scala .collection .mutable .{ GrowableBuilder , Builder }
@@ -818,18 +1283,18 @@ class PrefixMap[A]
818
1283
819
1284
def get (s : String ): Option [A ] =
820
1285
if (s.isEmpty) value
821
- else suffixes get (s(0 )) flatMap (_.get(s substring 1 ))
1286
+ else suffixes. get(s(0 )). flatMap(_.get(s. substring( 1 ) ))
822
1287
823
1288
def withPrefix (s : String ): PrefixMap [A ] =
824
1289
if (s.isEmpty) this
825
1290
else {
826
1291
val leading = s(0 )
827
- suffixes get leading match {
1292
+ suffixes. get( leading) match {
828
1293
case None =>
829
1294
suffixes = suffixes + (leading -> empty)
830
1295
case _ =>
831
1296
}
832
- suffixes(leading) withPrefix (s substring 1 )
1297
+ suffixes(leading). withPrefix(s. substring( 1 ) )
833
1298
}
834
1299
835
1300
def iterator : Iterator [(String , A )] =
@@ -844,7 +1309,7 @@ class PrefixMap[A]
844
1309
845
1310
def subtractOne (s : String ): this .type = {
846
1311
if (s.isEmpty) { val prev = value; value = None ; prev }
847
- else suffixes get (s(0 )) flatMap (_.remove(s substring 1 ))
1312
+ else suffixes. get(s(0 )). flatMap(_.remove(s. substring( 1 ) ))
848
1313
this
849
1314
}
850
1315
@@ -864,7 +1329,7 @@ class PrefixMap[A]
864
1329
// Members declared in scala.collection.IterableOps
865
1330
override protected def fromSpecific (coll : IterableOnce [(String , A )]): PrefixMap [A ] = PrefixMap .from(coll)
866
1331
override protected def newSpecificBuilder : mutable.Builder [(String , A ), PrefixMap [A ]] = PrefixMap .newBuilder
867
-
1332
+
868
1333
override def className = " PrefixMap"
869
1334
}
870
1335
@@ -892,6 +1357,91 @@ object PrefixMap {
892
1357
893
1358
}
894
1359
~~~
1360
+ {% endtab %}
1361
+ {% tab 'Scala 3' for=prefixMap_3 %}
1362
+ ~~~ scala
1363
+ import scala .collection .*
1364
+ import scala .collection .mutable .{ GrowableBuilder , Builder }
1365
+
1366
+ class PrefixMap [A ]
1367
+ extends mutable.Map [String , A ],
1368
+ mutable.MapOps [String , A , mutable.Map , PrefixMap [A ]],
1369
+ StrictOptimizedIterableOps [(String , A ), mutable.Iterable , PrefixMap [A ]]:
1370
+
1371
+ private var suffixes : immutable.Map [Char , PrefixMap [A ]] = immutable.Map .empty
1372
+ private var value : Option [A ] = None
1373
+
1374
+ def get (s : String ): Option [A ] =
1375
+ if s.isEmpty then value
1376
+ else suffixes.get(s(0 )).flatMap(_.get(s.substring(1 )))
1377
+
1378
+ def withPrefix (s : String ): PrefixMap [A ] =
1379
+ if s.isEmpty then this
1380
+ else
1381
+ val leading = s(0 )
1382
+ suffixes.get(leading) match
1383
+ case None =>
1384
+ suffixes = suffixes + (leading -> empty)
1385
+ case _ =>
1386
+ suffixes(leading).withPrefix(s.substring(1 ))
1387
+
1388
+ def iterator : Iterator [(String , A )] =
1389
+ (for v <- value.iterator yield (" " , v)) ++
1390
+ (for (chr, m) <- suffixes.iterator
1391
+ (s, v) <- m.iterator yield (chr +: s, v))
1392
+
1393
+ def addOne (kv : (String , A )): this .type =
1394
+ withPrefix(kv._1).value = Some (kv._2)
1395
+ this
1396
+
1397
+ def subtractOne (s : String ): this .type =
1398
+ if s.isEmpty then { val prev = value; value = None ; prev }
1399
+ else suffixes.get(s(0 )).flatMap(_.remove(s.substring(1 )))
1400
+ this
1401
+
1402
+ // Overloading of transformation methods that should return a PrefixMap
1403
+ def map [B ](f : ((String , A )) => (String , B )): PrefixMap [B ] =
1404
+ strictOptimizedMap(PrefixMap .newBuilder, f)
1405
+ def flatMap [B ](f : ((String , A )) => IterableOnce [(String , B )]): PrefixMap [B ] =
1406
+ strictOptimizedFlatMap(PrefixMap .newBuilder, f)
1407
+
1408
+ // Override `concat` and `empty` methods to refine their return type
1409
+ override def concat [B >: A ](suffix : IterableOnce [(String , B )]): PrefixMap [B ] =
1410
+ strictOptimizedConcat(suffix, PrefixMap .newBuilder)
1411
+ override def empty : PrefixMap [A ] = PrefixMap ()
1412
+
1413
+ // Members declared in scala.collection.mutable.Clearable
1414
+ override def clear (): Unit = suffixes = immutable.Map .empty
1415
+ // Members declared in scala.collection.IterableOps
1416
+ override protected def fromSpecific (coll : IterableOnce [(String , A )]): PrefixMap [A ] = PrefixMap .from(coll)
1417
+ override protected def newSpecificBuilder : mutable.Builder [(String , A ), PrefixMap [A ]] = PrefixMap .newBuilder
1418
+
1419
+ override def className = " PrefixMap"
1420
+ end PrefixMap
1421
+
1422
+ object PrefixMap :
1423
+ def empty [A ] = new PrefixMap [A ]
1424
+
1425
+ def from [A ](source : IterableOnce [(String , A )]): PrefixMap [A ] =
1426
+ source match
1427
+ case pm : PrefixMap [A @ unchecked] => pm
1428
+ case _ => (newBuilder ++= source).result()
1429
+
1430
+ def apply [A ](kvs : (String , A )* ): PrefixMap [A ] = from(kvs)
1431
+
1432
+ def newBuilder [A ]: mutable.Builder [(String , A ), PrefixMap [A ]] =
1433
+ mutable.GrowableBuilder [(String , A ), PrefixMap [A ]](empty)
1434
+
1435
+ import scala .language .implicitConversions
1436
+
1437
+ implicit def toFactory [A ](self : this .type ): Factory [(String , A ), PrefixMap [A ]] =
1438
+ new Factory [(String , A ), PrefixMap [A ]]:
1439
+ def fromSpecific (it : IterableOnce [(String , A )]): PrefixMap [A ] = self.from(it)
1440
+ def newBuilder : mutable.Builder [(String , A ), PrefixMap [A ]] = self.newBuilder
1441
+ end PrefixMap
1442
+ ~~~
1443
+ {% endtab %}
1444
+ {% endtabs %}
895
1445
896
1446
The previous listing shows the definition of ` PrefixMap ` . The map has
897
1447
keys of type ` String ` and the values are of parametric type ` A ` . It extends
@@ -984,11 +1534,19 @@ present for all other collections in Scala's collection framework, so
984
1534
it makes sense to define them here, too. With the two methods, you can
985
1535
write ` PrefixMap ` literals like you do for any other collection:
986
1536
987
- scala> PrefixMap("hello" -> 5, "hi" -> 2)
988
- res0: PrefixMap[Int] = PrefixMap(hello -> 5, hi -> 2)
1537
+ {% tabs prefixMap_4 %}
1538
+ {% tab 'Scala 2 and 3' for=prefixMap_4 %}
1539
+
1540
+ ``` scala
1541
+ scala> PrefixMap (" hello" -> 5 , " hi" -> 2 )
1542
+ val res0 : PrefixMap [Int ] = PrefixMap (hello -> 5 , hi -> 2 )
1543
+
1544
+ scala> res0 += " foo" -> 3
1545
+ val res1 : res0.type = PrefixMap (hello -> 5 , hi -> 2 , foo -> 3 )
1546
+ ```
989
1547
990
- scala> res0 += "foo" -> 3
991
- res1: res0.type = PrefixMap(hello -> 5, hi -> 2, foo -> 3)
1548
+ {% endtab %}
1549
+ {% endtabs %}
992
1550
993
1551
## Summary ##
994
1552
0 commit comments