2013-03-29 13 views
6

Stavo cercando di capire come scrivere una funzione di scambio funzionale che funziona su qualsiasi Traversable[_], dato un insieme e gli indici da scambiare. Sono venuto con la seguente:Enrich-My-Library per tutti i Traversables

def swap[A, CC <% Traversable[A]](xs: CC, i: Int, j: Int): Traversable[A] = { 
    xs.slice(0, i) ++ 
    xs.slice(j, j+1) ++ 
    xs.slice(i+1, j) ++ 
    xs.slice(i, i+1) ++ 
    xs.slice(j+1, xs.size) 
} 

swap(List(1,2,3,4,5), 0, 4) // => List(5,2,3,4,1) 

Mi piacerebbe sapere come fare questo in un'estensione implicita di Traversable, mi permette di chiamarla con List(1,2,3,4,5).swap(0, 4). Il più vicino che ho potuto ottenere è stato il seguente:

import language.implicitConversions 
class RichTraversable[A, B <% Traversable[A]](xs: B) { 
    def swap(i: Int, j: Int): Traversable[A] = { 
    xs.slice(0, i) ++ 
     xs.slice(j, j+1) ++ 
     xs.slice(i+1, j) ++ 
     xs.slice(i, i+1) ++ 
     xs.slice(j+1, xs.size) 
    } 
} 
implicit def richTraversable[A, B <% Traversable[A]](ys: B)(implicit b: Traversable[A]) 
    = new RichTraversable[A, B](ys) 

Sfortunatamente non è proprio così. Chiamata List(1,2,3,4,5).swap(0, 4) risultati nel seguente errore:

error: No implicit view available from List[Int] => Traversable[A]

sento di dover essere perso qualcosa, o enormemente di complicare eccessivamente la questione. Qualcuno sa come dovrebbe essere strutturato?


Nota: Questo è puramente accademica, e non viene utilizzato in un ambiente di produzione in alcun modo. Sto cercando di gestire meglio il sistema di tipi e limiti di Scala.

risposta

4

Se stai usando Scala 2.10:

import scala.collection.generic.CanBuildFrom 
import scala.collection.TraversableLike 

implicit class TraversableWithSwap[A, Repr <: Traversable[A]](val xs: TraversableLike[A, Repr]) extends AnyVal { 
    def swap[That](i: Int, j: Int)(implicit bf: CanBuildFrom[Repr, A, That]): That = { 
    val builder = bf(xs.asInstanceOf[Repr]) 
    builder.sizeHint(xs) 
    builder ++= xs.take(i) 
    builder ++= xs.slice(j, j + 1) 
    builder ++= xs.slice(i + 1, j) 
    builder ++= xs.slice(i, i + 1) 
    builder ++= xs.drop(j + 1) 
    builder.result 
    } 
} 

(La cosa AnyVal lo trasforma in un valore di classe, il che significa che il compilatore riscriverlo come una funzione statica per evitare effettivamente facendo l'involucro durante il runtime .)

Se si utilizza una versione precedente, cadere il extends AnyVal e utilizzare una funzione implicita conversione invece di implicit class:

implicit def toTraversableWithSwap[A, Repr <: Traversable[A]](xs: TraversableLike[A, Repr]) = new TraversableWithSwap(xs) 

e di utilizzarlo:

scala> Vector(1,2,3,4,5,6,7,8,9).swap(2,5) 
res0: scala.collection.immutable.Vector[Int] = Vector(1, 2, 6, 4, 5, 3, 7, 8, 9) 

Si noti che in realtà non ha senso definire questa funzione per tutti Traversable dal momento che alcuni traversables (come Set, Map, ecc) sono non ordinata, in modo da scambiare due elementi è privo di significato . Probabilmente vorresti definirlo per tutto lo Seq o qualcosa del genere.

Inoltre: Possiamo anche solo concordare di chiamarlo "migliorare la mia biblioteca" già?

+0

Primo: Woah, cos'è questo builder che non ho mai visto prima? Secondo: Sì, possiamo concordare di chiamarlo da ora in poi =) – KChaloux

+0

Il builder è ciò che viene utilizzato nelle raccolte interne. Vedi, ad esempio: https://github.com/scala/scala/blob/master/src/library/scala/collection/TraversableLike.scala#L237 – dhg

+0

Hmm. Non riesco a trovare CanBuildFrom o TraversableLike. C'è un'importazione che mi manca? – KChaloux

Problemi correlati