scala: how scala converts foldLeft as currying function - scala

Could someone explain how currying happens in foldLeft for the below example:
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>numbers: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val numberFunc = numbers.foldLeft(List[Int]())_
>numberFunc: ((List[Int], Int) => List[Int]) => List[Int]
My Understanding is:
(List[Int], Int) - (accumulator which in this case is empty List, each element of the numbers list)
=> List[Int] - which is the output numberFunc list.
=> List[Int] - what does this represent?
Thanks.

Step by step:
foldLeft on a List[A] has a signature foldLeft[B](b: B)(f: (B, A) => B): B
therefore, in general, for a list: List[A] and b: B, the curried expression list.foldLeft(b) _ would have type ((B, A) => B) => B
numbers has type List[Int], thus A is inferred to be Int
List[Int]() has type List[Int], thus B is inferred to be List[Int]
Substituting Int for A and List[Int] for B in ((B, A) => B) => B, you obtain (List[Int], Int) => List[Int]) => List[Int]. That's exactly what the compiler gives you.
In words:
(numberFunc : // `numberFunc` is a function that
( // given a function that
(List[Int], Int) // takes an accumulator and an int
=> // and produces
List[Int] // an updated list of ints
)
=> // produces
List[Int] // a list of ints
)

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.

Merge two collections by interleaving values

How can I merge two lists / Seqs so it takes 1 element from list 1, then 1 element from list 2, and so on, instead of just appending list 2 at the end of list 1?
E.g
[1,2] + [3,4] = [1,3,2,4]
and not [1,2,3,4]
Any ideas? Most concat methods I've looked at seem to do to the latter and not the former.
Another way:
List(List(1,2), List(3,4)).transpose.flatten
So maybe your collections aren't always the same size. Using zip in that situation would create data loss.
def interleave[A](a :Seq[A], b :Seq[A]) :Seq[A] =
if (a.isEmpty) b else if (b.isEmpty) a
else a.head +: b.head +: interleave(a.tail, b.tail)
interleave(List(1, 2, 17, 27)
,Vector(3, 4)) //res0: Seq[Int] = List(1, 3, 2, 4, 17, 27)
You can do:
val l1 = List(1, 2)
val l2 = List(3, 4)
l1.zip(l2).flatMap { case (a, b) => List(a, b) }
Try
List(1,2)
.zip(List(3,4))
.flatMap(v => List(v._1, v._2))
which outputs
res0: List[Int] = List(1, 3, 2, 4)
Also consider the following implicit class
implicit class ListIntercalate[T](lhs: List[T]) {
def intercalate(rhs: List[T]): List[T] = lhs match {
case head :: tail => head :: (rhs.intercalate(tail))
case _ => rhs
}
}
List(1,2) intercalate List(3,4)
List(1,2,5,6,6,7,8,0) intercalate List(3,4)
which outputs
res2: List[Int] = List(1, 3, 2, 4)
res3: List[Int] = List(1, 3, 2, 4, 5, 6, 6, 7, 8, 0)

How to set the initial value Nil using /: (Not the foldRight)

The code below doesn't compile because of the type mismatch, and I suppose this can be solved by explicitly giving the type parameter List[Int] to Nil but I couldn't figure it out how.
(there are some info about the foldRight method but I would like to use /: here)
val li = List.range(1, 10)
(Nil /: li)((a, b) => b :: a) // doesn't compile
(List(0): li)((a, b) => b :: a) // compiles
<pastie>:14: error: type mismatch;
found : List[Int]
required: scala.collection.immutable.Nil.type
(Nil /: li)((a, b) => b :: a)
^
Could anyone tell how to fix this?
How about using:
scala> val li = List.range(1, 10)
li: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> (List.empty[Int] /: li)((a, b) => b :: a)
res20: List[Int] = List(9, 8, 7, 6, 5, 4, 3, 2, 1)
Its because, when you provide Nil as Initial value, you are providing Nil.type as type parameter and hence you are getting error.
def /:[B](z: B)(op: (B, A) => B): B
If you see the /: method definition, it is a higher order function that take function op as parameter. If you look at the function type for op i.e. (B, A) => B, the return type is B. When you invoke /: as Nil /: li, the type parameter passed is Nil.type i.e. Nil /:[Nil.type] li. Hence, B is Nil.type.
Now, lets look at your function literal for parameter op:
(Nil /: li)((a, b) => b :: a)
Here, you are returning b::a in your function literal. If you look closely, the type of b is Int since li is List[Int] and a is List() (i.e. value of Nil). Hence, b :: a will return type of List[Int]. However, function type op expect B as return type which is Nil.type as explained above but you are returning List[Int] type. Hence you are getting error.
When you provide List(0) as initial value instead of Nil, you are passing List[Int] as type of B and in your function literal the returned type of b::a is List[Int], hence it worked.
(List(0): li)((a, b) => b :: a) ~~ (List(0)/[List[Int]] li)((a, b) => b :: a)
To avoid this issue, you need to explicitly provide type parameter in /: method.
(Nil /:[List[Int]] li)((a, b) => b :: a)
It's not lovely but...
scala> val li = List.range(1, 10)
li: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> ((Nil : List[Int]) /: li)((a, b) => b :: a)
res0: List[Int] = List(9, 8, 7, 6, 5, 4, 3, 2, 1)

