如何将enrich-my-library模式应用于Scala集合?

如何将enrich-my-library模式应用于Scala集合?

一个Scala中最强大的模式是充实,我的图书馆*模式,它采用隐式转换为出现添加方法,以现有的类,而不需要动态方法解析。例如,如果我们希望所有字符串都有spaces计算他们有多少个空格字符的方法,我们可以:


class SpaceCounter(s: String) {

  def spaces = s.count(_.isWhitespace)

}

implicit def string_counts_spaces(s: String) = new SpaceCounter(s)


scala> "How many spaces do I have?".spaces

res1: Int = 5

不幸的是,这种模式在处理泛型集合时遇到了麻烦。例如,已经询问了许多关于按顺序对项目进行分组的问题。没有内置的东西可以一次性工作,所以这似乎是使用泛型集合C和泛型元素类型的rich-my-library模式的理想候选者A:


class SequentiallyGroupingCollection[A, C[A] <: Seq[A]](ca: C[A]) {

  def groupIdentical: C[C[A]] = {

    if (ca.isEmpty) C.empty[C[A]]

    else {

      val first = ca.head

      val (same,rest) = ca.span(_ == first)

      same +: (new SequentiallyGroupingCollection(rest)).groupIdentical

    }

  }

}

当然,除了它不起作用。REPL告诉我们:


<console>:12: error: not found: value C

               if (ca.isEmpty) C.empty[C[A]]

                               ^

<console>:16: error: type mismatch;

 found   : Seq[Seq[A]]

 required: C[C[A]]

                 same +: (new SequentiallyGroupingCollection(rest)).groupIdentical

                      ^

有两个问题:我们如何C[C[A]]从一个空C[A]列表(或从空气中)获得一个?我们如何C[C[A]]从same +:线上取代而不是Seq[Seq[A]]?


* 以前称为pimp-my-library。


繁星淼淼
浏览 424回答 3
3回答

SMILET

在此提交中,“充实”Scala集合要比Rex给出的出色答案要容易得多。对于简单的情况,它可能看起来像这样,import scala.collection.generic.{ CanBuildFrom, FromRepr, HasElem }import language.implicitConversionsclass FilterMapImpl[A, Repr](val r : Repr)(implicit hasElem : HasElem[Repr, A]) {&nbsp; def filterMap[B, That](f : A => Option[B])&nbsp; &nbsp; (implicit cbf : CanBuildFrom[Repr, B, That]) : That = r.flatMap(f(_).toSeq)}implicit def filterMap[Repr : FromRepr](r : Repr) = new FilterMapImpl(r)它filterMap为所有GenTraversableLikes 增加了一个“相同的结果类型” ,scala> val l = List(1, 2, 3, 4, 5)l: List[Int] = List(1, 2, 3, 4, 5)scala> l.filterMap(i => if(i % 2 == 0) Some(i) else None)res0: List[Int] = List(2, 4)scala> val a = Array(1, 2, 3, 4, 5)a: Array[Int] = Array(1, 2, 3, 4, 5)scala> a.filterMap(i => if(i % 2 == 0) Some(i) else None)res1: Array[Int] = Array(2, 4)scala> val s = "Hello World"s: String = Hello Worldscala> s.filterMap(c => if(c >= 'A' && c <= 'Z') Some(c) else None)res2: String = HW对于问题的例子,解决方案现在看起来像,class GroupIdenticalImpl[A, Repr : FromRepr](val r: Repr)&nbsp; (implicit hasElem : HasElem[Repr, A]) {&nbsp; def groupIdentical[That](implicit cbf: CanBuildFrom[Repr,Repr,That]): That = {&nbsp; &nbsp; val builder = cbf(r)&nbsp; &nbsp; def group(r: Repr) : Unit = {&nbsp; &nbsp; &nbsp; val first = r.head&nbsp; &nbsp; &nbsp; val (same, rest) = r.span(_ == first)&nbsp; &nbsp; &nbsp; builder += same&nbsp; &nbsp; &nbsp; if(!rest.isEmpty)&nbsp; &nbsp; &nbsp; &nbsp; group(rest)&nbsp; &nbsp; }&nbsp; &nbsp; if(!r.isEmpty) group(r)&nbsp; &nbsp; builder.result&nbsp; }}implicit def groupIdentical[Repr : FromRepr](r: Repr) = new GroupIdenticalImpl(r)示例REPL会话,scala> val l = List(1, 1, 2, 2, 3, 3, 1, 1)l: List[Int] = List(1, 1, 2, 2, 3, 3, 1, 1)scala> l.groupIdenticalres0: List[List[Int]] = List(List(1, 1),List(2, 2),List(3, 3),List(1, 1))scala> val a = Array(1, 1, 2, 2, 3, 3, 1, 1)a: Array[Int] = Array(1, 1, 2, 2, 3, 3, 1, 1)scala> a.groupIdenticalres1: Array[Array[Int]] = Array(Array(1, 1),Array(2, 2),Array(3, 3),Array(1, 1))scala> val s = "11223311"s: String = 11223311scala> s.groupIdenticalres2: scala.collection.immutable.IndexedSeq[String] = Vector(11, 22, 33, 11)同样,请注意,已经观察到相同的结果类型原则,其方式与groupIdentical直接定义的方式完全相同GenTraversableLike。

