作为练习,我想将Scala Array集合扩展到我自己的OneBasedArray(做你期望的,索引从1开始)。由于这是一个不可变的集合,我想让它在调用filter / map等时返回正确的类型。
我已阅读资源here,here和here,但我正在努力了解如何将其转换为数组(或示例中的其他集合)。我是否在这种结构的正确轨道上?
class OneBasedArray[T]
extends Array[T]
with GenericTraversableTemplate[T, OneBasedArray]
with ArrayLike[T, OneBasedArray]
是否还有其他资源可以帮助解释扩展集合?
答案 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
}
}