2017-02-20 4 views
0

私はクラスGraphとサブクラスMyGraphを持っていると言って、Graphにはメソッドbuildがあり、独自のビルドプロセスを定義できます。継承を伴うスカラのフィールド初期化とコンストラクタシーケンス

MyGraphには、nodeのフィールドが宣言されています。これはビルドメソッドでは初期化する必要があります。したがって、私はそれがnullであると宣言します。しかし、MyGraphインスタンスを作成すると、実行順序は最初にMyGraph.buildになり、次にvar node : Node = nullになり、ノードはnullのままになります。

親から呼び出されたメソッドでこのようなフィールドを初期化する正しい方法は何ですか?

class Graph { 
    def build:Unit = Unit 
    build 
} 

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

[編集]私の使用事例の詳細: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

ファーストd。

コードは今あるように、私は考えることができる唯一の解決策は、以下の通りである。特定の問題に

trait Graph 

case class MyGraph(node: Node) extends Graph 

val myGraph = MyGraph(new Node) 

しかし、より多くの詳細がより適切な解決策をもたらすかもしれません。

EDIT:あなたは、次の

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メソッドを削除し、値を初期化する代わりに、コンストラクタを使用していました。また、出力ノードをNoneGraphSome(add)LinearRegGraphに初期化しました。

Graphは、あなたが次のことを試みることができる出力を持っていることはありませんので、あなたの代わりにOptionを使用しない場合:

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

投稿したコードの2番目のバージョンでは、 'output'はメソッドとして定義されているので、使用するたびに'output'メソッド呼び出しがあり、出力値が再計算されます。 – acidghost

1

ScalaではJavaのnullを使用しないでください。

あなたのフィールドが常に定義されていない場合はOption[Node]を使用することをお勧めします。フィールドが初期化されるのを待つようにしたい場合はフィールドは初期化の前にアクセスする危険性がありませんは、次の構文を使用できます。

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

出典:私はvarを使用しないように、あなたを提案してval insteaを使用して、より機能的なアプローチを使用することができ、コードをリファクタリングしようとするすべてのalvinalexander.com

+0

これはクールです!私は前にスカラーでこれを行うことができるのか分からなかった。ありがとう! – Harper

関連する問題