基于泛型类型的函数,根据类型对数字求和

时间:2016-02-13 19:11:52

标签: scala

假设x和y属于同一类型,可以是BooleanIntDouble。这是我想写的函数:

f(x, y) = 
   - if x == Boolean ==>   !x 
   - if x == Integer or x == Double ==> x+ y 

这样做的一种方法可以是以下方法。我想知道是否有人对此有更好的想法。

def fun[T](x: T, y: T): T { 
   x match { 
       case xP: Boolean => !xP 
       case xP: Double => y match { case yP: Double =>  xP + yP }
       case xP: Int => y match { case yP: Int =>  xP + yP }
   }
}

我对此不满意的原因是xy具有相同的类型。我不应该需要两个match-case;对?

另外两件事:

  • 仅设置[T <: Int, Double, Boolean]是否足以将类型限制为仅三种类型?
  • 输出类型需要再次T

3 个答案:

答案 0 :(得分:11)

这正是类型类旨在解决的问题。在你的情况下,你可以这样写:

trait Add[A] {
  def apply(a: A, b: A): A
}

object Add {
  implicit val booleanAdd: Add[Boolean] = new Add[Boolean] {
    def apply(a: Boolean, b: Boolean): Boolean = !a
  }

  implicit def numericAdd[A: Numeric]: Add[A] = new Add[A] {
    def apply(a: A, b: A): A = implicitly[Numeric[A]].plus(a, b)
  }
}

类型Add[X]的值描述了如何添加两个类型为X的值。您将Add[X]类型的隐式“实例”放在您希望能够对其执行此操作的每个类型X的范围内。在这种情况下,我为Boolean和任何具有scala.math.Numeric实例的类型(标准库提供的类型类)提供了实例。如果您只想要IntDouble的实例,则可以简单地省略numericAdd并编写自己的Add[Int]Add[Double]实例。

你这样写fun

def fun[T: Add](x: T, y: T) = implicitly[Add[T]].apply(x, y)

并像这样使用它:

scala> fun(true, false)
res0: Boolean = false

scala> fun(1, 2)
res1: Int = 3

scala> fun(0.01, 1.01)
res2: Double = 1.02

这具有非常显着的优点,即在运行时不会在尚未定义操作的类型上爆炸。当您通过例如MatchError例外时,不会使您的程序崩溃。两个字符串到fun,你得到一个很好的编译失败:

scala> fun("a", "b")
<console>:14: error: could not find implicit value for evidence parameter of type Add[String]
       fun("a", "b")
          ^

一般来说,“类型案例”匹配(即看起来像case x: X => ...的匹配)在Scala中是一个坏主意,而且几乎总有一个更好的解决方案。通常它会涉及类型类。

答案 1 :(得分:4)

如果您想要一个用于求和数字的通用函数,您可以使用您想要Traceback (most recent call last): File "./test1.py", line 60, in <module> concated = tf.concat(1, [indices, tf.cast(labels,tf.int32)]) File "/Users/username/tensorflow/lib/python2.7/site-packages/tensorflow/python/ops/array_ops.py", line 309, in concat name=name) File "/Users/username/tensorflow/lib/python2.7/site-packages/tensorflow/python/ops/gen_array_ops.py", line 70, in _concat name=name) File "/Users/username/tensorflow/lib/python2.7/site-packages/tensorflow/python/ops/op_def_library.py", line 664, in apply_op op_def=op_def) File "/Users/username/tensorflow/lib/python2.7/site-packages/tensorflow/python/framework/ops.py", line 1836, in create_op set_shapes_for_outputs(ret) File "/Users/username/tensorflow/lib/python2.7/site-packages/tensorflow/python/framework/ops.py", line 1476, in set_shapes_for_outputs shapes = shape_func(op) File "/Users/username/tensorflow/lib/python2.7/site-packages/tensorflow/python/ops/array_ops.py", line 364, in _ConcatShape concat_dim + 1:].merge_with(value_shape[concat_dim + 1:]) File "/Users/username/tensorflow/lib/python2.7/site-packages/tensorflow/python/framework/tensor_shape.py", line 527, in merge_with self.assert_same_rank(other) File "/Users/username/tensorflow/lib/python2.7/site-packages/tensorflow/python/framework/tensor_shape.py", line 570, in assert_same_rank "Shapes %s and %s must have the same rank" % (self, other)) ValueError: Shapes TensorShape([]) and TensorShape([Dimension(10)]) must have the same rank 的数字进行隐式转换的特征Summable[A]。这些转换可以是隐式方法,也可以是隐式对象中的方法,后面的内容如下所示。

Summable

这称为类型类模式。我包含了布尔情况,因为你要求它,但我坚信它在一个对元素求和的函数中没有位置。遵循的一条不错的规则是让每个函数只做一件事和一件事。然后,您可以轻松地将它们组合成更大的功能。反转布尔值在对其参数求和的函数中没有位置。

答案 2 :(得分:-1)

首先,您的示例在语法上是错误的(case中缺少match)。我现在可以想到的一种简单而简短的方法是这样的:

def fun[T <: AnyVal](x: T, y: T) = {
  x match {
    case xP: Boolean => !xP
    case xP: Double => xP + y.asInstanceOf[Double]
    case xP: Int => xP + y.asInstanceOf[Int]
  }
}

fun(1, 2)            // res0: AnyVal = 3
fun(2.5, 2.6)        // res1: AnyVal = 5.1
fun(true, false)     // res2: AnyVal = false