Scala: List to List of Lists, idiomatically? - scala

In Scala, I wrote a Higher Order Function that iterates a list, and calls back with the head and list, for each element, that is:
def headAndSelf[A, B](li: List[A], f: (A, List[A]) => B): List[B] = {
if (li == Nil) Nil
else f(li.head, li) :: headAndSelf(li.tail, f)
}
Given val a = List(1,2,3)
scala> headAndSelf(a, (x: Int, y: List[Int]) => (x,y))
res4: List[(Int, List[Int])] = List((1,List(1, 2, 3)), (2,List(2, 3)), (3,List(3)))
It then occurred to me I could get the head from the list, so it was simpler to write:
def self[A, B](li: List[A], f: (List[A]) => B): List[B] = {
if (li == Nil) Nil
else f(li) :: self(li.tail, f)
}
(even though it makes the lambda a bit less elegant)
scala> self(a, (x: List[Int]) => x)
res7: List[List[Int]] = List(List(1, 2, 3), List(2, 3), List(3))
Then I thought, surely there's an even easier, idiomatic way to turn a List into a List of Lists. So what is it?
Bonus Question: why can't Scala infer the type of the lambda? a is a List[Int], so x should be a List[Int] too, no?:
scala> self(a, x => x)
<console>:13: error: missing parameter type
self(a, x => x)
^

tails will do most of the job.
tails will have an empty list as its last element, and you need to filter that out, which is what collect does below:
def headAndSelf[A,B](l: List[A])(f: (A, List[A]) => B) =
l.tails.collect{case list # head :: _ => f(head, list)}.toList
Having two parameters lists as demonstrated here will allow proper type inference. You need two list at call site too, but it is often convenient :
headAndSelf(yourList){(h,l) => whatever}

Related

Scala: Implementation of flatMap using foldRight

I am having trouble understanding the solution to the functional programming exercise:
Implement flatMap using only foldRight, Nil and :: (cons).
The solution is as follows:
def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] =
xs.foldRight(List[B]())((outCurr, outAcc) =>
f(outCurr).foldRight(outAcc)((inCurr, inAcc) => inCurr :: inAcc))
I have tried to factor out anonymous functions into function definitions to rewrite the solution to no luck. I cannot understand what is happening or think of a way to break it down so it's less complicated. So, any help or explanation regarding the solution would be appreciated.
Thanks!
First, just ignore the constraints and think about the flatMap function in this case. You have a List[A] and a function f: A => List[B]. Normally, if you just do a map on the list and apply the f function, you'll get back a List[List[B]], right? So to get a List[B], what would you do? You would foldRight on the List[List[B]] to get back a List[B] by just appending all elements in the List[List[B]]. So the code will look somewhat like this:
def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = {
val tmp = xs.map(f) // List[List[B]]
tmp.foldRight(List[B]())((outCurr, outAcc) => outCurr ++ outAcc)
}
To verify what we have so far, running the code in REPL and verify the result against built-in flatMap method:
scala> def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = {
| val tmp = xs.map(f) // List[List[B]]
| tmp.foldRight(List[B]())((outCurr, outAcc) => outCurr ++ outAcc)
| }
flatMap: [A, B](xs: List[A])(f: A => List[B])List[B]
scala> flatMap(List(1, 2, 3))(i => List(i, 2*i, 3*i))
res0: List[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9)
scala> List(1,2,3).flatMap(i => List(i, 2*i, 3*i))
res1: List[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9)
OK, so now, look at our constraints, we are not allowed to use map here. But we don't really need to, because the map here is just for iterating through the list xs. We can then use foldRight for this same purpose. So let's rewrite the map part using foldRight:
def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = {
val tmp = xs.foldRight(List[List[B]]())((curr, acc) => f(curr) :: acc) // List[List[B]]
tmp.foldRight(List[B]())((outCurr, outAcc) => outCurr ++ outAcc)
}
OK, let's verify the new code:
scala> def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = {
| val tmp = xs.foldRight(List[List[B]]())((curr, acc) => f(curr) :: acc) // List[List[B]]
| tmp.foldRight(List[B]())((outCurr, outAcc) => outCurr ++ outAcc)
| }
flatMap: [A, B](xs: List[A])(f: A => List[B])List[B]
scala> flatMap(List(1, 2, 3))(i => List(i, 2*i, 3*i))
res3: List[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9)
OK, so far so good. So let's optimize the code a bit, which is instead of having two foldRight in sequential, we'll combine them into just one foldRight. That shouldn't be too hard:
def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = {
xs.foldRight(List[B]()) { (curr, acc) => // Note: acc is List[B]
val tmp2 = f(curr) // List[B]
tmp2 ++ acc
}
}
Verify again:
scala> def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = {
| xs.foldRight(List[B]()) { (curr, acc) => // Note: acc is List[B]
| val tmp2 = f(curr) // List[B]
| tmp2 ++ acc
| }
| }
flatMap: [A, B](xs: List[A])(f: A => List[B])List[B]
scala> flatMap(List(1, 2, 3))(i => List(i, 2*i, 3*i))
res4: List[Int] = List(1, 2, 3, 2, 4, 6, 3, 6, 9)
OK, so let's look at our constraints, it looks like we can't use ++ operation. Well, ++ is just a way to append the two List[B] together, so we can certainly achieve the same thing using foldRight method, like this:
def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] = {
xs.foldRight(List[B]()) { (curr, acc) => // Note: acc is List[B]
val tmp2 = f(curr) // List[B]
tmp2.foldRight(acc)((inCurr, inAcc) => inCurr :: inAcc)
}
}
And then, we can combine them all into one line by:
def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] =
xs.foldRight(List[B]())((curr, acc) =>
f(curr).foldRight(acc)((inCurr, inAcc) => inCurr :: inAcc))
Isn't it the given answer :)
Sometimes it is easier to understand with a simple example:
Suppose we have a val xs = List[Int](1,2,3) and a function f: Int => List[Int], f(x) = List(x,x) (lambda x => List(x,x))
Appling f to each element of xs List(f(1),f(2), f(3)) will result in List(List(1,1),List(2,2),List(3,3)) so we need to flatten out this List[List[Int]]. The final result should be List(1, 1, 2, 2, 3, 3). Given Cons (::) the constructor of nonempty List, this should be Cons(1, Cons(1, Cons(2, Cons(2, Cons(3, Cons(3, Nil)))))). Observe foldRight operation in the result, which applies the constructor Cons (::) to the result of f applied to each element of the list xs. So a first implementation of flatMap would be
def flatMap[A,B](xs: List[A])(f: A => List[B]): List[B] = xs match {
case Cons(head, tail) => foldRight(f(head), Nil)((a,b) => Cons(a,b))
}
In this form, flatMap(List(1,2,3)) will return List(1,1) or Cons(1,1,Nil) (by substitution). So we need to continue down with a recursive call to flatMap on the tail (reducing the problem by 1 from 3(elements) to 2(elements)) and adding the base case for the case of an empty List as Nil (Nil is the "zero" element for Cons operation)
def flatMap[A,B](xs: List[A])(f: A => List[B]): List[B] = xs match {
case Nil => Nil
case Cons(head, tail) => foldRight(f(head), flatMap(tail)(f))((a,b) => Cons(a,b))
}
which is the final implementation.

