Ответ 1
Scala 2.12 имеет mutable.TreeMap
наконец: https://github.com/scala/scala/pull/4504
Я переношу свою базу кода Java в чистую Scala, и я застрял на этом одном фрагменте кода. У меня есть реализация IntervalMap, т.е. Структуры данных, которые позволяют эффективно отображать диапазоны [from,to]
в values
, где выполняются операции set
, delete
и get
O(log n)
(немного отличающиеся от IntervalTree или SegmentTree).
В этом коде используется Java java.util.TreeMaps
, а при переносе на Scala я столкнулся с двумя большими проблемами:
Scala не имеет mutable.TreeMap
- я решил обойти его, используя mutable.TreeSet
(нечетно Scala имеет mutable.TreeSet
, но no mutable.TreeMap
) для хранения ключей и сохранения значений в вспомогательный mutable.Map
. Это неприятный взлом, но есть ли лучший способ?
Следующая проблема Scala mutable.TreeSet
не имеет эквивалента java.util.TreeSet
ceilingKey
, floorEntry
, pollFirst
, pollLast
, которые являются всеми операциями O(log n)
в Java.
Итак, как я могу лучше всего перенести свой код на Scala? Каковы наилучшие практики в этих ситуациях? Я действительно не хочу писать свои собственные реализации дерева. Есть ли более идиоматический способ Scala для записи IntervalMaps, о котором я не знаю? Или там какая-то уважаемая библиотека? Или Scala просто сосать здесь с его gimped TreeSet и несуществующими TreeMaps. Конечно, я могу просто использовать Java TreeMap
в Scala, но это уродливо, и я теряю все приятные функции коллекции Scala, и я мог бы также использовать Java.
Вот мой текущий код Java: https://gist.github.com/pathikrit/5574521
Scala 2.12 имеет mutable.TreeMap
наконец: https://github.com/scala/scala/pull/4504
Ответ, к сожалению, заключается в том, чтобы просто использовать класс Java TreeMap
.
Scala не имеет собственной копии всего, и это одно из самых заметных исключений. Одна из причин, по которой он совместим с Java, заключается в том, что вам не нужно повторно изобретать каждое колесо.
Причина, по которой вы все еще хотите использовать Scala, заключается в том, что не каждый бит кода, который вы пишете, касается этой TreeMap. Ваш IntervalMap
может быть Scala IntervalMap
; вы просто используете Java TreeMap
внутренне для его реализации. Или вы можете использовать неизменяемую версию в Scala, которая теперь достаточно хорошо работает для неизменяемой версии.
Возможно, в 2.11 или 2.12 будет изменяться TreeMap
; это требует, чтобы кто-то написал его, протестировал, оптимизировал и т.д., но я не думаю, что есть философское возражение против его наличия.
1) В чем проблема с использованием неизменяемого TreeMap внутри? Он более или менее столь же эффективен, как и изменчивая карта деревьев, делает все в O (log n).
2) В версии Scala нет ceilingKey
и floorKey
, но вместо этого у вас есть методы from
и to
, которые по существу то же самое, но возвращают целое поддерево вместо отдельных записей.
Полный порт 1:1 Java-кода:
import scala.collection._
import scala.collection.immutable.TreeMap
case class Segment[T](start: Int, end: Int, value: T) {
def contains(x: Int) = (start <= x) && (x < end)
override def toString = "[%d,%d:%s]".format(start, end, value)
}
class IntervalMap[T] {
private var segments = new TreeMap[Int, Segment[T]]
private def add(s: Segment[T]): Unit = segments += (s.start -> s)
private def destroy(s: Segment[T]): Unit = segments -= s.start
def ceiling(x: Int): Option[Segment[T]] = {
val from = segments.from(x)
if (from.isEmpty) None
else Some(segments(from.firstKey))
}
def floor(x: Int): Option[Segment[T]] = {
val to = segments.to(x)
if (to.isEmpty) None
else Some(segments(to.lastKey))
}
def find(x: Int): Option[Segment[T]] = {
floor(x).filter(_ contains x).orElse(ceiling(x))
}
// This is replacement of `set`, used as myMap(s,e) = v
def update(x: Int, y: Int, value: T): Unit = {
require(x < y)
find(x) match {
case None => add(Segment[T](x, y, value))
case Some(s) => {
if (x < s.start) {
if (y <= s.start) {
add(Segment[T](x, y, value))
} else if (y < s.end) {
destroy(s)
add(Segment[T](x, y, value))
add(Segment[T](y, s.end, s.value))
} else {
destroy(s)
add(Segment[T](x, s.end, value))
this(s.end, y) = value
}
} else if (x < s.end) {
destroy(s)
add(Segment[T](s.start, x, s.value))
if (y < s.end) {
add(Segment[T](x, y, value))
add(Segment[T](y, s.end, s.value))
} else {
add(Segment[T](x, s.end, value))
this(s.end, y) = value
}
} else {
throw new IllegalStateException
}
}
}
}
def get(x: Int): Option[T] = {
for (seg <- floor(x); if (seg contains x)) yield seg.value
}
def apply(x: Int) = get(x).getOrElse{
throw new NoSuchElementException(
"No value set at index " + x
)
}
override def toString = segments.mkString("{", ",", "}")
}
// little demo
val m = new IntervalMap[String]
println(m)
m(10, 20) = "FOOOOOOOOO"
m(18, 30) = "_bar_bar_bar_"
m(5, 12) = "bazzz"
println(m)
for (x <- 1 to 42) printf("%3d -> %s\n",x,m.get(x))
Результат:
{}
{5 -> [5,12:bazzz],12 -> [12,18:FOOOOOOOOO],18 -> [18,20:_bar_bar_bar_],20 -> [20,30:_bar_bar_bar_]}
1 -> None
2 -> None
3 -> None
4 -> None
5 -> Some(bazzz)
6 -> Some(bazzz)
7 -> Some(bazzz)
8 -> Some(bazzz)
9 -> Some(bazzz)
10 -> Some(bazzz)
11 -> Some(bazzz)
12 -> Some(FOOOOOOOOO)
13 -> Some(FOOOOOOOOO)
14 -> Some(FOOOOOOOOO)
15 -> Some(FOOOOOOOOO)
16 -> Some(FOOOOOOOOO)
17 -> Some(FOOOOOOOOO)
18 -> Some(_bar_bar_bar_)
19 -> Some(_bar_bar_bar_)
20 -> Some(_bar_bar_bar_)
21 -> Some(_bar_bar_bar_)
22 -> Some(_bar_bar_bar_)
23 -> Some(_bar_bar_bar_)
24 -> Some(_bar_bar_bar_)
25 -> Some(_bar_bar_bar_)
26 -> Some(_bar_bar_bar_)
27 -> Some(_bar_bar_bar_)
28 -> Some(_bar_bar_bar_)
29 -> Some(_bar_bar_bar_)
30 -> None
31 -> None
32 -> None
33 -> None
34 -> None
35 -> None
Класс Segment
должен быть установлен private[yourPackage]
, необходимо добавить некоторую документацию.
Похоже, вы хотите использовать красивые функции коллекций Scala. Я не думаю, что вам нужно переопределить свой класс.
Вы видели scala.collection.JavaConversions
?
Вы можете следовать аналогичному подходу с оболочкой, а затем реализовать соответствующие методы. Возможно, вам нужно быть более креативным с тем, как вы определяете, а затем используете методы, уникальные для вашей карты, но не должны быть большой проблемой.
Надеюсь, это даст вам представление. Дайте мне знать, если вам нужно больше руководства, и я мог бы помочь вам (похоже, прошло некоторое время с тех пор, как вы спросили).