Расширение коллекций Scala

Я хотел бы получить версию встроенной коллекции Scala, которая расширяет функциональность для определенного универсального типа, например,

import scala.collection.immutable._
class Tuple2Set[T1,T2] extends HashSet[Tuple2[T1,T2]] {
 def left = map ( _._1 )
 def right = map ( _._2 )
}

Однако, когда я пытаюсь использовать его со следующим тестом

  new Tuple2Set[String,String]() + (("x","y")) left

Я получаю следующую ошибку компиляции

error: value left is not a member of scala.collection.immutable.HashSet[(String, String)]

Как я могу изменить класс, чтобы это работало?


person John McCrae    schedule 29.01.2011    source источник
comment
Я новичок в Scala, кто-нибудь может объяснить, что означает map( _._1 ) слышать? Насколько я понимаю, например, set.map(_ + 1) создаст новый набор с элементами, увеличенными на единицу, но я не могу понять, что делает _._1   -  person Nutel    schedule 30.01.2011
comment
@Vetal: (_._1) в этом контексте совпадает с ((x: Tuple[T1, T2]) => x._1), а _1 — это поле в классе Tulple2, представляющее первый элемент кортежа.   -  person tenshi    schedule 30.01.2011
comment
@Easy Спасибо, если бы я мог, я бы принял ваш ответ   -  person Nutel    schedule 30.01.2011
comment
Vetal, _X — это методы для кортежей, которые возвращают x-й компонент.   -  person Raphael    schedule 30.01.2011


Ответы (4)


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

object Tuple2Set {
    implicit def canBuildFrom[T1, T2] = 
        new CanBuildFrom[Tuple2Set[T1, T2], (T1, T2), Tuple2Set[T1, T2]] {...}
}
person tenshi    schedule 30.01.2011
comment
Спасибо, этот ответ, кажется, решает проблему, как получить +, чтобы вернуть более конкретный тип, но я думаю, что решение с какой-то оболочкой может быть лучше в целом. Для моей реальной проблемы я хочу, чтобы Tuple2Set обертывал базу данных, а затем предоставлял оптимизированные реализации поиска, существования и т. д. Я думаю, что на самом деле мне было бы лучше просто написать новый интерфейс в конце... - person John McCrae; 30.01.2011

Вы уверены, что вам действительно нужно расширить коллекцию Scala? Чтобы заставить код выше работать, вы можете сделать это:

class Tuple2Set[T1,T2](set: Set[(T1, T2)]) {
  def left = set map ( _._1 )
  def right = set map ( _._2 )
}

implicit def toTuple2Set[T1, T2](set: Set[(T1, T2)]) = new Tuple2Set(set)

Set[(String, String)]() + (("x","y")) left

В этом случае Tuple2Set является просто оболочкой для любых других реализаций Set. Это означает, что вы больше не ограничены HashSet, и ваши методы left и right будут доступны и в любых других реализациях (например, в TreeSet).

Я думаю, что в большинстве случаев упаковка или композиция + делегирование работают намного лучше, чем наследование (и вызывают меньше проблем).

person tenshi    schedule 29.01.2011
comment
Не могли бы вы сделать эти два отдельных ответа, чтобы за них можно было проголосовать отдельно? См. meta.stackexchange.com/questions/25209/ для официальной этики по этому поводу. (И да, ваше объяснение CanBuildFrom стоит отдельного ответа от ответа Кевина Райта.) - person Ken Bloom; 30.01.2011

Общий ответ на ваш вопрос слишком сложен для ответа здесь. Но это было написано на некоторых веб-страницах.

Тот же материал с большим контекстом содержится во втором издании нашей книги Programming in Scala, Artima Press.

person Martin Odersky    schedule 31.01.2011

Причина, по которой ваш пример не работает, заключается в том, что возвращаемый тип операции + — это HashSet, а не Tuple2Set.

Вам повезет больше, если вы будете использовать шаблон «прокачать мою библиотеку» вместо наследования.

person Kevin Wright    schedule 29.01.2011