为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

繁星淼淼 2019-08-30 10:55:14
如何将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?".spacesres1: 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。
查看完整描述

3 回答

?
SMILET

TA贡献1796条经验 获得超4个赞

在此提交中,“充实”Scala集合要比Rex给出的出色答案要容易得多。对于简单的情况,它可能看起来像这样,


import scala.collection.generic.{ CanBuildFrom, FromRepr, HasElem }

import language.implicitConversions


class FilterMapImpl[A, Repr](val r : Repr)(implicit hasElem : HasElem[Repr, A]) {

  def filterMap[B, That](f : A => Option[B])

    (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 World


scala> s.filterMap(c => if(c >= 'A' && c <= 'Z') Some(c) else None)

res2: String = HW

对于问题的例子,解决方案现在看起来像,


class GroupIdenticalImpl[A, Repr : FromRepr](val r: Repr)

  (implicit hasElem : HasElem[Repr, A]) {

  def groupIdentical[That](implicit cbf: CanBuildFrom[Repr,Repr,That]): That = {

    val builder = cbf(r)

    def group(r: Repr) : Unit = {

      val first = r.head

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

      builder += same

      if(!rest.isEmpty)

        group(rest)

    }

    if(!r.isEmpty) group(r)

    builder.result

  }

}


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.groupIdentical

res0: 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.groupIdentical

res1: Array[Array[Int]] = Array(Array(1, 1),Array(2, 2),Array(3, 3),Array(1, 1))


scala> val s = "11223311"

s: String = 11223311


scala> s.groupIdentical

res2: scala.collection.immutable.IndexedSeq[String] = Vector(11, 22, 33, 11)

同样,请注意,已经观察到相同的结果类型原则,其方式与groupIdentical直接定义的方式完全相同GenTraversableLike。


查看完整回答
反对 回复 2019-08-30
?
撒科打诨

TA贡献1934条经验 获得超2个赞

在这个提交中,魔术咒语与Miles给出的出色答案略有不同。


以下作品,但它是规范的吗?我希望其中一个经典能够纠正它。(或者说,大炮,大枪之一。)如果视图绑定是上限,则会丢失对Array和String的应用程序。如果绑定是GenTraversableLike或TraversableLike似乎并不重要; 但IsTraversableLike为您提供了GenTraversableLike。


import language.implicitConversions

import 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]) {

  def groupIdentical[That](implicit cbf: CBF[R, R, That]): That = {

    val builder = cbf(r.repr)

    def group(r: GTL[_,R]) {

      val first = r.head

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

      builder += same

      if (!rest.isEmpty) group(rest)

    }

    if (!r.isEmpty) group(r)

    builder.result

  }

}


implicit def groupIdentical[A, R <% GTL[_,R]](r: R)(implicit fr: ITL[R]):

  GroupIdenticalImpl[fr.A, R] =

  new GroupIdenticalImpl(fr conversion r)

只有一种方法可以让有九条生命的猫皮肤美化。这个版本说,一旦我的源转换为GenTraversableLike,只要我可以从GenTraversable构建结果,就这样做。我对我的旧Repr不感兴趣。


class GroupIdenticalImpl[A, R](val r: GTL[A,R]) {

  def groupIdentical[That](implicit cbf: CBF[GT[A], GT[A], That]): That = {

    val builder = cbf(r.toTraversable)

    def group(r: GT[A]) {

      val first = r.head

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

      builder += same

      if (!rest.isEmpty) group(rest)

    }

    if (!r.isEmpty) group(r.toTraversable)

    builder.result

  }

}


implicit def groupIdentical[A, R](r: R)(implicit fr: ITL[R]):

  GroupIdenticalImpl[fr.A, R] =

  new GroupIdenticalImpl(fr conversion r)

第一次尝试包括将Repr转换为GenTraversableLike。


import language.implicitConversions

import 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]) { 

  def filterMap[B, That](f: A => Option[B])(implicit cbf : CanBuildFrom[Repr, B, That]): That = 

    r.flatMap(f(_).toSeq)


implicit def filterMap[A, Repr](r: Repr)(implicit fr: ITL[Repr]): FilterMapImpl[fr.A, Repr] = 

  new FilterMapImpl(fr conversion r)


class GroupIdenticalImpl[A, R](val r: GT[A,R])(implicit fr: ITL[R]) { 

  def groupIdentical[That](implicit cbf: CBF[R, R, That]): That = { 

    val builder = cbf(r.repr)

    def group(r0: R) { 

      val r = fr conversion r0

      val first = r.head

      val (same, other) = r.span(_ == first)

      builder += same

      val rest = fr conversion other

      if (!rest.isEmpty) group(rest.repr)

    } 

    if (!r.isEmpty) group(r.repr)

    builder.result

  } 


implicit def groupIdentical[A, R](r: R)(implicit fr: ITL[R]):

  GroupIdenticalImpl[fr.A, R] = 

  new GroupIdenticalImpl(fr conversion r)


查看完整回答
反对 回复 2019-08-30
  • 3 回答
  • 0 关注
  • 421 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信