簡體   English   中英

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

[英]What's the best way to extract a value from a 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獲取整數或雙精度本身?

請注意,兩個參數都在同一個等式中使用

如果類構造函數參數是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
} 

模式匹配的替代方法可以是重新定義您的類,如下所示:

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

然后, Something任何實例都將公開input屬性。 唯一可能的缺點是,當您訪問它時它將是通用類型。

在您的示例中, 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
}

替代方法

除了 在您的方法中 直接解決模式匹配問題之外,我將嘗試在這種情況下展示一種更復雜,通用和功能性的方法。 靜止模式匹配是最直接, 簡單的答案!


如果您可以在界面中明確“認證” 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 ),並且您希望在一個案例類(即FooBar )中對這些輸入進行操作

//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]) => RSomething s作為參數。

例子

//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 FunctorsComonads ,但我不是專家所以如果你覺得這種抽象是有用和有趣的,我鼓勵你自己搜索。

其他答案涵蓋了基本情景。 有一些有用的變化需要考慮。

構造函數模式

正如已經回答的那樣:

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編程有很多關於案例類和模式匹配的章節,涵蓋了許多其他場景。 模式匹配是語言中非常豐富的一部分,值得投資。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM