2010-12-07 57 views
6

Comme un exercice, je voudrais étendre la collection Scala Array à mon propre OneBasedArray (fait ce que vous attendez, l'indexation commence à partir de 1). Étant donné que c'est une collection immuable, je voudrais avoir retourner le type correct lorsque vous appelez filtre/carte etc.Extension des collections Scala: un exercice basé sur l'index Array

J'ai lu les ressources here, here et here, mais je suis mal à comprendre comment traduire cette à des tableaux (ou des collections autres que celles des exemples). Suis-je sur la bonne voie avec ce genre de structure?

class OneBasedArray[T] 
    extends Array[T] 
    with GenericTraversableTemplate[T, OneBasedArray] 
    with ArrayLike[T, OneBasedArray] 

Existe-t-il d'autres ressources permettant d'expliquer l'extension des collections?

Répondre

2

Array n'est pas une collection Scala typique ... C'est simplement un tableau Java qui est pimpé pour ressembler à une collection au moyen de conversions implicites. Compte tenu de la variance ratée de Java Arrays, vous ne voulez vraiment pas les utiliser sans une raison extrêmement convaincante, car ils sont une source de bugs cachés.

(voir ici: http://www.infoq.com/presentations/Java-Puzzlers)

Grincement une collection de base 1 comme ce n'est pas vraiment une bonne idée non plus, que vous avez aucun moyen de savoir combien d'autres méthodes de collecte reposent sur l'hypothèse que les séquences sont basées sur 0. Donc, pour le faire en toute sécurité (si vous vraiment devez) vous voulez ajouter une nouvelle méthode qui laisse la valeur par défaut un inchangé:

class OneBasedLookup[T](seq:Seq) { 
    def atIdx(i:Int) = seq(i-1) 
} 

implicit def seqHasOneBasedLookup(seq:Seq) = new OneBasedLookup(seq) 

// now use `atIdx` on any sequence. 

encore plus sûr encore, vous pouvez créer un Map[Int,T], les indices étant une base Il s'agit sans doute de la solution la plus "correcte", même si elle présente également le coût de performance le plus élevé.

5
  • Pour un aperçu en profondeur des nouvelles collections API: The Scala 2.8 Collections API
  • Pour une belle vue sur la relation entre les classes et les traits principaux this

D'ailleurs, je ne pense pas que les tableaux sont collection à Scala.

+0

Bravo pour ajouter des liens vers des informations pour répondre à la plus grande question, non seulement celui demandé^_^ –

+1

@Dylan juste répondu « Y at-il d'autres ressources qui aident à expliquer les collections s'étendant? » :) – pedrofurla

3

Voici un exemple de proxénétisme iterables avec une méthode qui retourne toujours le type d'exécution prévu de itérable il fonctionne sur:

import scala.collection.generic.CanBuildFrom 

trait MapOrElse[A] { 
    val underlying: Iterable[A] 

    def mapOrElse[B, To] 
     (m: A => Unit) 
     (pf: PartialFunction[A,B]) 
     (implicit cbf: CanBuildFrom[Iterable[A], B, To]) 
     : To = { 

    var builder = cbf(underlying.repr)   

    for (a <- underlying) if (pf.isDefinedAt(a)) builder += pf(a) else m(a) 

    builder.result 
    } 
} 

implicit def toMapOrElse[A](it: Iterable[A]): MapOrElse[A] = 
    new MapOrElse[A] {val underlying = it} 

La nouvelle fonction mapOrElse est similaire à la fonction collect mais il vous permet de passer une méthode m: A => Unit en plus d'une fonction partielle pf invoquée chaque fois que pf est indéfini. m peut par exemple être une méthode de journalisation.

3

Un Array n'est pas un Traversable - essayer de travailler avec une classe de base causerait toutes sortes de problèmes. En outre, il n'est pas immuable non plus, ce qui le rend totalement inadapté à ce que vous voulez. Enfin, Array est une implémentation - essayez d'hériter de traits ou de classes abstraites.

0

Ce n'est pas un tableau, mais voici une implémentation d'IndexedSeq immuable à base de One que j'ai récemment créée. J'ai suivi l'exemple donné here où ils implémenter une classe d'ARN. Entre cet exemple, les ScalaDocs, et beaucoup d'erreurs de compilation "utiles", j'ai réussi à le configurer correctement. Le fait que OneBasedSeq soit générique le rend un peu plus complexe que l'exemple de l'ARN. En outre, en plus des traits étendus et des méthodes surchargées dans l'exemple, j'ai dû étendre IterableLike et remplacer la méthode iterator, car diverses méthodes appellent cette méthode en arrière-plan, et l'itérateur par défaut est basé sur zéro.

Veuillez pardonner toutes les bizarreries stylistiques ou idiomadiques; Je programme à Scala depuis moins de 2 mois.

import collection.{IndexedSeqLike, IterableLike} 
import collection.generic.CanBuildFrom 
import collection.mutable.{Builder, ArrayBuffer} 

// OneBasedSeq class 
final class OneBasedSeq[T] private (s: Seq[T]) extends IndexedSeq[T] 
    with IterableLike[T, OneBasedSeq[T]] with IndexedSeqLike[T, OneBasedSeq[T]] 
{ 
    private val innerSeq = s.toIndexedSeq 

    def apply(idx: Int): T = innerSeq(idx - 1) 
    def length: Int = innerSeq.length 
    override def iterator: Iterator[T] = new OneBasedSeqIterator(this) 
    override def newBuilder: Builder[T, OneBasedSeq[T]] = OneBasedSeq.newBuilder 
    override def toString = "OneBasedSeq" + super.toString 
} 

// OneBasedSeq companion object 
object OneBasedSeq { 
    private def fromSeq[T](s: Seq[T]) = new OneBasedSeq(s) 

    def apply[T](vals: T*) = fromSeq(IndexedSeq(vals: _*)) 

    def newBuilder[T]: Builder[T, OneBasedSeq[T]] = 
    new ArrayBuffer[T].mapResult(OneBasedSeq.fromSeq) 

    implicit def canBuildFrom[T, U]: CanBuildFrom[OneBasedSeq[T], U, OneBasedSeq[U]] = 
    new CanBuildFrom[OneBasedSeq[T], U, OneBasedSeq[U]] { 
     def apply() = newBuilder 
     def apply(from: OneBasedSeq[T]): Builder[U, OneBasedSeq[U]] = newBuilder[U] 
    } 
} 

// Iterator class for OneBasedSeq 
class OneBasedSeqIterator[T](private val obs: OneBasedSeq[T]) extends Iterator[T] 
{ 
    private var index = 1 
    def hasNext: Boolean = index <= obs.length 

    def next: T = { 
    val ret = obs(index) 
    index += 1 
    ret 
    } 
}