3

提取我保存在案例类中的值的最佳和/或最简单的方法是什么?

以下面的代码为例:

abstract class Something
case class Foo(input: Int) extends Something
case class Bar(input: Double) extends Something

def someMethod(a: Something, b: Something) {
    // code that extracts values goes here
}

someMethod(Foo(10), Foo(20))
someMethod(Bar(2.1), Bar(21.2))

a然后,b当我像在其定义下那样调用该方法时,我将如何着手获取整数或双精度数本身?

请注意,这两个参数都在同一个方程中使用

4

5 回答 5

10

如果类构造函数参数是 val,那么只需调用:

a.input
b.input

您还可以借助以下unapply方法使用提取器:

val Foo(val1) = a
val Bar(val2) = b

然后使用val1val2

更新

然后你应该对你的值使用模式匹配:

value match {
  case Foo(val1) => val1
  case Bar(val1) => val1
}

它就像在你的类中val Foo(val1) = a使用生成的unapply方法(提取器)一样工作,它也是一个表达式,所以你将结果分配给变量

如果您有多个参数,只需根据参数的数量更改 PatMat 构造,在您的情况下:

someMethod(a: Something, b: Something) = (a, b) match {
  case (Foo(v1), Foo(v2)) => (v1, v2) // or any other logic with values
  case (Foo(v1), Bar(v2)) => // logic for this case
  ... // logic for other cases
}

参数越多,您应该提供的案例越多,但如果您不需要它们,则为空白案例

someMethod(a: Something, b: Something) = (a, b) match {
  case (Foo(v1), Foo(v2)) => (v1, v2) // or any other logic with values
  case _ => 
}

在这种情况下,所有其他情况都将被忽略,不是最佳选择,导致结果类型不正确。你也可以黑色值

someMethod(a: Something, b: Something) = (a, b) match {
  case (Foo(v1), _) => v1 // in such case you can work only with v1
  ... // logic for other cases
} 
于 2013-05-23T09:57:49.613 回答
4

模式匹配的替代方法可以像这样重新定义您的类:

trait Something[T]{
  def input:T
}
case class Foo(input: Int) extends Something[Int]
case class Bar(input: Double) extends Something[Double]

然后,任何实例Something都会暴露该input属性。唯一潜在的缺点是当你访问它时它将是一个泛型类型。

于 2013-05-23T10:19:53.410 回答
2

替代方法

除了在您的方法中直接解决模式匹配之外,我将尝试展示一种更复杂、更通用和更实用的方法来处理这种情况。仍然模式匹配是最直接最简单的答案!


如果您可以在您的界面中明确地“证明”input访问器,您可以概括您如何使用Something该类。

在代码中,这转化为

trait Something[T] {
  def input: T
}

case class Foo(input: Int) extends Something[Int]
case class Bar(input: Double) extends Something[Double]

从这里您可以定义如何将您喜欢的任何功能“提升”到适用于Somethings 的功能

假设您有接受两个输入(例如IntsDoubles)的方法,并且您想在您的一个案例类中对这些输入进行操作(即Foo, Bar

//this function lift your specific input method to one that takes Somethings
def liftSomething2[T, R](f: (T, T) => R): (Something[T], Something[T]) => R =
  (a, b) => f(a.input, b.input)

让我们稍微检查一下:它接受一个
(T, T) => R由 2 个类型参数T和一个结果组成的函数R

并将其转换为
(Something[T], Something[T]) => RSomethings 作为参数的 a。

例子

//lifts a function that sums ints
scala> val sumInts = liftSomething2[Int, Int](_ + _)
sumInts: (Something[Int], Something[Int]) => Int = <function2>

//lifts a function that multiplies ints
scala> val multInts = liftSomething2[Int, Int](_ * _)
multInts: (Something[Int], Something[Int]) => Int = <function2>

//lifts a function that divides doubles
scala> val divDbl = liftSomething2[Double, Double](_ / _)
divDbl: (Something[Double], Something[Double]) => Double = <function2>

//Now some test
scala> sumInts(Foo(1), Foo(2))
res2: Int = 3

scala> multInts(Foo(4), Foo(-3))
res3: Int = -12

scala> divDbl(Bar(20.0), Bar(3.0))
res4: Double = 6.666666666666667

//You can even complicate things a bit
scala> val stringApp = liftSomething2[Int, String](_.toString + _)
stringApp: (Something[Int], Something[Int]) => String = <function2>

scala> stringApp(Foo(1), Foo(2))
res5: String = 12

上述所有示例提升类型的函数,(T,T) => R但可以为您需要的所有和任何参数进行“提升”

//This takes three args of different types and returns another type
// the logic doesn't change
def liftSomething3[A,B,C,R](f: (A,B,C) => R): (Something[A], Something[B], Something[C]) => R =
  (a,b,c) => f(a.input, b.input, c.input)

//sums to ints and divides by a double
scala> val sumDiv = liftSomething3[Int,Int,Double,Double]((i,j,d) => (i + j) / d)
sumDiv: (Something[Int], Something[Int], Something[Double]) => Double = <function3>

scala> sumDiv(Foo(5), Foo(30), Bar(4.2))
res7: Double = 8.333333333333332

更多的...

到目前为止,我们所看到的都应该与Applicative Functors和Comonads等范畴论概念有些相关,但我不是专家,所以如果你觉得这种抽象有用且有趣,我鼓励你自己搜索。

于 2013-05-23T13:06:12.310 回答
1

在您的示例中, theab都具有特定的类型:FooBar。这就是为什么您可以像这样简单地访问他们的字段:

scala> a.input
res4: Int = 10

scala> b.input
res5: Double = 25.1

但是,如果您的值具有 type Something,那么您需要进行模式匹配:

val input = somethingOfTypeSomething match {
  case Foo(input) => input
  case Bar(input) => input
}
于 2013-05-23T09:57:55.643 回答
0

其他答案已经涵盖了基本场景。有一些有用的变化需要考虑。

构造函数模式

正如已经回答的那样:

value match {
  case Foo(x) => x
  ...
}

深度匹配

构造函数模式还支持深度匹配。例如,在 Foo 内的 Bar 中提取 3 层深的 x:

value match {
    case Foo(y, Bar(x)) => x
    ...
}

变量绑定

如果要提取的值是另一个案例类中的实际案例类,则可以使用变量绑定。例如将整个 Bar(x) 提取到 b 中:

value match {
  case Foo(y, b @ Bar(x)) => b
  ...
}

M. Odersky、Spoon 和 Venners在 Scala 中编程有一个关于案例类和模式匹配的精彩章节,涵盖了许多其他场景。模式匹配是语言中如此丰富的一部分,因此值得投资。

于 2013-05-23T11:41:28.047 回答