2011-03-10 14 views
14

Bir türün varsayılan değerini döndüren bir Scala işlevi yazmaya çalışıyorum (değer türleri için 0, 0.0, false, '\ 0', vb. Ve referans türleri için null). Ben bu geldi:Scala'da bir tür için varsayılan değeri nasıl edinebilirim?

def defaultValue[U]: U = { 
    class Default[U] { var default: U = _ } 
    new Default[U].default 
} 

ve doğrudan denilen bu iyi çalışıyor iken bu REPL oturumda görüldüğü gibi, bu hatta kendisi jenerik bir işlevi aracılığıyla denilen değer türleri için null döndürür:

Welcome to Scala version 2.8.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_24). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> def defaultValue[U]: U = { class Default[U] {var default: U = _ }; new Default[U].default } 
defaultValue: [U]U 

scala> defaultValue[Boolean] // direct call works 
res0: Boolean = false 

scala> var res: Any = 0 
res: Any = 0 

scala> def setRes[U] = { res = defaultValue[U]; defaultValue[U] } 
setRes: [U]U 

scala> setRes[Boolean] // returns a Boolean, but... 
res1: Boolean = false 

scala> res 
res2: Any = null // ... sets the res variable to null. 
Bu durumda neden

  1. (ve gerçek bir Boole dönmek için yeterli bilgi yoksa neden derleyici/yorumlayıcı şikayet etmiyor);:

    birisi bana açıklayabilir ve

  2. nasıl düzeltebilirim? İşte
+2

ClassManifest'lerle ve silinmeyle eşleştirmenin bir yolunu buldum, ancak hoş görünmüyor. –

+1

Nedeni bahse girmek istemez tip silme değil mi? Her neyse, sadece çok fazla bilinen istenen davranışa sahip olduğunuz için, neden onu kodlamıyorsunuz? Çirkin, evet, ama muhtemelen en verimli. – Raphael

cevap

4

Bunu işlemek için kendi Defaulttype-class'nu oluşturabilirsiniz. İşte kod böyle görünüyor. Boş yerine null yerine bir koleksiyon döndüren scala koleksiyonları için özel işlem ekledim.

import scala.collection.immutable 

class Default[+A](val default: A) 

trait LowerPriorityImplicits { 
    // Stop AnyRefs from clashing with AnyVals 
    implicit def defaultNull[A <: AnyRef]:Default[A] = new Default[A](null.asInstanceOf[A]) 
} 

object Default extends LowerPriorityImplicits { 
    implicit object DefaultDouble extends Default[Double](0.0) 
    implicit object DefaultFloat extends Default[Float](0.0F) 
    implicit object DefaultInt extends Default[Int](0) 
    implicit object DefaultLong extends Default[Long](0L) 
    implicit object DefaultShort extends Default[Short](0) 
    implicit object DefaultByte extends Default[Byte](0) 
    implicit object DefaultChar extends Default[Char]('\u0000') 
    implicit object DefaultBoolean extends Default[Boolean](false) 
    implicit object DefaultUnit extends Default[Unit](()) 

    implicit def defaultSeq[A]: Default[immutable.Seq[A]] = new Default[immutable.Seq[A]](immutable.Seq()) 
    implicit def defaultSet[A]: Default[Set[A]] = new Default[Set[A]](Set()) 
    implicit def defaultMap[A, B]: Default[Map[A, B]] = new Default[Map[A, B]](Map[A, B]()) 
    implicit def defaultOption[A]: Default[Option[A]] = new Default[Option[A]](None) 

    def value[A](implicit value: Default[A]): A = value.default 
} 

Bunlar, repl. Yeni bir örtülü Default[String] oluşturarak String için varsayılan değerin geçersiz kılınabileceğini unutmayın.

scala> Default.value[Int] 
res0: Int = 0 

scala> Default.value[Boolean] 
res1: Boolean = false 

scala> Default.value[String] 
res2: String = null 

scala> Default.value[Set[Int]] 
res3: Set[Int] = Set() 

scala> Default.value[immutable.Seq[Int]] 
res4: scala.collection.immutable.Seq[Int] = List() 

scala> Default.value[String] 
res5: String = null 

scala> Default.value[AnyRef] 
res6: AnyRef = null 

scala> implicit val emptyStringAsDefault:Default[String] = new Default[String]("") 
emptyStringAsDefault: Default[String] = [email protected] 

scala> Default.value[String] 
res7: String = "" 
8

sorununuzun daha yoğun sürümü:

scala> defaultValue[Boolean]: Any 
res0: Any = null 

