扩展Scala集合:一种基于Array的索引练习

时间:2010-12-07 21:52:40

标签: scala scala-collections scala-2.8

作为练习,我想将Scala Array集合扩展到我自己的OneBasedArray(做你期望的,索引从1开始)。由于这是一个不可变的集合,我想让它在调用filter / map等时返回正确的类型。

我已阅读资源hereherehere,但我正在努力了解如何将其转换为数组(或示例中的其他集合)。我是否在这种结构的正确轨道上?

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

是否还有其他资源可以帮助解释扩展集合?

5 个答案:

答案 0 :(得分:5)

顺便说一下,我认为Array不是Scala中的集合。

答案 1 :(得分:3)

以下是一个使用方法对遍历迭代的示例,该方法始终返回其操作的迭代的预期运行时类型:

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}

新函数mapOrElse类似于collect函数,但除了m: A => Unit之外,它还允许您传递方法pf。 {1}}未定义。 pf例如可以是日志记录方法。

答案 2 :(得分:3)

Array不是Traversable - 尝试使用它作为基类会导致各种问题。此外,它也不是一成不变的,这使得它完全不适合你想要的东西。最后,Array是一个实现 - 尝试从特征或抽象类继承。

答案 3 :(得分:2)

Array不是典型的Scala集合......它只是一个Java数组,通过隐式转换来看起来像一个集合。

鉴于Java Arrays的混乱变化,你真的不想在没有极其令人信服的理由的情况下使用它们,因为它们是潜伏的bug的来源。

(请参阅此处:http://www.infoq.com/presentations/Java-Puzzlers

这样一个基于1的集合也不是一个好主意,因为你无法知道有多少其他集合方法依赖于序列从0开始的假设。所以要安全地做(如果你真的必须),你需要添加一个新方法,保持默认方法不变:

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.

更安全的是,你可以创建一个Map[Int,T],索引是从一开始的

(Iterator.from(1) zip seq).toMap

这可以说是最“正确”的解决方案,尽管它也会带来最高的性能成本。

答案 4 :(得分:0)

不是数组,但这是我最近放在一起的基于单一的不可变IndexedSeq实现。我按照here给出的例子来实现RNA类。在该示例,ScalaDocs和许多“有用的”编译器错误之间,我设法正确设置它。 OneBasedSeq被泛化的事实使它比RNA示例稍微复杂一些。此外,除了示例中扩展的traits和方法之外,我还必须扩展IterableLike并覆盖iterator方法,因为各种方法在后台调用该方法,并且默认迭代器为零基

请原谅任何风格或惯用的怪异;我在Scala编程的时间不到2个月。

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
  }
}