2013-07-16 13 views
16

मुझे एक मानचित्र की आवश्यकता है जहां मैंने इसमें विभिन्न प्रकार के मान (डबल, स्ट्रिंग, इंट, ...) डाल दिए हैं, कुंजी स्ट्रिंग हो सकती है।मानचित्र स्कैला में विभिन्न प्रकार

वहाँ यह करने के लिए एक रास्ता है, ताकि मैं साथ map.apply(k)

तरह
val map: Map[String, SomeType] = Map() 
val d: Double = map.apply("double") 
val str: String = map.apply("string") 

मैं पहले से ही एक सामान्य प्रकार

class Container[T](element: T) { 
    def get: T = element 
} 

val d: Container[Double] = new Container(4.0) 
val str: Container[String] = new Container("string") 
val m: Map[String, Container] = Map("double" -> d, "string" -> str) 

साथ इसे करने की कोशिश सही प्रकार मिल लेकिन यह Container के बाद से संभव नहीं है एक पैरामीटर लेता है। इस के लिए कोई भी समाधान है?

+1

http://stackoverflow.com/questions/4309835/scala-reflection/4310959 –

उत्तर

15

यह अब shapeless में बहुत स्पष्ट है,

scala> import shapeless._ ; import syntax.singleton._ ; import record._ 
import shapeless._ 
import syntax.singleton._ 
import record._ 

scala> val map = ("double" ->> 4.0) :: ("string" ->> "foo") :: HNil 
map: ... <complex type elided> ... = 4.0 :: foo :: HNil 

scala> map("double") 
res0: Double with shapeless.record.KeyTag[String("double")] = 4.0 

scala> map("string") 
res1: String with shapeless.record.KeyTag[String("string")] = foo 

scala> map("double")+1.0 
res2: Double = 5.0 

scala> val map2 = map.updateWith("double")(_+1.0) 
map2: ... <complex type elided> ... = 5.0 :: foo :: HNil 

scala> map2("double") 
res3: Double = 5.0 

थी एस इस जवाब की तारीख के रूप में आकारहीन 2.0.0-SNAPSHOT के साथ है।

+3

यह बहुत साफ है। लेकिन मानचित्र के रूप में एचएलआईस्ट का उपयोग करते समय एक्सेस विशेषताओं क्या हैं? चूंकि यह मूल रूप से एक सिंगल-लिंक्ड सूची है, इसलिए आपको प्रत्येक तत्व की जांच करनी है, इसलिए लुकअप ओ (एन) होगा, है ना? इसके अलावा, क्या आपके पास कुछ तत्वों से अधिक होने के बाद यह बहुत जटिल प्रकार नहीं बनता है? –

3

(क) स्काला कंटेनर क्या उन्हें अंदर रखा है के लिए प्रकार की जानकारी को ट्रैक नहीं है, और

(ख) एक सरल String पैरामीटर/कुंजी के साथ वापसी "प्रकार" एक लागू/प्राप्त विधि के लिए जा रहा है ऑब्जेक्ट के किसी दिए गए उदाहरण के लिए स्थैतिक रहें जिस पर विधि लागू की जानी चाहिए।

यह एक डिजाइन निर्णय की तरह बहुत अधिक महसूस करता है जिसे पुनर्विचार की आवश्यकता है।

1

यदि आप ऐसा करना चाहते हैं तो आप क्योंकि AnyDouble और String दोनों का एक महाप्रकार है, Any होने की Container का प्रकार निर्दिष्ट करना होगा।

val d: Container[Any] = new Container(4.0) 
val str: Container[Any] = new Container("string") 
val m: Map[String, Container[Any]] = Map("double" -> d, "string" -> str) 

या चीजों को आसान बनाने के लिए, आप Container की परिभाषा इतना है कि यह अब अपरिवर्तनीय प्रकार है बदल सकते हैं:

class Container[+T](element: T) { 
    def get: T = element 
    override def toString = s"Container($element)" 
} 

val d: Container[Double] = new Container(4.0) 
val str: Container[String] = new Container("string") 
val m: Map[String, Container[Any]] = Map("double" -> d, "string" -> str) 
+0

का संभावित डुप्लिकेट मैंने पहले ही यह कोशिश की है। लेकिन अगर मैं 'किसी भी' होने के लिए 'कोयलेनर' निर्दिष्ट करता हूं, तो मुझे 'map.apply (k) 'पर' कोई भी 'मान मिलता है, इसलिए उदा। 'वैल डी: डबल = map.apply (" डबल ")' संभव नहीं है। – 3x14159265

2

