2017-02-20 65 views
0

说我有一个类Graph和子类MyGraph,Graph有一个方法build允许子类定义自己的构建过程。字段初始化和继承的斯卡拉构造函数序列

MyGraph中,我声明了一个字段node,它应该是构建方法中的init。因此我宣布它为空。但是,当我创建MyGraph实例时,执行顺序首先转到MyGraph.build,然后var node : Node = null,这会使节点为空。

在从父对象调用的方法中初始化此类字段的正确方法是什么?

class Graph { 
    def build:Unit = Unit 
    build 
} 

class MyGraph extends Graph { 
    var node : Node = null 
    override def build:Unit = { 
     node = new Node 
    } 
} 

[编辑]更多关于我的用例的细节:Graph类代表一个Computational Graph,它包含计算任务的节点。该图有一些输入和一个输出。在子类中,我需要公开输入节点以供用户提供输入数据。以下是一些提供更多细节的代码。

class Graph { 
    val inputs = new ArrayBuffer[InputNode]() 
    var output: Node = null 

    def build:Unit = Unit 
    build 

    def newInput(): InputNode = { 
     val in = new InputNode() 
     inputs += in 
     in 
    } 

    def setOutput(out: Node) { 
     this.output = out 
    } 

    def compute():Unit = { 
     inputs.foreach(_.computeAndForward()) 
    } 
} 

class LinearRegGraph extends Graph { 
    var w : InputNode = null 
    var x : InputNode = null 
    var b : InputNode = null 

    override def build:Unit = { 
     w = newInput() 
     x = newInput() 
     b = newInput() 
     val mul = new MulNode(w,x) 
     val add = new AddNode(mul, b) 
     setOutput(add) 
    } 
} 

object Main extends App { 
    val graph = new LinearRegGraph() 

    graph.x.setData(...) 
    graph.w.setData(...) 
    graph.b.setData(...) 

    graph.compute() 
    graph.output.getData() 
} 

我目前使用以下临时解决方案。但是,这些代码易受节点构建顺序的影响,我不喜欢它。

class LinearRegGraph extends Graph { 
    def w = inputs(0) 
    def x = inputs(1) 
    def b = inputs(2) 

    override def build:Unit = { 
     val w = newInput() 
     val x = newInput() 
     val b = newInput() 
     val mul = new MulNode(w,x) 
     val add = new AddNode(mul, b) 
     setOutput(add) 
    } 
} 
+0

如果你可以给你的具体使用情况下更多的细节,我可以帮你通过一些得到重构 – acidghost

+0

@acidghost当然!我已经添加了关于我正在处理的问题的更详细的解释。 – Harper

回答

1

首先我会建议你避免使用var并尝试重构你的代码中使用使用val instea功能更强大的方法d。

由于代码现在,我能想到的唯一的解决办法是:您的具体问题

trait Graph 

case class MyGraph(node: Node) extends Graph 

val myGraph = MyGraph(new Node) 

但更多的细节可能会产生更适合的解决方案。

编辑:你可以做以下

abstract class Graph { 
    val inputs = new ArrayBuffer[InputNode]() 
    val output: Option[Node] = None 

    def newInput(): InputNode = { 
     val in = new InputNode() 
     inputs += in 
     in 
    } 

    def compute():Unit = { 
     inputs.foreach(_.computeAndForward()) 
    } 
} 

class LinearRegGraph extends Graph { 
    val w: InputNode = newInput() 
    val x: InputNode = newInput() 
    val b: InputNode = newInput() 

    val output = { 
     val mul = new MulNode(w,x) 
     Some(new AddNode(mul, b)) 
    } 
} 

object Main extends App { 
    val graph = new LinearRegGraph() 

    graph.x.setData(...) 
    graph.w.setData(...) 
    graph.b.setData(...) 

    graph.compute() 
    graph.output.get.getData() 
} 

我删除了build方法和所用的构造函数,而不是初始化值。我还将输出节点初始化为None,GraphSome(add),LinearRegGraph

如果你不是不想使用Option因为Graph将永远不会有输出,你可以尝试以下方法:

trait Graph { 
    val inputs = new ArrayBuffer[InputNode]() 
    def output: Node 

    def newInput(): InputNode = { 
     val in = new InputNode() 
     inputs += in 
     in 
    } 

    def compute():Unit = { 
     inputs.foreach(_.computeAndForward()) 
    } 
} 

class LinearRegGraph extends Graph { 
    val w: InputNode = newInput() 
    val x: InputNode = newInput() 
    val b: InputNode = newInput() 

    override def output = { 
     val mul = new MulNode(w,x) 
     new AddNode(mul, b) 
    } 
} 

object Main extends App { 
    val graph = new LinearRegGraph() 

    graph.x.setData(...) 
    graph.w.setData(...) 
    graph.b.setData(...) 

    graph.compute() 
    graph.output.getData() 
} 
+0

感谢您的详细解释。只关心使用构造函数而不使用构建方法:这将使所有中间变量(如mul和add)最终成为类变量,我不想保留它们。如果我建立一个大图,将会有很多这样的变量。我宁愿把它们当作局部变量。你有什么建议如何以优雅的方式做到这一点? – Harper

+0

我更新了我的答案。你可以在块内移动你想成为本地变量' – acidghost

+0

请注意/小心在我发布的代码的第二个版本中,'output'被定义为一种方法,以便每次使用'output'将会有一个方法调用,并且输出值将被重新计算。 – acidghost

1

你应该避免在Scala中使用Java的null

人们应该更喜欢使用Option[Node]如果你的领域是没有定义的时候,或者在你的情况,如果你只是想你的领域等待初始化和领域不具有初始化之前被访问的任何风险,你可以使用这个语法:

class MyGraph extends Graph { 
    var node: Node = _ 
    override def build: Unit { 
    node = new Node 
    } 
} 

来源:alvinalexander.com

+0

这很酷!我不知道你可以在Scala之前做到这一点。谢谢! – Harper