Scala: как объединить набор Карт

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

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

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

<час>

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

34
задан Eugene Yokota 15 December 2010 в 20:15
поделиться

3 ответа

Как насчет этого:

def mergeMap[A, B](ms: List[Map[A, B]])(f: (B, B) => B): Map[A, B] =
  (Map[A, B]() /: (for (m <- ms; kv <- m) yield kv)) { (a, kv) =>
    a + (if (a.contains(kv._1)) kv._1 -> f(a(kv._1), kv._2) else kv)
  }

val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
val mm = mergeMap(ms)((v1, v2) => v1 + v2)

println(mm) // prints Map(hello -> 5.5, world -> 2.2, goodbye -> 3.3)

И это работает как в 2.7.5, так и в 2.8.0.

27
ответ дан 27 November 2019 в 16:08
поделиться

Что ж, вы могли бы сделать:

mapList reduce (_ ++ _)

за исключением специального требования для столкновения.

Поскольку у вас есть это особое требование, возможно, лучше всего было бы сделать что-то вроде этого (2.8) :

def combine(m1: Map, m2: Map): Map = {
  val k1 = Set(m1.keysIterator.toList: _*)
  val k2 = Set(m2.keysIterator.toList: _*)
  val intersection = k1 & k2

  val r1 = for(key <- intersection) yield (key -> (m1(key) + m2(key)))
  val r2 = m1.filterKeys(!intersection.contains(_)) ++ m2.filterKeys(!intersection.contains(_)) 
  r2 ++ r1
}

Затем вы можете добавить этот метод в класс карты через шаблон Pimp My Library и использовать его в исходном примере вместо « ++ »:

class CombiningMap(m1: Map[Symbol, Double]) {
  def combine(m2: Map[Symbol, Double]) = {
    val k1 = Set(m1.keysIterator.toList: _*)
    val k2 = Set(m2.keysIterator.toList: _*)
    val intersection = k1 & k2
    val r1 = for(key <- intersection) yield (key -> (m1(key) + m2(key)))
    val r2 = m1.filterKeys(!intersection.contains(_)) ++ m2.filterKeys(!intersection.contains(_))
    r2 ++ r1
  }
}

// Then use this:
implicit def toCombining(m: Map[Symbol, Double]) = new CombiningMap(m)

// And finish with:
mapList reduce (_ combine _)

Хотя это было написано в версии 2.8 , поэтому keysIterator становится keys для 2.7, filterKeys может потребоваться записать в терминах filter и map , & становится ** и т. Д., Это не должно сильно отличаться.

43
ответ дан 27 November 2019 в 16:08
поделиться

Интересно, немного поработав с этим, я получил следующее (на 2.7.5):

Общие карты:

   def mergeMaps[A,B](collisionFunc: (B,B) => B)(listOfMaps: Seq[scala.collection.Map[A,B]]): Map[A, B] = {
    listOfMaps.foldLeft(Map[A, B]()) { (m, s) =>
      Map(
        s.projection.map { pair =>
        if (m contains pair._1)
          (pair._1, collisionFunc(m(pair._1), pair._2))
        else
          pair
      }.force.toList:_*)
    }
  }

Но, черт возьми, это ужасно с проекцией и принуждением и toList и еще много чего. Отдельный вопрос: как лучше справиться с этим внутри свертки?

Для изменяемых карт, с которыми я имел дело в своем коде, и с менее общим решением я получил следующее:

def mergeMaps[A,B](collisionFunc: (B,B) => B)(listOfMaps: List[mutable.Map[A,B]]): mutable.Map[A, B] = {
    listOfMaps.foldLeft(mutable.Map[A,B]()) {
      (m, s) =>
      for (k <- s.keys) {
        if (m contains k)
          m(k) = collisionFunc(m(k), s(k))
        else
          m(k) = s(k)
      }
      m
    }
  }

Это кажется немного чище, но будет работать только с изменяемыми картами, как написано. Интересно, что сначала я попробовал описанное выше (до того, как задал вопрос), используя /: вместо foldLeft, но я получал ошибки типа. Я думал, что /: и foldLeft в основном эквивалентны, но компилятор продолжал жаловаться, что мне нужны явные типы для (m, s). Что с этим?

2
ответ дан 27 November 2019 в 16:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: