Showing posts with label tuple. Show all posts
Showing posts with label tuple. Show all posts
Saturday, December 26, 2009
Product
As a response to a comment on a previous post about about Tuples this topic is about Products and the most common subclasses of Tuples.
I should preface this post by saying that I am not going to attempt any scholastic style discussions, instead I am going to try to explain Product in practical terms and stay away from theory. That said on can think of Products as (potentially) heterogenous collections (non-resizable collections). There are several Product classes (Product, Product1, Product2...). All Product classes extend Product, which contains the common methods. Product has the methods for accessing the Product members and the subclass adds type information about the Members.
A good way to think about Product is to look at Tuple which is a Product. There is a good stack overflow question that is worth looking at.
To give more context consider other well known subclasses of Product:
I should preface this post by saying that I am not going to attempt any scholastic style discussions, instead I am going to try to explain Product in practical terms and stay away from theory. That said on can think of Products as (potentially) heterogenous collections (non-resizable collections). There are several Product classes (Product, Product1, Product2...). All Product classes extend Product, which contains the common methods. Product has the methods for accessing the Product members and the subclass adds type information about the Members.
A good way to think about Product is to look at Tuple which is a Product. There is a good stack overflow question that is worth looking at.
To give more context consider other well known subclasses of Product:
- All case classes
- List
- Option
- scala> val product : Product = (1,'2',3)
- product: Product = (1,2,3)
- scala> product.productIterator.foreach {println _}
- 1
- 2
- 3
- scala> product.productArity
- res1: Int = 3
- scala> product.productElement(2)
- res2: Any = 3
- scala> product.productPrefix
- res3: java.lang.String = Tuple3
- scala> product.toString
- res4: java.lang.String = (1,2,3)
- scala> val product3 = product.asInstanceOf[Product3[Int,Char,Int]]
- product3: Product3[Int,Char,Int] = (1,2,3)
- scala> product3._2
- res5: Char = 2
- scala> product3._3
- res6: Int = 3
- scala> case class Test(name:String, passed:Boolean, error:String)
- defined class Test
- scala> Test("Chicken Little", false, "the sky is falling")
- res7: Test = Test(Chicken Little,false,the sky is falling)
- scala> res7.productArity
- res8: Int = 3
- scala> res7.productIterator mkString ", "
- res9: String = Chicken Little, false, the sky is falling
Friday, December 18, 2009
Tuples are (not?) Collections
Tuples are quite handy but a potential annoyance is that at a glance they seem list-like but appearances can be deceiving. The normal collection methods are not supported by Tuples. For example suppose you like the simple syntax:
Word of warning. Unlike collections Tuples are very often not homogeneous. IE you do not have
Note: this was done with Scala 2.8 so results may be slightly different with 2.7. But I believe the syntax is valid.
(1,2,3)
better than List(1,2,3)
. With the Tuple you cannot do the maps, filters, etc... (with good reason) but there is a use-case for being able to convert a Tuple to a Traversable. Word of warning. Unlike collections Tuples are very often not homogeneous. IE you do not have
Tuple2[Int]
you have Tuple2[A,B]
. So the best you can do is to map a tuple to an Iterator[Any]. Note: this was done with Scala 2.8 so results may be slightly different with 2.7. But I believe the syntax is valid.
- // the productIterator method gives access to an iterator over the elements
- scala> (1,2,3).productIterator.map {_.toString} mkString (",")
- res1: String = 1,2,3
- scala> (1,2,3).productIterator foreach {println _}
- 1
- 2
- 3
- // if you know the tuple is heterogeneous then you can use partial functions
- // for casting the elements to a particular type.
- scala> (1,2,3).productIterator map {case i:Int => i + 2} foreach {println _}
- 3
- 4
- 5
- // To get a full Traversable out of the deal you use one of the many
- // to* methods to convert to Seq, List, Array, etc...
- scala> (1,2,3).productIterator.toList map {case i:Int => i + 2}
- res15: List[Int] = List(3, 4, 5)
- // and if you want you can use an implicit to clean up the syntax a bit
- // Problem with this is you need an implicit for each Tuple length
- scala> implicit def tupleToTraversable[T](t:(T,T,T)) = t.productIterator.toList map { case e:T => e}
- warning: there were unchecked warnings; re-run with -unchecked for details
- tupleToTraversable: [T](t: (T, T, T))List[T]
- scala> (1,2,3) foreach {println _}
- 1
- 2
- 3
- /*
- EDIT: Dan pointed out that the methods I am using are inherited from the
- Product super class of Tuple. So you can do something similar as follows.
- Note: I am using the same name as the previous method so that they don't interfer
- with one another
- */
- scala> implicit def tupleToTraversable[T](t:Product) = t.productIterator.toList map { case e:T => e}
- warning: there were unchecked warnings; re-run with -unchecked for details
- tupleToTraversable: [T](t: Product)List[T]
- scala> (1,2,3) foreach {println _}
- 1
- 2
- 3
- // this is interesting... it does cast to int unless required
- scala> (1,2,'t') foreach {println _}
- 1
- 2
- t
- // lets verify we are getting correct conversion
- scala> def inc(l:List[Int]) = l map {_ + 1}
- inc: (l: List[Int])List[Int]
- scala> inc ( (1,2,3))
- res4: List[Int] = List(2, 3, 4)
- // yep this I expected
- scala> inc ( (1,2,'t'))
- java.lang.ClassCastException: java.lang.Character cannot be cast to java.lang.Integer
- at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
- at $anonfun$inc1ドル.apply(< console>:7)
- at scala.collection.TraversableLike$$anonfun$map1ドル.apply(TraversableLike.scala:238)
- at .< clinit>(< console>)
- scala> def inc(l:List[Int]) = l foreach {println _}
- inc: (l: List[Int])Unit
- scala> def p(l:List[Int]) = l foreach {println _}
- p: (l: List[Int])Unit
- scala> p ( (1,2,'t'))
- 1
- 2
- t
Labels:
collections,
Scala,
tuple
Tuesday, November 24, 2009
Equals, Tuples and Case-classes
This is mostly a quick tip (or opportunity for refactoring).
Most of Scala's built in classes implement a useful equals and hashcode. The very commonly used case-classes and Tuple classes are the examples that spring to mind. So this enables the following options:
Most of Scala's built in classes implement a useful equals and hashcode. The very commonly used case-classes and Tuple classes are the examples that spring to mind. So this enables the following options:
- // Assume Box is out of your control and you cannot refactor it into a case class
- scala> class Box(val name:String, val minx:Int, val miny:Int, val maxx:Int, val maxy:Int)
- defined class Box
- scala> val box = new Box("mybox", 0, 0, 10, 10)
- box: Box = Box@568bf3ec
- // before:
- scala> box.minx == 0 && box.miny == 0 && box.maxx == 10 && box.maxy == 10
- res3: Boolean = true
- // after
- scala> import box._
- import box._
- scala> (minx,miny,maxx,maxy) == (0,0,10,10)
- res5: Boolean = true
- // another box definition:
- scala> case class Box2 (name:String, ul:(Int,Int), lr:(Int,Int))
- defined class Box2
- // case classes have nice equals for comparison
- scala> box2 == Box2("a nicer box", (0,0), (10,10))
- res6: Boolean = true
- // but what if you don't want to compare names
- scala> import box2._
- import box2._
- scala> (ul,lr) == ((0,0),(10,10))
- res7: Boolean = true
Labels:
case-classes,
equals,
intermediate,
Scala,
tuple
Thursday, November 5, 2009
Function._
If you have not looked at the Function object I would recommend you do. It contains several useful methods for combining and converting functions.
Examples:
Examples:
- cala> def twoParamFunc( i:Int, j:String) = println(i,j)
- twoParamFunc: (i: Int,j: String)Unit
- // the tupled method converts the function with n parameters into
- // a function that takes one tupleN
- scala> val tupled = Function tupled twoParamFunc _
- tupled: ((Int, String)) => Unit = < function1>
- scala> tupled (1 -> "one")
- (1,one)
- // A practical use-case is to convert an existing method
- // for use with a Map
- // Example with out using the tupled method
- scala> Map( 1 -> "one", 2 -> "two") map (entry => twoParamFunc(entry._1, entry._2))
- (1,one)
- (2,two)
- res5: scala.collection.immutable.Iterable[Unit] = List((), ())
- // example using tupled
- scala> Map( 1 -> "one", 2 -> "two") map (tupled)
- (1,one)
- (2,two)
- res7: scala.collection.immutable.Iterable[Unit] = List((), ())
- // and for balance the opposite
- scala> val untupled = Function untupled tupleParamFunc _
- untupled: (Int, String) => Unit = < function2>
- scala> untupled(1, "one")
- tuple = (1,one)
- // Chain is for chaining together an arbitrary number of functions
- scala> def inc(i:Int) = i + 1
- inc: (i: Int)Int
- scala> def double(i:Int) = i*i
- double: (i: Int)Int
- scala> def decr(i:Int) = i - 1
- decr: (i: Int)Int
- scala> (Function chain List(inc _, double _, decr _, double _, inc _, inc _))(3)
- res10: Int = 227
- // Now examples curries two methods then chains them together
- // define basic methods
- scala> def inc(step:Int,i:Int) = i + step
- inc: (step: Int,i: Int)Int
- scala> def multiply(i:Int, j:Int) = i * j
- multiply: (i: Int,j: Int)Int
- // Convert them to methods where the first argument is 3
- scala> val inc3 = (Function curried inc _)(3)
- inc3: (Int) => Int = < function1>
- scala> val multiplyBy3 = (Function curried multiply _)(3)
- multiplyBy3: (Int) => Int = < function1>
- // chain the resulting Function1 objects together and execute chain with parameter 3
- // (3 + 3)*3 = 18
- scala> chain(List(inc3, multiplyBy3))(3)
- res12: Int = 18
Thursday, September 17, 2009
Tuples and the -> implicit method
Defined in the Predef object are several implicit methods. All implicit methods defined in the Predef method are applicable to all programs without importing them. One such method is the -> method. This method is applicable to all objects and creates a Tuple2.
Examples:
Examples:
- // normal way to make a Tuple2
- scala> (1,2)
- res3: (Int, Int) = (1,2)
- // another way to make a Tuple2 using the implicit -> method
- scala> 1 -> 2
- res2: (Int, Int) = (1,2)
- // Creating a map using the normal tuples creation mechanism
- scala> Map (("one",1),
- | ("two",2))
- res5: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2)
- // -> syntax allows a second "prettier" way of creating the Map
- scala> Map("one" -> 1,
- | "two" -> 2)
- res6: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2)
Thursday, September 10, 2009
Assignments
Assignment in Scala follows more or less the same rules as Java and other related rules. There are some differences.
Increment operators do not work (
Assignments do not return a value. For example you cannot do
One feature that is fairly unique in Scala is the ability to expand a case-class or other class that has an associated extractor. For details look at the previous topic Assignment and Parameter Objects.
Examples:
Increment operators do not work (
i++
). As I understand it the rational is that it is too specific an idiom. There is not an easy way to generalize it. Instead you must use i += 1
.Assignments do not return a value. For example you cannot do
val i = j = 2
or while( (i = read(buffer)) > 0 ){...}
.One feature that is fairly unique in Scala is the ability to expand a case-class or other class that has an associated extractor. For details look at the previous topic Assignment and Parameter Objects.
Examples:
- scala> val i,j=2
- i: Int = 2
- j: Int = 2
- scala> val (i,j) = (1,2)
- i: Int = 1
- j: Int = 2
- scala> val (i,j,k) = (1,"two",3.0)
- i: Int = 1
- j: java.lang.String = two
- k: Double = 3.0
- scala> case class Data( name:String, age:Int, weight:Float)
- defined class Data
- scala> val Data(name, age, weight) = Data("Jesse", 133, 100f)
- name: String = Jesse
- age: Int = 133
- weight: Float = 100.0
- scala> val value = 1
- value: Int = 1
- scala> i += 1
:10: error: reassignment to val - i += 1
- ^
- scala> var variable = 1
- variable: Int = 1
- scala> variable += 1
- scala> println(variable)
- 2
Labels:
assignment,
beginner,
case-classes,
java,
Scala,
tuple
Sunday, August 16, 2009
Assignment and Parameter Objects
One of the principle design goals of Scala is to be "deep" not wide, which means the language attempts to have a small set of rules that can be applied in many different ways in different situations. Pattern matching is one of my favourite examples of this. Pattern matching is commonly seen in match { case... } statements but you will also see it frequently in exception handling, function declaration and, most important for this post, assignment.
Scala does not have multiple assignment like some languages. Instead it has tuples and matching. Tuples are a light-weight way of grouping data in a simple data object.
This is pattern matching.
Notice that in both cases you need the brackets around firstname, lastname. This instructs the compiler that you are matching against a Tuple.
Now the interesting use is with parameter objects. Tuples are a poor substitute for parameter objects because they do not have context. Changing:
to
Is not a good change because you loose context. What are the 3 strings? The information must go in the Javadocs. A better option:
The beauty is that you have an object that you can pass around easily. It is a case class therefore extracting the information is incredibly easy and unlike a tuple it has context and can have methods added easily.
Yes it is longer to write but if you need to reuse the data in several locations the trade off is well worth it in clarity.
Examples:
Scala does not have multiple assignment like some languages. Instead it has tuples and matching. Tuples are a light-weight way of grouping data in a simple data object.
(1,2.0,'c',"string", true)
. A simple example of a 5 element tuple. Tuples can be up to 22 elements long and can be homogenous or heterogenous. Using this for multiple assignement works something like:- val (firstname, lastname) = ("Jesse","Eichar")
This is pattern matching.
- scala> ("Jesse","Eichar") match {
- | case (firstname,lastname) => println(firstname,lastname)
- | }
- (Jesse,Eichar)
Notice that in both cases you need the brackets around firstname, lastname. This instructs the compiler that you are matching against a Tuple.
Now the interesting use is with parameter objects. Tuples are a poor substitute for parameter objects because they do not have context. Changing:
- def myMethod( firstname:String, middlename:String, lastname:String) = {...}
to
- def myMethod( name:(String,String,String)) = {...}
Is not a good change because you loose context. What are the 3 strings? The information must go in the Javadocs. A better option:
- case class Name(first:String, middle:String, last:String)
- def myMethod( name:Name ) = {
- val Name(first, middle, last) = name
- // do something with first middle last
- }
The beauty is that you have an object that you can pass around easily. It is a case class therefore extracting the information is incredibly easy and unlike a tuple it has context and can have methods added easily.
Yes it is longer to write but if you need to reuse the data in several locations the trade off is well worth it in clarity.
Examples:
- // define name data object.
- // Notice toString is a lazy val. This means value is calculated only once.
- scala> case class Name(first:String, middle:String, last:String) {
- | override lazy val toString="%s, %s %s" format (last, first,middle)
- | }
- defined class Name
- // toString formats name nicely.
- scala> Name("Jesse","Dale","Eichar")
- res1: Name = Eichar, Jesse Dale
- scala> def readName() = {
- | //maybe load from a file or database
- | Name("Jesse","Dale","Eichar") :: Name("Jody","","Garnett") :: Name("Andrea","","Antonello"):: Nil
- | }
- readName: ()List[Name]
- scala> def firstLastName(name:Name) = {
- | // we are putting _ because we don't care about middle name
- | val Name( first, _, last ) = name
- | (first, last)
- | }
- firstLastName: (Name)(String, String)
- // convert the list of Names to a list of tuples of first and last name
- scala> readName().map( firstLastName _ )
- res2: List[(String, String)] = List((Jesse,Eichar), (Jody,Garnett), (Andrea,Antonello))
- // print all first names starting with J
- scala> for( Name(first,_,_) <- readname; if (first.startsWith("J"))) println(first)
- Jesse
- Jody
- // print the first and middle parts of the first name in the list
- scala> readName() match {
- | // the :: _ indicates that we are matching against a list but we don't care about the rest of the list
- | case Name(f,m,_) :: _ => println( f, m)
- | case _ => println("Not a list of names")
- | }
- (Jesse,Dale)
Labels:
assignment,
case-classes,
data object,
intermediate,
matching,
Scala,
tuple
Subscribe to:
Comments (Atom)