How to call merge sort

The code below is based on Merge sort from "Programming Scala" causes stack overflow
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T] = {
def merge(xs: List[T], ys: List[T], acc: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => ys.reverse ::: acc
case (_, Nil) => xs.reverse ::: acc
case (x :: xs1, y :: ys1) =>
if (less(x, y)) merge(xs1, ys, x :: acc)
else merge(xs, ys1, y :: acc)
}
val n = xs.length / 2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(less)(ys), msort(less)(zs), Nil).reverse
}
}
When I try to invoke msort using :
val l = List(5, 2, 4, 6, 1, 3)
msort[Int](l)
I receive error :
Multiple markers at this line - type mismatch; found : List[Int] required: (Int, Int) => Boolean - type mismatch;
found : List[Int] required: (Int, Int) => Boolean - missing arguments for method msort in object mergesort; follow
this method with `_' if you want to treat it as a partially applied function
How do I invoke msort & why is a function required as part of the invocation ?
In Scala it is possible to have Multiple Parameters Lists. Your invocation only passes one argument.
The method is declared as def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T], so the first argument is of type (T, T) => Boolean, which is a function taking two parameters of type T and returning a Boolean value. You pass there a List[Int], which makes Scala complain.
Why would you like to have such a thing you may ask. Well, consider following example.
val stringSorter = msort[String]((a, b) => a.compareTo(b) < 0) _
// stringSorter: List[String] => List[String] = <function1>
val integerSorter = msort[Int]((a, b) => a < b) _
// integerSorter: List[Int] => List[Int] = <function1>
Those two invocation create two new functions taking only a single parameter - the list you want to sort. You don't have to tell it how to compare the elements, because you already did. Note you can invoke the same function with different lists as an argument.
integerSorter(List(2, 3, 1))
// res0: List[Int] = List(1, 2, 3)
integerSorter(List(2, 4, 1))
// res1: List[Int] = List(1, 2, 4)
stringSorter(List("b", "a", "c"))
res3: List[String] = List(a, b, c)
Note also that the newly created functions are type safe and following code will fail:
integerSorter(List("b", "a", "c"))
<console>:10: error: type mismatch;
found : String("b")
required: Int
integerSorter(List("b", "a", "c"))
Implicit Parameters
As the article in the link mentioned one of the reasons you may want to use Multiple Parameter Lists are implicit parameters.
When using implicit parameters, and you use the implicit keyword, it
applies to the entire parameter list. Thus, if you want only some
parameters to be implicit, you must use multiple parameter lists.
Let's modify the example code you gave us a bit to introduce a new type:
trait Comparator[T] {
def less(a: T, b: T): Boolean
}
and let's swap the parameter lists, and add implicit keyword to the second one, so now it becomes:
def msort[T](xs: List[T])(implicit c: Comparator[T]): List[T] = {
def merge(xs: List[T], ys: List[T], acc: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => ys.reverse ::: acc
case (_, Nil) => xs.reverse ::: acc
case (x :: xs1, y :: ys1) =>
if (c.less(x, y)) merge(xs1, ys, x :: acc)
else merge(xs, ys1, y :: acc)
}
val n = xs.length / 2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(ys)(c), msort(zs)(c), Nil).reverse
}
}
Now you can declare implicit object which will be used in case you don't supply one, e.g.
implicit val intComparator = new Comparator[Int] { def less(a: Int, b: Int) = a < b }
msort(List(5, 3, 1, 3))
// res8: List[Int] = List(1, 3, 3, 5)
While this may not seem to be very appealing it gives you extra flexibility when designing your API. Let's assume that we have a type called CustomType. It can declare an implicit in the companion object and it will be resolved "automatically" by the compiler.
case class CustomType(ordinal: Int, name: String)
object CustomType {
implicit val customTypeComparator = new Comparator[CustomType] {
def less(a: CustomType, b: CustomType) = a.ordinal < b.ordinal
}
}
msort(List(CustomType(2, "Second"), CustomType(1, "First")))
// res11: List[CustomType] = List(CustomType(1,First), CustomType(2,Second))
def msort[T](less: (T, T) => Boolean)(xs: List[T]): List[T]
This function takes two arguments: a function less and a list xs.
How do I invoke msort?
You have to provide values for both arguments: msort(...)(...).
Why is a function required as part of the invocation?
Because the argument less is declared with function type (T, T) => Boolean.

Stream-y Implementation of Stream.foldRight

I'm working on a Functional Programming in Scala exercise to implement foldRight on a Stream.
Before getting to that exercise, let me show how I've implemented foldLeft.
def foldLeft[A, B](as: Stream[A])(z: B)(f: (B, A) => B): B = {
def go(bs: Stream[A], acc: B) : B = bs match {
case x #:: xs => go(xs, f(acc, x))
case Stream() => acc
}
}
My understanding is that, via #::, I'm executing the fold in a tail-recursive and stream-y fashion, i.e. the tail isn't fully evaluated.
However, when I thought of how to implement foldRight, I figured I could simply do:
stream.reverse.foldLeft(monoid.zero)(monoid.op)
However, calling reverse will result in full evaluation of the stream:
scala> val x = Stream(1,2,3)
x: scala.collection.immutable.Stream[Int] = Stream(1, ?)
scala> x.reverse
res15: scala.collection.immutable.Stream[Int] = Stream(3, 2, 1)
How can I stream-ily implement foldRight?

Scala method definition named parameters vs. unnamed

i'm new to Scala and i'm struggling sometimes with method signatures.
Lets take this code, i'm especially interested in naming the parameters to do further operations on them.
def divide(xs: List[Int]): List[Int] = {
val mid = xs.length/2
(xs take mid, xs drop mid)
}
Here I defined the input list named as "xs", I've seen this convention on many web pages. But in university we had another method signature definition method (I am missing the name, sorry) in which we didn't name the input parameter(s) but pattern matching takes place:
def mylength: List[Any] => Int = {
case Nil => 0
case x::xs => mylength(xs)+1
}
In this case, it is very trivial to identify the input parameter because there is just a single one. How could I use the same style as in the code below with 2 or more input parameters in the coding style shown above?
def myConcat(xs: List[Any], ys: List[Any]) = {
xs ++ ys
}
Sorry for my English. I didn't find anything on google because I didn't relly have a clue what terms to search for...
Edit: I have to stick to an interface. I make another example with which you could help me.
myAppend1 and myAppend2 shall behave the same way, putting a new element in the front of the list.
def myAppend1(xs: List[Any], y: Any): List[Any] = {
y :: xs
}
My problem is now the naming of my inputs in myAppend2...
def myAppend2: List[Any] => Any => List[Any] = {
/* how can i do this here, when no names y/xs are given?*/
}
To use the same style with 2 or more parameters, just treat the parameters as a tuple of two:
def myConcat: (List[Any], List[Any]) => List[Any] = {
case (Nil, Nil) => List()
case (xs,ys) => ...
}
Let's take the myAppend example:
def myAppend2: (List[Any], Any) => List[Any] = {
case (xs, y) => y :: xs
}
This has (more or less) the same signature as:
def myAppend1(xs: List[Any], y: Any): List[Any] = {
y :: xs
}
Usage:
scala> myAppend1(List(1,2,3), 4)
res3: List[Any] = List(4, 1, 2, 3)
scala> myAppend2(List(1,2,3), 4)
res4: List[Any] = List(4, 1, 2, 3)
If you had a higher-order function that wanted a function argument of (List[Any], Any) = List[Any], then both will work and are (for most practical purposes) equivalent.
Note that by defining it like
def myAppend3: List[Any] => Any => List[Any] = {
xs => y => y::xs
}
you will be creating a curried function, which in scala has a different signature
(from what you want):
myAppend3(List(1,2,3), 4) // won't compile
myAppend3(List(1,2,3))(4) // need to do this
def myAppend2: List[Any] => Any => List[Any] = { xs => y =>
y :: xs
}
with full form of function literal syntax:
def myAppend2: List[Any] => Any => List[Any] = {
(xs: List[Any]) => {
(y: Any) => {
y :: xs
}
}
}

Type inference on anonymous functions with enrich-my-library

Say I have a method that turns a (function on two elements) into a (function on two sequences):
def seqed[T](f: (T,T) => T): (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
In words, the resulting function takes two sequences xs and ys, and creates a new sequence consisting of (xs(0) f ys(0), xs(1) f ys(1), ...)
So, for example, if xss is Seq(Seq(1,2),Seq(3,4)) and f is (a: Int, b: Int) => a + b, we can invoke it thus:
xss reduceLeft seqed(f) // Seq(4, 6)
or with an anonymous function:
xss reduceLeft seqed[Int](_+_)
This is pretty good; it would be nice to get rid of the [Int] type argument but I don't see how (any ideas?).
To make it feel a bit more like the tupled method, I also tried the enrich-my-library pattern:
class SeqFunction[T](f: (T,T) => T) {
def seqed: (Seq[T], Seq[T]) => Seq[T] = (_,_).zipped map f
}
implicit def seqFunction[T](f: (T,T) => T) = new SeqFunction(f)
For a pre-defined function this works great, but it's ugly with anonymous ones
xss reduceLeft f.seqed
xss reduceLeft ((_:Int) + (_:Int)).seqed
Is there another way I can reformulate this so that the types are inferred, and I can use syntax something like:
// pseudocode
xss reduceLeft (_+_).seqed // ... or failing that
xss reduceLeft (_+_).seqed[Int]
? Or am I asking too much of type inference?
You can't do it the way you want, but look at Function.tupled, which is a counter-part to .tupled that solves this very same problem.
scala> List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
<console>:8: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
^
<console>:8: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
List(1, 2, 3) zip List(1, 2, 3) map (_ + _).tupled
^
scala> List(1, 2, 3) zip List(1, 2, 3) map Function.tupled(_ + _)
res7: List[Int] = List(2, 4, 6)
I am pretty sure you are asking too much. Type inference in Scala goes from left to right, so the type of (_+_) needs to be figured out first before even considering the .sedeq part. And there isn't enough information there.
The reason why a type annotation is required in
xss reduceLeft seqed[Int](_+_)
but not in
xs zip ys map Function.tupled(_+_)
is due to the difference in type requirements between map and reduceLeft.
def reduceLeft [B >: A] (f: (B, A) ⇒ B): B
def map [B] (f: (A) ⇒ B): Seq[B] // simple version!
reduceLeft expects seqed to return type B where B >: Int. It seems that therefore the precise type for seqed cannot be known, so we have to provide the annotation. More info in this question.
One way to overcome this is to re-implement reduceLeft without the lower bound.
implicit def withReduceL[T](xs: Seq[T]) = new {
def reduceL(f: (T, T) => T) = xs reduceLeft f
}
Test:
scala> Seq(Seq(1,2,3), Seq(2,2,2)) reduceL seqed(_+_)
res1: Seq[Int] = List(3, 4, 5)
The problem now is that this now doesn't work on subtypes of Seq (e.g. List), with or without the [Int] parameter:
scala> Seq(List(1,2,3), List(2,2,2)) reduceL seqed(_+_)
<console>:11: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
Seq(List(1,2,3), List(2,2,2)) reduceL seqed(_+_)
^
reduceL expects a function of type (List[Int], List[Int]) => List[Int]. Because Function2 is defined as Function2 [-T1, -T2, +R], (Seq[Int], Seq[Int]) => Seq[Int] is not a valid substitution.