मैं वहाँ एक रास्ता नंगे map.apply() क्या करना प्राप्त करने के लिए है नहीं लगता है आप चाहते हैं जैसा कि अन्य उत्तरों सुझाव देते हैं, कुछ प्रकार के कंटेनर वर्ग आवश्यक होंगे।

sealed trait MapVal 
case class StringMapVal(value: String) extends MapVal 
case class DoubleMapVal(value: Double) extends MapVal 
case class IntMapVal(value: Int) extends MapVal 

val myMap: Map[String, MapVal] =                
    Map("key1" -> StringMapVal("value1"), 
     "key2" -> DoubleMapVal(3.14), 
     "key3" -> IntMapVal(42)) 

myMap.keys.foreach { k => 
    val message = 
    myMap(k) match { // map.apply() in your example code 
     case StringMapVal(x) => "string: %s".format(x) 
     case DoubleMapVal(x) => "double: %.2f".format(x) 
     case IntMapVal(x) => "int: %d".format(x) 
    } 
    println(message) 
} 

sealted trait का मुख्य लाभ यह संकलन समय पैटर्न मिलान में गैर संपूर्ण मैचों के लिए जाँच है: यहाँ एक उदाहरण है कि (, स्ट्रिंग, डबल, इंट इस मामले में) को प्रतिबंधित करता है मानों केवल कुछ प्रकार के होने के लिए है ।

मुझे यह दृष्टिकोण भी पसंद है क्योंकि यह स्कैला मानकों द्वारा अपेक्षाकृत सरल है। आप कुछ और मजबूत के लिए खरपतवार में जा सकते हैं, लेकिन मेरी राय में आप बहुत जल्दी रिटर्न कम कर रहे हैं।

1

एक तरीका है लेकिन यह जटिल है। Unboxed union types in Scala देखें। Int और Double दोनों को पकड़ने में सक्षम होने के लिए आपको Map को कुछ प्रकार Int |v| Double पर टाइप करना होगा। आप संकलन के समय में एक उच्च कीमत का भुगतान भी करेंगे।

21

यह सीधा नहीं है।

मूल्य का प्रकार कुंजी पर निर्भर करता है। इसलिए कुंजी को जानकारी लेनी है कि इसका मूल्य किस प्रकार है। यह एक आम पैटर्न है। इसका उपयोग एसबीटी में उदाहरण के लिए किया जाता है (उदाहरण के लिए SettingsKey[T] देखें) और बेकार रिकॉर्ड्स (Example)। हालांकि, एसबीटी में चाबियाँ अपने आप की एक विशाल, जटिल श्रेणी पदानुक्रम हैं, और एचएलआईस्ट बेकार में बहुत जटिल है और आप जितना चाहें उतना अधिक करते हैं।

तो यहां एक छोटा उदाहरण है कि आप इसे कैसे कार्यान्वित कर सकते हैं।कुंजी प्रकार को जानता है, और रिकॉर्ड बनाने का एकमात्र तरीका या रिकॉर्ड से मूल्य प्राप्त करने का एकमात्र तरीका कुंजी है। हम एक मानचित्र [कुंजी, कोई भी] आंतरिक रूप से भंडारण के रूप में उपयोग करते हैं, लेकिन जानवरों को छुपाया जाता है और सफल होने की गारंटी दी जाती है। कुंजी से रिकॉर्ड बनाने के लिए एक ऑपरेटर है, और एक ऑपरेटर रिकॉर्ड मर्ज करने के लिए है। मैंने ऑपरेटरों को चुना ताकि आप ब्रैकेट का उपयोग किए बिना रिकॉर्ड्स को जोड़ सकें।

sealed trait Record { 

    def apply[T](key:Key[T]) : T 

    def get[T](key:Key[T]) : Option[T] 

    def ++ (that:Record) : Record 
} 

private class RecordImpl(private val inner:Map[Key[_], Any]) extends Record { 

    def apply[T](key:Key[T]) : T = inner.apply(key).asInstanceOf[T] 

    def get[T](key:Key[T]) : Option[T] = inner.get(key).asInstanceOf[Option[T]] 

    def ++ (that:Record) = that match { 
    case that:RecordImpl => new RecordImpl(this.inner ++ that.inner) 
    } 
} 

final class Key[T] { 
    def ~>(value:T) : Record = new RecordImpl(Map(this -> value)) 
} 

object Key { 

    def apply[T] = new Key[T] 
} 

यहां बताया गया है कि आप इसका उपयोग कैसे करेंगे।

val a = Key[Int] 
val b = Key[String] 
val c = Key[Float] 

फिर एक रिकॉर्ड

val record = a ~> 1 ++ b ~> "abc" ++ c ~> 1.0f 

जब कुंजियों का उपयोग रिकॉर्ड पहुंचने पर, आप वापस सही प्रकार के एक मूल्य मिल जाएगा

scala> record(a) 
res0: Int = 1 

scala> record(b) 
res1: String = abc 

scala> record(c) 
res2: Float = 1.0 
बनाने के लिए उन्हें का उपयोग करें: सबसे पहले कुछ सुझाव दिए परिभाषित

मुझे इस तरह की डेटा संरचना बहुत उपयोगी लगता है। कभी-कभी आपको किसी केस क्लास की तुलना में अधिक लचीलापन की आवश्यकता होती है, लेकिन आप मानचित्र [स्ट्रिंग, कोई भी] की तरह पूरी तरह से टाइप-असुरक्षित कुछ नहीं लेना चाहते हैं। यह एक अच्छा मध्य मैदान है।


संपादित करें: एक और विकल्प एक नक्शा होना चाहिए जो (नाम, प्रकार) जोड़ी को आंतरिक कुंजी के रूप में उपयोग करता है। मूल्य प्राप्त करते समय आपको नाम और प्रकार दोनों प्रदान करना होगा। यदि आप गलत प्रकार चुनते हैं तो कोई प्रविष्टि नहीं है। हालांकि इसमें त्रुटियों के लिए एक बड़ी संभावना है, जैसे कि जब आप एक बाइट डालते हैं और एक int प्राप्त करने का प्रयास करते हैं। तो मुझे लगता है कि यह एक अच्छा विचार नहीं है।

import reflect.runtime.universe.TypeTag 

class TypedMap[K](val inner:Map[(K, TypeTag[_]), Any]) extends AnyVal { 
    def updated[V](key:K, value:V)(implicit tag:TypeTag[V]) = new TypedMap[K](inner + ((key, tag) -> value)) 

    def apply[V](key:K)(implicit tag:TypeTag[V]) = inner.apply((key, tag)).asInstanceOf[V] 

    def get[V](key:K)(implicit tag:TypeTag[V]) = inner.get((key, tag)).asInstanceOf[Option[V]] 
} 

object TypedMap { 
    def empty[K] = new TypedMap[K](Map.empty) 
} 

उपयोग:

scala> val x = TypedMap.empty[String].updated("a", 1).updated("b", "a string") 
x: TypedMap[String] = [email protected] 

scala> x.apply[Int]("a") 
res0: Int = 1 

scala> x.apply[String]("b") 
res1: String = a string 

// this is what happens when you try to get something out with the wrong type. 
scala> x.apply[Int]("b") 
java.util.NoSuchElementException: key not found: (b,Int) 
+1

यह एक अच्छा पैटर्न है। दुर्भाग्यवश आपको 'लागू' विधि टाइप-एनोटेट करना होगा। वैसे भी, मुझे लगता है कि अब तक यह सबसे अच्छा समाधान है। आपके उत्तर के लिए धन्यवाद! – 3x14159265

+0

आपका दूसरा मतलब है? कृपया ध्यान रखें कि क्लासटाग का उपयोग करके, आपको स्केल प्रकार डालने पर समस्याएं होंगी जिनके पास JVM समकक्ष नहीं है। उदाहरण के लिए आप एक सूची [int] में डाल सकते हैं और एक सूची [स्ट्रिंग] के लिए पूछ सकते हैं, और आप कुछ प्राप्त करेंगे क्योंकि सूची [Int] और सूची [स्ट्रिंग] में वही मिटा है। यदि आप इसे इस मामले में भी काम करना चाहते हैं, तो आपको इसके बजाय टाइपटाग का उपयोग करना होगा, जो पूरे स्कैला प्रकार को कैप्चर करता है न कि सिर्फ JVM प्रकार। मैंने तदनुसार उदाहरण अपडेट किया। –

+0

आपका पहला पैटर्न एक बहुत ही उपयोगी उत्तर है यदि किसी को आकारहीन के सभी सामान्य जादू की आवश्यकता नहीं है, लेकिन फिर भी कई प्रकार के मूल्यों वाले कंटेनर की आवश्यकता है, बहुत बहुत धन्यवाद! – Egregore

3

मैं अंत में अपने ही समाधान है, जो मेरे मामले में सबसे अच्छा काम किया पाया:

case class Container[+T](element: T) { 
    def get[T]: T = { 
     element.asInstanceOf[T] 
    } 
} 

val map: Map[String, Container[Any]] = Map("a" -> Container[Double](4.0), "b" -> Container[String]("test")) 
val double: Double = map.apply("a").get[Double] 
val string: String = map.apply("b").get[String] 
संबंधित मुद्दे