I tried googling for this but all I got were stories about minor celebrities. Given the lack of documentation, what is a DList?
It's a Difference List, along the lines of "Difference List as functions"
scala> val (l1, l2, l3) = (List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
l1: List[Int] = List(1, 2, 3)
l2: List[Int] = List(4, 5, 6)
l3: List[Int] = List(7, 8, 9)
Efficient prepending:
scala> l1 ::: l2 ::: l3
res8: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
Inefficient appending. This creates an intermediate list (l1 ++ l2), then ((l1 ++ l2) ++ l3)
scala> l1 ++ l2 ++ l3 // inefficient
res9: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
DList stores up the appends, and only needs to create one complete list, effectively invoking:
scala> List(l1, l2, l3) reduceRight ( _ ::: _)
res10: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
Difference lists are a list-like data structure that supports O(1) append operations.
Append, and other operations that modify a list are represented via function composition of the modification functions, rather than directly copying the list.
An example, from Haskell's dlist library:
-- Lists as functions
newtype DList a = DL { unDL :: [a] -> [a] }
-- The empty list
empty = DL id
-- The append case: composition, a la Hughes
append xs ys = DL (unDL xs . unDL ys)
-- Converting to a regular list, linear time.
toList = ($[]) . unDL
The technique goes back at least to Hughes 84, A novel representation of lists and its application to the function "reverse", R. John Hughes, 1984., where, he proposes representing lists as functions, and append as function composition, allowing e.g. reverse to run in linear time. From the paper:
It's a data type in the non-canonical scalaz package, useful for typed lists with constant-time access at both ends. (The trick is to google for "scala" AND "dlist".)
From the project page of scalaz:
DList, a data type for representing elements of the same type with constant time append/prepend operations.
Related
I am a beginner to Scala language. In Scala, List is Immutable as per below code:
scala> var list = List(1,2,3,4,5) // List created named ‘ list ’
list: List[Int] = List(1, 2, 3, 4, 5)
scala> 25 :: list // Prepend with Cons( :: ) , But here new list created.
res2: List[Int] = List(25, 1, 2, 3, 4, 5)
scala> list // print ‘ list ’
res3: List[Int] = List(1, 2, 3, 4, 5)
But,
scala> list
res1: List[Int] = List(1, 2, 3, 4, 5)
scala> list :+= 12 // append list with :+=
scala> list
res2: List[Int] = List(1, 2, 3, 4, 5, 12)
In above example, same "list" is appended. Then how list is immutable? It's confusing me. Any one kindly explain to me?
http://daily-scala.blogspot.co.uk/2010/03/implicit-operator.html
:+= is not just appending, it's appending to a new list and reassigning the variable to point to the new list. It's equivalent to list = list + 12.
25 ++ list is making a new list, but not assigning it anywhere.
In Scala, what is the best way to add an element to a list while making sure that the list always contains latest n elements.
So if list is (1, 2, 3, 4, 5) and n = 5, then adding 6 should result in (2, 3, 4, 5, 6).
One possible way could be:
list = list ::: List(6)
list = list.takeRight(5)
Are there any better ways? Also, is there a better data-structure for maintaining such frequently changing collection?
It sounds like a fixed size Circular Buffer would satisfy your need. I think apache commons provides some implementation.
A solution in scala using List could be:
scala> def dropHeadAndAddToList[T](obj: T, list: List[T]):List[T] = {
list.drop(1) :+ obj
}
dropHeadAndAddToList: [T](obj: T, list: List[T])List[T]
scala> val a = List(1,2,3,4,5)
a: List[Int] = List(1, 2, 3, 4, 5)
scala> dropHeadAndAddToList(6, a)
res0: List[Int] = List(2, 3, 4, 5, 6)
I am very new to Scala so this question may be very naive.
I have a list like this List[Int] = List(0, 3, 6, 12, 14, 15, 16, 17). I am trying to create a list like this [(0,3),(3,6),(6,12)..] and so on. So far this is what I have tried:
val l1= List(0, 3, 6, 12, 14, 15, 16, 17)
var l2=scala.collection.mutable.ListBuffer[(Int,Int)]()
l1.zipWithIndex.slice(0,l1.length-1).foreach(x=>{val newval=(x._1,l1(x._2+1)); l2+=newval})
Two questions here:
If I don't use val newval, i.e. try to do l1.zipWithIndex.slice(0,l1.length-1).foreach(x=>l2+=(x._1,l1(x._2+1))), the compiler says:
<console>:10: error: type mismatch;
found : Int
required: (Int, Int)
l1.zipWithIndex.slice(0,l1.length-1).foreach(x=>l2+=(x._1,l1(x._2+1))). Why is that?
What would a way to do it without the mutable listbuffer?
+= is a method on the ListBuffer l2 that accepts repeated parameters. That means when you do something like this:
scala> var l2 = scala.collection.mutable.ListBuffer[(Int, Int)]()
l2: scala.collection.mutable.ListBuffer[(Int, Int)] = ListBuffer()
scala> l2 += (1, 2)
<console>:9: error: type mismatch;
found : Int(1)
required: (Int, Int)
l2 += (1, 2)
.. The compiler thinks you are trying to add multiple Ints to the ListBuffer, when you are trying to add a tuple. You need an extra set of parentheses.
l1.zipWithIndex.slice(0,l1.length-1).foreach(x=> l2 += ((x._1,l1(x._2+1)) ))
You can use sliding, which will create a "sliding window" across the collection to return a list of lists of a specific group size, with a step size of one by default:
scala> List(0, 3, 6, 12, 14, 15, 16, 17).sliding(2)
.map { case List(a, b) => (a, b) }.toList
res10: List[(Int, Int)] = List((0,3), (3,6), (6,12), (12,14), (14,15), (15,16), (16,17))
besides the sliding, you could slide like following:
val l1= List(0, 3, 6, 12, 14, 15, 16, 17)
val l2 = l1.take(l1.size - 1).zip(l1.tail)
updated
l1.zip(l1.tail) works.
here is my code:
val l1 = List(1,2,3)
val l2 = List(4,5,6)
val l1l2_1 = l1 ::: l2
val l1l2_2 = l1.:::(l2)
val l1l2_3 = l2.:::(l1)
println(s"l1 = $l1 \nl2 = $l2 \nl1 ::: l2 = $l1l2_1 \nl1.:::(l2) = $l1l2_2 \nl2.:::(l1) = $l1l2_3 }")
and here is output:
l1 = List(1, 2, 3)
l2 = List(4, 5, 6)
l1 ::: l2 = List(1, 2, 3, 4, 5, 6)
l1.:::(l2) = List(4, 5, 6, 1, 2, 3)
l2.:::(l1) = List(1, 2, 3, 4, 5, 6) }
Why l1 ::: l2 is not equal with l1.:::(l2)?
Operators that end in : are treated differently from other ones. When used with infix notation, they reverse their arguments - the method is invoked on the argument on the right and the argument on the left is used as the parameter. So l1 ::: l2 is the same as l2.:::(l1).
Reason is two rules in Scala:
1. : operator/method is right associative.
2. Operator/Methods associativity is determined by the last character in the operator name.
For example:
Any method with arbitrary name, but ending with a :
def myMethod:(a: Any)
is treated like a :, so x myMethod: y is right associative too, and executed like y.myMethod:(x)
Read more in Programming in Scala book: Basic Types and Operations
Is there any difference between ::: and ++ for concatenating lists in Scala?
scala> List(1,2,3) ++ List(4,5)
res0: List[Int] = List(1, 2, 3, 4, 5)
scala> List(1,2,3) ::: List(4,5)
res1: List[Int] = List(1, 2, 3, 4, 5)
scala> res0 == res1
res2: Boolean = true
From the documentation it looks like ++ is more general whereas ::: is List-specific. Is the latter provided because it's used in other functional languages?
Legacy. List was originally defined to be functional-languages-looking:
1 :: 2 :: Nil // a list
list1 ::: list2 // concatenation of two lists
list match {
case head :: tail => "non-empty"
case Nil => "empty"
}
Of course, Scala evolved other collections, in an ad-hoc manner. When 2.8 came out, the collections were redesigned for maximum code reuse and consistent API, so that you can use ++ to concatenate any two collections -- and even iterators. List, however, got to keep its original operators, aside from one or two which got deprecated.
Always use :::. There are two reasons: efficiency and type safety.
Efficiency
x ::: y ::: z is faster than x ++ y ++ z, because ::: is right associative. x ::: y ::: z is parsed as x ::: (y ::: z), which is algorithmically faster than (x ::: y) ::: z (the latter requires O(|x|) more steps).
Type safety
With ::: you can only concatenate two Lists. With ++ you can append any collection to List, which is terrible:
scala> List(1, 2, 3) ++ "ab"
res0: List[AnyVal] = List(1, 2, 3, a, b)
++ is also easy to mix up with +:
scala> List(1, 2, 3) + "ab"
res1: String = List(1, 2, 3)ab
::: works only with lists, while ++ can be used with any traversable. In the current implementation (2.9.0), ++ falls back on ::: if the argument is also a List.
A different point is that the first sentence is parsed as:
scala> List(1,2,3).++(List(4,5))
res0: List[Int] = List(1, 2, 3, 4, 5)
Whereas the second example is parsed as:
scala> List(4,5).:::(List(1,2,3))
res1: List[Int] = List(1, 2, 3, 4, 5)
So if you are using macros, you should take care.
Besides, ++ for two lists is calling ::: but with more overhead because it is asking for an implicit value to have a builder from List to List. But microbenchmarks did not prove anything useful in that sense, I guess that the compiler optimizes such calls.
Micro-Benchmarks after warming up.
scala>def time(a: => Unit): Long = { val t = System.currentTimeMillis; a; System.currentTimeMillis - t}
scala>def average(a: () => Long) = (for(i<-1 to 100) yield a()).sum/100
scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ++ List(e) } })
res1: Long = 46
scala>average (() => time { (List[Int]() /: (1 to 1000)) { case (l, e) => l ::: List(e ) } })
res2: Long = 46
As Daniel C. Sobrai said, you can append the content of any collection to a list using ++, whereas with ::: you can only concatenate lists.