map function in Scala clarification

I am confused on how to write the map function, which maps over two lists:
for example:
def map[A,B,C](f: (A, B) => C, lst1: List[A], lst2: List[B]): List[C]
The input would be 2 lists and the output could be a list that adds the integers alternatively
Test example:
assert(map(add, List(1, 2, 3), List(4, 5, 6)) == List(5, 7, 9))
You could use f.tupled to convert f from a function that accepts to arguments (A, B) => C, to a function that accepts one argument as a tuple ((A, B)) => C. Then, you can zip the lists together (make them one list of tuples) and feed them to f using the traditional map.
def map[A,B,C](f: (A, B) => C, lst1: List[A], lst2: List[B]): List[C] =
(lst1 zip lst2) map f.tupled
scala> def add(a: Int, b: Int): Int = a + b
add: (a: Int, b: Int)Int
scala> map(add, List(1, 2, 3), List(4, 5, 6))
res20: List[Int] = List(5, 7, 9)
Keep in mind that if the two lists are not the same size, then zip will truncate the longer list to match the size of the smaller one.
As stated in m-z's answer you can zip the lists, and then map on the list of tuples. If you want to avoid the use of tupled, you can do the destructure explicitly:
def map[A,B,C](f: (A, B) => C, lst1: List[A], lst2: List[B]): List[C] = {
val zipped = lst1 zip lst2
zipped.map { case (a,b) => f(a,b) }
}

Inserting at position in List

This insert function is taken from :
http://aperiodic.net/phil/scala/s-99/p21.scala
def insertAt[A](e: A, n: Int, ls: List[A]): List[A] = ls.splitAt(n) match {
case (pre, post) => pre ::: e :: post
}
I want to insert an element at every second element of a List so I use :
val sl = List("1", "2", "3", "4", "5") //> sl : List[String] = List(1, 2, 3, 4, 5)
insertAt("'a", 2, insertAt("'a", 4, sl)) //> res0: List[String] = List(1, 2, 'a, 3, 4, 'a, 5)
This is a very basic implementation, I want to use one of the functional constructs. I think I need
to use a foldLeft ?
Group the list into Lists of size 2, then combine those into lists separated by the separation character:
val sl = List("1","2","3","4","5") //> sl : List[String] = List(1, 2, 3, 4, 5)
val grouped = sl grouped(2) toList //> grouped : List[List[String]] = List(List(1, 2), List(3, 4), List(5))
val separatedList = grouped flatMap (_ :+ "a") //> separatedList : <error> = List(1, 2, a, 3, 4, a, 5, a)
Edit
Just saw that my solution has a trailing token that isn't in the question. To get rid of that do a length check:
val separatedList2 = grouped flatMap (l => if(l.length == 2) l :+ "a" else l)
//> separatedList2 : <error> = List(1, 2, a, 3, 4, a, 5)
You could also use sliding:
val sl = List("1", "2", "3", "4", "5")
def insertEvery(n:Int, el:String, sl:List[String]) =
sl.sliding(2, 2).foldRight(List.empty[String])( (xs, acc) => if(xs.length == n)xs:::el::acc else xs:::acc)
insertEvery(2,"x",sl) // res1: List[String] = List(1, 2, x, 3, 4, x, 5)
Forget about insertAt, use pure foldLeft:
def insertAtEvery[A](e: A, n: Int, ls: List[A]): List[A] =
ls.foldLeft[(Int, List[A])]((0, List.empty)) {
case ((pos, result), elem) =>
((pos + 1) % n, if (pos == n - 1) e :: elem :: result else elem :: result)
}._2.reverse
Recursion and pattern matching are functional constructs. Insert the new elem by pattern matching on the output of splitAt then recurse with the remaining input. Seems easier to read but I'm not satisfied with the type signature for this one.
def insertEvery(xs: List[Any], n: Int, elem: String):List[Any] = xs.splitAt(n) match {
case (xs, List()) => if(xs.size >= n) xs ++ elem else xs
case (xs, ys) => xs ++ elem ++ insertEvery(ys, n, elem)
}
Sample runs.
scala> val xs = List("1","2","3","4","5")
xs: List[String] = List(1, 2, 3, 4, 5)
scala> insertEvery(xs, 1, "a")
res1: List[Any] = List(1, a, 2, a, 3, a, 4, a, 5, a)
scala> insertEvery(xs, 2, "a")
res2: List[Any] = List(1, 2, a, 3, 4, a, 5)
scala> insertEvery(xs, 3, "a")
res3: List[Any] = List(1, 2, 3, a, 4, 5)
An implementation using recursion:
Note n must smaller than the size of List, or else an Exception would be raised.
scala> def insertAt[A](e: A, n: Int, ls: List[A]): List[A] = n match {
| case 0 => e :: ls
| case _ => ls.head :: insertAt(e, n-1, ls.tail)
| }
insertAt: [A](e: A, n: Int, ls: List[A])List[A]
scala> insertAt("'a", 2, List("1", "2", "3", "4"))
res0: List[String] = List(1, 2, 'a, 3, 4)
Consider indexing list positions with zipWithIndex, and so
sl.zipWithIndex.flatMap { case(v,i) => if (i % 2 == 0) List(v) else List(v,"a") }