【问题标题】:Int and Char scala interpolation (generics and upper bounds)Int 和 Char scala 插值(泛型和上限)
【发布时间】:2016-11-11 03:23:23
【问题描述】:

我在 scala 中使用泛型和上限,我面临以下问题:

假设我有以下函数(我只期望 Int 或 Char):

def foo[T](tuple: Tuple2[T, T]) = tuple match {
  case Tuple2(x: Int, y: Int) => (x to y).toArray
  case Tuple2(x: Char, y: Char) => (x to y).toArray
}

我希望有一个更好的压缩形式,例如:

def foo2[T >: Int with Char <: AnyVal](tuple: (T, T))  = (tuple._1 to tuple._2).toArray

但这绝对行不通。

有什么想法吗?

【问题讨论】:

  • 我不太了解T 的下限称为Int with Char?那是想做什么?
  • 抱歉,已编辑。
  • 我认为你想要一个联合类型。见stackoverflow.com/questions/3508077/…
  • @YuvalItzchakov 我更新了 foo2 的定义。
  • @ChrisShain 我试图避免冗长的 2 个案例,而不是确保 T 将是 Int 或 Char (我可以假设它只是这两种类型之一)

标签: scala generics


【解决方案1】:

以下是可用于在您的实现中获取 (tup._1 to tup._2).toArray 语法的相对可怕的签名:

def foo[T 
  <% RangedProxy[T]{ type ResultWithoutStep <: TraversableOnce[T] } 
  : ClassTag
](tup: (T, T))

分解:
T 可以隐式转换为 RangedProxy[T],这是定义 to 方法的地方。 to 的结果类型取决于类型成员 ResultWithoutStep,我们必须确保它是TraversableOnce[T] 的子类,以便我们可以在其上调用.toArray

请注意,上述签名或@MichaelZajac 的回答都不会处理您请求的“仅适用于 Int 和 Char”的功能。确实,满足这一要求的最佳方法是使用 typeclass

sealed trait Fooable[T] extends ((T, T) => Array[T])
object Fooable {
  private def impl[T](f: (T, T) => Array[T]) = 
    new Fooable[T]{ def apply(min: T, max: T): Array[T] = f(min, max) }
  implicit val intFooable = impl[Int]{ (i,j) => (i to j).toArray }
  implicit val charFooable = impl[Char]{ (a,b) => (a to b).toArray }
}
def foo[T: Fooable](tup: (T, T)) = implicitly[Fooable[T]].apply(tup._1, tup._2)

不幸的是,对于这个特殊问题,typeclass 方法有点麻烦,但如果你关注implicit val xFooable,你会发现它有点类似于以下内容:

// unfortunately this doesn't work due to type erasure on the JVM,
// which is why we've resorted to typeclasses as above
def foo(tup: (Int, Int)) = ...
def foo(tup: (Char, Char)) = ...

【讨论】:

  • &gt;: Int with Char 添加到我的会限制它为IntChar,尽管没有什么实际的理由来强制执行它,因为Integral 会阻止客户端代码使用其他会破坏的类型它。
【解决方案2】:

自己简单地调用NumericRange.inclusive 会更容易,而不是尝试理清使用to 语法所需的隐式和类型约束。如果我们需要一个隐式的Integral,我们将知道如何以一的步长构造范围(由Integral 实例提供)。我们还需要一个ClassTag[A] 来创建Array 通常:

import scala.reflect.ClassTag
import scala.collection.immutable.NumericRange

def foo2[A >: Int with Char : ClassTag](tuple: (A, A))(implicit int: Integral[A]) = 
   NumericRange.inclusive(tuple._1, tuple._2, int.one).toArray

scala> foo2(('a', 'z'))
res13: Array[Char] = Array(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)

scala> foo2((1, 10))
res14: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

其他失败(如果您希望他们使用类型约束):

scala> foo2((1L, 10L))
<console>:19: error: could not find implicit value for parameter int: Integral[AnyVal]
       foo2((1L, 10L))
           ^

【讨论】:

  • 只有一件事,最终的数组也应该包含第二个 char/int:foo2((1, 10)) 应该返回 Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
  • @jalv1039 更新为使用NumericRange.inclusive,而不是。
猜你喜欢
  • 1970-01-01
  • 2017-04-17
  • 2014-01-21
  • 1970-01-01
  • 2021-08-25
  • 2023-03-25
  • 2015-10-15
  • 1970-01-01
  • 2011-11-15
相关资源
最近更新 更多