2013-05-30 23 views
9

我認爲以下將是最簡潔,最正確的形式收集滿足給定類型集合的元素:ClassTag基於模式匹配失敗的原語

def typeOnly[A](seq: Seq[Any])(implicit tag: reflect.ClassTag[A]): Seq[A] = 
    seq.collect { 
    case tag(t) => t 
    } 

但這隻適用於AnyRef類型,不原語:

typeOnly[String](List(1, 2.3, "foo")) // ok. List(foo) 
typeOnly[Double](List(1, 2.3, "foo")) // fail. List() 

顯然直接形式的作品:

List(1, 2.3, "foo") collect { case d: Double => d } // ok. List(2.3) 

所以必須有一個(簡單的!)方法來修復上述方法。

回答

19

這是在這個例子中的盒裝,對吧?

scala> typeOnly[java.lang.Double](vs) 
res1: Seq[Double] = List(2.3) 

更新:甲骨文是適當的神祕:"boxing is supposed to be invisible, plus or minus"。我不知道這種情況是正值還是負值。

我的感覺是它是一個錯誤,因爲否則它就是一個空洞的遊戲。

更多Delphic猶豫不決:「我不知道給出的例子預計會做什麼。」請注意,它沒有指定,由誰預計

這是一個有用的練習,詢問誰知道盒裝知識,以及盒子是什麼?就好像編譯器是一個魔術師,他正在努力隱藏一個讓撲克牌懸掛在半空中的電線,儘管每個人都知道必須有電線。

scala> def f[A](s: Seq[Any])(implicit t: ClassTag[A]) = s collect { 
    | case v if t.runtimeClass.isPrimitive && 
    | ScalaRunTime.isAnyVal(v) && 
    | v.getClass.getField("TYPE").get(null) == t.runtimeClass => 
    | v.asInstanceOf[A] 
    | case t(x) => x 
    | } 
f: [A](s: Seq[Any])(implicit t: scala.reflect.ClassTag[A])Seq[A] 

scala> f[Double](List(1,'a',(),"hi",2.3,4,3.14,(),'b')) 
res45: Seq[Double] = List(2.3, 3.14) 

ScalaRunTime不支持API - 調用isAnyVal只是在類型匹配;人們也可以只檢查「類型」字段存在或

Try(v.getClass.getField("TYPE").get(null)).map(_ == t.runtimeClass).getOrElse(false) 

但要回到一個不錯的一行,就可以推出自己ClassTag處理特製套管提取。

版本爲2.11。這可能不是流血的邊緣,但它是最近燒灼的邊緣。

object Test extends App { 

    implicit class Printable(val s: Any) extends AnyVal { 
    def print = Console println s.toString 
    } 

    import scala.reflect.{ ClassTag, classTag } 
    import scala.runtime.ScalaRunTime 

    case class Foo(s: String) 

    val vs = List(1,'a',(),"hi",2.3,4,Foo("big"),3.14,Foo("small"),(),null,'b',null) 

    class MyTag[A](val t: ClassTag[A]) extends ClassTag[A] { 
    override def runtimeClass = t.runtimeClass 
    /* 
    override def unapply(x: Any): Option[A] = (
     if (t.runtimeClass.isPrimitive && (ScalaRunTime isAnyVal x) && 
      x.getClass.getField("TYPE").get(null) == t.runtimeClass) 
     Some(x.asInstanceOf[A]) 
     else super.unapply(x) 
    ) 
    */ 
    override def unapply(x: Any): Option[A] = (
     if (t.runtimeClass.isPrimitive) { 
     val ok = x match { 
      case _: java.lang.Integer => runtimeClass == java.lang.Integer.TYPE 
      //case _: java.lang.Double => runtimeClass == java.lang.Double.TYPE 
      case _: java.lang.Double => t == ClassTag.Double // equivalent 
      case _: java.lang.Long  => runtimeClass == java.lang.Long.TYPE 
      case _: java.lang.Character => runtimeClass == java.lang.Character.TYPE 
      case _: java.lang.Float  => runtimeClass == java.lang.Float.TYPE 
      case _: java.lang.Byte  => runtimeClass == java.lang.Byte.TYPE 
      case _: java.lang.Short  => runtimeClass == java.lang.Short.TYPE 
      case _: java.lang.Boolean => runtimeClass == java.lang.Boolean.TYPE 
      case _: Unit    => runtimeClass == java.lang.Void.TYPE 
      case _ => false // super.unapply(x).isDefined 
     } 
     if (ok) Some(x.asInstanceOf[A]) else None 
     } else if (x == null) { // let them collect nulls, for example 
     if (t == ClassTag.Null) Some(null.asInstanceOf[A]) else None 
     } else super.unapply(x) 
    ) 
    } 

