Как я формирую объединение scala SortedMaps?

(Я использую Scala nightlies и вижу то же поведение в 2.8.0b1 RC4. Я - вновь прибывший Scala.)

Я имею два SortedMaps, что я хотел бы сформировать объединение. Вот код, который я хотел бы использовать:

import scala.collection._

object ViewBoundExample {
    class X
    def combine[Y](a: SortedMap[X, Y], b: SortedMap[X, Y]): SortedMap[X, Y] = {
        a ++ b
    }
    implicit def orderedX(x: X): Ordered[X] = new Ordered[X] { def compare(that: X) = 0 }
}

Идея здесь является 'неявными' средствами оператора Xs может быть преобразован в Ordered[X]s, и затем это имеет смысл объединиться SortedMaps в другого SortedMap, вместо просто карты.

Когда я компилирую, я добираюсь

sieversii:scala-2.8.0.Beta1-RC4 scott$ bin/scalac -versionScala compiler version
2.8.0.Beta1-RC4 -- Copyright 2002-2010, LAMP/EPFL

sieversii:scala-2.8.0.Beta1-RC4 scott$ bin/scalac ViewBoundExample.scala
ViewBoundExample.scala:8: error: type arguments [ViewBoundExample.X] do not
    conform to method ordered's type parameter bounds [A <: scala.math.Ordered[A]]
        a ++ b
          ^
one error found

Кажется, что моя проблема ушла бы, если бы тот связанный параметр типа был [A <% scala.math.Ordered[A]], вместо [A <: scala.math.Ordered[A]]. К сожалению, я не могу выровнять работу, где метод 'заказал' жизни! Кто-либо может помочь мне разыскать его?

Сбой этого, что я предназначен, чтобы сделать для создания объединения два SortedMaps? Если я удаляю тип возврата объединения (или измените его на Map) все хорошо работает---, но затем я не могу полагаться на отсортированный возврат!

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

2 ответа

В настоящее время вы используете черту scala.collection.SortedMap , чей метод ++ унаследован от черты MapLike . Следовательно, вы видите следующее поведение:

scala> import scala.collection.SortedMap
import scala.collection.SortedMap

scala> val a = SortedMap(1->2, 3->4)
a: scala.collection.SortedMap[Int,Int] = Map(1 -> 2, 3 -> 4)

scala> val b = SortedMap(2->3, 4->5)
b: scala.collection.SortedMap[Int,Int] = Map(2 -> 3, 4 -> 5)

scala> a ++ b
res0: scala.collection.Map[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5)

scala> b ++ a
res1: scala.collection.Map[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5)

Тип возвращаемого результата ++ - это Map [Int, Int] , потому что это будет единственный тип, который он делает ощутить возвращение метода ++ объекта MapLike . Кажется, что ++ сохраняет свойство сортировки SortedMap , что, я думаю, связано с тем, что ++ использует абстрактные методы для объединения, а те абстрактные методы определены для сохранения порядка на карте.

Чтобы объединить две отсортированные карты, я предлагаю вам использовать scala.collection.immutable.SortedMap .

scala> import scala.collection.immutable.SortedMap
import scala.collection.immutable.SortedMap

scala> val a = SortedMap(1->2, 3->4)
a: scala.collection.immutable.SortedMap[Int,Int] = Map(1 -> 2, 3 -> 4)

scala> val b = SortedMap(2->3, 4->5)
b: scala.collection.immutable.SortedMap[Int,Int] = Map(2 -> 3, 4 -> 5)

scala> a ++ b
res2: scala.collection.immutable.SortedMap[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5)

scala> b ++ a
res3: scala.collection.immutable.SortedMap[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5)

Эта реализация трейта SortedMap объявляет ++ метод, который возвращает SortedMap .

Теперь пара ответов на ваши вопросы о границах типов:

  • Упорядоченный [T] - это черта, которая при смешивании класс, который указывает, что этот класс можно сравнить с помощью <, > , = , > = , <= . Вам просто нужно определить абстрактный метод compare (that: T) , который возвращает -1 для this , 1 для this> that и 0 для this == that . Затем все остальные методы реализуются в трейте на основе результата compare .

  • T <% U представляет собой ограничение представления в Scala. Это означает, что тип T либо является подтипом U , либо он может быть неявно преобразован в U неявным преобразованием в области видимости. Код работает, если вы поместите <% , но не с <: , поскольку X не является подтипом Упорядоченный [X] , но может быть неявно преобразовано в Ordered [X] с использованием неявного преобразования OrderedX .

Изменить: Относительно вашего комментария. Если вы используете scala.collection.immutable.SortedMap , вы по-прежнему программируете для интерфейса, а не для реализации, поскольку неизменяемая SortedMap определяется как черта . Вы можете рассматривать его как более специализированный признак scala.collection.SortedMap , который предоставляет дополнительные операции (например, ++ , который возвращает SortedMap ) и свойство быть неизменным. Это соответствует философии Scala - предпочитайте неизменность - поэтому я не вижу никаких проблем с использованием неизменяемого SortedMap . В этом случае вы можете гарантировать тот факт, что результат будет определенно отсортирован, и это не может быть изменено, поскольку коллекция неизменяема.

