Confused about appending to a List - scala

I create a List like this :
var multiList : List[MyObj] = Nil
To append objects to this List I use :
multiList = multiList ::: List(MyObj)
this works but should I not be using
multiList = multiList ::: MyObj
as here I am appending an object to the List, wheras in example (that works) it looks syntactically as if im appending a new List to a List ?

The ::: operators mean concatenate, not append. There's :+ for element-append.

The :: method is used to 'prepend' stuff to a list. The ::: operator is used to prepend one list to another. The :: ends with a colon and thus is 'right associative' which can be explained as follows
x :: y
// will be translated to
y.::(x)
So to add your object to an empty list you could do
MyObj :: Nil
// will be translated to
Nil.::(MyObj)
If you wanted to add a list of objects to an empty list you could do
List(MyObj, MyObj) ::: Nil
// will be translated to
Nil.:::(List(MyObj, MyObj))
If you do want to append you could use the :+ method. This however performs differently for different types of collections. More info about the perfomance can be found here: Performance Characteristics

Related

Why does a list initialised with Nil and cons (::) not have a NULL value at the end?

Today is my first day of learning Scala, and one of the things I have learnt is that a common way to initialise lists combines Nil (an empty list) with :: (prepend method).
Let's say I initialise a list this way:
val myList = List("A", "B", "C")
which is the same as:
val myList = "A" :: "B" :: "C" :: Nil
I understand that you can read the second chunk as the following:
Start with an empty list
Add "C" to the beginning of that list. The list is no longer empty.
Add "B" to the beginning of the list.
Add "A" to the beginning of the list.
Assign the list to the immutable List collection. The data type of each list element is inferred (String).
What I don't understand is why doesn't Nil act the same as NULL? Why is there no NULL value at the end of the list when it is initialised in this way?
(Forgive me, OOP and FP are not my thing but I'd like to learn.)
If you want to be able to call methods on all lists including empty list (for example Nil.length), Nil must be an object, not null. Otherwise you'll have NullPointerException instead of 0.
Also it's necessary for type safety. Nil is a value of type List[Nothing], i.e. of List[A] for all A (because List is covariant). And null is a value of type Null i.e. of all reference types (because Null is a subtype of any reference type), not only of list types.
Too many things to learn. You first need to understand what is an object, what is a class, what is an interface / trait, what is a value, what is a type. Then with those, you may need to learn about ADTs.
I will try to give a quick answer to that.
The List datatype is defined as
sealed trait List[+A]
final case class ::[+A](head: A, tail: List[A]) extends List[A]
final case object Nil extends List[Nothing]
So here we can see three things: a trait, a class and an object.
A class is a blueprint of memory for creating objects (also called instances). It is not a value, for using it you have to call its constructor (in this case, it receives two values, the head and the tail) to get a new value of that class.
An object is like an anonymous class that is already instantiated, so it is already a value, which you can use as it is.
A trait is a like a class that can not be instantiated, it is said to be abstract. It is used to define common behaviors that all subclasses have to follow.
Also, when the trait is sealed and the classes and objects are cases, we call those ADTs (algebraic data types).
Do not let the fancy name to scare you, it just means that the trait represents a single type (om this case the List) and all the cases represents the parts of such type.
ADTs are formed of products and sums, sums represents alternatives (it is an A or a B) and products represents conjunctions (it is an A and a B).
In the case of the List we say that it is either the empty list (Nil) or a cons (::) which has its own head and tail, where the tail is another list.
With all that, and a the knowledge that Scala has a syntactic trick to allow symbolic names to be used in the middle, you can see that this:
val list = 1 :: 2 :: 3 :: Nil
Is the same as:
val list = new ::(
head = 1,
tail = new ::(
head = 2,
tail = new ::(
head = 3,
tail = Nil
)
)
)
Bonus:
There are more types than classes.
null its a value of type Null (the only value to habit such type), which is a subtype of all the AnyRef types. It is, however, a special kind of value; because it will fail if you try to operate with it in any way. My advice, forget that it exists, it was a mistake and it is only there for Java interop. If you need to model the absence of a value use Option.

Scala: flatten mixed set of sets (or lists or arrays)

I have a Set which incorporates a combination of strings, and subSets of strings, like so:
val s = Set(brand1-_test, Set(brand-one, brand_one, brandone), brands-two, brandthree1, Set(brand-three2, brand_three2, brandthree2))
How do I flatten this so that I have one flat set of strings? s.flatten doesn't work with the following error:
error: No implicit view available from Object => scala.collection.GenTraversableOnce[B]
Neither does flatMap. What am I missing here? The Set could just as easily incorporate a subLists or subArrays (they are the result of a previous function), if that makes a difference.
s.flatMap { case x:Iterable[_] => x; case y => Seq(y) }
Try putting it in a REPL:
scala> val s = Set("s1", Set("s2", "s3"))
s: scala.collection.immutable.Set[Object] = Set(s1, Set(s2, s3))
since you are providing two types (Set and String) then scala infers a type which covers both (Object in this case, but probably Any or AnyRef in most cases) which is not a collection and therefore cannot be flattened.

Why can't I append Nil to a list?

I have the following recursive method:
def myMethod(foo: List[FooBar], acc: List[MyClass]): List[MyClass] {
// ... some code ...
myMethod(foo.tail, acc :+ getMyObject(foo.head).getOrElse(Nil))
}
The method getMyObject returns optionally an instance of MyClass. Unfortunately, I can't compile this because I get this error:
[error] found : List[Product with Serializable]
[error] required: List[MyClass]
This compile error indicates that I cannot append Nil to the list acc, so I have to use the following code:
def myMethod(foo: List[FooBar], acc: List[MyClass]): List[MyClass] {
// ... some code ...
val bar = getMyObject(foo.head)
myMethod(foo.tail, if (bar.isDefined) acc :+ bar.get else acc)
}
However, I would prefer the first approach since it is more concise. Why can't I append Nil to a list?
:+ takes an n-elemnt list and an element x and returns an n+1-element list where the last element is x. This means two things:
There's no argument that you can use as the right operand of :+ to get a list of the same size
The right operand of :+ needs to be of the element type of the list.
So you can do acc :+ Nil only if acc is a list of lists and even then it won't do what you want since it will put an additional empty list into your list, rather than keeping the list unchanged.
The most concise way to solve your problem is acc ++ bar. This uses concatenation rather than appending and works because options are seen as collections of 0 or 1 elements. So acc ++ bar appends the zero or one elements of bar to those of acc.
PS: You generally should use pattern matching or higher-order functions to operate on lists, not head and tail.
The problem is the append operation :+ takes a non-list value and appends it to the list. The trouble is Nil and Myclass are different types, so the resulting list takes the most specific type that satisfies both MyClass and Nil. They are totally unrelated types so you end up getting Product with Serializable as the common super type.
To append an element or nothing to a list, wrap that element into a list first. Then concatenate your singleton List or Nil with the old list.
myMethod(foo.tail, acc ++ getMyObject(foo.head).map(x => List(x)).getOrElse(Nil))
You are using the wrong operator, use ++ instead
myMethod(foo.tail, acc ++ getMyObject(foo.head).map(List(_)).getOrElse(Nil))
or
myMethod(foo.tail, getMyObject(foo.head).map(acc :+ _).getOrElse(acc))

Scala foldLeft with a List

I have the following code snippet:
import scala.io.Source
object test extends App {
val lineIterator = Source.fromFile("test1.txt").getLines()
val fileContent = lineIterator.foldLeft(List[String]())((list, currentLine) => {
currentLine :: list
list
})
fileContent foreach println
}
Let's assume the test1.txt file is not empty and has some values in it.
So my question about the foldLeft function is, why does this example here return an empty list, and when I remove the list at the end of the foldLeft function it works?
Why is it returning an empty list under the value fileContent?
The line currentLine :: list does not mutate the original list. It creates a new list with currentLine prepended, and returns that new list. When this expression is not the last one in your block, it will just be discarded, and the (still) empty list will be returned.
When you remove the list at the end, you will actually return currentLine :: list.
You call foldLeft with some start value (in your case an empty list) and a function, which takes an accumulator and the current value. This function returns the new list then. In your implementation the empty list from the first call will propagate to the end of function execution. This is why you get an empty list as a result.
Please look at this example: https://twitter.github.io/scala_school/collections.html#fold
list is immutable, so it is still empty after currentLine :: list. Thus the code within brackets returns an empty List, which is then folded with the next item, still returning an empty List.

Scala: mutable HashMap does not update inside for loop

I have a var permutedTables = HashMap[List[Int], List[String, String]] defined globally. I first populate the Hashmap with the keys in a method, which works.
print(permutedTables) :
Map(List(1,2,3,4) -> List(),
List(2,4,5,6) -> List(), etc...)
The problem occurs when I want to update the values (empty lists) of the HashMap inside a for loop (inside a second method). In other words, I want to add tuples (String, String) in the List() for each key.
for(pi_k <- permutedTables.keySet){
var mask = emptyMask;
mask = pi_k.foldLeft(mask)((s, i) => s.updated(i, '1'))
val maskB = Integer.parseInt(mask,2)
val permutedFP = (intFP & maskB).toBinaryString
// attempt 1 :
// permutedTables(pi_k) :+ (url, permutedFP)
// attempt 2 :
// permutedTables.update(pi_k, permutedTables(pi_k):::List((url, permutedFP)))
}
The values do not update. I still have empty lists as values. I don't understand what is wrong with my code.
EDIT 1: When I call print(permutedTables) after any of the two attempts (inside the loop), the value seem updated, but when I call it outside of the loop, the Lists are empty
EDIT 2: The second attempt in my code seems to work now(!). But why does first not work ?
The second attempt in my code seems to work now(!). But why does first not work ?
Because what you do in the first case is get a list from permutedTables, add an element and throw away the result without storing it back. It would work if you mutated the value, but a List is immutable. With List, you need
permutedTables += pi_k -> permutedTables(pi_k) :+ (url, permutedFP)
Or, as you saw, update.
You can use e.g. ArrayBuffer or ListBuffer as your value type instead (note that you need :+= instead of :+ to mutate them), and convert to your desired type at the end. This is going to be rather more efficient than appending to the end of the list, unless the lists are quite small!
Finally, note that you generally want either var or a mutable type, not both at the same time.