Подтвердить что ты не робот

Перенос кода Java TreeMap на Scala?

Я переношу свою базу кода 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

4b9b3361

Ответ 2

Ответ, к сожалению, заключается в том, чтобы просто использовать класс Java TreeMap.

Scala не имеет собственной копии всего, и это одно из самых заметных исключений. Одна из причин, по которой он совместим с Java, заключается в том, что вам не нужно повторно изобретать каждое колесо.

Причина, по которой вы все еще хотите использовать Scala, заключается в том, что не каждый бит кода, который вы пишете, касается этой TreeMap. Ваш IntervalMap может быть Scala IntervalMap; вы просто используете Java TreeMap внутренне для его реализации. Или вы можете использовать неизменяемую версию в Scala, которая теперь достаточно хорошо работает для неизменяемой версии.

Возможно, в 2.11 или 2.12 будет изменяться TreeMap; это требует, чтобы кто-то написал его, протестировал, оптимизировал и т.д., но я не думаю, что есть философское возражение против его наличия.

Ответ 3

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], необходимо добавить некоторую документацию.

Ответ 4

Похоже, вы хотите использовать красивые функции коллекций Scala. Я не думаю, что вам нужно переопределить свой класс.

Вы видели scala.collection.JavaConversions?

Вы можете следовать аналогичному подходу с оболочкой, а затем реализовать соответствующие методы. Возможно, вам нужно быть более креативным с тем, как вы определяете, а затем используете методы, уникальные для вашей карты, но не должны быть большой проблемой.

Надеюсь, это даст вам представление. Дайте мне знать, если вам нужно больше руководства, и я мог бы помочь вам (похоже, прошло некоторое время с тех пор, как вы спросили).