【问题标题】:Extending Scala collections: One based Array index exercise扩展 Scala 集合:基于数组索引的练习
【发布时间】:2011-05-21 21:16:38
【问题描述】:

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

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

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

是否有任何进一步的资源可以帮助解释扩展集合?

【问题讨论】:

    标签: scala scala-collections scala-2.8


    【解决方案1】:

    不是数组,但这是我最近放在一起的基于一个的不可变 IndexedSeq 实现。我按照here 给出的示例进行操作,他们在其中实现了一个 RNA 类。在该示例、ScalaDocs 和许多“有用的”编译器错误之间,我设法正确设置了它。 OneBasedSeq 被泛化的事实使它比 RNA 示例复杂一点。此外,除了示例中扩展的特征和覆盖的方法之外,我还必须扩展 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
      }
    }
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

        考虑到 Java 数组的混乱变化,你真的不想在没有非常令人信服的理由的情况下使用它们,因为它们是潜伏错误的来源。

        (见此处: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】:

          这是一个使用总是返回它所操作的迭代器的预期运行时类型的方法对迭代器进行拉皮条的示例:

          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 =&gt; Unit 以及一个在pf 未定义时调用的部分函数pfm 例如可以是一个日志记录方法。

          【讨论】:

            【解决方案5】:

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

            【讨论】:

            • Bravo 添加信息链接以回答更大的问题,而不仅仅是被问到的问题 ^_^
            • @Dylan 刚刚回答“是否有任何进一步的资源可以帮助解释扩展集合?” :)
            猜你喜欢
            • 1970-01-01
            • 2016-07-08
            • 1970-01-01
            • 2019-08-01
            • 2011-05-17
            • 1970-01-01
            • 2019-07-06
            • 2018-06-12
            相关资源
            最近更新 更多