In scala, you can have 2 types of set where the elements are immutable or mutable, But as you cannot index these sets, so how can you change the elements of the latter sets??
In scala, you can have 2 types of set where the elements are immutable or mutable
That is not what the distinction between scala.collection.immutable.Set and scala.collection.mutable.Set is. It is not about mutability or immutability of the elements, it is about mutability or immutability of the sets themselves.
But as you cannot index these sets, so how can you change the elements of the latter sets
It is not about changing the elements of mutable sets. It is about changing the mutable sets themselves:
import scala.collection.mutable.{ Set => MSet}
import scala.collection.immutable.{Set => ISet}
val mset = MSet(1, 2, 3)
val iset = ISet(1, 2, 3)
mset += 4
mset //=> HashSet(1, 2, 3, 4): scala.collection.mutable.Set[Int]
val iset2 = iset + 4
iset //=> Set(1, 2, 3): scala.collection.immutable.Set[Int]
iset2 //=> Set(1, 2, 3, 4): scala.collection.immutable.Set[Int]
iset += 4
// ERROR: value += is not a member of scala.collection.immutable.Set[Int]
[Scastie link]
The difference between the two is that you can't add an element to an immutable set. Instead, when you call the scala.collection.immutable.Set.+(elem: A): Set[A] method, it will return a new set (iset2 in this case) that has the same elements as the original set (iset) plus the element we wanted to add.
Whereas the scala.collection.mutable.Set.+=(elem: A): Set.this.type method returns the same set (and in fact, as you can see in my example above, I actually ignore the return value) and mutates it to contain the additional element.
In Scala, the idea of a set is much closer to the mathematical idea of sets instead of as a collection. It is not a collection that contains the elements, rather it is a predicate function that you can ask "is this element a member of the set"?
Related
I want to define an array, each element of array is a data set read from certain path in the file system, because the data reading is costly and the position in array to be visited is sparse, so I want to use Lazy modifier to realize that one data set will not be read until being visited. How can define this kind of array?
Yes,we can define it with view function.
Instead of (0 to 10).toArray
scala> val a=(0 to 10).toArray
a: Array[Int] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
View can not instantiate the array but when ever we call then only it executes
val a=(0 to 10).view
a: scala.collection.SeqView[Int,scala.collection.immutable.IndexedSeq[Int]] = SeqView(...)
scala> for (x <- a ){
| println(x)}
0
1
2
3
4
5
6
7
8
9
10
I hope this answered your question
No, you cannot use lazy on the array to make the elements lazy. The most natural thing to do would be to use a caching library like ScalaCache.
You can also make a kind of wrapper class with a lazy field as you suggested in your comment. However, I would prefer not to expose the caching to the clients of the array. You should be able to just write myArray(index) to access an element.
If you don't want to use a library, this is another option (without lazy) that gives you an array like object with caching:
class CachingArray[A](size: Int, getElement: Int => A) {
val elements = scala.collection.mutable.Map[Int, A]()
def apply(index: Int) = elements.getOrElseUpdate(index, getElement(index))
}
Just initialize it with the size and a function that computes an element at a given index.
If you like, you can make it extend IndexedSeq[A] so it can be used more like a real array. Just implement length like this:
override def length: Int = size
mutable vs. immutable in Scala collections
Before I post this question, I have read the above article. Apparently if you store something in val, you can't modify it, but then if you store a mutable collection such as ArrayBuffer, you can modify it!
scala> val b = ArrayBuffer[Int](1,2,3)
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)
scala> b += 1
res50: b.type = ArrayBuffer(1, 2, 3, 1)
scala> b
res51: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 1)
What is the use of using val to store a mutable ArrayBuffer? I assume the only reason b changes is because val b holds the memory address to that ArrayBuffer(1,2,3).
If you try var x = 1; val y = x; x = 5; y, the output will still be 1. In this case, y stores an actual value instead of the address to x.
Java doesn't have this confusion because it's clear an Object can't be assigned to an int variable .
How do I know when is the variable in scala carrying a value, when is a memory address? What's the point of storing a mutable collection in a immutable variable?
A simple answer is that vals and vars are all references. There're no primitive types in Scala. They're all objects.
val x = 1
is a reference named x that points to an immutable integer object 1. You cannot do 1.changeTo(2) or something, so if you have
val value = 5
val x = value
var y = value
You can do y += 10 This changes y to reference a new object, (5 + 10) = 15. The original 5 remains 5.
On the other hand, you cannot do x += 10 because x is a val which means it must always point to 5. So, this doesn't compile.
You may wonder why you can do val b = ArrayBuffer(...) and then b += something even though b is a val. That's because += is actually a method, not an assignment. Calling b += something gets translated to b.+=(something). The method += just adds a new element (something) to its mutable self and returns itself for further assignment.
Let's see an example
scala> val xs = ArrayBuffer(1,2,3)
xs: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)
scala> val ys = ( xs += 999 )
ys: xs.type = ArrayBuffer(1, 2, 3, 999)
scala> xs
res0: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 999)
scala> ys
res1: xs.type = ArrayBuffer(1, 2, 3, 999)
scala> xs eq ys
res2: Boolean = true
This confirms xs and ys point to the same (mutable) ArrayBuffer. The eq method is like Java's ==, which compares object identity. Mutable/Immutable references (val/var) and mutable/immutable data structures (ArrayBuffer, List) are different. So, if you do another xs += 888, the ys which is an immutable reference pointing to a mutable data structure also contains 888.
What's the point of storing a mutable collection in a immutable variable
val a = new ArrayBuffer(1)
a = new ArrayBuffer[Int]()
<console>:9: error: reassignment to val
It prevents the variable from being assigned to a new memory address. In practice though scala encourages you not to use mutable state (to avoid locking, blocking, etc), so I'm having trouble coming up with an example for a real situation where the choice of var or val for mutable state matters.
Immutable object and constant value are two different things.
If you define your collection as val means that the referenced instance of the collection will always be the same. But this instance can be mutable or immutable: if it is immutable you cannot add or remove items in that instance, vice versa if it is mutable you can do it. When a collection is immutable to add or remove items you always create a copy.
How do I know when is the variable in scala carrying a value, when is a memory address?
Scala always runs on the JVM (.NET support was discontinued), so types that are primitive types on JVM will be treated as primitive types by Scala.
What is the use of using val to store a mutable ArrayBuffer?
The closest alternative would be to use a var to store an immutable Seq. If that Seq was very large, you wouldn't want to have to copy the whole Seq every time you made a change to it - but that's what you might have to do! That would be very slow!
How do I replace the first element in a sorted set in Scala? Is there an analogous function to 'patch' for Sorted Sets? Is it even possible?
val a = SortedSet(1,5,6)
val b = a.patch(0, seq[2], 1)
println(b)
Result should be:
TreeSet(2, 5, 6)
How about this:
scala> val a = SortedSet(1,5,6)
a: scala.collection.SortedSet[Int] = TreeSet(1, 5, 6)
scala> val b = a.drop(1) + 2
b: scala.collection.SortedSet[Int] = TreeSet(2, 5, 6)
Note: You're not really replacing anything here (at least not like an array.) What you are doing is taking a SortedSet and using drop to remove the first element (which happens to be the lowest value in sorted order in this case) and then you are adding another element to the set. 2 is only in the first position because that is where it is supposed to be in sorted order.
scala> a.drop(1) + 10
res21: scala.collection.SortedSet[Int] = TreeSet(5, 6, 10)
As you see, if you add 10, it also takes its place in sorted order which is at the end.
Furthermore, because sets cannot contain duplicates, doing something like:
scala> a.drop(1) + 6
res22: scala.collection.SortedSet[Int] = TreeSet(5, 6)
removes the first element and leaves you with only two elements in the set. This is because 6 was already in the set, so it is not added (again, a property of a set is that it does not contain duplicates.)
In the Scala Collections framework, I think there are some behaviors that are counterintuitive when using map().
We can distinguish two kinds of transformations on (immutable) collections. Those whose implementation calls newBuilder to recreate the resulting collection, and those who go though an implicit CanBuildFrom to obtain the builder.
The first category contains all transformations where the type of the contained elements does not change. They are, for example, filter, partition, drop, take, span, etc. These transformations are free to call newBuilder and to recreate the same collection type as the one they are called on, no matter how specific: filtering a List[Int] can always return a List[Int]; filtering a BitSet (or the RNA example structure described in this article on the architecture of the collections framework) can always return another BitSet (or RNA). Let's call them the filtering transformations.
The second category of transformations need CanBuildFroms to be more flexible, as the type of the contained elements may change, and as a result of this, the type of the collection itself maybe cannot be reused: a BitSet cannot contain Strings; an RNA contains only Bases. Examples of such transformations are map, flatMap, collect, scanLeft, ++, etc. Let's call them the mapping transformations.
Now here's the main issue to discuss. No matter what the static type of the collection is, all filtering transformations will return the same collection type, while the collection type returned by a mapping operation can vary depending on the static type.
scala> import collection.immutable.TreeSet
import collection.immutable.TreeSet
scala> val treeset = TreeSet(1,2,3,4,5) // static type == dynamic type
treeset: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 2, 3, 4, 5)
scala> val set: Set[Int] = TreeSet(1,2,3,4,5) // static type != dynamic type
set: Set[Int] = TreeSet(1, 2, 3, 4, 5)
scala> treeset.filter(_ % 2 == 0)
res0: scala.collection.immutable.TreeSet[Int] = TreeSet(2, 4) // fine, a TreeSet again
scala> set.filter(_ % 2 == 0)
res1: scala.collection.immutable.Set[Int] = TreeSet(2, 4) // fine
scala> treeset.map(_ + 1)
res2: scala.collection.immutable.SortedSet[Int] = TreeSet(2, 3, 4, 5, 6) // still fine
scala> set.map(_ + 1)
res3: scala.collection.immutable.Set[Int] = Set(4, 5, 6, 2, 3) // uh?!
Now, I understand why this works like this. It is explained there and there. In short: the implicit CanBuildFrom is inserted based on the static type, and, depending on the implementation of its def apply(from: Coll) method, may or may not be able to recreate the same collection type.
Now my only point is, when we know that we are using a mapping operation yielding a collection with the same element type (which the compiler can statically determine), we could mimic the way the filtering transformations work and use the collection's native builder. We can reuse BitSet when mapping to Ints, create a new TreeSet with the same ordering, etc.
Then we would avoid cases where
for (i <- set) {
val x = i + 1
println(x)
}
does not print the incremented elements of the TreeSet in the same order as
for (i <- set; x = i + 1)
println(x)
So:
Do you think this would be a good idea to change the behavior of the mapping transformations as described?
What are the inevitable caveats I have grossly overlooked?
How could it be implemented?
I was thinking about something like an implicit sameTypeEvidence: A =:= B parameter, maybe with a default value of null (or rather an implicit canReuseCalleeBuilderEvidence: B <:< A = null), which could be used at runtime to give more information to the CanBuildFrom, which in turn could be used to determine the type of builder to return.
I looked again at it, and I think your problem doesn't arise from a particular deficiency of Scala collections, but rather a missing builder for TreeSet. Because the following does work as intended:
val list = List(1,2,3,4,5)
val seq1: Seq[Int] = list
seq1.map( _ + 1 ) // yields List
val vector = Vector(1,2,3,4,5)
val seq2: Seq[Int] = vector
seq2.map( _ + 1 ) // yields Vector
So the reason is that TreeSet is missing a specialised companion object/builder:
seq1.companion.newBuilder[Int] // ListBuffer
seq2.companion.newBuilder[Int] // VectorBuilder
treeset.companion.newBuilder[Int] // Set (oops!)
So my guess is, if you take proper provision for such a companion for your RNA class, you may find that both map and filter work as you wish...?
How in the world do you get just an element at index i from the List in scala?
I tried get(i), and [i] - nothing works. Googling only returns how to "find" an element in the list. But I already know the index of the element!
Here is the code that does not compile:
def buildTree(data: List[Data2D]):Node ={
if(data.length == 1){
var point:Data2D = data[0] //Nope - does not work
}
return null
}
Looking at the List api does not help, as my eyes just cross.
Use parentheses:
data(2)
But you don't really want to do that with lists very often, since linked lists take time to traverse. If you want to index into a collection, use Vector (immutable) or ArrayBuffer (mutable) or possibly Array (which is just a Java array, except again you index into it with (i) instead of [i]).
Safer is to use lift so you can extract the value if it exists and fail gracefully if it does not.
data.lift(2)
This will return None if the list isn't long enough to provide that element, and Some(value) if it is.
scala> val l = List("a", "b", "c")
scala> l.lift(1)
Some("b")
scala> l.lift(5)
None
Whenever you're performing an operation that may fail in this way it's great to use an Option and get the type system to help make sure you are handling the case where the element doesn't exist.
Explanation:
This works because List's apply (which sugars to just parentheses, e.g. l(index)) is like a partial function that is defined wherever the list has an element. The List.lift method turns the partial apply function (a function that is only defined for some inputs) into a normal function (defined for any input) by basically wrapping the result in an Option.
Why parentheses?
Here is the quote from the book programming in scala.
Another important idea illustrated by this example will give you insight into why arrays are accessed with parentheses in Scala. Scala has fewer special cases than Java. Arrays are simply instances of classes like any other class in Scala. When you apply parentheses surrounding one or more values to a variable, Scala will transform the code into an invocation of a method named apply on that variable. So greetStrings(i) gets transformed into greetStrings.apply(i). Thus accessing an element of an array in Scala is simply a method call like any other. This principle is not restricted to arrays: any application of an object to some arguments in parentheses will be transformed to an apply method call. Of course this will compile only if that type of object actually defines an apply method. So it's not a special case; it's a general rule.
Here are a few examples how to pull certain element (first elem in this case) using functional programming style.
// Create a multdimension Array
scala> val a = Array.ofDim[String](2, 3)
a: Array[Array[String]] = Array(Array(null, null, null), Array(null, null, null))
scala> a(0) = Array("1","2","3")
scala> a(1) = Array("4", "5", "6")
scala> a
Array[Array[String]] = Array(Array(1, 2, 3), Array(4, 5, 6))
// 1. paratheses
scala> a.map(_(0))
Array[String] = Array(1, 4)
// 2. apply
scala> a.map(_.apply(0))
Array[String] = Array(1, 4)
// 3. function literal
scala> a.map(a => a(0))
Array[String] = Array(1, 4)
// 4. lift
scala> a.map(_.lift(0))
Array[Option[String]] = Array(Some(1), Some(4))
// 5. head or last
scala> a.map(_.head)
Array[String] = Array(1, 4)
Please use parentheses () to access the list of elements, as shown below.
list_name(index)