提取我在案例类中保存的值的最佳和/或最简单方法是什么?
以下面的代码为例:
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
获取整数或双精度本身?
请注意,两个参数都在同一个等式中使用
答案 0 :(得分:8)
如果类构造函数参数是val,那么只需调用:
a.input
b.input
您还可以在unapply
方法的帮助下使用提取器:
val Foo(val1) = a
val Bar(val2) = b
然后使用val1
和val2
<强>更新强>
然后你应该在你的价值上使用模式匹配:
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)
在您的示例中,a
和b
分别具有特定类型:Foo
和Bar
。这就是为什么你可以像这样简单地访问他们的字段:
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
假设您有两个输入的方法(例如Ints
或Doubles
),并且您希望在一个案例类中对此类输入进行操作(即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]) => R
以Something
为参数。
<强>实施例强>
//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编程在案例类和模式匹配方面有一个很好的章节,涵盖了许多其他场景。模式匹配是语言的一个丰富部分,值得投资。