Scala'da nasıl silinir? Ya da neden koleksiyonlarımın type parametresini alamıyorum?


370

Scala'da yaşamın üzücü bir gerçeği, bir Liste [Int] başlatırsanız, örneğinizin bir Liste olduğunu doğrulayabilir ve bunun herhangi bir öğesinin Int olduğunu doğrulayabilirsiniz, ancak bir Liste olmadığını doğrulayabilirsiniz [ Int], kolayca doğrulanabilir:

scala> List(1,2,3) match {
     | case l : List[String] => println("A list of strings?!")
     | case _ => println("Ok")
     | }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!

-Konuklanmış seçenek suçu düzgün bir şekilde silme işlemine sokar:

scala>  List(1,2,3) match {
     |  case l : List[String] => println("A list of strings?!")
     |  case _ => println("Ok")
     |  }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
        case l : List[String] => println("A list of strings?!")
                 ^
A list of strings?!

Bu neden ve nasıl dolaşırım?


Scala 2.8 Beta 1 RC4, silme işleminin çalışma biçiminde bazı değişiklikler yaptı. Bunun doğrudan sorunuzu etkileyip etkilemediğinden emin değilim.
Scott Morrison

1
Bu sadece ne tür silme var için değişti. Kısaltması " Teklif:" A ile Nesne "nin silinmesi" Nesne "yerine" A " olarak özetlenebilir . Gerçek şartname oldukça karmaşıktır. Her halükarda karışımlarla ilgilidir ve bu soru jeneriklerle ilgilidir.
Daniel C. Sobral

Açıklama için teşekkürler - Ben bir scala yeni gelenim. Şu anda Scala'ya atlamak için kötü bir zaman gibi hissediyorum. Daha önce, 2.8'deki değişiklikleri iyi bir temelden öğrenebilirdim, daha sonra farkı asla bilmek zorunda kalmazdım!
Scott Morrison

1
İşte TypeTags ile ilgili biraz ilgili bir soru .
pvorb

2
Çalışırken scala 2.10.2, bunun yerine bu uyarıyı gördüm: <console>:9: warning: fruitless type test: a value of type List[Int] cannot also be a List[String] (but still might match its erasure) case list: List[String] => println("a list of strings?") ^Sorunuzu ve yanıtınızı çok yardımcı buluyorum, ancak bu güncellenmiş uyarının okuyucular için yararlı olup olmadığından emin değilim.
Kevin Meredith

Yanıtlar:


243

Bu cevapta ManifestScala 2.10'dan itibaren kullanımdan kaldırılmış olan -API kullanılmıştır. Daha güncel çözümler için lütfen aşağıdaki yanıtlara bakın.

Scala, Type Erasure ile tanımlandı çünkü Java'nın aksine Java Sanal Makinesi (JVM) jenerikler alamadı. Bu, çalışma zamanında tür sınıflarının değil, yalnızca sınıfın var olduğu anlamına gelir. Örnekte, JVM, a'nın işlediğini biliyor scala.collection.immutable.List, ancak bu listeninInt .

Neyse ki, Scala'da bunun üstesinden gelmenizi sağlayan bir özellik var. Bu Manifest . Bir Manifest, örnekleri türleri temsil eden nesnelerdir. Bu örnekler nesne olduğundan, bunları etrafa aktarabilir, depolayabilir ve genellikle bunlara yöntem çağırabilirsiniz. Örtük parametrelerin desteği ile çok güçlü bir araç haline gelir. Örneğin aşağıdaki örneği ele alalım:

object Registry {
  import scala.reflect.Manifest

  private var map= Map.empty[Any,(Manifest[_], Any)] 

  def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
    map = map.updated(name, m -> item)
  }

  def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
    map get key flatMap {
      case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
    }     
  }
}

scala> Registry.register("a", List(1,2,3))

scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))

scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None

Bir öğeyi saklarken, onun bir "Bildirisi" de saklarız. Manifest, örnekleri Scala tiplerini temsil eden bir sınıftır. Bu nesneler, tam, parametrelenmiş türü test etmemizi sağlayan JVM'den daha fazla bilgiye sahiptir.

Ancak Manifesta'nın hala gelişen bir özellik olduğuna dikkat edin . Sınırlamalarının bir örneği olarak, şu anda varyans hakkında hiçbir şey bilmiyor ve her şeyin birlikte varyant olduğunu varsayıyor. Halen geliştirme aşamasında olan Scala yansıma kütüphanesi bittikten sonra daha istikrarlı ve sağlam hale gelmesini bekliyorum.


3
getYöntem olarak tanımlanabilir for ((om, v) <- _map get key if om <:< m) yield v.asInstanceOf[T].
Aaron Novstrup