Хотя мне все еще кажется странным, что scala.collection.SortedMap не предоставляет метод ++ , который в результате возвращает SortedMap . Все проведенное мной ограниченное тестирование, похоже, предполагает, что результат конкатенации двух scala.collection.SortedMap действительно дает карту, которая сохраняет свойство сортировки.

5
ответ дан 10 December 2019 в 02:48
поделиться

Вы выбрали крепкий орешек, будучи новичком в Scala! : -)

Хорошо, краткая экскурсия, не ждите, что сейчас полностью ее поймете. Во-первых, обратите внимание, что проблема возникает в методе ++ . В поисках его определения мы находим его у признака MapLike , получая либо Iterator , либо Traversable . Поскольку y представляет собой SortedMap , то используется версия Traversable .

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

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

Итак, найдите типаж CanBuildFrom в ScalaDoc и выберите его. Он имеет большое количество подклассов, каждый из которых отвечает за создание определенного типа коллекции. Найдите и щелкните подкласс SortedMapCanBuildFrom . Это класс объекта, который вам нужен для создания SortedMap из Traversable . Обратите внимание на конструктор экземпляра (конструктор класса), который получает неявный параметр Ordering . Теперь мы приближаемся.

На этот раз используйте фильтр filter для поиска Ordering . Его сопутствующий объект (щелкните на маленьком «о» имени) содержит неявный объект, который будет генерировать Ordering s, поскольку сопутствующие объекты исследуются на предмет имплицитного создания экземпляров или преобразований для этого класса. Он определен внутри трейта LowPriorityOrderingImplicits , который расширяет объект Ordering , и, глядя на него, вы увидите метод упорядоченный [A <: Ordered [A]] , который создаст Требуется упорядочивание ... или произведет его, если бы не было проблемы.

Можно было бы предположить, что неявного преобразования из X в Упорядоченный [X] будет достаточно, как и я до того, как рассмотрел это более внимательно. Это, однако, преобразование объектов , а упорядоченный ожидает получения типа , который является подтипом Упорядоченный [X] . Хотя можно преобразовать объект типа X в объект типа Ordered [X] , X сам по себе не является подтипом Ordered [X] , поэтому его нельзя передать в качестве параметра упорядоченному .

С другой стороны, вы можете создать неявное val Ordering [X] вместо def Ordered [X] , и вы обойти проблему. В частности:

object ViewBoundExample {
    class X
    def combine[Y](a: SortedMap[X, Y], b: SortedMap[X, Y]): SortedMap[X, Y] = {
        a ++ b
    }
    implicit val orderingX = new Ordering[X] { def compare(x: X, y: X) = 0 }
}

Я думаю, что у большинства людей первоначальная реакция на Упорядоченный / Порядок должна вызывать недоумение: зачем нужны классы для одного и того же? Первый расширяет java.lang.Comparable , тогда как второй расширяет java.util.Comparator . Увы, подпись типа для compare в значительной степени суммирует основное различие:

def compare(that: A): Int     // Ordered
def compare(x: T, y: T): Int  // Ordering

Использование упорядоченного [A] требует, чтобы либо A расширяли Упорядоченный [A] , что потребует от человека возможности изменить определение A или передать метод, который может преобразовать A в Заказанный [A] . Scala прекрасно справляется с последним, но тогда у вас есть для преобразования каждого экземпляра перед сравнением.

С другой стороны, использование Упорядочивание [A] требует создания одного объекта, как показано выше. Когда вы его используете, вы просто передаете два объекта типа A в compare - в процессе не создаются объекты.

Таким образом, можно добиться некоторого прироста производительности, но есть гораздо более важная причина, по которой Scala предпочитает Упорядочивание над Упорядоченное . Еще раз посмотрите на сопутствующий объект в Упорядочивание . Вы заметите, что для многих классов Scala, определенных в нем, есть несколько имплицитов. Как вы помните, я упоминал ранее, что неявный для класса T будет искать внутри сопутствующего объекта T , и это именно то, что происходит.

Это может быть сделано и для Упорядоченных . Однако, и это является камнем преткновения, это означает, что любой метод, поддерживающий как Упорядочивание , так и Упорядоченное , не сработает! Это потому, что Scala будет искать неявное, чтобы заставить его работать, и найдет два: один для Ordering , один для Ordered . Поскольку Scala не может решить, что именно вам нужно, выдает сообщение об ошибке. Итак, выбор должен был быть сделан, и Заказ потребовал дополнительных действий.

Да, я забыл объяснить, почему подпись не определяется как заказанный [A <% Заказанный [A]] вместо заказанный [A <: заказанный [A]] . Я подозреваю, что это приведет к двойной имплицитной неудаче, о которой я упоминал ранее, но я

4
ответ дан 10 December 2019 в 02:48
поделиться
Другие вопросы по тегам:

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