2009-08-24 23 views
299

Scala, Java'nın sahip olduğu güvenli enum s ürününe sahip değildir. İlgili sabitlerin bir kümesi verildiğinde, Scala'da bu sabitleri temsil etmenin en iyi yolu ne olurdu?Tip güvenli enum türleri nasıl modellenir?

+2

Neden sadece java enum kullanmıyorsunuz? Bu hala sade java kullanmayı tercih ettiğim birkaç şeyden biri. – Max

+1

Scala Numaralandırma ve alternatifleri hakkında küçük bir genel bakış açmışım, yararlı bulabileceğiniz yerler: pedrorijo.com/blog/scala-enums/ – pedrorijo91

cevap

179

http://www.scala-lang.org/docu/files/api/scala/Enumeration.html

Örnek (siz de case object kullanabilir pratikte sınırlı kul olduğunu

object Main extends App { 

    object WeekDay extends Enumeration { 
     type WeekDay = Value 
     val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value 
    } 
    import WeekDay._ 

    def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) 

    WeekDay.values filter isWorkingDay foreach println 
    } 
+2

Cidden, Uygulama kullanılmamalıdır. Bu düzeltildi; Schildmeijer'den bahsettiği sorunların olmadığı yeni bir sınıf olan App tanıtıldı. Yani "foo nesnesini App {...}" 'e genişletir. Ve komut satırı değişkeni üzerinden komut satırı argümanlarına hemen erişebilirsiniz. – AmigoNico

+0

scala.Enumeration (yukarıdaki "object WeekDay" kod örneğinizde kullandığınız), ayrıntılı desen eşlemesi sunmuyor. Şu anda Scala'da kullanılmakta olan tüm farklı numaralandırma desenlerini araştırdım ve bu StackOverflow yanıtında bunlara genel bir bakış ve genel bakış sağladım (hem scala'nın en iyilerini sunan yeni bir model de dahil olmak üzere.İzleme ve hem de "kapalı nesne + durum nesnesi" deseni: http: //stackoverflow.com/a/25923651/501113 – chaotic3quilibrium

372

Ben örnek yukarıda skaffman tarafından Scala belgelerine üzerinden kopyalanan söylemeliyim kullanmak s). Bir Java Enum benzeyen en yakın şey almak için

(yani mantıklı toString ve valueOf yöntemlerle - belki bir veritabanına enum değerleri devam ettirirler) bunu biraz değiştirmeniz gerekir. Eğer skaffman 'ın kodu kullanılmış olsaydı:

WeekDay.valueOf("Sun") //returns None 
WeekDay.Tue.toString //returns Weekday(2) 

Oysa şu bildiriyi kullanarak: of

WeekDay.valueOf("Sun") //returns Some(Sun) 
WeekDay.Tue.toString //returns Tue 
+6

Btw değeriOf yöntemi şu anda ölü :-( – greenoldman

+35

@macias 'valueOf' yerine 'withName', bir Seçenek döndürmez ve orada bir NSE atar – Bluu

+6

@Bluu Kendinize değer katabilirsiniz: def değeriOf (name: String) = HaftaDay.values.find (_. toString == name) bir seçeneğe sahip olmak – centr

52

Biraz daha az ayrıntılı yol: Sen daha mantıklı sonuçlar elde

object WeekDay extends Enumeration { 
    type WeekDay = Value 
    val Mon = Value("Mon") 
    val Tue = Value("Tue") 
    ... etc 
} 

adlandırılmış numaralar bildiriliyor:

object WeekDay extends Enumeration("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") { 
    type WeekDay = Value 
    val Sun, Mon, Tue, Wed, Thu, Fri, Sat = Value 
} 

WeekDay.valueOf("Wed") // returns Some(Wed) 
WeekDay.Fri.toString // returns Fri 

Tabii ki burada sorun, isim ve val'lerin aynı satırda bildirilmesi durumunda daha kolay olan senkronizasyonda isimlerin ve valslerin sıralamasını tutmanız gerekecek olmasıdır.

+10

Bu, ilk bakışta daha temiz görünüyor, ancak denetleyicinin her iki listenin de eşitlenmesini senkronize etmesini gerektiren dezavantaja sahiptir. Haftanın günleri için örnek, olası görünmüyor. Ancak genel olarak, yeni bir değer eklenebilir veya bir silinebilir ve iki liste senkronize edilemez, bu durumda, ince hatalar ortaya çıkabilir. –

+1

Önceki yorum için, risk iki farklı listenin sessizce senkronizasyondan çıkabileceğidir.Mevcut küçük örneğiniz için bir sorun olmasa da, daha fazla üye varsa (düzinelerce yüzlerce gibi), iki listenin sessizce sessizce çıkması olasılığı büyük ölçüde daha yüksektir. Ayrıca scala.Enumeration, Scala'nın derleme zamanı uyarlama/hatalarla eşleşen kapsamlı modelinden yararlanamaz. İki listenin senkronizasyonda kalmasını sağlamak için bir çalışma zamanı denetimi gerçekleştiren bir çözüm içeren bir StackOverflow yanıtı oluşturdum: http://stackoverflow.com/a/25923651/501113 – chaotic3quilibrium

96

Yapmanın birçok yolu vardır.

1) Sembolleri kullanın. Bir sembolün beklenmediği sembollerin kabul edilmemesinden başka, size herhangi bir tip güvenlik vermez. Sadece burada tamlık için bahsediyorum.

