我试图用包含标签和goto的命令为一种小型语言创建一个解析器:
...
lazy val cmds = opt("{")~>rep(cmd<~opt(";"))<~opt("}") ^^ {...}
lazy val cmd = ("if"~boolexpr~"then"~cmds~"else"~cmds
^^ { case _~b~_~c1~_~c2 => IFCMD(boolexpr,c1
| ident ~":="~numericLit ^^ {case i1~_~v => ASSIGN(i1,v) }
| "goto" ~>ident ^^ { case l => GOTO(l) }
| ident~":"~cmd ^^ { case l~_~c => <APPENDLABELTO_CORE>
...
GOTO
、IFCMD
等是扩展抽象类Core
的案例类。
为了与函数式/scala类/不可变对象-way保持一致,我认为这样定义Core
是错误的
abstract class Core(var label:Option[String] = None )
但请允许我用<APPENDLABELTO_CORE>
替换为:
| ident~":"~cmd ^^ { case l~_~c => c.label = Some(l); c }
有人能指出做这件事的“标尺”方法吗?
(我尝试过c copy (label=Some(l))
,但抽象基类没有自动复制构造函数魔法)
发布于 2010-08-11 06:54:55
完全可以创建您自己的类似复制的方法:
abstract class Core(val label: Option[String]) {
def set(label: Option[String]): Core
}
class Impl(label: Option[String] = None) extends Core(label) {
def set(label: Option[String] = this.label) = new Impl(label)
}
用于:
scala> val i = new Impl
i: Impl = Impl@1930ebb
scala> i.label
res0: Option[String] = None
scala> i.set(label = Some("thing"))
res1: Impl = Impl@b28f30
scala> res1.label
res2: Option[String] = Some(thing)
但是,从务实的角度来看,我不会太快地拒绝在这里使用vars。关于不可变值的推理比较容易,但据我所知,在解析器中得到的值是非常独立的。另一种方法是创建一个方法,在最后将所有数据转换为不可变的版本,或者如果解析器代码最终将所有数据存储在其他地方,只会使其变。
另一种方法是使抽象类不抽象,而实际上使其成为case类。您可以从案例类派生类(但是,从案例类派生案例类是无-否的)。然后,诀窍是将您可能需要的变量数据保存在一个字段中:
abstract class SpecializedStuff { }
case class ParticularStuff(val i: Int) extends SpecializedStuff
case class Core(details: SpecializedStuff, label: Option[String] = None)
class Impl(p: ParticularStuff) extends Core(p)
scala> val i = new Impl( ParticularStuff(5) )
i: Impl = Core(ParticularStuff(5),None)
scala> i.copy(label = Some("thing"))
res0: Core = Core(ParticularStuff(5),Some(thing))
https://stackoverflow.com/questions/3459187
复制