4
@Aaron Çok iyi bir öneri, ama korkuyorum ki Scala için nispeten yeni insanlar için kodu gizleyebilir. Ben bu soruyu / cevabı koymadan bir süre önce olan bu kodu yazarken Scala ile pek tecrübem yoktu.
Daniel C.Sobral

6
@KimStebel TypeTagAslında desen eşleştirmede otomatik olarak kullanıldığını biliyor musunuz ? Harika, ha?
Daniel C.Sobral

1
Güzel! Belki de cevaba eklemelisin.
Kim Stebel

1
Hemen yukarıdaki kendi soruma cevap vermek için: Evet, derleyici Manifestparametrenin kendisini üretir , bkz: stackoverflow.com/a/11495793/694469 "[manifest / type-tag] örneği [...] derleyici tarafından örtük olarak oluşturuluyor "
KajMagnus

96

Bunu TypeTags kullanarak yapabilirsiniz (Daniel'in belirttiği gibi, ancak açıkça açıkça söyleyeceğim):

import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
  case intlist: List[Int @unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}

Bunu ClassTags kullanarak da yapabilirsiniz (bu sizi scala-yansıtmaya bağımlı olmaktan kurtarır):

import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
  case intlist: List[Int @unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}

ClassTags, type parametresinin Akendisinin genel bir tür olmasını beklemediğiniz sürece kullanılabilir .

Ne yazık ki biraz ayrıntılı ve derleyici uyarısını bastırmak için @eckecked ek açıklamaya ihtiyacınız var. TypeTag gelecekte derleyici tarafından otomatik olarak kalıp eşleşmesine dahil edilebilir: https://issues.scala-lang.org/browse/SI-6517


2
[List String @unchecked]Bu kalıp eşleşmesine hiçbir şey eklemediği için gereksiz olanları kaldırmaya ne dersiniz (Sadece kullanmak case strlist if typeOf[A] =:= typeOf[String] =>bunu yapacaktır veya case _ if typeOf[A] =:= typeOf[String] =>gövdesinde bağlı değişken gerekli olmasa bile case).
Nader Ghanbari

1
Sanırım verilen örnek için işe yarayacaktır, ancak bence çoğu gerçek kullanım, öğelerin türüne sahip olmaktan fayda sağlayacaktır.
tksfz

Yukarıdaki örneklerde, koruma koşulunun önündeki denetlenmeyen kısım döküm yapmıyor mu? Bir dizeye atılamayan ilk nesnenin maçlarında ilerlerken bir sınıf istisnası alamaz mıydınız?
Toby

Hm hayır Ben korumayı uygulamadan önce hiçbir döküm olduğuna inanıyorum - denetlenmeyen bit sağdaki kod =>yürütülene kadar bir tür bir op-up . (Ve rhs üzerindeki kod yürütüldüğünde, korumalar elemanların türü üzerinde statik bir garanti sağlar. Orada bir döküm olabilir, ancak güvenlidir.)
tksfz

Bu çözüm önemli çalışma zamanı yükü üretiyor mu?
stanislav.chetvertkov

65

Sen kullanabilirsiniz Typeablegelen tip sınıfını shapeless peşinde olduğunuz sonuç almak için,

Örnek REPL oturumu,

scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._

scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)

scala> l1.cast[List[String]]
res0: Option[List[String]] = None

scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))

Mevcut castkapsam içi Typeabledurumlar göz önüne alındığında, işlem mümkün olduğunca hassas bir şekilde silinecektir .