scala> defaultValue[Boolean]: Boolean 
res1: Boolean = false 

ilk sürümü res tip herhangi

olduğunu rağmen U tipi Boolean olduğu için res = defaultValue[U] çağırdığınızda kural şudur

Bu küçük programı -Xprint:all seçeneğini kullanarak derlerseniz

.210 Sen silme aşamasından önce bu hakkı göreceksiniz

şunlara sahip: Sonra

val any: Any = (Test.this.defaultValue[Boolean](): Any); 
scala.this.Predef.println(any); 
val bool: Boolean = (Test.this.defaultValue[Boolean](): Boolean); 
scala.this.Predef.println(bool) 

silme fazının sonunda:

val any: java.lang.Object = (Test.this.defaultValue(): java.lang.Object); 
scala.this.Predef.println(any); 
val bool: Boolean = (scala.Boolean.unbox(Test.this.defaultValue()): Boolean); 
scala.this.Predef.println(scala.Boolean.box(bool)) 

ne olur olduğunu kaputun defaultValue[Boolean] altında her iki durumda da null değerini döndürür, ancak dönüş türü bir Boolean olduğunda null, false'ye kutusundan çıkarılır. Sen repl içinde doğrulayabilirsiniz:

scala> Boolean.unbox(null) 
res0: Boolean = false 

scala> null.asInstanceOf[Boolean] 
res1: Boolean = false 

Edit: Bir fikrim vardı - tavsiye kulüpler değil. (res = false bana daha kolay görünüyor ..) Kayıt için

scala> def f[@specialized U] = { class X { var x: U = _ }; (new X).x } 
f: [U]U 

scala> var res: Any = _ 
res: Any = null 

scala> def g[@specialized U] = { res = f[U]; f[U] } 
g: [U]U 

scala> g[Boolean] 
res0: Boolean = false 

scala> res 
res1: Any = false 
+0

Teşekkürler, Bu Neden için çok güzel bir açıklama. Şimdi derleyici neden derleme zamanında daha fazla şey söyleyemiyor? Bak, bak adamım, Boole ister misin, ama burda seni boş verebilirim, üzgünüm? –

+0

Diğer kullanım durumları olduğundan emin değilsiniz. Herhangi bir geçiş parametresi olmayan bir T maddesini gerçekleştirmek zordur. Bu yüzden uyarı gelmeyebilir çünkü kimse bunu yapmayı düşünmemişti. – huynhjl

+0

@Özelleştirilmiş hüner güzel, ama tehlikeli çünkü aramayı uzman olmayan bir bağlamdan koparır bozmaz bozuluyor. Bu, izlenmesi zor olan hatalara neden olabilir. –

7

Kullanım durumunuz ne olduğundan emin değil, burada buldum sadece var (henüz) güvenilir bu işi yapmak için. İyileştirmeler açıktır.

def defaultValue[T: ClassManifest]: T = classManifest[T].erasure.toString match { 
    case "void" =>().asInstanceOf[T] 
    case "boolean" => false.asInstanceOf[T] 
    case "byte" => (0: Byte).asInstanceOf[T] 
    case "short" => (0: Short).asInstanceOf[T] 
    case "char" => '\0'.asInstanceOf[T] 
    case "int" => 0.asInstanceOf[T] 
    case "long" => 0L.asInstanceOf[T] 
    case "float" => 0.0F.asInstanceOf[T] 
    case "double" => 0.0.asInstanceOf[T] 
    case _ => null.asInstanceOf[T] 
} 

Ben bir sorun olan hatta T <: NotNull eğer boş olsun farkındayım. Daha sonra, NotNull alt sınıfları için varsların _ ile başlatılması ile ilgili bir sorun vardır.

5

ben "en iyi cevap" zaten orada olduğunu biliyorum, ama ne gerçekten basit hakkında:

def defaultValue[U: ClassManifest]: U = new Array[U](1)(0) 

güçlü olması nedeni ile geçici bir dizi nesnesi yaratmaya biraz pahalı olmasına rağmen, iş gibi görünüyor. Yanlış değer verdiği herhangi bir durumu bilen var mı?

"Daha ucuz" bir alternatif arıyordum ama bu soru bana muhtemelen bir tane olmadığını söylüyor.

2

Scala için varsayılan bir mekanizma oluşturmaya yönelik bir blog yazısı yazdım. here'u bulabilirsiniz.

Eğer Option[_], None için String"" varsayılan olarak istemiyorsanız

sonra cisim Default gelen ilgili implicits kurtulmak vb.

İlgili konular