What is the difference between ::: and :::.() in scala - 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.

Related

flatMap() in Scala during recursion

I was going through a Scala-99 problem to reduce a complex nested list into a flat list. Code given below:
def flatten(l: List[Any]): List[Any] = l flatMap {
case ms:List[_] => flatten(ms)
case l => List(l)
}
val L = List(List(1, 1), 2, List(3, List(5, 8)))
val flattenedList = flatten(L)
For the given input above L, I understood this problem by drawing a tree (given below)
List(List(1, 1), 2, List(3, List(5, 8))) (1)
| \ \
List(1, 1) List(2) List(3, List(5, 8)) (2)
| \ | \
List(1) List(1) List(3) List(5, 8) (3)
| \
List(5) List(8) (4)
What I've understood is that, the program results in the leaf nodes being added in a list maintained by Scala internally, like:
li = List(List(1), List(1), List(2), List(3), List(5), List(8))
and then the result is passed to the flatten method which results in the final answer:
List(1, 1, 2, 3, 5, 8)
Is my understanding correct?
EDIT: I'm sorry, I forgot to add this:
I wanted to ask that if my understanding is correct then why does replacing flatMap with map in the flatten's definition above produces this list:
List(List(List(1), List(1)), List(2), List(List(3), List(List(5), List(8))))
I mean isn't flatMap just map then flatten. Shouldn't I be getting like the one I mentioned above:
li = List(List(1), List(1), List(2), List(3), List(5), List(8))
You're right that flatMapis just map and flatten but note that this flatten is not the same flatten you define, for list it only concatenate inner lists at 1 level.
One very useful way to unpack these is to use substitution model, just like maths
if I define it like this, (calling it f to avoid confusion with flatten here and flatten in std library)
def f(l: List[Any]): List[Any] = l map {
case ms:List[_] => f(ms)
case l => List(l)
}
then
f(List( List(1, 1), 2))
= List(f(List(1, 1)), f(2)) // apply f to element of the outer most list
= List(List(f(1), f(1)), f(2)) // apply f to element of the inner list
= List(List(List(1), List(1)), List(2))) // no more recursion
Notice map doesn't change the structure of your list, it only applies the function to each element. This should explains how you have the result if you replace flatMap with map
Now if you have flatMap instead of map, then the flatten step is simply concatenating
def f(l: List[Any]): List[Any] = l flatMap {
case ms:List[_] => f(ms)
case l => List(l)
}
then
f(List(List(1,1), 2))
= f(List(1,1)) ++ f(2) // apply f to each element and concatenate
= (f(1) ++ f(1)) ++ f(2)
= (List(1) ++ List(1)) ++ List(2)
= List( 1,1) ++ List(2)
= List(1,2,3)
or in another way, using flatten instead of ++
f( List( List(1,1), 2))
= flatten(List( f( List( 1, 1)) , f(2))) // map and flatten
= flatten(List( flatten(List(f(1), f(1))), f(2))) // again map and flatten
= flatten(List( flatten(List(List(1), List(1))), List(2))))
now you can see that flatten is called multiple times, at every level where you recursively apply f which will collapse your tree 1 level at a time into just 1 big list.
To answer your comment: why is List(1,1) is turned into flatten(List(List(1), List(1)). It's because this is the simple case, but consider List(1, List(2)), then f will be applied for 1 and List(2). Because the next step is to 'flatten' (in stdlib) then both 1 & List(2) must be turned into a List so that it is in the right shape

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.

Calling lists concatenation in 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.

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)

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))