I'm trying to create a trait
for sorting functions like this:
trait Sorting:
def sort[A, B >: A, C <: IterableOnce[A]](list: C)(using ord: Ordering[B], factory: Factory[A, C]): C
and here is the implementation:
object InsertionSorting extends Sorting:
override def sort[A, B >: A, C <: IterableOnce[A]](list: C)(using ord: Ordering[B], factory: Factory[A, C]): C =
list.iterator
.foldLeft(ListBuffer.empty[A]) { (acc, x) =>
{
val index = acc.view.takeWhile(y => ord.lt(y, x)).size
acc.insert(index, x)
acc
}
}
.to(factory)
With a test:
class InsertionSortingSpec extends AnyFlatSpec with should.Matchers:
"A Sorting" should "sort correctly" in {
val sorting: Sorting = InsertionSorting
val input = Seq(31, 41, 59, 26, 41, 58)
val actual = sorting.sort(input)
actual shouldBe sorted
}
It works, so far so good.
But when I'm trying to replace the input in the test with an Array
class InsertionSortingSpec extends AnyFlatSpec with should.Matchers:
"A Sorting" should "sort correctly" in {
val sorting: Sorting = InsertionSorting
val input = Array(31, 41, 59, 26, 41, 58)
val actual = sorting.sort(input)
actual shouldBe sorted
}
Compile failed:
[error] 11 | val actual = sorting.sort(input)
[error] | ^
[error] |no implicit argument of type collection.Factory[A, scala.collection.mutable.ArraySeq.ofInt] was found for parameter factory of method sort in trait Sorting
[error] |
[error] |where: A is a type variable with constraint >: Int
So I had to add another method for Array in Sorting
:
trait Sorting:
def sort[A, B >: A, C <: IterableOnce[A]](list: C)(using ord: Ordering[B], factory: Factory[A, C]): C
def sort[A: ClassTag, B >: A](array: Array[A])(using Ordering[B]): Array[A] =
sort(array.iterator).toArray[A]
It works again. I know.
I just wondering is there any way that we can do it in one method?
BTW, the to
method for IterableOnce
, why the parameter factory
is not implicit?
def to[C1](/*using*/ factory: Factory[A, C1]): C1
Update 2022-06-09
Based on the correct answer, I rewrite it like this:
object InsertionSorting extends Sorting:
override def sort[A, B >: A: Ordering, C](
xs: C
)(using view: C => IterableOnce[A], factory: Factory[A, C])(using ClassTag[A]): C =
import math.Ordered.orderingToOrdered
view(xs).iterator
.foldLeft(ListBuffer.empty[A]) { (acc, x) =>
{
val index = acc.view.takeWhile(_ < x).size
acc.insert(index, x)
acc
}
}
.to(factory)
Just add some syntactic sugar (Context Bounds, Given Imports)
How about this: