2011-09-05 19 views
6

Sto cercando di scrivere un metodo generico fill, e che segue è quello che io sono venuto in mente finora:Scrivi metodo generico 'riempimento'

scala> import collection.generic.{GenericTraversableTemplate => GTT} 
import collection.generic.{GenericTraversableTemplate=>GTT} 

scala> import collection.generic.{TraversableFactory => TF} 
import collection.generic.{TraversableFactory=>TF} 

scala> def fill[A, CC[X] <: Traversable[X] with GTT[X, CC]] 
    | (n: Int)(elem: => A)(tf: TF[CC]) = tf.fill(n)(elem) 
fill: [A, CC[X] <: Traversable[X] with scala.collection.generic.GenericTraversab 
leTemplate[X,CC]](n: Int)(elem: => A)(tf: scala.collection.generic.TraversableFa 
ctory[CC])CC[A] 

scala> fill(3)('d')(List) 
res42: List[Char] = List(d, d, d) 

Questo funziona con tutte le collezioni attraversabili eccetto array. Come faccio a far funzionare questo codice con gli array?

+0

Ho anche venire con [questo] (https://gist.github.com/1194858) versione alternativa che funziona con le matrici, ma non mi piace per due motivi: 1. Richiede per dare più annotazioni di tipo di quanto vorrei. 2. Mi fa scrivere personalmente l'intera implementazione invece di riutilizzare i metodi esistenti nelle raccolte. – missingfaktor

risposta

10

Se non ti dispiace creazione di un oggetto in più, c'è

def fill[CC[_]](n: Int) = new { 
    def apply[A](elem: => A)(implicit cbf: CanBuildFrom[Nothing, A, CC[A]]) = { 
    val b = cbf() 
    1 to n foreach { _ => b += elem } 
    b.result 
    } 
} 

Non c'è niente intorno obiezioni (2), ma l'utilizzo è bello:

scala> fill[List](3)("wish") 
res0: List[java.lang.String] = List(wish, wish, wish) 

scala> fill[Array](3)("wish") 
res1: Array[java.lang.String] = Array(wish, wish, wish) 
+0

+1, buono. '' – missingfaktor

+0

Penso di aver capito come aggirare l'obiezione (2). Controlla la mia risposta qui sotto. (Anche se sento che forse c'è un modo migliore.) – missingfaktor

1

E 'possibile per modificare la sintassi della soluzione di Rex' un po':

class Filler(n: Int) { 
    def timesOf[A](elem: => A) = new Builder[A](elem) 

    class Builder[A](elem: => A) { 
    import scala.collection.generic.CanBuildFrom 
    def fillIn[CC[_]](implicit cbf: CanBuildFrom[Nothing, A, CC[A]]) = { 
     val b = cbf() 
     for (_ <- 1 to n) b += elem 
     b.result 
    } 
    } 
} 
implicit def int2Filler(n: Int) = new Filler(n) 

// use as 
(3 timesOf true).fillIn[List] 

Perché notazione operatore è consentito solo per parenthes es, non possiamo omettere le parentesi.

+0

+1, anche una buona soluzione. – missingfaktor

0

Ho risolto la soluzione di Rex utilizzando il metodo ++=Builder. Usa qualsiasi raccolta, esegui su di essa tutte le operazioni che vuoi eseguire, e infine aggiungila all'oggetto costruttore e poi prendi il suo risultato.

scala> def fill[CC[_]](n: Int) = new { 
    | def apply[A](elem: => A) 
    |    (implicit cbf: CanBuildFrom[Nothing, A, CC[A]]) = { 
    |  val b = cbf.apply 
    |  b ++= Vector.fill(n)(elem) 
    |  b.result 
    | } 
    | } 
fill: [CC[_]](n: Int)java.lang.Object{def apply[A](elem: => A)(implicit cbf: sca 
la.collection.generic.CanBuildFrom[Nothing,A,CC[A]]): CC[A]} 

scala> fill[List](3)("hullo") 
res8: List[java.lang.String] = List(hullo, hullo, hullo) 
+0

Immagino che sia "migliore" se non ti dispiace prenderlo il doppio (perché costruisci la stessa collezione due volte). –

+0

@Rex: In quale altro modo posso riutilizzare le funzioni da stdlib invece di implementare un'implementazione imperativa basata su builder? – missingfaktor

+0

Forse non puoi, ma molte delle funzioni di supporto sono banali da ri-implementare. –