撒科打诨

在这个提交中,魔术咒语与Miles给出的出色答案略有不同。以下作品,但它是规范的吗?我希望其中一个经典能够纠正它。(或者说,大炮,大枪之一。)如果视图绑定是上限,则会丢失对Array和String的应用程序。如果绑定是GenTraversableLike或TraversableLike似乎并不重要; 但IsTraversableLike为您提供了GenTraversableLike。import language.implicitConversionsimport scala.collection.{ GenTraversable=>GT, GenTraversableLike=>GTL, TraversableLike=>TL }import scala.collection.generic.{ CanBuildFrom=>CBF, IsTraversableLike=>ITL }class GroupIdenticalImpl[A, R <% GTL[_,R]](val r: GTL[A,R]) {&nbsp; def groupIdentical[That](implicit cbf: CBF[R, R, That]): That = {&nbsp; &nbsp; val builder = cbf(r.repr)&nbsp; &nbsp; def group(r: GTL[_,R]) {&nbsp; &nbsp; &nbsp; val first = r.head&nbsp; &nbsp; &nbsp; val (same, rest) = r.span(_ == first)&nbsp; &nbsp; &nbsp; builder += same&nbsp; &nbsp; &nbsp; if (!rest.isEmpty) group(rest)&nbsp; &nbsp; }&nbsp; &nbsp; if (!r.isEmpty) group(r)&nbsp; &nbsp; builder.result&nbsp; }}implicit def groupIdentical[A, R <% GTL[_,R]](r: R)(implicit fr: ITL[R]):&nbsp; GroupIdenticalImpl[fr.A, R] =&nbsp; new GroupIdenticalImpl(fr conversion r)只有一种方法可以让有九条生命的猫皮肤美化。这个版本说,一旦我的源转换为GenTraversableLike,只要我可以从GenTraversable构建结果,就这样做。我对我的旧Repr不感兴趣。class GroupIdenticalImpl[A, R](val r: GTL[A,R]) {&nbsp; def groupIdentical[That](implicit cbf: CBF[GT[A], GT[A], That]): That = {&nbsp; &nbsp; val builder = cbf(r.toTraversable)&nbsp; &nbsp; def group(r: GT[A]) {&nbsp; &nbsp; &nbsp; val first = r.head&nbsp; &nbsp; &nbsp; val (same, rest) = r.span(_ == first)&nbsp; &nbsp; &nbsp; builder += same&nbsp; &nbsp; &nbsp; if (!rest.isEmpty) group(rest)&nbsp; &nbsp; }&nbsp; &nbsp; if (!r.isEmpty) group(r.toTraversable)&nbsp; &nbsp; builder.result&nbsp; }}implicit def groupIdentical[A, R](r: R)(implicit fr: ITL[R]):&nbsp; GroupIdenticalImpl[fr.A, R] =&nbsp; new GroupIdenticalImpl(fr conversion r)第一次尝试包括将Repr转换为GenTraversableLike。import language.implicitConversionsimport scala.collection.{ GenTraversableLike }import scala.collection.generic.{ CanBuildFrom, IsTraversableLike }type GT[A, B] = GenTraversableLike[A, B]type CBF[A, B, C] = CanBuildFrom[A, B, C]type ITL[A] = IsTraversableLike[A]class FilterMapImpl[A, Repr](val r: GenTraversableLike[A, Repr]) {&nbsp;&nbsp; def filterMap[B, That](f: A => Option[B])(implicit cbf : CanBuildFrom[Repr, B, That]): That =&nbsp;&nbsp; &nbsp; r.flatMap(f(_).toSeq)}&nbsp;implicit def filterMap[A, Repr](r: Repr)(implicit fr: ITL[Repr]): FilterMapImpl[fr.A, Repr] =&nbsp;&nbsp; new FilterMapImpl(fr conversion r)class GroupIdenticalImpl[A, R](val r: GT[A,R])(implicit fr: ITL[R]) {&nbsp;&nbsp; def groupIdentical[That](implicit cbf: CBF[R, R, That]): That = {&nbsp;&nbsp; &nbsp; val builder = cbf(r.repr)&nbsp; &nbsp; def group(r0: R) {&nbsp;&nbsp; &nbsp; &nbsp; val r = fr conversion r0&nbsp; &nbsp; &nbsp; val first = r.head&nbsp; &nbsp; &nbsp; val (same, other) = r.span(_ == first)&nbsp; &nbsp; &nbsp; builder += same&nbsp; &nbsp; &nbsp; val rest = fr conversion other&nbsp; &nbsp; &nbsp; if (!rest.isEmpty) group(rest.repr)&nbsp; &nbsp; }&nbsp;&nbsp; &nbsp; if (!r.isEmpty) group(r.repr)&nbsp; &nbsp; builder.result&nbsp; }&nbsp;}&nbsp;implicit def groupIdentical[A, R](r: R)(implicit fr: ITL[R]):&nbsp; GroupIdenticalImpl[fr.A, R] =&nbsp;&nbsp; new GroupIdenticalImpl(fr conversion r)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript