Your code is neat, and structured just like I would expect to see Java ;-) - separate files for each "class".
In your case, though, the separation in to files is probably over the top, and if you are talking idiomatic go, I would not expect to see separate files for the NotGate and the XorGate.
There is a small bug in your code, you use: fmt.Print("%d: {%t, %t}\n", i, w.BeginningValue, w.EndValue)
where that should be a Printf
and not a Print
.
You have declared the Gate
interface, and I understand why you have it, but there's a subtle thing about interfaces in go, they are not the same as Java. Go uses duck-typing, and, because you have an Execute
method on the pointer to Wire
, it is also actually a Gate
too. You may not be expecting that.
You have an inconsistency in your pointers too. You keep your wires
as pointers to your Wire
instances, but you keep your gates
as the actual Gate values.
I would expect that your Execute
methods on the Notgate
and XorGate
should be on the pointer to them, not on the actual gate. Thus, your methods are currently:
func (gate XorGate) Execute() {
gate.Output.BeginningValue = gate.A.EndValue != gate.B.EndValue
}
func (gate NotGate) Execute() {
gate.Output.BeginningValue = !gate.A.EndValue
}
but should be:
func (gate *XorGate) Execute() {
gate.Output.BeginningValue = gate.A.EndValue != gate.B.EndValue
}
func (gate *NotGate) Execute() {
gate.Output.BeginningValue = !gate.A.EndValue
}
This makes the *XorGate
and the *NotGate
implement the Gate
interface. Before it was the XorGate
and NotGate
that implemented it. I know, subtle difference, but, what this means is that your line of code:
gates := []Gate{ga, gb}
can be:
gates := []Gate{&ga, &gb}
The value of the above changes is that now you have consistency in the use of gates and wires.
Talking wires, your initialization of your various wires would be considered redundant in go - in go, it is idiomatic to ignore the specification of zero-values. The zero-value of a boolean is false
, so specifying it is wrong. Your code:
a := Wire{false, false}
b := Wire{false, false}
c := Wire{false, false}
d := Wire{false, false}
Should just be:
a := Wire{}
b := Wire{}
c := Wire{}
d := Wire{}
Note, in the above, that a
, b
, etc. all are actual wires. It would also be idiomatic to resort to having everything in pointer-space, and use the new
keyword.... new(...)
returns a pointer to the value.
The result would be:
a := new(Wire)
b := new(Wire)
c := new(Wire)
d := new(Wire)
ga := &NotGate{a, b}
gb := &XorGate{b, c, d}
wires := []*Wire{a, b, c, d}
gates := []Gate{ga, gb}
Note, taking the pointer at the initialization of the NotGate
and XorGate
brings those in to pointer-space as well.
You are missing a bunch of documentation on the exported methods, types and constants ;-)
All told, I would expect your program to look like:
package main
import (
"fmt"
)
const ITERATIONS = 10
type Wire struct {
BeginningValue bool
EndValue bool
}
func (wire *Wire) Execute() {
wire.EndValue = wire.BeginningValue
}
type Gate interface {
Execute()
}
type XorGate struct {
A *Wire
B *Wire
Output *Wire
}
func (gate *XorGate) Execute() {
gate.Output.BeginningValue = gate.A.EndValue != gate.B.EndValue
}
type NotGate struct {
A *Wire
Output *Wire
}
func (gate *NotGate) Execute() {
gate.Output.BeginningValue = !gate.A.EndValue
}
func main() {
a := new(Wire)
b := new(Wire)
c := new(Wire)
d := new(Wire)
ga := &NotGate{a, b}
gb := &XorGate{b, c, d}
wires := []*Wire{a, b, c, d}
gates := []Gate{ga, gb}
for i := 0; i < ITERATIONS; i++ {
for _, w := range wires {
fmt.Printf("%d: {%t, %t}\n", i, w.BeginningValue, w.EndValue)
w.Execute()
}
for _, g := range gates {
g.Execute()
}
}
}
See it in the playground here: https://play.golang.org/p/VdN4driYRn
Finally, I take some exception to the actual circuit nature of your code ;-) A Wire does not have different beginning and ending values - a wire is a single-state thing.... I am not sure why your values change on each iteration, I think your simulation is based on a broken understanding of logic. I would instead model the circuit as having a "Value()" and then find a way to chase back (recursively) what that Value would be when called.