    implicit def mytag[A](implicit t: ClassTag[A]): MyTag[A] = new MyTag(t) 

    // the one-liner 
    def g[A](s: Seq[Any])(implicit t: ClassTag[A]) = s collect { case t(x) => x } 

    // this version loses the "null extraction", if that's a legitimate concept 
    //def g[A](s: Seq[Any])(implicit t: ClassTag[A]) = s collect { case x: A => x } 

    g[Double](vs).print 
    g[Int](vs).print 
    g[Unit](vs).print 
    g[String](vs).print 
    g[Foo](vs).print 
    g[Null](vs).print 
} 

對於2.10.x,樣板的一個額外的行,因爲隱含的分辨率是 - 好了,我們不會說這是壞,我們只能說這是行不通的。

// simplified version for 2.10.x 
object Test extends App { 
    implicit class Printable(val s: Any) extends AnyVal { 
    def print = Console println s.toString 
    } 
    case class Foo(s: String) 

    val vs = List(1,'a',(),"hi",2.3,4,Foo("big"),3.14,Foo("small"),(),null,'b',null) 

    import scala.reflect.{ ClassTag, classTag } 
    import scala.runtime.ScalaRunTime 

    // is a ClassTag for implicit use in case x: A 
    class MyTag[A](val t: ClassTag[A]) extends ClassTag[A] { 
    override def runtimeClass = t.runtimeClass 
    override def unapply(x: Any): Option[A] = (
     if (t.runtimeClass.isPrimitive && (ScalaRunTime isAnyVal x) && 
      (x.getClass getField "TYPE" get null) == t.runtimeClass) 
     Some(x.asInstanceOf[A]) 
     else t unapply x 
    ) 
    } 

    // point of the exercise in implicits is the type pattern. 
    // there is no need to neutralize the incoming implicit by shadowing. 
    def g[A](s: Seq[Any])(implicit t: ClassTag[A]) = { 
    implicit val u = new MyTag(t) // preferred as more specific 
    s collect { case x: A => x } 
    } 

    s"Doubles? ${g[Double](vs)}".print 
    s"Ints? ${g[Int](vs)}".print 
    s"Units? ${g[Unit](vs)}".print 
    s"Strings? ${g[String](vs)}".print 
    s"Foos? ${g[Foo](vs)}".print 
} 

推進評論:

@WilfredSpringer有人聽到了。 SI-6967

+1

是的,但我想自動覆蓋這種情況,而不是要求使用盒裝類型作爲參數。 –

+0

不幸的是我不認爲這個問題有一個很好的解決方案。你可以看到一些你將面臨的問題[這裏](http://stackoverflow.com/questions/13233728/scala-2-10-typetag-usage/13234888#13234888)。你可以使用類型標籤和實例鏡像來做你想做的事情,但它肯定不會很優雅。 –

+1

'.getClass.getField(「TYPE」)。get(null)'很聰明;我一直在尋找一種簡單的方法來匹配盒裝類/類型/符號/ ...到其原始的沒有大模式匹配,但沒有運氣到目前爲止。 – gourlaysama