2012-11-16 13 views
6

Stavo scrivendo codice per qualcosa di simile a un array con dimensioni variabili. Quello che faccio è mantenere una collezione lineare sottostante e avvolgerla con metodi di accesso all'indice. Poiché la dimensione della struttura dati non è nota, scrivo qualcosa comemetodo "aggiornamento" con indice di lunghezza variabile in Scala

def apply(i: Int*): Double = ... 

E funziona perfettamente. Tuttavia, non posso fare la stessa cosa per aggiornare il metodo e gli operatori come + =, così finisco scrivendo metodi come

def set(v: Double, i: Int*) ... 
def add(v: Double, i: Int*) ... 

che va bene, ma non è quello che voglio. Immagino che il problema dell'aggiornamento possa essere risolto in due modi:

  1. Modificare l'ordine degli argomenti nella funzione di aggiornamento, il che lo rende strano.
  2. Consenti argomenti di lunghezza variabile non come l'ultimo. Trovo queste domande poste in un contesto generale e può essere risolto utilizzando le funzioni di curriculum, che non si applicano qui.

Il problema relativo a + = sembra più complicato e esiste persino quando l'indice è di lunghezza fissa. Forse possiamo aggiungere un oggetto che ha + = operatore e usare questo (...) per ottenere l'oggetto (in modo che questo (...) + = v invochi un metodo come ci aspettiamo), ma che entrerà in conflitto con il applicare il metodo.

Se qualcuno ha una soluzione a qualsiasi delle domande di cui sopra o ha un motivo per cui non dovremmo essere in grado di scrivere codice come questo, per favore condividi le tue idee! Grazie ~

risposta

2

La soluzione più semplice che vedo ora è di avere molti diversi sovraccarichi di update per ogni dimensione che si desidera supportare. Dì che puoi stabilire che la dimensione massima che utilizzerai è 10, questo significa che avrai bisogno di 10 sovraccarichi. Questo potrebbe non sembrare molto pratico, ma io può essere facilmente estratta distanza quindi è molto pratico in realtà:

trait MultiKeyUpdate[K, V] { 
    def doUpdate(k: K*)(v: V) 
    def update(k1: K, v: V) { doUpdate(k1)(v) } 
    def update(k1: K, k2: K, v: V) { doUpdate(k1, k2)(v) } 
    def update(k1: K, k2: K, k3: K, v: V) { doUpdate(k1, k2, k3)(v) } 
    // ... and so on, up until max dimension ... 
} 

Usage:

class C extends MultiKeyUpdate[Int, Double] { 
    def apply(i: Int*): Double = { 
    println("Returning element " + i.mkString("[",",","]")) 
    123 
    } 
    def doUpdate(i: Int*)(v: Double) { 
    println("Updating element " + i.mkString("[",",","]") + " to value " + v) 
    } 
} 

E alcuni test nella REPL:

scala> val o = new C 
o: C = [email protected] 
scala> o(1,2,3) 
Returning element [1,2,3] 
res3: Double = 123.0 
scala> o(1,2,3) = 5.0 
Updating element [1,2,3] to value 5.0 
scala> o(1,2,3) += 7.0 
Returning element [1,2,3] 
Updating element [1,2,3] to value 130.0 
+0

Penso che questa sia gli sviluppatori Scala modo in cui implementare molte cose nella libreria di sistema, che sembra un po 'brutto per me ... Ma io non sanno di avere lo zucchero grammatica automaticamente genera + = operatore quando hai già applicato e aggiornato, che è una cosa utile da imparare. Grazie! – Kane

1
class SetMe { 
    def set(i: Int*)(v: Double) { println("Set "+v+" with "+i.mkString(",")) } 
} 

scala> (new SetMe).set(4,7,19,3)(math.Pi) 
Set 3.141592653589793 with 4,7,19,3 

non posso farlo trucco con update sé, tuttavia. Potrebbe valere la pena presentare una richiesta di miglioramento.

5

update è un artefatto piuttosto particolare in Scala perché è principalmente zucchero sintattico e non corrisponde a nessuna firma di metodo particolare. Questo significa che siamo in grado di essere creativi e dare aggiornare una firma arity-polimorfico,

scala> class Indexed { def update[P <: Product](p: P) = p } 
defined class Indexed 

scala> val i = new Indexed 
i: Indexed = [email protected] 

scala> i(0) = 1.0 
res0: (Int, Double) = (0,1.0) 

scala> i(0, 1) = 1.0 
res1: (Int, Int, Double) = (0,1,1.0) 

scala> i(0, 1, 2) = 1.0 
res2: (Int, Int, Int, Double) = (0,1,2,1.0) 

scala> i(0, 1, 2, 3) = 1.0 
res3: (Int, Int, Int, Int, Double) = (0,1,2,3,1.0) 

Così com'è, questo lascia i tipi di indici sul lato sinistro e il tipo del valore sul RHS completamente vincolati,

scala> i(23, true, 'c') = "foo" 
res4: (Int, Boolean, Char, String) = (23,true,c,foo) 

ma possiamo rimediare con qualche prova implicita fornita dalla nuova support for tuples in shapeless 2.0.0-SNAPSHOT,

scala> import shapeless._ 
import shapeless._ 

scala> import syntax.tuple._ 
import syntax.tuple._ 

scala> class Indexed { 
    | def update[P <: Product, I](p: P) 
    |  (implicit 
    |  init: TupleInit.Aux[P, I], 
    |  toList: TupleToList[I, Int], 
    |  last: TupleLast.Aux[P, Double]) = (toList(init(p)), last(p)) 
    | } 
defined class Indexed 

scala> val i = new Indexed 
i: Indexed = [email protected] 

scala> i(0) = 1.0 
res10: (List[Int], Double) = (List(0),1.0) 

scala> i(0, 1) = 2.0 
res11: (List[Int], Double) = (List(0, 1),2.0) 

scala> i(0, 1, 2) = 3.0 
res12: (List[Int], Double) = (List(0, 1, 2),3.0) 

scala> i(0, 1, 2, 3) = 4.0 
res13: (List[Int], Double) = (List(0, 1, 2, 3),4.0) 

scala> i(0, 1, 2) = "foo" // Doesn't compile 
<console>:22: error: could not find implicit value for parameter 
    last: shapeless.ops.tuple.TupleLast.Aux[(Int, Int, Int, String),Double] 
       i(0, 1, 2) = "foo" // Doesn't compile 
         ^

scala> i(23, "foo", true) = 5.0 // Doesn't compile 
<console>:22: error: could not find implicit value for parameter 
    toList: shapeless.ops.tuple.TupleToList[I,Int] 
       i(23, "foo", true) = 5.0 // Doesn't compile 
           ^
+0

questa è un'informazione veramente utile e un uso molto pratico di shapeless. Sareste in grado di aggiungere un breve post di blog su questa scoperta? – iain

+0

Vorrei ... guardare questo spazio. –

Problemi correlati