Как я могу переопределить Tie :: File в Scala? - PullRequest
6 голосов
/ 14 июля 2011

Мне действительно нравится Tie :: File , который позволяет вам tie массив строк файла.Вы можете изменить массив любым способом, и когда вы закончите с ним, вы untie создадите его, и содержимое файла изменится соответствующим образом.

Я хотел бы переопределить такое поведение в Scala, и это то, что я имею до сих пор:

class TiedBuffer(val file:File) extends ArrayBuffer[String] {

  tieFile

  def untie = {
      val writer = new PrintStream(new FileOutputStream(file))
      this.foreach(e => writer.println(e))
      writer.close
      this
  }

  private def tieFile = this ++= scala.io.Source.fromFile(file).getLines()
}

Однако «операторы», определенные в ArrayBuffer, возвращают различные классы, разныечем мой, например:

println((new TiedBuffer(somefile) +: "line0").getClass)

дает мне immutable.Vector.Я мог бы ограничить класс очень небольшим набором предопределенных методов, но я подумал, что было бы неплохо, если бы я мог предложить все из них (foreach / map / ...).

От чего мне наследовать,или как мне подойти к этой проблеме, чтобы у меня был интерфейс, похожий на массив, который позволяет мне изменять содержимое файла?

BOUNTY : чтобы выиграть награду, вы можете показатьрабочий пример, который использует CanBuildFrom для выполнения этой задачи?

Ответы [ 2 ]

4 голосов
/ 15 июля 2011

Расширение существующей коллекции требует определения компоновщика в объекте-компаньоне, таком как

object TiedBuffer {
  implict def canBuildFrom[T] = new CanBuildFrom[TiedBuffer[T],T,TiedBuffer[T]] { ... }
}

Это полностью объясняется здесь:

http://www.scala -lang.org / доку / файлы / сборники-апи / коллекции-impl.html

Как отметил Маркс Джейксела, причина, по которой вы получаете вектор, заключается в том, что вы используете правильные ассоциативные операторы, в противном случае будет выбран неявный конструктор и вы получите ArrayBuffer

4 голосов
/ 15 июля 2011

Методы, заканчивающиеся на двоеточие, ассоциативно справа , поэтому в вашем примере вы вызываете +: из String с TiedBuffer в качестве параметра. Если вы хотите проверить +: из ArrayBuffer, вы можете сделать:

println((new TiedBuffer(somefile).+:("line0")).getClass)

или

println(("line0" +: new TiedBuffer(somefile)).getClass)

EDIT

Я упустил смысл вашего вопроса, см. ответ Джона о возвращении TiedBuffer объектов вместо ArrayBuffer.

EDIT2

Вот пример с CanBuildFrom. Вам придется вызывать tie вручную, хотя, чтобы предотвратить привязку файла каждый раз, когда сборщик создает новый экземпляр TiedBuffer. Есть еще много возможностей для улучшения, например, ++ не будет работать, но оно должно помочь вам начать работу.

import collection.generic.CanBuildFrom
import collection.mutable._
import java.io.{PrintStream, FileOutputStream, File}

class TiedBuffer(val file: File) extends ArrayBuffer[String]
                                 with BufferLike[String, TiedBuffer]
                                 with IndexedSeqOptimized[String, TiedBuffer] {

  def tie = {
    clear
    this ++= scala.io.Source.fromFile(file).getLines()
  }

  def untie = {
    val writer = new PrintStream(new FileOutputStream(file))
    this.foreach(e => writer.println(e))
    writer.close
    this
  }

  override def newBuilder: Builder[String, TiedBuffer] =
    new ArrayBuffer mapResult {
      x: Seq[String] => (new TiedBuffer(file) ++= x)
    }
}

object TiedBuffer {
  implicit def canBuildFrom: CanBuildFrom[TiedBuffer, String, TiedBuffer] =
    new CanBuildFrom[TiedBuffer, String, TiedBuffer] {
      def apply(): Builder[String, TiedBuffer] =
        throw new RuntimeException("Cannot create a new TiedBuffer from scratch")

      def apply(from: TiedBuffer): Builder[String, TiedBuffer] = from.newBuilder
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...