2012-11-28 18 views
5

Sto cercando di implementare un nuovo tipo, Chunk, che è simile a una mappa. Fondamentalmente, un "Chunk" è una mappatura da String -> Chunk, o una stringa stessa.scala mappa personalizzata

esempio che dovrebbe essere in grado di lavorare in questo modo:

val m = new Chunk("some sort of value") // value chunk 
assert(m.getValue == "some sort of value") 

val n = new Chunk("key" -> new Chunk("value"), // nested chunks 
        "key2" -> new Chunk("value2")) 
assert(n("key").getValue == "value") 
assert(n("key2").getValue == "value2") 

ho questo per lo più a lavorare, se non che sono un po 'confuso da quanto l'operatore + funziona per le mappe immutabili.

Ecco quello che ho adesso:

class Chunk(_map: Map[String, Chunk], _value: Option[String]) extends Map[String, Chunk] { 
    def this(items: (String, Chunk)*) = this(items.toMap, None) 
    def this(k: String) = this(new HashMap[String, Chunk], Option(k)) 
    def this(m: Map[String, Chunk]) = this(m, None) 

    def +[B1 >: Chunk](kv: (String, B1)) = throw new Exception(":(do not know how to make this work") 
    def -(k: String) = new Chunk(_map - k, _value) 
    def get(k: String) = _map.get(k) 
    def iterator = _map.iterator 

    def getValue = _value.get 
    def hasValue = _value.isDefined 

    override def toString() = { 
    if (hasValue) getValue 
    else "Chunk(" + (for ((k, v) <- this) yield k + " -> " + v.toString).mkString(", ") + ")" 
    } 

    def serialize: String = { 
    if (hasValue) getValue 
    else "{" + (for ((k, v) <- this) yield k + "=" + v.serialize).mkString("|") + "}" 
    } 
} 

object main extends App { 
    val m = new Chunk("message_info" -> new Chunk("message_type" -> new Chunk("boom"))) 
    val n = m + ("c" -> new Chunk("boom2")) 
} 

Inoltre, commenta sia, in generale, questa implementazione è opportuno sarebbe apprezzato.

Grazie!

Modifica: la soluzione dei tipi di dati algebrici è eccellente, ma rimane un problema.

def +[B1 >: Chunk](kv: (String, B1)) = Chunk(m + kv) // compiler hates this 
def -(k: String) = Chunk(m - k) // compiler is pretty satisfied with this 

l'operatore - qui sembra funzionare, ma l'operatore + mi ha davvero vuole restituire qualcosa di tipo B1 (credo)? Viene a mancare con il seguente problema:

overloaded method value apply with alternatives: (map: Map[String,Chunk])MapChunk <and> (elems: (String, Chunk)*)MapChunk cannot be applied to (scala.collection.immutable.Map[String,B1]) 

Edit2: Xiefei risposto a questa domanda - che si estende mappa richiede che io gestire + con un supertipo (B1) di Chunk, quindi per fare questo devo avere alcuni implementazione per questo, quindi questo sarà sufficiente:

def +[B1 >: Chunk](kv: (String, B1)) = m + kv 

Tuttavia, non ho mai veramente intenzione di utilizzare quella, invece, sarò anche includere mia implementazione che restituisce un pezzo come segue:

def +(kv: (String, Chunk)):Chunk = Chunk(m + kv) 
+0

L'implementazione per '+', nel caso di mappe immutabili, deve restituire una nuova mappa con la chiave/valore aggiunto, giusto? Hai già "avvolto" una mappa ('_map'), quindi per un'implementazione del tuo' + ', solo la delega a' _map' funzionerà: 'def + [B1>: Chunk] (kv: (String, B1)) = _map + kv'. Il vero problema è che questo non ha assolutamente senso per "semplici" Chunk's (quelli che sono solo una stringa). In altre parole, avendo l'insieme di costruttori che descrivi sopra, ** e ** che implementano '+' interrompe la definizione di un 'Chunk' ... perché consente a un pezzo di stringa di // anche // diventare una mappa- pezzo. – Faiz

risposta

1

Avete considerato l'utilizzo della composizione anziché dell'ereditarietà? Quindi, invece di Chunk che estende direttamente Map [String, Chunk], basta che Chunk mantenga internamente un'istanza di Map [String, Chunk] e fornisca i metodi extra di cui hai bisogno e delegando altrimenti ai metodi della mappa interna.

+1

Ho pensato a questo, ed è un buon punto, sono stato tentato, comunque, da tutti i gadget che sono inclusi gratuitamente estendendo la mappa. – mattomatic

1
def +(kv: (String, Chunk)):Chunk = new Chunk(_map + kv, _value) 
override def +[B1 >: Chunk](kv: (String, B1)) = _map + kv 

Quello che vi serve è un nuovo metodo +, e anche implementare quello dichiarato nella Map trait.

2

Il modo in cui è scritto, non c'è modo di far rispettare che non può essere sia un Map e un String allo stesso tempo. Sarei guardando catturare il valore utilizzando Either e l'aggiunta di qualsiasi convenienza metodi si richiede:

case class Chunk(value:Either[Map[String,Chunk],String]) { 
    ... 
} 

che inoltre ti costringono a pensare a quello che si ha realmente bisogno di fare in situazioni come l'aggiunta di una coppia chiave/valore di a Chunk che rappresenta un String.

5

Come su un approccio Algebraic data type?

abstract sealed class Chunk 
    case class MChunk(elems: (String, Chunk)*) extends Chunk with Map[String,Chunk] { 
    val m = Map[String, Chunk](elems:_*) 
    def +[B1 >: Chunk](kv: (String, B1)) = m + kv 
    def -(k: String) = m - k 
    def iterator = m.iterator 
    def get(s: String) = m.get(s) 
    } 
    case class SChunk(s: String) extends Chunk 
    // A 'Companion' object that provides 'constructors' and extractors.. 
    object Chunk { 
    def apply(s: String) = SChunk(s) 
    def apply(elems: (String, Chunk)*) = MChunk(elems: _*) 
    // just a couple of ideas... 
    def unapply(sc: SChunk) = Option(sc).map(_.value) 
    def unapply(smc: (String, MChunk)) = smc match { 
     case (s, mc) => mc.get(s) 
    } 

    } 

che può essere utilizzato come:

val simpleChunk = Chunk("a") 
val nestedChunk = Chunk("b" -> Chunk("B")) 
// Use extractors to get the values. 
val Chunk(s) = simpleChunk // s will be the String "a" 
val Chunk(c) = ("b" -> nestedChunk) // c will be a Chunk: Chunk("B") 
val Chunk(c) = ("x" -> nestedChunk) // will throw a match error, because there's no "x" 
// pattern matching: 
("x" -> mc) match { 
    case Chunk(w) => Some(w) 
    case _ => None 
} 

I unapply estrattori sono solo un suggerimento; si spera che tu possa confondere questa idea fino a ottenere quello che vuoi.

+2

+1 La formulazione * "Fondamentalmente, un Chunk è una mappatura da String -> Chunk, o una stringa stessa." * Imho chiama qualche tipo di classe/tratto astratti sigillati. La domanda aperta per me è quale tipo di funzionalità dovrebbe essere inclusa in questa super classe. Dovrebbe in generale comportarsi come una mappa o una stringa? A questo proposito, la domanda non mi è del tutto chiara ... – bluenote10

Problemi correlati