More efficient than acc.reverse ::: b? - scala

I'm tail optimizing a recursive function. At the end, the result will be acc.reverse ::: b. This is O(n) because of reverse and :::. Is there a better performance way to combine the two lists? Thanks.
Ex. Combine List(3, 2, 1) and List(4, 5, 6) to List(1, 2, 3, 4, 5, 6)

The standard library includes the reverse_::: method for this:
scala> List(3, 2, 1) reverse_::: List(4, 5, 6)
res0: List[Int] = List(1, 2, 3, 4, 5, 6)
This is still O(n), but avoids the separate call to :::.
Just for the sake of fun and learning, you could easily implement this as a tail-recursive function:
#tailrec
def reverseConcat[A](lefts: List[A], rights: List[A]): List[A] =
lefts match {
case Nil => rights
case head::tail => reverseConcat(tail, head::rights)
}
Or using foldLeft:
def reverseConcat[A](lefts: List[A], rights: List[A]): List[A] =
lefts.foldLeft(rights)((xs, x) => x :: xs)
Note that reverse_::: is not implemented using tail-recursion; it uses a var behind the scenes, so might perform differently.

Related

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)

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.

For-comprehensions of dynamic depth in Scala

I am writing a code that needs to generate all combinations of integer sequences which are (element-wise) within bounds of two other integer sequences. The code will be probably more readable than the above explanation:
def combinations(startingCounts: List[Int], endingCounts: List[Int] ) = for(
a <- startingCounts(0) to endingCounts(0);
b <- startingCounts(1) to endingCounts(1);
c <- startingCounts(2) to endingCounts(2)
) yield List(a, b, c)
combinations(List(0,7,3), List(1,7,5))
//^returns Vector(List(0, 7, 3), List(0, 7, 4), List(0, 7, 5), List(1, 7, 3), List(1, 7, 4), List(1, 7, 5))
The above code works as expected, but it has two problems:
It only works correctly with lists of a certain length. This isn't really an issue with my use-case, but in general it is.
The code length is proportional to the list length I need to take care of. In my case the length is 6 and I have a for-comprehension with 6 generators.
My question is: what is the best way of implementing the same function in a way that it works with all "bound list" lenghts? By "best" I mean correct, simple enough, and preferably not (much) slower than the original.
How about this?
def combinations(startingCounts: List[Int], endingCounts: List[Int] ) : IndexedSeq[List[Int]] = {
if(startingCounts.isEmpty)
IndexedSeq(Nil)
else
for{
ns <- combinations(startingCounts.tail, endingCounts.tail)
n <- startingCounts.head to endingCounts.head
} yield
n :: ns
}
Here is my initial solution. It looks OK, but I wonder if it can be done better.
import scala.annotation.tailrec
type SLInt = IndexedSeq[List[Int]]
def combinations2(startingCounts: List[Int], endingCounts: List[Int] ): SLInt = {
#tailrec
def inner(acc: SLInt, startingCounts: List[Int], endingCounts: List[Int]): SLInt = {
(startingCounts, endingCounts) match {
case (sh :: st, eh :: et) if (sh <= eh) => {
val newAcc = for(
ls <- acc;
last <- (sh to eh)
) yield (last :: ls)
inner(newAcc, st, et)
}
case (Nil, Nil) => acc
case _ => throw new IllegalArgumentException()
}
}
inner(IndexedSeq(List()), startingCounts.reverse, endingCounts.reverse)
}
combinations2(List(0,7,3), List(1,7,5))
//res3: SLInt = Vector(List(0, 7, 3), List(1, 7, 3), List(0, 7, 4), List(1, 7, 4), List(0, 7, 5), List(1, 7, 5))
The order of the results is different, but that doesn't make a difference. I am performing the List.reverse to avoid using List append operation and to use prepend instead, which should be constant time.

create pairs from sets

If I have unknown number of Set[Int] (or List[Int]) as an input and want to combine
i don't know size of input List[Int] for which I need to produce these tuples as a final result, what's the best way to achieve this? My code looks like below.
Ok. Since combine(xs) yields a List[List[Any]] and you have a :: combine(xs) you just insert a into the the List of all combinations. You want to combine a with each element of the possible combinations. That lead me to this solution.
You can also generalize it to lists:List[List[T]] because when you combine from lists:List[List[Int]] you will get a List[List[Int]].
def combine[T](lists: List[List[T]]): List[List[T]] = lists match {
case Nil => lists
case x :: Nil => for(a <- x) yield List(a) //needed extra case because if comb(xs) is Nil in the for loop, it won't yield anything
case x :: xs => {
val comb = combine(xs) //since all further combinations are constant, you should keep it in a val
for{
a <- x
b <- comb
} yield a :: b
}
}
Tests:
val first = List(7, 3, 1)
val second = List(2, 8)
val third = List("a","b")
combine(List(first, second))
//yields List(List(7, 2), List(7, 8), List(3, 2), List(3, 8), List(1, 2), List(1, 8))
combine(List(first, second, third))
//yields List(List(7, 2, a), List(7, 2, b), List(7, 8, a), List(7, 8, b), List(3, 2, a), List(3, 2, b), List(3, 8, a), List(3, 8, b), List(1, 2, a), List(1, 2, b), List(1, 8, a), List(1, 8, b))
I think you can also generalize this to work with other collections than List, but then you can't use pattern-match this easily and you have to work via iterators.

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