Scala, enum
Java'nın yaptığı gibi güvenli değildir . Bir takım ilgili sabitler göz önüne alındığında, Scala'da bu sabitleri temsil etmenin en iyi yolu ne olurdu?
Scala, enum
Java'nın yaptığı gibi güvenli değildir . Bir takım ilgili sabitler göz önüne alındığında, Scala'da bu sabitleri temsil etmenin en iyi yolu ne olurdu?
Yanıtlar:
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
Örnek kullanım
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
}
Skaffman tarafından Scala belgelerinden kopyalanan örneğin pratikte sınırlı faydası olduğunu söylemeliyim (siz de kullanabilirsiniz ).case object
Bir Java'ya en çok benzeyen bir şey elde etmek için Enum
(yani mantıklı toString
ve valueOf
yöntemlerle - belki de veritabanına enum değerlerini devam ettiriyorsunuz) onu biraz değiştirmeniz gerekir. Eğer skaffman'ın kodunu kullandıysanız:
WeekDay.valueOf("Sun") //returns None
WeekDay.Tue.toString //returns Weekday(2)
Halbuki aşağıdaki beyanı kullanırken:
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon = Value("Mon")
val Tue = Value("Tue")
... etc
}
Daha mantıklı sonuçlar elde edersiniz:
WeekDay.valueOf("Sun") //returns Some(Sun)
WeekDay.Tue.toString //returns Tue
valueOf
değiştirilmesi, withName
bir Seçenek döndürmez ve eşleşme yoksa bir NSE fırlatır. Ne oldu!
Map[Weekday.Weekday, Long]
ve buna bir değer eklemeye çalıştığımda Mon
derleyici geçersiz bir tür hatası atıyor. Hafta içi değer bulundu. Bu neden oluyor?
Yapmanın birçok yolu var.
1) Semboller kullanın. Bununla birlikte, bir sembolün beklendiği yerlerde sembol olmayanları kabul etmemenin yanı sıra, size herhangi bir tür güvenlik sağlamaz. Burada sadece tamlık için söz ediyorum. İşte bir kullanım örneği:
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 kullanma Enumeration
:
object Dimension extends Enumeration {
type Dimension = Value
val Row, Column = Value
}
veya serileştirmeniz veya görüntülemeniz gerekiyorsa:
object Dimension extends Enumeration("Row", "Column") {
type Dimension = Value
val Row, Column = Value
}
Bu şu şekilde kullanılabilir:
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 /
Maalesef, tüm eşleşmelerin dikkate alındığından emin değildir. Eğer maça Satır veya Sütun koymayı unutsaydım, Scala derleyicisi beni uyarmazdı. Bu yüzden bana bir tür güvenlik veriyor , ancak kazanılabilecek kadar değil.
3) Vaka nesneleri:
sealed abstract class Dimension
case object Row extends Dimension
case object Column extends Dimension
Şimdi, a'da bir dava bırakırsam match
derleyici beni uyarır:
MatrixInt.scala:70: warning: match is not exhaustive!
missing combination Column
what match {
^
one warning found
Hemen hemen aynı şekilde kullanılır ve aşağıdakilere bile gerek yoktur import
:
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 /
Öyleyse neden vaka nesneleri yerine bir Numaralandırma kullandığınızı merak edebilirsiniz. Aslında, vaka nesnelerinin burada olduğu gibi birçok kez avantajları vardır. Enumeration sınıfının, yine de, bir Yineleyici, harita, flatMap, filtre vb. Döndüren öğeler (Scala 2.8'de yineleyici) gibi birçok Koleksiyon yöntemi vardır.
Bu cevap aslında blogumdaki bu makaleden seçilen bir bölüm .
Symbol
örneklerin boşlukları veya özel karakterleri olamaz. Çoğu insan Symbol
sınıfla ilk karşılaştığında muhtemelen böyle düşünür, ama aslında yanlıştır. Symbol("foo !% bar -* baz")
derler ve mükemmel çalışır. Başka bir deyişle, herhangi bir dizeyi Symbol
saran mükemmel örnekler oluşturabilirsiniz (bunu "tek koma" sözdizimsel şekerle yapamazsınız). Garanti eden tek şey , herhangi bir sembolün benzersizliğidir, bu da karşılaştırmayı ve eşleştirmeyi marjinal olarak daha hızlı hale getirir. Symbol
String
, örneğin bir Symbol
parametreye argüman olarak geçemezsiniz .
String
temelde bir dize etrafındaki bir sarıcı olan ve her iki yönde de serbestçe dönüştürülebilen başka bir sınıfla değiştirirseniz oldukça tartışmalı bir nokta Symbol
. Sanırım "Bu size herhangi bir tür güvenlik vermeyecek" derken kastettiğin şey, OP'nin açık bir şekilde güvenli tip çözümler istediği göz önüne alındığında çok açık değildi. Yazarken, sadece güvenli bir tür olmadığını bildiğinizden emin değildim, çünkü bunlar hiç numaralandırma değil, aynı zamanda Symbol
geçilen argümanın özel karakterlere sahip olmayacağını bile garanti etmiyor.
'foo
gösterimde yapar engellemez tanımlayıcı olmayan dizeler). Bu, gelecekteki herhangi bir okuyucu için gidermek istediğim yanlış anlamadır.
Adlandırılmış numaralandırmaları bildirmek için biraz daha az ayrıntılı bir yol:
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, ad ve val aynı satırda bildirilirse daha kolay olan adların ve vallerin sırasını senkronize tutmanız gerekecek.
Numaralandırma yerine kapalı soyut bir sınıf kullanabilirsiniz, örneğin:
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))
}
}
sadece enumeratum keşfetti . oldukça şaşırtıcı ve aynı derecede şaşırtıcı daha iyi bilinmemektedir!
Scala'daki "numaralandırmalar" çevresindeki tüm seçenekler hakkında kapsamlı bir araştırma yaptıktan sonra, bu alanın başka bir StackOverflow iş parçacığında çok daha eksiksiz bir genel görünümünü yayınladım . JVM sınıfı / nesne başlatma siparişi sorununu çözdüğüm "mühürlü özellik + vaka nesnesi" desenine bir çözüm içerir.
Scala'da https://github.com/lloydmeta/enumeratum ile çok rahat
Proje, örnekler ve belgelerle gerçekten iyi
Dokümanlarındaki bu örnek sizi ilgilendirir
import enumeratum._
sealed trait Greeting extends EnumEntry
object Greeting extends Enum[Greeting] {
/*
`findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`
You use it to implement the `val values` member
*/
val values = findValues
case object Hello extends Greeting
case object GoodBye extends Greeting
case object Hi extends Greeting
case object Bye extends Greeting
}
// Object Greeting has a `withName(name: String)` method
Greeting.withName("Hello")
// => res0: Greeting = Hello
Greeting.withName("Haro")
// => java.lang.IllegalArgumentException: Haro is not a member of Enum (Hello, GoodBye, Hi, Bye)
// A safer alternative would be to use `withNameOption(name: String)` method which returns an Option[Greeting]
Greeting.withNameOption("Hello")
// => res1: Option[Greeting] = Some(Hello)
Greeting.withNameOption("Haro")
// => res2: Option[Greeting] = None
// It is also possible to use strings case insensitively
Greeting.withNameInsensitive("HeLLo")
// => res3: Greeting = Hello
Greeting.withNameInsensitiveOption("HeLLo")
// => res4: Option[Greeting] = Some(Hello)
// Uppercase-only strings may also be used
Greeting.withNameUppercaseOnly("HELLO")
// => res5: Greeting = Hello
Greeting.withNameUppercaseOnlyOption("HeLLo")
// => res6: Option[Greeting] = None
// Similarly, lowercase-only strings may also be used
Greeting.withNameLowercaseOnly("hello")
// => res7: Greeting = Hello
Greeting.withNameLowercaseOnlyOption("hello")
// => res8: Option[Greeting] = Some(Hello)