Расширение коллекций Scala: одно основанное на индексе упражнение - PullRequest
6 голосов
/ 08 декабря 2010

В качестве упражнения я хотел бы расширить коллекцию Scala Array до моего собственного OneBasedArray (выполняет то, что вы ожидаете, индексирование начинается с 1).Поскольку это неизменяемая коллекция, я хотел бы, чтобы она возвращала правильный тип при вызове filter / map и т. Д.

Я прочитал ресурсы здесь , здесь и здесь , но я пытаюсь понять, как перевести это на Массивы (или коллекции, отличные от тех, что в примерах).Я на правильном пути с такой структурой?

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

Есть ли какие-либо дополнительные ресурсы, которые помогут объяснить расширение коллекций?

Ответы [ 5 ]

5 голосов
/ 08 декабря 2010
  • Для углубленного обзора API новых коллекций: API коллекций Scala 2.8
  • Для хорошего обзора взаимосвязи между основными классами и признаками this

Кстати, я не думаю, что Array - это коллекция в Scala.

3 голосов
/ 08 декабря 2010

Array не является Traversable - попытка работать с ним как с базовым классом вызовет всевозможные проблемы.Кроме того, он также не является неизменным, что делает его совершенно неподходящим для того, что вы хотите.Наконец, Array является реализацией - попробуйте наследовать от черт или абстрактных классов.

3 голосов
/ 08 декабря 2010

Вот пример выполнения итераций pimping с помощью метода, который всегда возвращает ожидаемый тип времени выполнения итерируемого, на котором он работает:

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, которая вызывается всякий раз, когда pf не определено.m может быть, например, методом регистрации.

2 голосов
/ 08 декабря 2010

Массив - это не типичная коллекция 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

Это, пожалуй, самое «правильное» решение, хотя оно также будет иметь самую высокую стоимость производительности.

0 голосов
/ 13 февраля 2013

Не массив, а вот основанная на неизменяемости реализация IndexedSeq, которую я недавно собрал.Я последовал примеру, приведенному здесь , где они реализуют класс RNA.Между этим примером, ScalaDocs и множеством «полезных» ошибок компилятора, мне удалось правильно его настроить.Тот факт, что OneBasedSeq является обобщенным, сделал его немного более сложным, чем пример RNA.Кроме того, в дополнение к расширенным признакам и переопределенным методам в примере мне пришлось расширить IterableLike и переопределить метод iterator, потому что различные методы вызывают этот метод за кулисами, а итератор по умолчанию равен нулю.1005 *

Прошу прощения за любые стилистические или идиомадные странности;Я программирую в 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
  }
}
...