扩展 Scala 集合:一个基于数组索引的练习

发布于 2024-10-07 04:09:06 字数 698 浏览 4 评论 0原文

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

我已阅读资源 此处此处此处,但我很难理解如何将其转换为数组(或示例中以外的集合)。我的这种结构走在正确的轨道上吗?

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

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

As an exercise, I'd like to extend the Scala Array collection to my own OneBasedArray (does what you'd expect, indexing starts from 1). Since this is an immutable collection, I'd like to have it return the correct type when calling filter/map etc.

I've read the resources here, here and here, but am struggling to understand how to translate this to Arrays (or collections other than the ones in the examples). Am I on the right track with this sort of structure?

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

Are there any further resources that help explain extending collections?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

神经暖 2024-10-14 04:09:06

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

By the way I don't think Array is a collection in Scala.

2024-10-14 04:09:06

下面是一个使用始终返回其操作的可迭代对象的预期运行时类型的方法来拉皮迭代的示例:

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}

新函数 mapOrElsecollect 函数类似,但它允许您传递方法 m: A =>; Unit 除了当 pf 未定义时调用的部分函数 pf 之外。例如,m 可以是一种日志记录方法。

Here is an example of pimping iterables with a method that always returns the expected runtime type of the iterable it operates on:

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}

The new function mapOrElse is similar to the collect function but it allows you to pass a method m: A => Unit in addition to a partial function pf that is invoked whenever pf is undefined. m can for example be a logging method.

可是我不能没有你 2024-10-14 04:09:06

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

An Array is not a Traversable -- trying to work with that as a base class will cause all sorts of problems. Also, it is not immutable either, which makes it completely unsuited to what you want. Finally, Array is an implementation -- try to inherit from traits or abstract classes.

潜移默化 2024-10-14 04:09:06

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

考虑到 Java 数组混乱的差异,如果没有极其令人信服的理由,你真的不想使用它们,因为它们是潜在错误的来源。

(参见此处:http://www.infoq.com/presentations/Java-Puzzlers< /a>)

像这样创建一个基于 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],其中索引基于 1

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

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

Array isn't a typical Scala collection... It's simply a Java array that's pimped to look like a collection by way of implicit conversions.

Given the messed-up variance of Java Arrays, you really don't want to be using them without an extremely compelling reason, as they're a source of lurking bugs.

(see here: http://www.infoq.com/presentations/Java-Puzzlers)

Creaking a 1-based collection like this isn't really a good idea either, as you have no way of knowing how many other collection methods rely on the assumption that sequences are 0-based. So to do it safely (if you really must) you'll want add a new method that leaves the default one unchanged:

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.

Even safer still, you can create a Map[Int,T], with the indices being one-based

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

This is arguably the most "correct" solution, although it will also carry the highest performance cost.

远山浅 2024-10-14 04:09:06

不是一个数组,而是我最近整理的一个基于 1 的不可变 IndexedSeq 实现。我按照此处给出的示例进行操作RNA 类别。在该示例、ScalaDocs 和许多“有用的”编译器错误之间,我设法正确设置它。 OneBasedSeq 的泛化事实使其比 RNA 示例稍微复杂一些。另外,除了示例中扩展的特征和重写的方法之外,我还必须扩展 IterableLike 并重写 iterator 方法,因为各种方法在幕后调用该方法,默认迭代器是从零开始的。

请原谅任何风格或习惯上的奇怪之处;我用 Scala 编程还不到两个月。

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

Not an array, but here's a one-based immutable IndexedSeq implementation that I recently put together. I followed the example given here where they implement an RNA class. Between that example, the ScalaDocs, and lots of "helpful" compiler errors, I managed to get it set up correctly. The fact that OneBasedSeq is genericized made it a little more complex than the RNA example. Also, in addition to the traits extended and methods overridden in the example, I had to extend IterableLike and override the iterator method, because various methods call that method behind the scenes, and the default iterator is zero-based.

Please pardon any stylistic or idiomadic oddities; I've been programming in Scala for less than 2 months.

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
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文