I feel insertSortRight is less efficient than insertSortLeft because insertSortRight needs to call List.last (which is O(n)) as one of the arguments to insert(), where insertSortLeft calls List.head (which is O(1)) as one of the arguments to insert().
Is this understanding correct? Thanks.
def insertSortRight(unsorted: List[Int]) : List[Int] = {
(unsorted :\ List[Int]()) ((a, b) => insert(a, b))
}
def insertSortLeft(unsorted: List[Int]) : List[Int] = {
(List[Int]() /: unsorted) ((a, b) => insert(b, a))
}
def insert(a: Int, list: List[Int]) : List[Int] = list match {
case List() => List(a)
case y::ys => if (a > y) y::insert(a, ys) else a::y::ys
}
DHG answered “always prefer left folding”. But, Programming in Scala has an example the other way.
def flattenLeft[T](xss: List[List[T]]) = (List[T]() /: xss) (_ ::: )
def flattenRight[T](xss: List[List[T]]) = (xss :~List[T]()) ( ::: _)
I guess that is because flattenRight in this case is achieved by just one function call, while flattenLeft is achieved by n function call?
So, for a
List, since head operations are desired,foldLeftis the natural choice. That way you work through the list from left to right, always taking the head. As you can see, its implementation (onLinearSeqOptimized) simply uses a while-loop and traverses once.It seems like ‘foldRight’ would be O(n^2) since, in order to take the last element, you have to traverse the n elements of the
Listn times, but the library actually optimizes this for you. Behind the scenes,foldRightis implemented like this (also onLinearSeqOptimized):As you can see, this function is constructed by recursively calling
foldRighton the tail, holding each head on the stack, and applying the function to each head in reverse order after reaching the last element.