Сведение набора пар множеств к одной паре множеств
-
20-12-2019 - |
Вопрос
У меня есть for-comprehension
с генератором от Set[MyType]
Этот MyType
имеет lazy val
вызываемая переменная factsPair
который возвращает пару наборов:(Установите[MyFact], установите[MyFact]).
Я хочу перебрать их все и объединить факты в одну сглаженную пару (Set[MyFact], Set[MyFact]) следующим образом, однако я получаю No implicit view available ...
и not enough arguments for flatten: implicit (asTraversable ...
ошибки.(Я немного новичок в Scala, поэтому все еще пытаюсь привыкнуть к ошибкам).
lazy val allFacts =
(for {
mytype <- mytypeList
} yield mytype.factsPair).flatten
Что мне нужно указать для сглаживания, чтобы это сработало?
Решение
Вы не сможете использовать flatten
для этого, потому что flatten
on a collection возвращает коллекцию, а кортеж - это не коллекция.
Конечно, вы можете просто разделить, выровнять и снова соединить:
val pairs = for {
mytype <- mytypeList
} yield mytype.factsPair
val (first, second) = pairs.unzip
val allFacts = (first.flatten, second.flatten)
Другие советы
Scala flatten работает с теми же типами.У вас есть Seq[(Set[MyFact], Set[MyFact])], который нельзя сгладить.
Я бы порекомендовал изучить foldLeft
функция, потому что она очень общая и довольно проста в использовании, как только вы освоитесь с ней:
lazy val allFacts = myTypeList.foldLeft((Set[MyFact](), Set[MyFact]())) {
case (accumulator, next) =>
val pairs1 = accumulator._1 ++ next.factsPair._1
val pairs2 = accumulator._2 ++ next.factsPair._2
(pairs1, pairs2)
}
Первый параметр принимает начальный элемент, к которому будут добавлены другие элементы.Мы начинаем с пустого Tuple[Set[MyFact], Set[MyFact]]
инициализируется следующим образом: (Set[MyFact](), Set[MyFact]())
.
Далее мы должны указать функцию, которая принимает накопитель и добавляет к нему следующий элемент и возвращает с новым накопителем, в котором есть следующий элемент.Из-за всех этих кортежей это выглядит некрасиво, но работает.
Кортеж недоступен для перемещения, поэтому вы не можете сгладить его.Вам нужно вернуть что-то, по чему можно повторять, например, список:
List((1,2), (3,4)).flatten // bad
List(List(1,2), List(3,4)).flatten // good
Я хотел бы предложить более алгебраический взгляд.То, что у вас здесь есть, может быть легко решено с помощью моноиды.Для каждого моноида существует нулевой элемент и операция объединения двух элементов в один.
В этом случае задается для моноида:нулевой элемент - это пустое множество, а операция - объединение.И если у нас есть два моноида, то их декартово произведение также является моноидом, где операции определены попарно (см. примеры в Википедии).
Scalaz определяет моноиды как для наборов, так и для кортежей, поэтому нам не нужно ничего там делать.Нам просто понадобится вспомогательная функция, объединяющая несколько моноидных элементов в один, которая легко реализуется с помощью folding:
def msum[A](ps: Iterable[A])(implicit m: Monoid[A]): A =
ps.foldLeft(m.zero)(m.append(_, _))
(возможно, в Scala уже есть такая функция, я ее не нашел).С помощью msum
мы можем легко определить
def pairs(ps: Iterable[MyType]): (Set[MyFact], Set[MyFact]) =
msum(ps.map(_.factsPair))
использование неявных моноидов Scalaz для кортежей и множеств.