enum
जावा की तरह स्काला के पास टाइप-सेफ नहीं है। संबंधित स्थिरांक के एक सेट को देखते हुए, उन स्थिरांक का प्रतिनिधित्व करने के लिए स्काला में सबसे अच्छा तरीका क्या होगा?
enum
जावा की तरह स्काला के पास टाइप-सेफ नहीं है। संबंधित स्थिरांक के एक सेट को देखते हुए, उन स्थिरांक का प्रतिनिधित्व करने के लिए स्काला में सबसे अच्छा तरीका क्या होगा?
जवाबों:
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
उदाहरण का उपयोग करें
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
}
मुझे कहना होगा कि स्कैफ़मैन द्वारा स्कैला दस्तावेज़ीकरण के ऊपर कॉपी किया गया उदाहरण व्यवहार में सीमित उपयोगिता का है (आप उपयोग कर सकते हैं )।case object
एक जावा Enum
(यानी समझदार toString
और valueOf
तरीकों के साथ) के सबसे निकट से कुछ प्राप्त करने के लिए - शायद आप एक डेटाबेस के लिए एनुम मूल्यों को बरकरार रख रहे हैं) आपको इसे थोड़ा संशोधित करने की आवश्यकता है। अगर आपने skaffman का कोड इस्तेमाल किया था :
WeekDay.valueOf("Sun") //returns None
WeekDay.Tue.toString //returns Weekday(2)
जबकि निम्नलिखित घोषणा का उपयोग:
object WeekDay extends Enumeration {
type WeekDay = Value
val Mon = Value("Mon")
val Tue = Value("Tue")
... etc
}
आपको अधिक समझदार परिणाम मिलते हैं:
WeekDay.valueOf("Sun") //returns Some(Sun)
WeekDay.Tue.toString //returns Tue
valueOf
का प्रतिस्थापन है withName
, जो एक विकल्प को वापस नहीं करता है, और एक एनएसई फेंकता है अगर कोई मैच नहीं होता है। क्या!
Map[Weekday.Weekday, Long]
और एक मान जोड़ता हूं Mon
तो यह कंपाइलर एक अमान्य प्रकार की त्रुटि को फेंकता है। वीकएंड की उम्मीद है। ऐसा क्यों होता है?
करने के कई तरीके हैं।
1) प्रतीकों का उपयोग करें। यह आपको किसी भी प्रकार की सुरक्षा नहीं देगा, हालांकि, गैर-प्रतीकों को स्वीकार नहीं करने से अलग, जहां एक प्रतीक की उम्मीद है। मैं केवल पूर्णता के लिए यहां इसका उल्लेख कर रहा हूं। यहाँ उपयोग का एक उदाहरण है:
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) कक्षा का उपयोग करना Enumeration
:
object Dimension extends Enumeration {
type Dimension = Value
val Row, Column = Value
}
या, यदि आपको इसे क्रमबद्ध या प्रदर्शित करने की आवश्यकता है:
object Dimension extends Enumeration("Row", "Column") {
type Dimension = Value
val Row, Column = Value
}
इसका उपयोग इस तरह किया जा सकता है:
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 /
दुर्भाग्य से, यह सुनिश्चित नहीं करता है कि सभी मैचों के लिए जिम्मेदार हैं। अगर मैं मैच में रो या कॉलम लगाना भूल जाता, तो स्काला कंपाइलर मुझे चेतावनी नहीं देता। तो यह मुझे कुछ प्रकार की सुरक्षा देता है , लेकिन उतना नहीं जितना कि प्राप्त किया जा सकता है।
3) केस ऑब्जेक्ट:
sealed abstract class Dimension
case object Row extends Dimension
case object Column extends Dimension
अब, अगर मैं किसी केस को छोड़ देता हूं match
, तो कंपाइलर मुझे चेतावनी देगा:
MatrixInt.scala:70: warning: match is not exhaustive!
missing combination Column
what match {
^
one warning found
यह उसी तरह से बहुत अधिक उपयोग किया जाता है, और यहां तक कि इसकी आवश्यकता नहीं है 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 /
आप आश्चर्यचकित हो सकते हैं, फिर, कभी भी केस ऑब्जेक्ट्स के बजाय एन्यूमरेशन का उपयोग क्यों करें। तथ्य की बात के रूप में, केस ऑब्जेक्ट्स के कई बार फायदे हैं, जैसे कि यहां। गणना वर्ग, हालांकि, कई संग्रह विधियां हैं, जैसे कि तत्व (स्केल 2.8 पर पुनरावृत्ति), जो एक Iterator, मानचित्र, फ़्लैटमैप, फ़िल्टर, आदि देता है।
यह उत्तर अनिवार्य रूप से मेरे ब्लॉग में इस लेख से चयनित भागों है ।
Symbol
उदाहरणों में रिक्त स्थान या विशेष वर्ण नहीं हो सकते। ज्यादातर लोग जब पहली बार Symbol
कक्षा का सामना करते हैं तो शायद ऐसा सोचते हैं, लेकिन वास्तव में गलत है। Symbol("foo !% bar -* baz")
संकलन करता है और पूरी तरह से ठीक चलता है। दूसरे शब्दों में, आप किसी भी स्ट्रिंग को Symbol
लपेटने वाले उदाहरणों को पूरी तरह से बना सकते हैं (आप इसे "सिंगल कोमा" सिंथैटिक शुगर के साथ नहीं कर सकते हैं)। केवल एक चीज जो गारंटी देती है, वह है किसी भी प्रतीक की विशिष्टता, तुलनात्मक रूप से तुलना करना और मैच करना। Symbol
String
तर्क के रूप में, उदाहरण के लिए, पास नहीं कर सकते Symbol
।
String
किसी अन्य वर्ग से प्रतिस्थापित करते हैं जो मूल रूप से एक स्ट्रिंग के चारों ओर एक आवरण है और दोनों दिशाओं में स्वतंत्र रूप से परिवर्तित किया जा सकता है (जैसा कि मामला है Symbol
)। मुझे लगता है कि आपके कहने का मतलब यह है कि "यह आपको किसी भी प्रकार की सुरक्षा नहीं देगा", यह सिर्फ इतना स्पष्ट नहीं था कि ओपी ने स्पष्ट रूप से प्रकार के समाधान के लिए कहा था। मुझे यकीन नहीं था कि लिखने के समय आप जानते थे कि न केवल यह सुरक्षित नहीं है, क्योंकि वे बिल्कुल भी नहीं हैं, लेकिन यह Symbol
भी गारंटी नहीं है कि पारित तर्क में विशेष वर्ण नहीं होंगे।
'foo
अंकन जो करता रोकना गैर-पहचानकर्ता तार)। यह इस गलत धारणा है कि मैं किसी भी भविष्य के पाठक के लिए दूर करना चाहता था।
नामांकित घोषित करने का थोड़ा कम क्रिया तरीका:
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
निश्चित रूप से यहां समस्या यह है कि आपको नाम और वैल के क्रम को सिंक में रखने की आवश्यकता होगी जो कि एक ही लाइन पर नाम और वैल घोषित होने पर करना आसान है।
आप संयुक्ताक्षर के बजाय एक सील अमूर्त वर्ग का उपयोग कर सकते हैं, उदाहरण के लिए:
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))
}
}
बस खोजा हुआ प्रगंड । यह बहुत अद्भुत है और समान रूप से आश्चर्यजनक है यह अधिक प्रसिद्ध नहीं है!
स्काला में "एनुमरेशंस" के आसपास सभी विकल्पों पर गहन शोध करने के बाद, मैंने इस डोमेन के एक और स्टैक ऑवरऑवरफ्लो थ्रेड पर बहुत अधिक संपूर्ण अवलोकन पोस्ट किया । इसमें "सीलबंद विशेषता + केस ऑब्जेक्ट" पैटर्न का एक समाधान शामिल है जहां मैंने जेवीएम वर्ग / ऑब्जेक्ट इनिशियलाइज़ेशन ऑर्डरिंग समस्या को हल किया है।
स्काला में यह https://github.com/lloydmeta/enumeratum के साथ बहुत आरामदायक है
उदाहरण और प्रलेखन के साथ परियोजना वास्तव में अच्छी है
बस उनके डॉक्स से यह उदाहरण आपको रूचि लेना चाहिए
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)