【问题标题】:Collection extension method which preserved both element type A and representation type Repr保留元素类型 A 和表示类型Repr 的集合扩展方法
【发布时间】:2013-12-06 15:31:52
【问题描述】:

作为我previous question 的后续行动,我能否设计一个 隐式类来处理两种类型的SeqLike 扩展:

import collection.SeqLike
import collection.generic.CanBuildFrom

implicit class Test[A, Repr](val sq: SeqLike[A, Repr]) extends AnyVal {
  // no constraints on Repr
  def foo[B](f: A => B)(implicit ord: Ordering[B]): Repr = sq.sortBy(f)

  // constraint that actually sq is Repr
  def bar[B, To](fun: (A, A) => B)(implicit cbf: CanBuildFrom[Repr, B, To]): To = {
    val b = cbf(sq)  // NO!
    // ...
    b.result
  }
}

bar 方法无法编译,因为我们缺少typeOf(sq) == Repr 的约束。正如其他人指出的那样,如果我将构造函数更改为 sq: Repr,我们将失去与类型 A 的连接。

现在,Repr 已断开连接。例如:

// in Test:
def isSortedBy[B](fun: A => B)(implicit ord: Ordering[B]): Boolean =
  sq.sliding(2, 1).forall {
    case a +: b +: _  => ord.lteq(fun(a), fun(b))
    case _            => true  // happens when it size == 1
  }

[error] Test.scala: inferred type arguments [T,Equals] do not conform to method 
                    unapply's type parameter bounds 
                    [T,Coll <: scala.collection.SeqLike[T,Coll]]
[error]         case a +: b +: _  => ord.lteq(fun(a), fun(b))
[error]                ^

【问题讨论】:

  • 我认为self.sliding 应该是sq.sliding 对吧?
  • @stew - 是的,抱歉,这是复制+粘贴的错误

标签: scala collections implicit-conversion


【解决方案1】:

您可以像在其他问题中一样,使用repr 并将sq 的类型更改为SeqLike 而不是Repr。 但与我之前的解决方案不同,您可以将上限类型绑定到Repr (Repr&lt;:SeqLike[A,Repr]) 以解决剩余的打字问题。 举例:

implicit class Test[A, Repr<:SeqLike[A,Repr]](val sq: SeqLike[A, Repr]) extends AnyVal {
  // no constraints on Repr
  def foo[B](f: A => B)(implicit ord: Ordering[B]): Repr = sq.sortBy(f)

  // constraint that actually sq is Repr
  def bar[B, To](fun: (A, A) => B)(implicit cbf: CanBuildFrom[Repr, B, To]): To = {
    val b = cbf(sq.repr)  // NO!
    var x = sq.head
    sq.tail.foreach { y =>
      b+=fun(x,y)  
      x = y
    }
    b.result
  }
  def isSortedBy[B](fun: A => B)(implicit ord: Ordering[B]): Boolean =
    sq.sliding(2, 1).forall {
      case a +: b +: _  => ord.lteq(fun(a), fun(b))
      case _            => true  // happens when it size == 1
    }
}

【讨论】:

    【解决方案2】:

    所以如果我正确理解了这个问题,

    • 如果您将类参数sq 设置为Repr,则您将丢失A 类型(不再可以从中推断),

    • 但是如果你将它设置为SeqLike[A, Repr],那么你就失去了它也是Repr的事实,所以cbf(sq)不再编译了。

    那么两者都做呢:

    implicit class Test[A, Repr](val sq: Repr with SeqLike[A, Repr]) extends AnyVal {
       ...
    }
    

    这个版本编译得很好,我可以在一个简单的List(1,2,3) 上调用foobar

    编辑:如果您想将Repr 类型的值作为集合操作,而不是sq,您还需要Régis 回答中的类型绑定Repr &lt;: SeqLike[A,Repr]

    sq: Repr with SeqLike[A, Repr] 给你的唯一好处是你可以在任何地方使用sq 而不是sq.repr,因为它已经正确输入了。

    【讨论】:

      猜你喜欢
      • 2013-08-25
      • 2011-07-22
      • 1970-01-01
      • 2014-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多