Применяя `map` к` Set`, вы иногда хотите, чтобы результат не был сетом, но пропустите - PullRequest
8 голосов
/ 12 августа 2011

Или как избежать случайного удаления дубликатов при отображении Set?

Это ошибка, которую я делаю очень часто. Посмотрите на следующий код:

def countSubelements[A](sl: Set[List[A]]): Int = sl.map(_.size).sum

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

Это только у меня такая проблема? Что я могу сделать, чтобы предотвратить это? Я думаю, что я хотел бы иметь два метода mapToSet и mapToSeq для Set. Но нет никакого способа обеспечить это, и иногда вы не замечаете локально, что работаете с Set.

Может быть, даже возможно, что вы пишете код для Seq, и что-то меняется в другом классе, и базовый объект становится Set?

Может быть, что-то вроде лучшей практики, чтобы вообще не допустить возникновения этой ситуации?

Удаленное редактирование нарушает мой код

Представьте себе следующую ситуацию:

val totalEdges = graph.nodes.map(_.getEdges).map(_.size).sum / 2

Вы извлекаете коллекцию Node объектов из графа, используете их, чтобы получить смежные ребра и суммировать их. Это работает, если graph.nodes возвращает Seq.

И он ломается, если кто-то решит заставить Graph вернуть его узлы как Set; без этого кода, который выглядит подозрительно (по крайней мере, не для меня, вы ожидаете, что каждая коллекция может в конечном итоге быть Set?) и не касаясь его.

Ответы [ 2 ]

2 голосов
/ 12 августа 2011

Похоже, будет много возможных "ошибок", если кто-то ожидает Seq и получает Set.Не удивительно, что реализации метода могут зависеть от типа объекта и (с перегрузкой) аргументов.С учетом последствий Scala метод может даже зависеть от ожидаемого типа возвращаемого значения .

Способ защиты от неожиданностей заключается в явной маркировке типов.Например, аннотирование методов с возвращаемыми типами, даже если это не требуется.По крайней мере, таким образом, если тип graph.nodes изменяется с Seq на Set, программист осознает, что существует потенциальная поломка.

Для вашей конкретной проблемы, почему бы не определить свой собственный mapToSeq method,

scala> def mapToSeq[A, B](t: Traversable[A])(f: A => B): Seq[B] =
           t.map(f)(collection.breakOut)
mapToSeq: [A, B](t: Traversable[A])(f: A => B)Seq[B]

scala> mapToSeq(Set(Seq(1), Seq(1,2)))(_.sum)
res1: Seq[Int] = Vector(1, 3)

scala> mapToSeq(Seq(Seq(1), Seq(1,2)))(_.sum)
res2: Seq[Int] = Vector(1, 3)

Преимущество использования breakOut: CanBuildFrom состоит в том, что преобразование из Set в Seq не требует дополнительных затрат.

Вы можете использовать Прокачай шаблон моей библиотеки , чтобы mapToSeq выглядело как часть черты Traversable, унаследованной от Seq и Set.

1 голос
/ 12 августа 2011

Обходные:

def countSubelements[A](sl: Set[List[A]]): Int = sl.toSeq.map(_.size).sum

def countSubelements[A](sl: Set[List[A]]): Int = sl.foldLeft(0)(_ + _.size)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...