object Dimension extends Enumeration("Row", "Column") { 
    type Dimension = Value 
    val Row, Column = Value 
} 

Bu, bu gibi kullanılabilir: Eğer seri hale getirilmeye veya görüntülemek gerekiyorsa,

object Dimension extends Enumeration { 
    type Dimension = Value 
    val Row, Column = Value 
} 

ya:

def update(what: Symbol, where: Int, newValue: Array[Int]): MatrixInt = 
    what match { 
    case 'row => replaceRow(where, newValue) 
    case 'col | 'column => replaceCol(where, newValue) 
    case _ => throw new IllegalArgumentException 
    } 

// At REPL: 
scala> val a = unitMatrixInt(3) 
a: teste7.MatrixInt = 
/1 0 0 \ 
| 0 1 0 | 
\ 0 0 1/

scala> a('row, 1) = a.row(0) 
res41: teste7.MatrixInt = 
/1 0 0 \ 
| 1 0 0 | 
\ 0 0 1/

scala> a('column, 2) = a.row(0) 
res42: teste7.MatrixInt = 
/1 0 1 \ 
| 0 1 0 | 
\ 0 0 0/

2) sınıfını Enumeration kullanarak: Burada kullanım örneği :

def update(what: Dimension, where: Int, newValue: Array[Int]): MatrixInt = 
    what match { 
    case Row => replaceRow(where, newValue) 
    case Column => replaceCol(where, newValue) 
    } 

// At REPL: 
scala> a(Row, 2) = a.row(1) 
<console>:13: error: not found: value Row 
     a(Row, 2) = a.row(1) 
     ^

scala> a(Dimension.Row, 2) = a.row(1) 
res1: teste.MatrixInt = 
/1 0 0 \ 
| 0 1 0 | 
\ 0 1 0/

scala> import Dimension._ 
import Dimension._ 

scala> a(Row, 2) = a.row(1) 
res2: teste.MatrixInt = 
/1 0 0 \ 
| 0 1 0 | 
\ 0 1 0/

Unfortu Doğal olarak, tüm maçların hesaba katılmasını sağlamaz. Maçta Row veya Column'u eklemeyi unutursam, Scala derleyicisi beni uyarmazdı. Yani bana tip güvenlik sağlıyor, ancak elde edilebilecek kadar değil.

3) Vaka nesneleri:

MatrixInt.scala:70: warning: match is not exhaustive! 
missing combination   Column 

    what match { 
    ^
one warning found 

Oldukça fazla kullanılan aynı şekilde ve:

sealed abstract class Dimension 
case object Row extends Dimension 
case object Column extends Dimension 

Şimdi, bir match bir dava dışarı bırakırsanız, derleyici beni uyarır

merak edebilirsiniz
scala> val a = unitMatrixInt(3) 
a: teste3.MatrixInt = 
/1 0 0 \ 
| 0 1 0 | 
\ 0 0 1/

scala> a(Row,2) = a.row(0) 
res15: teste3.MatrixInt = 
/1 0 0 \ 
| 0 1 0 | 
\ 1 0 0/

zaman neden şimdiye kadar bize: hatta bir import ihtiyacı yoktur Durum nesneleri yerine bir numaralandırma. Nitekim, vaka nesneleri burada olduğu gibi birçok kez avantajlara sahiptir. Yineleme sınıfı, bir yineleyici, harita, flatMap, filtre, vb döndüren öğeleri (Scala 2.8 üzerinde iterator) gibi birçok toplama yöntemlerine sahiptir.

Bu yanıt aslında blogumdaki this article'dan seçilen bölümlerden seçilmiştir. .

16

Örneğin, yerine numaralandırma mühürlü soyut sınıfını kullanabilirsiniz: çevresindeki tüm seçenekleri kapsamlı araştırmaların ardından

sealed abstract class Constraint(val name: String, val verifier: Int => Boolean) 

case object NotTooBig extends Constraint("NotTooBig", (_ < 1000)) 
case object NonZero extends Constraint("NonZero", (_ != 0)) 
case class NotEquals(x: Int) extends Constraint("NotEquals " + x, (_ != x)) 

object Main { 

    def eval(ctrs: Seq[Constraint])(x: Int): Boolean = 
    (true /: ctrs){ case (accum, ctr) => accum && ctr.verifier(x) } 

    def main(args: Array[String]) { 
    val ctrs = NotTooBig :: NotEquals(5) :: Nil 
    val evaluate = eval(ctrs) _ 

    println(evaluate(3000)) 
    println(evaluate(3)) 
    println(evaluate(5)) 
    } 

} 
+0

Kasa nesneleriyle gizli özellik de bir olasılık. – Ashalynd

+2

"Mühürlü trait + case nesneleri" deseninde StackOverflow yanıtında detaylandırdığım sorunlar var. Bununla birlikte, bu örüntüyle ilgili tüm konuları çözme işlemini çözdüm. Ayrıca, http://stackoverflow.com/a/25923651/501113 – chaotic3quilibrium

2

"numaralandırma" Scala, bu çok daha eksiksiz bir genel yayınlanmıştır başka bir StackOverflow thread etki alanı. JVM sınıf/nesne başlatma siparişi problemini çözdüğüm "mühürlenmiş trait + case object" desenine bir çözüm içerir.

6

sadece enumeratum numaralı ürünü buldu. Oldukça şaşırtıcı ve aynı derecede şaşırtıcı, daha iyi bilinmemektedir!