Calling lists concatenation in Scala - scala

Can someone explain what is going on with
scala> List(1,2).:::(List(3,4))
res15: List[Int] = List(3, 4, 1, 2)
scala> List(1,2) ::: List(3,4)
res16: List[Int] = List(1, 2, 3, 4)
How can the method call results differ while they should be the same method call?

In case of List(1,2).:::(List(3,4)) you call ::: method directly on object List(1,2). According to the docs:
#param prefix The list elements to prepend.
So you get: res15: List[Int] = List(3, 4, 1, 2)
When you do not use . (dot) notation ::: behaves as right associative operation according to the docs:
#usecase def :::(prefix: List[A]): List[A]
#inheritdoc
Example:
{{{List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4)}}}
That means that in the case of List(1,2) ::: List(3,4) method ::: is being called on object List(3,4).
Right associative operation means basically the following:
xs ::: ys ::: zs is interpreted as xs ::: (ys ::: zs)
Section 16.6 describes the same as example.

Related

List type mismatch when appending in Scala

Let's say I have following code:
val xs: List[Int] = List(1, 2, 3)
val ys: List[Int] = List(4, 5, 6)
val zs: List[Int] = xs.appended(ys)
The last line does not compile with an error:
Error:(162, 33) type mismatch; found : List[Int] required: Int val
zs: List[Int] = xs.appended(ys)
If I remove the explicit type declaration, then the code compiles, but the real problem is that
the error message appears in a recursive function where I would like to pass appended list as a parameter of type List[Int], so removing the explicit type is not an option.
According to scaladoc appended method takes only one argument, not an entire list. So the following examples will compile:
xs.appended(ys(0))
for(x <- xs) yield ys appended x
or appendAll:
xs appendAll ys
ys :++ xs
P.S.: Note, that appending to the list is not optimal, as it's time is proportional to the size of the list, prefer prepend instead:
ys ::: xs
According scala documentation appended method accepting just one element, not collection. And zs type after removing explicit types will be List[Any]:
val xs = List(1, 2, 3)
val ys = List(4, 5, 6)
val zs: List[Any] = xs.appended(ys) // List(1, 2, 3, List(4, 5, 6))
it compiles, but result will be List(1, 2, 3, List(4, 5, 6))
You can use method appendedAll to do that you want or just concatenate lists using concat or ++ operator :
val xs = List(1, 2, 3)
val ys = List(4, 5, 6)
val zs: List[Int] = xs ++ ys // List(1, 2, 3, 4, 5, 6)
val as: List[Int] = xs.appendedAll(ys) // List(1, 2, 3, 4, 5, 6)
val bs: List[Int] = xs.concat(ys) // List(1, 2, 3, 4, 5, 6)
1. val xs: List[Int] = List(1, 2, 3)
2. val ys: List[Int] = List(4, 5, 6)
3. val zs: List[Int] = xs.appended(ys)
The third line is a problem until you have the type declaration. Because when you compile your code compiler is not going to infer the type of the variable zs and it will expect the output of xs.appended(ys) to be a List[Int] which is not the case because xs is List[Int] now if you want to add an element in this list you can do xs.append(1) or any other integer but you are trying to insert List[Int] which is not Int.
Now when you remove the type declaration from line 3 it compile successfully because now compiler will infer the type of the variable zs and if you will see on REPL it will say the of this variable zs is List[Any].
Now if you want to add list into a list and get a flatten result you can simply use
val zs: List[Int] = xs ::: ys
If you will see the scala docs here
this is the signature of appended:
final def:+[B >: A](elem: B): List[B]
:+ is Alias for appended
:++ is Alias for appendedAll
As we can see from the signature appended function takes a parameter of type B and return List[B] in your case B is Int and you are trying to add List[Int].
I hope it clears why you are getting the compilation error.

Ommiting parenthesis in adding elements to List

I'm trying to add element to a List[String] while omitting annoying parenthesis. I tried this:
object Main extends App {
val l = List("fds")
val xs1: List[String] = l.+:("123") // ok
val xs2: List[String] = l +: "123" // compile-error
}
DEMO
Why is omitting parenthesis causing compile-error? These assignments look the same to me. What is the difference?
It's happening because of right associative methods.
scala> val l = List("abc")
l: List[String] = List(abc)
scala> "efg" +: l
res3: List[String] = List(efg, abc)
Read more here What good are right-associative methods in Scala?
Error case
scala> val l = List(1, 2, 3)
l: List[Int] = List(1, 2, 3)
scala> 4 +: l
res1: List[Int] = List(4, 1, 2, 3)
scala> l +: 1
<console>:13: error: value +: is not a member of Int
l +: 1
^
Because +: is right associative. Method +: is getting invoked on Int instead of list
In order to make it work we can explicitly invoke method on list without the special operator syntax
scala> val l = List(1, 2, 3)
l: List[Int] = List(1, 2, 3)
scala> l.+:(1)
res4: List[Int] = List(1, 1, 2, 3)
Above case works because its normal method invocation.

