Showing posts with label traversable. Show all posts
Showing posts with label traversable. Show all posts
Monday, April 19, 2010
Scala 2.8 Arrays are not Traversables
One performance/consistency change that has been make in Scala 2.8 is to make Scala Array always be a Java Array. This has some consequences which we will examine in this post. The biggest one is that Array is not a Scala Collection/Traversable. It is implicitly converted to one but it is not an instance of a Traversable. There are several reasons this was done. Probably the biggest is for performance. Because a Scala array is a Java array there is no overhead when using a Scala array.
Thanks to implicit type conversion all the normal collection methods are useable with an array. Even better, after running a method like map the result will again be a Java array. So the API is much more consistent.
An example illustrating that an Array is not a Traversable:
Another example:
So suppose you want to be able to accept and use arrays and Traversables in a method but you want to be able to
check that the parameter is an Array. Why not match against WrappedArray. You probably can, but you may get performance improvements in some cases if you don't require wrapping the array.
For a more concrete example of why you may want to do this. In a Input/Output routine I wrote I would write the data one way if the input was an Array:
So the work around is to define a Generic parameter for the method:
Thanks to implicit type conversion all the normal collection methods are useable with an array. Even better, after running a method like map the result will again be a Java array. So the API is much more consistent.
An example illustrating that an Array is not a Traversable:
- // This does not compile (which is good)
- // because Traversable[Int] can never be an array
- scala> def x(t:Traversable[Int]) = t match {
- | case x : Array[Int] => true
- | }
- < console>:13: error: pattern type is incompatible with expected type;
- found : Array[Int]
- required: Traversable[Int]
- case x : Array[Int] => true
- ^
- < console>:13: error: type mismatch;
- found : Array[Int]
- required: Traversable[Int]
- case x : Array[Int] => true
- ^
Another example:
- scala> def x(t:Traversable[Int]) = t.isInstanceOf[Array[_]]
- x: (t: Traversable[Int])Boolean
- /* this evaluates to false because Array is converted
- * to WrappedArray because it has to be implicitly converted
- * to a Traversable. Since Array is not a Traversable the resulting
- * object is not an Array
- */
- scala> x(Array(1,2,3))
- res24: Boolean = false
- scala> def x(t:Traversable[Int]) = println(t)
- x: (t: Traversable[Int])Unit
- // This method call demonstrates the previous assertion
- scala> x(Array(1,2,3))
- WrappedArray(1, 2, 3)
So suppose you want to be able to accept and use arrays and Traversables in a method but you want to be able to
check that the parameter is an Array. Why not match against WrappedArray. You probably can, but you may get performance improvements in some cases if you don't require wrapping the array.
For a more concrete example of why you may want to do this. In a Input/Output routine I wrote I would write the data one way if the input was an Array:
stream.write(array)
. But if the input was a traversable then I would have to handle it differently. My particular issue was more complicated than that but that is the basic issue.So the work around is to define a Generic parameter for the method:
- scala> def x[T <% Traversable[Int]](t:T) = t match {
- | case x : Array[Int] => true
- | }
- x: [T](t: T)(implicit evidence1ドル: (T) => Traversable[Int])Boolean
- scala> x(Array(1,2,3))
- res27: Boolean = true
Labels:
array,
implicit,
Scala,
traversable
Tuesday, April 13, 2010
Creating Custom Traversable implementations
One of the most talked about features of Scala 2.8 is the improved Collections libraries. Creating your own implementation is trivial, however if you want your new collection to behave the same way as all the included libraries there are a few tips you need to be aware of.
Note: All of these examples can either be ran in the REPL or put in a file and ran
Starting with the simple implementation:
This is a silly collection I admit but it is custom :).
This example works but if you test the result of a map operation (or any other operation that returns a new instance of the collection) you will notice it is not an instance of MyColl. This is expected because unless otherwise defined Traversable will return a new instance of Traversable.
To demonstrate run the following tests:
Both assertions will fail. The reason for these failures is because the collection is immutable which dictates by necessity that a new object must be returned from filter/map/etc... Since the Traversable trait returns instances of Traversable these two assertions fail. The easiest way to make these methods return an instance of MyColl is to make the following changes/additions.
Now instances of MyColl will be created by the various filter/map/etc... methods and that is fine as long as the new object is not required at compile-time. But suppose we added a method to the class and want that accessible after applying methods like map and filter.
Adding
All that needs to be done is to add
Note that the order in which the traits are mixed in is important.
Now add in a new method to demonstrate that the new collection works as desired and we are done.
The following is the complete implementation with the tests. You can put it in a file and run scala <filename> or paste it into a REPL
Note: All of these examples can either be ran in the REPL or put in a file and ran
Starting with the simple implementation:
- import scala.collection._
- import scala.collection.generic._
- class MyColl[A](seq : A*) extends Traversable[A] {
- // only abstract method in traversable is foreach... easy :)
- def foreach[U](f: A => U) = util.Random.shuffle(seq.toSeq).foreach(f)
- }
This is a silly collection I admit but it is custom :).
This example works but if you test the result of a map operation (or any other operation that returns a new instance of the collection) you will notice it is not an instance of MyColl. This is expected because unless otherwise defined Traversable will return a new instance of Traversable.
To demonstrate run the following tests:
- val c = new MyColl(1, 2, 3)
- println (c mkString ",")
- println(c mkString ",")
- println(c drop 1 mkString ",")
- // this two next assertions fail (see following explanation)
- assert(c.drop(1).isInstanceOf[MyColl[_]])
- assert((c map {_ + 1}).isInstanceOf[MyColl[_]])
Both assertions will fail. The reason for these failures is because the collection is immutable which dictates by necessity that a new object must be returned from filter/map/etc... Since the Traversable trait returns instances of Traversable these two assertions fail. The easiest way to make these methods return an instance of MyColl is to make the following changes/additions.
- import scala.collection._
- import scala.collection.generic._
- /*
- Adding GenericTraversableTemplate will delegate the creation of new
- collections to the companion object. Adding the trait and
- companion object causes all the new collections to be instances of MyColl
- */
- class MyColl[A](seq : A*) extends Traversable[A]
- with GenericTraversableTemplate[A, MyColl] {
- override def companion = MyColl
- def foreach[U](f: A => U) = util.Random.shuffle(seq.toSeq).foreach(f)
- }
- // The TraversableFactory trait is required by GenericTraversableTemplate
- object MyColl extends TraversableFactory[MyColl] {
- /*
- If you look at the signatures of many methods in TraversableLike they have an
- implicit parameter canBuildFrom. This allows one to define how the returned collections
- are built. For example one could make a list's map method return a Set
- In this case we define the default canBuildFrom for MyColl
- */
- implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, MyColl[A]] = new GenericCanBuildFrom[A]
- /*
- The method that builds the new collection. This is a simple implementation
- but it works. There are other implementations to assist with implementation if
- needed
- */
- def newBuilder[A] = new scala.collection.mutable.LazyBuilder[A,MyColl[A]] {
- def result = {
- val data = parts.foldLeft(List[A]()){(l,n) => l ++ n}
- new MyColl(data:_*)
- }
- }
- }
Now instances of MyColl will be created by the various filter/map/etc... methods and that is fine as long as the new object is not required at compile-time. But suppose we added a method to the class and want that accessible after applying methods like map and filter.
Adding
val o : MyColl[Long] = c map {_.toLong}
to the assertions will cause a compilation error since statically the class returned is Traversable[Long]. The fix is easy.All that needs to be done is to add
with TraversableLike[A, MyColl[A]]
to MyColl and we are golden. There may be other methods as well but this works and is simple.Note that the order in which the traits are mixed in is important.
TraversableLike[A, MyColl[A]]
must be mixed in after Traversable[A]
. The reason is that we want methods like map and drop to return instances of MyColl (statically as well as dynamically). If the order was reversed then those methods would return Traversable event though statically the actual instances would still be MyColl.- import scala.collection._
- import scala.collection.generic._
- class MyColl[A](seq : A*) extends Traversable[A]
- with GenericTraversableTemplate[A, MyColl]
- with TraversableLike[A, MyColl[A]] {
- override def companion = MyColl
- def foreach[U](f: A => U) = util.Random.shuffle(seq.toSeq).foreach(f)
- }
- object MyColl extends TraversableFactory[MyColl] {
- implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, MyColl[A]] = new GenericCanBuildFrom[A]
- def newBuilder[A] = new scala.collection.mutable.LazyBuilder[A,MyColl[A]] {
- def result = {
- val data = parts.foldLeft(List[A]()){(l,n) => l ++ n}
- new MyColl(data:_*)
- }
- }
- }
Now add in a new method to demonstrate that the new collection works as desired and we are done.
The following is the complete implementation with the tests. You can put it in a file and run scala <filename> or paste it into a REPL
- import scala.collection._
- import scala.collection.generic._
- import scala.collection.mutable.{ Builder, ListBuffer }
- class MyColl[A](seq : A*) extends Traversable[A]
- with GenericTraversableTemplate[A, MyColl]
- with TraversableLike[A, MyColl[A]] {
- override def companion = MyColl
- def foreach[U](f: A => U) = util.Random.shuffle(seq.toSeq).foreach(f)
- def sayhi = println("hi!")
- }
- object MyColl extends TraversableFactory[MyColl] {
- implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, MyColl[A]] = new GenericCanBuildFrom[A]
- def newBuilder[A] = new ListBuffer[A] mapResult (x => new MyColl(x:_*))
- }
- val c = new MyColl(1, 2, 3)
- println (c mkString ",")
- println(c mkString ",")
- assert(c.drop(1).isInstanceOf[MyColl[_]])
- assert((c map {_ + 1}).isInstanceOf[MyColl[_]])
- val o : MyColl[Int] = c filter {_ < 2}
- println(o mkString "," )
- o.sayhi
Monday, March 15, 2010
Unzip
_ Scala 2.8 only tip _
Unzip is a handy companion to partition.
- Partition divides a traversable into two traversables by a boolean predicate.
- Unzip divides a traversable into two by dividing each element into two parts (each becomes an element in one traversable). If an element is a Tuple2 then each tuple is divided into two otherwise a function is required to divide an element into two.
Unzip is a handy companion to partition.
- Partition divides a traversable into two traversables by a boolean predicate.
- Unzip divides a traversable into two by dividing each element into two parts (each becomes an element in one traversable). If an element is a Tuple2 then each tuple is divided into two otherwise a function is required to divide an element into two.
- // basic usage
- scala> List((1,2),(2,3)).unzip
- res2: (List[Int], List[Int]) = (List(1, 2),List(2, 3))
- /*
- tuples can be of different types
- and the resulting traversables reflect the differing types
- */
- scala> List((2,"a"),(3,"b")).unzip
- res3: (List[Int], List[java.lang.String]) = (List(2, 3),List(a, b))
- // Maps are Traversable[Collection] so unzip works with them
- scala> Map(1 -> 2, 3 -> 4).unzip
- res1: (scala.collection.immutable.Iterable[Int], scala.collection.immutable.Iterable[Int]) = (List(1, 3),List(2, 4))
- // Of course sets result in sets and duplicates are collected to a single element
- scala> Set((1,2),(2,2)).unzip
- res7: (scala.collection.immutable.Set[Int], scala.collection.immutable.Set[Int]) = (Set(1, 2),Set(2))
- /*
- Arbitrary elements can be unziped if a method is provided to decompose each element
- */
- scala> List("one word", "another word").unzip {e => (e takeWhile {_ != ' '}, e dropWhile {_ != ' '})}
- res6: (List[String], List[String]) = (List(one, another),List( word, word))
- /*
- The following shows the same function
- applied with map. It results in a single
- list of Tuples rather than two lists of single elements
- */
- scala> List("one word", "another word").map {e => (e takeWhile {_ != ' '}, e dropWhile {_ != ' '})}
- res8: List[(String, String)] = List((one, word), (another, word))
Labels:
2.8,
beginner,
Scala,
traversable,
unzip
Monday, December 7, 2009
Scala 2.8 Traversable and IndexedSequence
This topic is really just a collection of examples using the Scala 2.8 API. The examples are mostly based on Strings. But because Strings are in fact an IndexedSeq all of these examples also work with Lists arrays and the first several work with all collections.
These examples are all based on first Traversable. The basis of all collections and therefore will work with all collections. And the next set of examples work with IndexedSeq and most will work with Seq as well. Sets, Maps and others still need to be examined.
This is not an exhaustive set of examples and not all are even that interesting (sorry :-) ). But I let out the most trivial unless theay are being compared to others.
Traversable:
IndexedSequense additions to Traversable:
These examples are all based on first Traversable. The basis of all collections and therefore will work with all collections. And the next set of examples work with IndexedSeq and most will work with Seq as well. Sets, Maps and others still need to be examined.
This is not an exhaustive set of examples and not all are even that interesting (sorry :-) ). But I let out the most trivial unless theay are being compared to others.
Traversable:
- scala> val license = """Subject of Agreement. ?Product?, as referred to in this Agreement, shall be the binary software package ?Sun VirtualBox,? which Product allows for creating multiple virtual computers, each with different operating systems (?Guest Computers?), on a physical computer with a specific operating system (?Host Computer?), to allow for installing and executing these Guest Computers simultaneously. The Product consists of executable files in machine code for the Solaris, Windows, Linux, and Mac?OS?X operating systems as well as other data files as required by the executable files at run-time and documentation in electronic form. The Product includes all documentation and updates provided to You by Sun under this Agreement and the terms of this Agreement will apply to all such documentation and updates unless a different license is provided with an update or documentation."""
- res0: RandomAccessSeq[Char] =
- license: java.lang.String = ...
- scala> license count (_ == 'u')
- res1: Int = 37
- scala> license split ' ' count {_ == "and"}
- res3: Int = 6
- scala> license ++ " this is silly!"
- res0: RandomAccessSeq[Char] = ...
- scala> license dropWhile { _ != 'y'} drop 1 takeWhile { _ != 'y'}
- res6: Seq[Char] = RandomAccessSeqTW( , s, o, ...
- scala> license forall {_ != 'z'}
- res8: Boolean = true
- scala> (1 until 5).init
- res0: scala.collection.immutable.Range = Range(1, 2, 3)
- scala> (1 until 5).head
- res1: Int = 1
- scala> (1 until 5).tail
- res2: scala.collection.immutable.IndexedSeq[Int] = Range(2, 3, 4)
- scala> (1 until 5).last
- res3: Int = 4
- scala> (1 until 5).headOption
- res4: Option[Int] = Some(1)
- scala> (1 until 5).lastOption
- res6: Option[Int] = Some(4)
- scala> license max
- res8: Char = y
- scala> license min
- res9: Char =
- scala> List() nonEmpty
- res10: Boolean = false
- scala> List() isEmpty
- res11: Boolean = true
- scala> license partialMap {case c if ('a' to 'z' contains c) => c}
- res13: scala.collection.immutable.IndexedSeq[Any] = IndexedSeq(u, b, j, e, c ...)
- scala> 1 to 10000 by 2 product
- res19: Int = 481029393
- scala> 1 to 10000 by 2 sum
- res23: Int = 25000000
- scala> license partition {"aeiou" contains _}
- res20: (String, String) = (ueoeeeouaeeeoiieeeaeeia ....
- scala> license span {_ != 'a'}
- res22: (String, String) = (Subject of Agreement. ?Product?, ,as referred to in this Agreement, shall be the binary software package ?Sun VirtualBox,? which Product allows for creating multiple virtual computers, each with differ ...
- scala> val consonants = license withFilter {c => !("aeiuo" contains c)}
- consonants: scala.collection.immutable.StringOps#WithFilter = scala.collection.TraversableLike$WithFilter@78e8a591
- scala> consonants foreach print _
- Sbjct f Agrmnt. ?Prdct?, s rfrrd t n ths Agrmnt, shll b th bnry sftwr pckg ?Sn VrtlBx,? whch Prdct llws fr crtng mltpl vrtl cmptrs, ch wth dffrnt prtng systms (?Gst Cmptrs?), n physcl cmptr wth spcfc prtng systm (?Hst Cmptr?), t llw fr nstllng nd xctng ths Gst Cmptrs smltnsly. Th Prdct cnssts f xctbl fls n mchn cd fr th Slrs, Wndws, Lnx, nd Mc?OS?X prtng systms s wll s thr dt fls s rqrd by th xctbl fls t rn-tm nd dcmnttn n lctrnc frm. Th Prdct nclds ll dcmnttn nd pdts prvdd t Y by Sn ndr ths Agrmnt nd th trms f ths Agrmnt wll pply t ll sch dcmnttn nd pdts nlss dffrnt lcns s prvdd wth n pdt r dcmnttn.
IndexedSequense additions to Traversable:
- scala> '!' +: license
- res33: String = !Subject of Agreement
- scala> "hello" +: license
- res32: scala.collection.immutable.IndexedSeq[Any] = IndexedSeq(hello, S, u, b, j, e, c, t, , o, f, , A, g, r, e, e, m, e, n, t, ., , ?, P, r, o, d, u, c, t, ?, ,, , a, s, , r, e, f, e, r, r, e, d, , t, o, , i, n, , t, h, i, s, , A, g, r, e, e, m, e, n, t, ,, , s, h, a, l, l, , b, e, , t, h, e, , b, i, n, a, r, y, , s, o, f, t, w, a, r, e, , p, a, c, k, a, g, e, , ?, S, u, n, , V, i, r, t, u, a, l, B, o, x, ,, ?, , w, h, i, c, h, , P, r, o, d, u, c, t, , a, l, l
- scala> license(11)
- res35: Char = A
- scala> license diff ("aeiou")
- res47: String = Sbjct f Agreement. ?Product?, s referred to n this Agreement, shall be the binary software package ?Sun VirtualBox,? which Product allows for creating multiple virtual computers, each with different operating systems (?Guest Computers?), on a physical computer with a specific operating system (?Host Computer?), to allow for installing and executing these Guest Computers simultaneously. The Product consists of executable files in machine code for the Solaris, Windows, Linux, and Mac?OS?X operating systems as well as other data files as required by the executable files at run-time and documentation in electronic form. The Product includes all documentation and updates provided to You by Sun under this Agreement and the terms of this Agreement will apply to all such documentation and updates unless a different license is provided with an update or documentation.
- scala> license dropRight(30)
- res48: String = Subject of Agreement. ?Product?, as referred to in this Agreement, shall be the binary software package ?Sun VirtualBox,? which Product allows for creating multiple virtual computers, each with different operating systems (?Guest Computers?), on a physical computer with a specific operating system (?Host Computer?), to allow for installing and executing these Guest Computers simultaneously. The Product consists of executable files in machine code for the Solaris, Windows, Linux, and Mac?OS?X operating systems as well as other data files as required by the executable files at run-time and documentation in electronic form. The Product includes all documentation and updates provided to You by Sun under this Agreement and the terms of this Agreement will apply to all such documentation and updates unless a different license is provided wi
- scala> license takeRight(10)
- res49: String = mentation.
- scala> license indexWhere { "aeiou" contains _}
- res4: Int = 1
- scala> license lastIndexWhere { "aeiou" contains _}
- res6: Int = 873
- scala> List(1,2,3) flatten {0 to _}
- res7: List[Int] = List(0, 1, 0, 1, 2, 0, 1, 2, 3)
- scala> ' ' +: license zip license indexWhere {case (last, c) => "" + last + c == "an"}
- res14: Int = 343
- scala> license indexOfSlice ("and")
- res17: Int = 342
- scala> ('a' to 'z') ++ ('A' to 'Z')
- res25: scala.collection.IndexedSeqView[Char,IndexedSeq[_]] = IndexedSeqViewA(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z)
- scala> license intersect res25
- res26: String = SubjectofAgrmnPdasihlywpkVBxvGCHTWLMOXqY
- scala> license diff res25
- res27: String = eeet. ?rouct?, referred to n tis Agreement, shal be the binar softare acage ?Sun irtualo,? which Product allows for creating multiple irtual computers, each with different operating systems (?uest omputers?), on a physical computer with a specific operating system (?ost Computer?), to allow for installing and executing these Guest Computers simultaneously. he Product consists of executable files in machine code for the Solaris, indows, inux, and ac?S? operating systems as well as other data files as reuired by the executable files at run-time and documentation in electronic form. The Product includes all documentation and updates provided to ou by Sun under this Agreement and the terms of this Agreement will apply to all such documentation and updates unless a different license is provided with an update or documentation.
- scala> license filter res25
- < console>:7: error: type mismatch;
- found : scala.collection.IndexedSeqView[Char,IndexedSeq[_]]
- required: (Char) => Boolean
- license filter res25
- ^
- scala> license filter {res25 contains _}
- res29: String = SubjectofAgreementProductasreferredtointhisAgreementshallbethebinarysoftwarepackageSunVirtualBoxwhichProductallowsforcreatingmultiplevirtualcomputerseachwithdifferentoperatingsystemsGuestComputersonaphysicalcomputerwithaspecificoperatingsystemHostComputertoallowforinstallingandexecutingtheseGuestComputerssimultaneouslyTheProductconsistsofexecutablefilesinmachinecodefortheSolarisWindowsLinuxandMacOSXoperatingsystemsaswellasotherdatafilesasrequiredbytheexecutablefilesatruntimeanddocumentationinelectronicformTheProductincludesalldocumentationandupdatesprovidedtoYoubySununderthisAgreementandthetermsofthisAgreementwillapplytoallsuchdocumentationandupdatesunlessadifferentlicenseisprovidedwithanupdateordocumentation
- scala> license filterNot {res25 contains _}
- res30: String = . ??, , ? ,? , (? ?), (? ?), . , , , ?? - . .
- scala> license removeDuplicates
- res31: String = Subject ofAgrmn.?Pd,asihlywpkVBxv(GC)HTWLMOXq-Y
- scala> license segmentLength ({_ > 'A'},2)
- res37: Int = 5
- scala> license drop 2 span {_ > 'A'} _1
- res41: String = bject
- scala> license sortWith Ordering.Char
- res45: String = (()),,,,,,,,,-....??????????AAAABCCCGGHLMOPPPPSSSSSTTVWXYaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefffffffffffffffffggggggggggghhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijkllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnoooooooooooooooooooooooooooooooooooooooooooooooopppppppppppppppppppqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrsssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvwwwwwwwwwwxxxxxyyyyyyyyy
- scala> 'a' to 'z' union ('A' to 'Z')
- scala> "hello world" patch (6, "goof", 5)
- res51: String = hello goof
- scala> "hello world" updated(0,'H')
- res54: String = Hello world
Labels:
collections,
intermediate,
Scala,
seq,
traversable
Subscribe to:
Comments (Atom)