从scala案例类中提取值的最佳方法是什么?

时间:2013-05-23 09:53:24

标签: scala case-class

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

以下面的代码为例:

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))

当我按照我的定义调用方法时,如何从ab获取整数或双精度本身?

请注意,两个参数都在同一个等式中使用

5 个答案:

答案 0 :(得分:8)

如果类构造函数参数是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
} 

答案 1 :(得分: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属性。唯一可能的缺点是,当您访问它时,它将是通用类型。

答案 2 :(得分:1)

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

scala> a.input
res4: Int = 10

scala> b.input
res5: Double = 25.1

但是,如果您的值的类型为Something,那么您需要进行模式匹配:

val input = somethingOfTypeSomething match {
  case Foo(input) => input
  case Bar(input) => input
}

答案 3 :(得分:1)

替代方法

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


如果您可以在界面input访问者中明确“认证”,则可以概括您使用Something课程的方式。

在代码中,这转换为

trait Something[T] {
  def input: T
}

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

从这里你可以定义如何将你喜欢的任何功能“提升”到一个适用于Something s

的功能

假设您有两个输入的方法(例如IntsDoubles),并且您希望在一个案例类中对此类输入进行操作(即Foo,{{ 1}})

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的两个参数T和结果R

并将其转换为 (Something[T], Something[T]) => RSomething为参数。

<强>实施例

//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 ,但我不是如果你认为这种抽象是有用和有趣的话,我鼓励你自己寻找。

答案 4 :(得分:0)

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

构造函数模式

正如已经回答的那样:

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

深度匹配

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

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编程在案例类和模式匹配方面有一个很好的章节,涵盖了许多其他场景。模式匹配是语言的一个丰富部分,值得投资。