【问题标题】:How could I reimplement Tie::File in Scala?如何在 Scala 中重新实现 Tie::File?
【发布时间】:2011-10-05 15:16:03
【问题描述】:

我真的很喜欢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 来完成此任务的工作示例?

【问题讨论】:

  • 您知道,Tie::File 实际上比您在此处显示的要复杂。它不仅仅是在您绑定时读取文件并在您解绑时写入文件。它在您使用数组时读取和写入文件(通过一些优化来提高性能)。但即使是这样的基本实现仍然很有用。
  • @cjm,我当然知道。 Tie::File 比我在这里展示的要复杂得多。但我希望阅读本文的人能够清楚地了解发生了什么以及该模块为何有用。

标签: perl scala


【解决方案1】:

以冒号结尾的方法是right associative,因此在您的示例中,您使用TiedBuffer 作为参数调用String+:。如果你想从ArrayBuffer 测试+:,你可以这样做:

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

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

编辑

我错过了您问题的重点,请参阅 John 的 answer 以返回 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
    }
}

【讨论】:

    【解决方案2】:

    扩展现有集合需要在伴随对象中定义一个构建器,例如

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

    这里有完整的解释:

    http://www.scala-lang.org/docu/files/collections-api/collections-impl.html

    正如马克思 Jayxcela 所指出的,您获得 Vector 的原因是您使用了正确的关联运算符,否则将选择一个隐式构建器并且您将获得一个 ArrayBuffer

    【讨论】:

      猜你喜欢
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-08
      • 2016-12-10
      • 1970-01-01
      • 2011-02-01
      • 2011-09-26
      相关资源
      最近更新 更多