Why different behaviors for list concatenation?

What is the difference between a ::: b and a.:::(b) ?
scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)
scala> val b = List(5)
b: List[Int] = List(5)
scala> a.:::(b)
res6: List[Int] = List(5, 1, 2, 3, 4)
scala> a ::: b
res7: List[Int] = List(1, 2, 3, 4, 5)
All functions in Scala which end with a : are right-associative and, thus, the expression a ::: b evaluates to b.:::(a).
When you use infix notation, methods (or operators) that end with : are right associative - in other words, the method is called on the object to its right, and the object to its left is passed as an argument.
So 1 :: Nil is the same as Nil.::(1). Just as a ::: b is the same as b.:::(a).
Because when you type
a ::: b
the last ':' makes the function right associative.
Thus, you are calling ::: on b not a:
b.:::(a)

What is the difference between ::: and :::.() in scala

In the scala list class it is shown that:
List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4).
Shouldn't:
List(1,2) ::: List(3,4) = List(1,2).:::(List(3,4)) = List(3,4,1,2)
( method ::: prefixes the list)
From the docs:
def :::(prefix: List[A]): List[A]
[use case]
Adds the elements of a given list in front of this list.
Example:
List(1, 2) ::: List(3, 4) = List(3, 4).:::(List(1, 2)) = List(1, 2, 3, 4)
In Scala, operators that end in : are right associative and the invoking object appears on the right side of the operator. Because it is right associative, the prefix method will be called on the object "to the right", which is List(3, 4).
Then it will do what its name says, it will prefix List(3, 4) with List(1, 2), and that's why you get List(1, 2, 3 ,4). Not the most intuitive thing in the world but it's like this:
a ::: b
// ending in :, so flip the argument order, call the method on b.
b .:: a // :: = prefix b with a
result = a(concatenated) with b
Now let's look at the right hand side:
List(3, 4).:::(List(1, 2))
The . dot is performing an inversion of the ::: prefixed invocation, so it will simply invert the two List objects before performing the ::: prefix operation.
List(3, 4).:::(List(1,2)) = List(1, 2) ::: List(3, 4) // exactly as the above.
// and now you it's the same story.
The . dot is a simply way to invert associative operators. a ::: b is the same as b .::: a
I think that what you are doing with
List(1,2).:::(List(3,4)) is changing the order of things.
Ending with : makes scala want to call the method on the object to the right.
So
a :: b
is really
b.::(a)
When being explicit with the dots and parentheses, you change the order.
Not sure if this example makes it clearer:
scala> class X() {
def `a:`(s: String): Unit = {
println(s)
}}
scala> var x = new X()
scala> x.`a:`("X")
X
scala> x `a:` "X"
<console>:10: error: value a: is not a member of java.lang.String
x `a:` "X"
^
You see that scala wants to call the a: method on the string object on the right.

Scala - convert List of Lists into a single List: List[List[A]] to List[A]

What's the best way to convert a List of Lists in scala (2.9)?
I have a list:
List[List[A]]
which I want to convert into
List[A]
How can that be achieved recursively? Or is there any other better way?
List has the flatten method. Why not use it?
List(List(1,2), List(3,4)).flatten
> List(1,2,3,4)
.flatten is obviously the easiest way, but for completeness you should also know about flatMap
val l = List(List(1, 2), List(3, 4))
println(l.flatMap(identity))
and the for-comprehension equivalent
println(for (list <- l; x <- list) yield x)
flatten is obviously a special case of flatMap, which can do so much more.
Given the above example, I'm not sure you need recursion. Looks like you want List.flatten instead.
e.g.
scala> List(1,2,3)
res0: List[Int] = List(1, 2, 3)
scala> List(4,5,6)
res1: List[Int] = List(4, 5, 6)
scala> List(res0,res1)
res2: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6))
scala> res2.flatten
res3: List[Int] = List(1, 2, 3, 4, 5, 6)
If your structure can be further nested, like:
List(List(1, 2, 3, 4, List(5, 6, List(7, 8))))
This function should give you the desire result:
def f[U](l: List[U]): List[U] = l match {
case Nil => Nil
case (x: List[U]) :: tail => f(x) ::: f(tail)
case x :: tail => x :: f(tail)
}
You don't need recursion but you can use it if you want:
def flatten[A](list: List[List[A]]):List[A] =
if (list.length==0) List[A]()
else list.head ++ flatten(list.tail)
This works like flatten method build into List. Example:
scala> flatten(List(List(1,2), List(3,4)))
res0: List[Int] = List(1, 2, 3, 4)
If you want to use flatmap, here is the the way
Suppose that you have a List of List[Int] named ll, and you want to flat it to List,
many people already gives you the answers, such as flatten, that's the easy way. I assume that you are asking for using flatmap method. If it is the case, here is the way
ll.flatMap(_.map(o=>o))