Несмотря на то, что при кодировании Scala я привык отдавать предпочтение стилю функционального программирования (с помощью комбинаторов или рекурсии), а не императивному стилю (с помощью переменных и итераций), ЭТО ВРЕМЯ, для этой конкретной проблемы императивные вложенные циклы старой школы приводят к более простой и производительный код.
Я не думаю, что возврат к императивному стилю является ошибкой для определенных классов проблем, таких как алгоритмы сортировки, которые обычно преобразуют входной буфер (скорее как процедура), а не приводят к новой отсортированной коллекции.
Вот мое решение:
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)
}
}
Как видите, для достижения параметрического полиморфизма вместо использования java.lang.Comparable
я предпочел scala.math.Ordered
и Scala View Bounds, а не Upper Bounds. Это, безусловно, работает благодаря Scala Implicit преобразованию примитивных типов в Rich Wrappers.
Вы можете написать клиентскую программу следующим образом:
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))