14
"Döküm" işleminin tüm koleksiyon ve alt toplamalarını yinelemeli olarak gerçekleştireceği ve tüm ilgili değerin doğru tipte olup olmadığını kontrol edeceği unutulmamalıdır. (Yani l1.cast[List[String]]kabaca yapar for (x<-l1) assert(x.isInstanceOf[String]) Büyük veri yapıları için veya yayınlar çok sık gerçekleşiyorsa, bu kabul edilemez bir ek yük olabilir.
Dominique Unruh

16

Sınırlı kullanım durumlarında yeterli olacak nispeten basit bir çözüm buldum, aslında bir eşleşme ifadesinde kullanılabilecek sarıcı sınıflarında tür silme probleminden muzdarip olacak parametrelenmiş türleri sarma.

case class StringListHolder(list:List[String])

StringListHolder(List("str1","str2")) match {
    case holder: StringListHolder => holder.list foreach println
}

Bu beklenen çıktıya sahiptir ve vaka sınıfımızın içeriğini istenen Dize Listeleri türüyle sınırlar.

Daha fazla ayrıntı burada: http://www.scalafied.com/?p=60


14

Scala'da tip silme sorununun üstesinden gelmenin bir yolu var. Gelen eşleştirme 1'de Üstesinden Tip Erasure'in ve Matching Üstesinden Tip Erasure'in 2 (Varyans) koduna bazı yardımcıları eşleştirme için, Varyans dahil türlerini sarmak için nasıl bir açıklaması vardır.


Bu, tür silinmesinin üstesinden gelmez. Örneğinde val x: Any = List (1,2,3); x match {case IntList (l) => println (s "Match $ {l (1)}"); case _ => println (s "
Eşleşme

scala 2.10 makrolarına bir göz atabilirsiniz.
Alex

11

Aksi takdirde harika dilin bu sınırlaması için biraz daha iyi bir çözüm buldum.

Scala'da türlerde silme sorunu dizilerde görülmez. Bunu bir örnekle göstermenin daha kolay olduğunu düşünüyorum.

Diyelim ki bir listemiz var (Int, String), sonra aşağıdakiler bir tür silme uyarısı veriyor

x match {
  case l:List[(Int, String)] => 
  ...
}

Bu sorunu çözmek için önce bir vaka sınıfı oluşturun:

case class IntString(i:Int, s:String)

daha sonra desen eşleşmesinde şöyle bir şey yapın:

x match {
  case a:Array[IntString] => 
  ...
}

ki bu mükemmel çalışıyor.

Bu, listeler yerine dizilerle çalışmak için kodunuzda küçük değişiklikler gerektirir, ancak büyük bir sorun olmamalıdır.

Kullanmanın case a:Array[(Int, String)]yine de bir tür silme uyarısı vereceğini unutmayın , bu nedenle yeni bir kapsayıcı sınıfı kullanmak gerekir (bu örnekte IntString).


10
"aksi halde müthiş dilin sınırlandırılması" Scala'nın ve JVM'nin daha az bir sınırlamasıdır. Belki de Scala, JVM'de olduğu gibi tür bilgilerini içerecek şekilde tasarlanmış olabilir, ancak böyle bir tasarımın Java ile birlikte çalışabilirliği koruyacağını düşünmüyorum (yani, tasarlandığı gibi, Scala'yı Java'dan çağırabilirsiniz.)
Carl G

1
Bir takip olarak, Scala için .NET / CLR'de birleşik jenerikler için destek devam eden bir olasılıktır.
Carl G

6

Java gerçek eleman türünü bilmediğinden, sadece kullanmak için en kullanışlı buldum List[_]. Sonra uyarı kaybolur ve kod gerçeği tanımlar - bilinmeyen bir şeyin listesidir.


4

Bunun uygun bir çözüm olup olmadığını merak ediyorum:

scala> List(1,2,3) match {
     |    case List(_: String, _*) => println("A list of strings?!")
     |    case _ => println("Ok")
     | }

"Boş liste" durumu ile eşleşmiyor, ancak uyarı değil derleme hatası veriyor!

error: type mismatch;
found:     String
requirerd: Int

Bu da işe yarıyor gibi görünüyor ....

scala> List(1,2,3) match {
     |    case List(_: Int, _*) => println("A list of ints")
     |    case _ => println("Ok")
     | }

Biraz daha iyi değil mi yoksa buradaki noktayı kaçırıyor muyum?


3
List [Any] türüne sahip Liste (1, "a", "b") ile çalışmaz
sullivan-

1
Her ne kadar sullivan'ın noktası doğru ve kalıtımla ilgili sorunlar olsa da, bunu hala yararlı buldum.
Seth


0

Sorunu genelleştiren bir cevap eklemek istedim: Nasıl bir çalışma zamanında benim liste türü bir dize temsili almak

import scala.reflect.runtime.universe._

def whatListAmI[A : TypeTag](list : List[A]) = {
    if (typeTag[A] == typeTag[java.lang.String]) // note that typeTag[String] does not match due to type alias being a different type
        println("its a String")
    else if (typeTag[A] == typeTag[Int])
        println("its a Int")

    s"A List of ${typeTag[A].tpe.toString}"
}

val listInt = List(1,2,3)
val listString = List("a", "b", "c")

println(whatListAmI(listInt))
println(whatListAmI(listString))

-18

Desen eşleştirme korumasını kullanma

    list match  {
        case x:List if x.isInstanceOf(List[String]) => do sth
        case x:List if x.isInstanceOf(List[Int]) => do sth else
     }

4
Bunun çalışmamasının nedeni isInstanceOf, JVM'nin kullanabileceği tip bilgilerine dayalı bir çalışma zamanı kontrolü yapmasıdır. Ve bu çalışma zamanı bilgileri, tür argümanını içermeyecektir List(tür silme nedeniyle).
Dominique Unruh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.