Как сделать вложенный toSet в scala идиоматическим способом?

Есть ли более идиоматический способ превратить вложенную последовательность последовательностей во вложенный набор наборов?

def toNestedSet[T](tsss: Seq[Seq[Seq[T]]]): Set[Set[Set[T]]]  = 
   tsss.map(_.map(_.toSet).toSet).toSet

Можно ли реализовать функцию, которая работала бы со списками любой глубины?


person goozez    schedule 11.02.2014    source источник
comment
Определенно можно сделать это безопасным способом, без каких-либо Any или приведения - см., например, мой ответ здесь для решение аналогичной проблемы. Ваш вопрос немного сложнее, но я на 100% уверен, что подход сработает.   -  person Travis Brown    schedule 11.02.2014
comment
Моей первой мыслью было, может быть, Shapeless сможет справиться с общей проблемой произвольного вложения Seq to Set? (github.com/milessabin/shapeless)   -  person Randall Schulz    schedule 11.02.2014
comment
Когда я оказываюсь в такой ситуации, я обычно делаю определение типа для одного или нескольких внутренних вложений, которые мне в любом случае нужно повторно использовать.   -  person Ed Staub    schedule 11.02.2014
comment
@RandallSchulz: я не могу придумать, как Shapeless сделает все это намного проще, чем бесформенная реализация (например, моя ниже< /а>). Хотя, возможно, я ошибаюсь. может быть какой-то способ заставить SYB сделать это, чего я не вижу в данный момент.   -  person Travis Brown    schedule 12.02.2014


Ответы (2)


Чтобы ответить на вторую часть вашего вопроса (обработка списка произвольной глубины), сработает что-то вроде этого (стирание типа немного мешает):

  def toNestedSet(ts: Seq[Any]): Set[Any] = {
    ts.foldLeft[Set[Any]](Set())((acc, b) => b match {
        case s: Seq[_] => acc + toNestedSet(s)
        case x => acc + x
    })
  } 

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

Редактировать: актёрский состав был излишним

person James Adam    schedule 11.02.2014
comment
Мне на самом деле нравится решение op гораздо больше, чем это :) - person serejja; 11.02.2014
comment
Решение Op не работает с последовательностью произвольной глубины. Если известна глубина, то да - так и надо. - person James Adam; 11.02.2014
comment
Я бы не сказал, что стирание типа мешает так сильно, как стирание типа делает диспетчеризацию по типу в сопоставлении с шаблоном почти такой неприятной, какой она должна быть. - person Travis Brown; 12.02.2014

На самом деле это совсем не так уж плохо (см. мой ответ здесь на аналогичный вопрос для дополнительного обсуждения этого подхода) :

trait Setsifier[I, O] { def apply(i: I): O }

object Setsifier {
  def apply[I, O](f: I => O) = new Setsifier[I, O] { def apply(i: I) = f(i) }

  implicit def base[I](implicit ev: I <:!< Seq[_]) = apply((_: Seq[I]).toSet)

  implicit def rec[I, O](implicit s: Setsifier[I, O]) =
    apply((_: Seq[I]).map(s(_)).toSet)
}

def setsify[I, O](i: I)(implicit s: Setsifier[I, O]) = s(i)

А потом:

scala> println(setsify(Seq(Seq(Seq(Seq(1)), Seq(Seq(2, 3))))))
Set(Set(Set(Set(1)), Set(Set(2, 3))))

Статически типизирован как Set[Set[Set[Set[[Int]]]] и все.

Ну, я немного солгал. <:!< выше на самом деле нет в стандартной библиотеке. Однако он находится в Shapeless, или вы можете очень и очень легко определить его самостоятельно:

trait <:!<[A, B]

implicit def nsub[A, B] : A <:!< B = new <:!<[A, B] {}
implicit def nsubAmbig1[A, B >: A] : A <:!< B = sys.error("Don't call this!")
implicit def nsubAmbig2[A, B >: A] : A <:!< B = sys.error("Don't call this!")

И это действительно все.

person Travis Brown    schedule 11.02.2014
comment
Что ж, это умно. У меня есть проблема на работе, связанная с более сложной рекурсивной структурой данных, чем OP, которую я в настоящее время обрабатываю, используя более сложный вариант моего ответа ниже (и я никогда не был очень доволен). Я мог бы попытаться адаптировать это :) - person James Adam; 12.02.2014
comment
Одна сноска: я игнорирую здесь проблемы дисперсии для ясности, что означает, что эта версия будет работать, только если ваши последовательности статически типизированы как Seq. Исправить это будет нетрудно — просто немного грязнее. - person Travis Brown; 12.02.2014
comment
Привет, мне нравятся оба ответа, но ответ Джеймса Адама решает проблему и остается простым. Вот почему я принял его ответ. Тем не менее, ваш ответ может быть более удобным для продвинутых приложений и пользователей. +1 - person goozez; 12.02.2014
comment
@goozez: Достаточно справедливо, но я просто укажу, что потеря информации о типе означает, что вам придется привести результат, что устраняет большую часть преимуществ реализации этого для произвольных вложений сразу. - person Travis Brown; 12.02.2014
comment
@TravisBrown Есть ли ошибка в nsubAmbig2? Он выглядит точно так же, как nsubAmbig1. - person martin-g; 12.02.2014
comment
@martin-g: Нет, это своего рода уловка - вы делаете неявный поиск неудачным (для соответствующих типов), предоставляя неоднозначные имплициты. - person Travis Brown; 12.02.2014