patterngoMinor
Circuit Simulator
Viewed 0 times
simulatorcircuitstackoverflow
Problem
I'm trying to learn Golang from Java, and to do this, I wrote a basic circuit simulator with only two circuits. Is there anything I can do better to make it more idiomatic?
Simulator.go
XorGate.go
NotGate.go
Simulator.go
package main
import (
"fmt"
)
const ITERATIONS = 10
func main() {
a := Wire{false, false}
b := Wire{false, false}
c := Wire{false, false}
d := Wire{false, false}
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.Print("%d: {%t, %t}\n", i, w.BeginningValue, w.EndValue)
w.Execute()
}
for _, g := range gates {
g.Execute()
}
}
}
type Wire struct {
BeginningValue bool
EndValue bool
}
func (wire *Wire) Execute() {
wire.EndValue = wire.BeginningValue
}
type Gate interface {
Execute()
}XorGate.go
package main
import ()
type XorGate struct {
A *Wire
B *Wire
Output *Wire
}
func (gate XorGate) Execute() {
gate.Output.BeginningValue = gate.A.EndValue != gate.B.EndValue
}NotGate.go
package main
import ()
type NotGate struct {
A *Wire
Output *Wire
}
func (gate NotGate) Execute() {
gate.Output.BeginningValue = !gate.A.EndValue
}Solution
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:
You have declared the
You have an inconsistency in your pointers too. You keep your
I would expect that your
but should be:
This makes the
can be:
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
Should just be:
Note, in the above, that
The result would be:
Note, taking the pointer at the initialization of the
You are missing a bunch of documentation on the exported methods, types and constants ;-)
All told, I would expect your program to look like:
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.
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.
Code Snippets
func (gate XorGate) Execute() {
gate.Output.BeginningValue = gate.A.EndValue != gate.B.EndValue
}
func (gate NotGate) Execute() {
gate.Output.BeginningValue = !gate.A.EndValue
}func (gate *XorGate) Execute() {
gate.Output.BeginningValue = gate.A.EndValue != gate.B.EndValue
}
func (gate *NotGate) Execute() {
gate.Output.BeginningValue = !gate.A.EndValue
}gates := []Gate{ga, gb}gates := []Gate{&ga, &gb}a := Wire{false, false}
b := Wire{false, false}
c := Wire{false, false}
d := Wire{false, false}Context
StackExchange Code Review Q#121245, answer score: 7
Revisions (0)
No revisions yet.