I'm trying out Scala and I want to see how one would implement insertion sort in scala with the following requirements:
- Nested for loops
- Array[Int] for input
- If possible a way to modify the contents of the function in a call by reference way otherwise return an Array[Int]
If this isn't the Scala way of implementing insertion sort can you still provide code for the above and explain what is wrong with the approach. edit: This is an attempt using a while loop (doest work) and no it isn't a homework question, why the hostility?
def insert_sort(a:Array[Int]):Array[Int]={
for(i <- 0 until a.length)
{
var j=i+1
while(j>1&&a(j)<a(j-1)&&j<a.length)
{
var c=a(j)
a(j)=a(j-1)
a(j-1)=c
j-=1
}
}
return a
}
The implementation given in the wikipedia article fits your bill. Here it is, copied, pasted, and converted to Scala syntax:
def insertionSort(a: Array[Int]): Array[Int] = {
for (i <- 1 until a.length) {
// A[ i ] is added in the sorted sequence A[0, .. i-1]
// save A[i] to make a hole at index iHole
val item = a(i)
var iHole = i
// keep moving the hole to next smaller index until A[iHole - 1] is <= item
while (iHole > 0 && a(iHole - 1) > item) {
// move hole to next smaller index
a(iHole) = a(iHole - 1)
iHole = iHole - 1
}
// put item in the hole
a(iHole) = item
}
a
}
This is what I came up with, which seems to be functional, generic, tail-recursive (foldLeft
is tail-recursive)...:
def insertionSort[A](la: List[A])(implicit ord: Ordering[A]): List[A] = {
def insert(la: List[A], a: A) = {
val (h, t) = la.span(ord.lt(_, a))
h ::: (a :: t)
}
la.foldLeft(List[A]()) {(acc, a) => insert(acc, a)}
}
My version of the insertion sort procedure is the following. It consists of two functions, isort
and insert
which have the following signatures:
isort(xs: List[int]) : List[int]
: Takes an input list of integers and outputs a list of integers.insert(x: Int, xs: List[int]) : List[int]
Takes a sorted input list and an integerx
and addsx
to the right position in the list to maintain the invariant that the list is sorted. Example:insert(3, [1, 2]) = [1, 2, 3]
First let us code up the insert
function.
- If the list is empty, return a list of the integer to be added.
Otherwise, there are two possible cases. The head of the list is greater or equal to the given input integer, in which case we simply append the input integer to the beginning of the list and return it. The other case is when the input integer is greater than the head of the input list. In this case, we recursively insert the input integer into the tail of the list.
def insert(x : Int, xs : List[Int]) : List[Int] = { xs match{ case Nil => List(x) case y :: xs1 => if(y >= x) x :: xs else y :: insert(x, xs1) } }
The second function is isort
, which takes an input list and sorts it. The algorithm is as follows:
- If the input list is equal to
Nil
, returnNil
. Otherwise, recursively sort the tail of the list, and add the head at the right position in the (sorted) tail (using the
insert
procedure)def isort(xs : List[Int]) : List[Int] = { xs match{ case Nil => Nil case x :: xs1 => insert(x, isort(xs1)) } }
We can test this as isort(List(1, 6, 4, -2, 5, 12))
and we get the correct expected output.
The most elegant insertion sort algorithm implementation that I'we seen so far:
val rand = List(5,3,1,2)
def isort(xs: List[Int]): List[Int] =
if (xs.isEmpty) Nil
else insert(xs.head, isort(xs.tail))
def insert(x: Int, xs: List[Int]): List[Int] =
if (xs.isEmpty || x <= xs.head) x :: xs
else xs.head :: insert(x, xs.tail)
isort(rand) // Produces List(1,2,3,5)
Algorithm implementation is taken from this great book: https://www.artima.com/shop/programming_in_scala_3ed
Nested for loops are probably not the answer in Scala, whatever is the problem. They make sense in languages where for loops stand for "repeat until condition, changing this each iteration", which is not what for loops or for comprehensions are in Scala (a for comprehension is a for loop which yields something for each iteration).
In Scala, for loops and for comprehensions are iterations over the elements of a collection (or weirder things for non-collection monads), but for insertion sort you want to find a position, either swapping places up to that position, or inserting the element at that position. Finding stuff should not be done with for loops or for comprehensions.
Also, there's no pass-by-reference in Scala. And, in fact, Array
is not even a Scala collection, but a Java thingy which has unique characteristics granted to it by the JVM that are not reproducible with classes, so it can't be replaced.
here's another generic version of insertion sort:
def less[T <: Comparable[T]](i: T, j: T) = i.compareTo(j) < 0
def swap[T](xs: Array[T], i: Int, j: Int) { val tmp = xs(i); xs(i) = xs(j); xs(j) = tmp }
def insertSort[T <: Comparable[T]](xs: Array[T]) {
for { i <- 1 to xs.size
j <- List.range(1, i).reverse
if less(xs(j),xs(j - 1)) } swap(xs, j, j -1)
}
What do you need it for?
If you just want a sorted Array I would prefer: def sortArray(a: Array[Int]): Array[Int] = a.sortWith(_ < _)
.
At the beginning don't forget to add:
import scala.util.control.Breaks._
This is my solution, using a list as an input
def insertionSortLoop(list :List[Int]) : List[Int] = {
val intArray: Array[Int] = list.toArray
for ( j <- 1 until intArray.length ) {
breakable {
for ( i <- (1 to j).reverse ) {
if (intArray(i-1) < intArray(i)) {
break
} else {
val temp = intArray(i)
intArray(i) = intArray(i-1)
intArray(i-1) = temp
}
}
}
}
intArray.toList
}
I have the implementation here at github.
Here is a code sample written using foldLeft.
def insertionSort(input: List[Int]): List[Int] = {
input.foldLeft(List[Int]())( (acc, element) => {
val (firstHalf, secondHalf) = acc.span(_ < element)
//inserting the element at the right place
firstHalf ::: element :: secondHalf
})
}
The example can be found in this github repo.
Even though, when coding Scala, I'm used to prefer functional programming style (via combinators or recursion) over imperative style (via variables and iterations), THIS TIME, for this specific problem, old school imperative nested loops result in simpler code for the reader. I don't think falling back to imperative style is a mistake for certain classes of problems (such as sorting algorithms which usually mutate a buffer of items passed as input rather than resulting to a new sorted collection)
Here it is my solution:
package bitspoke.algo
import scala.math.Ordered
import scala.collection.mutable.Buffer
abstract class Sorter[T <% Ordered[T]] {
// algorithm provided by subclasses
def sort(buffer : Buffer[T]) : Unit
// check if the buffer is sorted
def sorted(buffer : Buffer[T]) = buffer.isEmpty || buffer.view.zip(buffer.tail).forall { t => t._2 > t._1 }
// swap elements in buffer
def swap(buffer : Buffer[T], i:Int, j:Int) {
val temp = buffer(i)
buffer(i) = buffer(j)
buffer(j) = temp
}
}
class InsertionSorter[T <% Ordered[T]] extends Sorter[T] {
def sort(buffer : Buffer[T]) : Unit = {
for {
i <- 1 until buffer.length
j <- i until 0 by -1
if (buffer(j) < buffer(j - 1))
}
swap(buffer, j, j - 1)
}
}
As you can see, rather than using java.lang.Comparable
, I preferred scala.math.Ordered
and Scala View Bounds rather than Upper Bounds. That's certainly works thanks to many Scala Implicit Conversions of primitive types to Rich Wrappers.
You can write a client program as follows:
import bitspoke.algo._
import scala.collection.mutable._
val sorter = new InsertionSorter[Int]
val buffer = ArrayBuffer(3, 0, 4, 2, 1)
sorter.sort(buffer)
assert(sorter.sorted(buffer))
Github link to Scala implementation
Pseudo Code:
Algorithm Insertion-Sort-Descending(A)
for j <- 2 to length[A]
key <- A[j] // element we wish to insert
i <- j - 1 // position to the left side
while i > 0 and A[i] < key // while has not found correct position <-- difference between Ascending and Descending
A[i + i] = A[i] // right shift element
i = i - 1 // decrement i
A[i + 1] = key // found position, place key
来源:https://stackoverflow.com/questions/10406064/insertion-sort-implementation-in-scala