jimka
December 25, 2021, 4:42pm
1
I needed to call this function takeNsmallest
on a List
and also on a MapView
. So I duplicated the function and only changed the type declarations.
Is there a better way in this case? Should I write the function once and just use cleverer declarations?
def takeNsmallest[T](n: Int, data: List[T], measure: T => Double): List[T] = {
if (n == 0 || data.isEmpty)
List()
else {
data
.foldLeft(MaxSizeList[T](n, measure, List[T]())) { (acc, next) => MaxSizeList(n, measure, next :: acc.items) }
.items
}.sortBy(measure)
}
def takeNsmallest[A,B](n: Int, data: MapView[A,B], measure: ((A,B)) => Double): List[(A,B)] = {
if (n == 0 || data.isEmpty)
List()
else {
data
.foldLeft(MaxSizeList[(A,B)](n, measure, List[(A,B)]())) { (acc, next) => MaxSizeList(n, measure, next :: acc.items) }
.items
}.sortBy(measure)
}
I think this should work:
def takeNsmallest[T](n: Int, data: IterableOnce[T], measure: T => Double): List[T] =
if (n == 0)
List.empty[T]
else
data
.iterator
.foldLeft(MaxSizeList[T](n, measure, List.empty[T])) {
(acc, next) => MaxSizeList(n, measure, next :: acc.items)
}.items
.sortBy(measure)
jimka
December 25, 2021, 5:09pm
3
ahhh, so the same code doesn’t work. we have to add the .iterator
call? Interesting.
Yeah, that is because IterableOnce
doesn’t provide any methods at all.
Rather, it just provides a way to get an Iterator
I think you could use Iterable
instead of IterableOnce
.
Why?
That would just allow fewer data types without any advantage to the code.
No reason other than that the code would work without .iterator
.
That’s an interesting question, whether a “native” foldLeft
is more desirable.
List
has both foldLeft
and iterator
that use tail
, so maybe the differential cost is only an indirection. Map
s resort to IterableOnceOps
.
Arrays and LazyList
get dedicated foldLeft
as well.
Doc says minimal IterableOnce
is to support parallel collections, which would not want to inherit IterableOnceOps
.
The method could take IterableOnceOps
, but probably no one does that.