Hallo zusammen,
in Rahmen einer Projektarbeit meines Studiums soll ich eine Anwendung schreiben, die die Simulation von Logikgattern ermöglicht. Dabei gibt es zwei Herausforderungen.
Die nächsten Klassen implementieren alle das interface LogicGate und stellen die fünf grundlegend Gatter zur Verfügung. Beachtet hierbei die copy-Methoden, die dazu dienen, beim späteren Laden einer komplexen Schaltung eine Kopie dieses Gatters zu laden, da ansonsten, die selben Gatter, wie bei der initialen Verschaltung für jede geladen komplexe Schaltung genutzt werden.
Die Klasse Inputgate dient dafür einen Eingang für logische Schaltungen zur Verfügung zu stellen.
Jetzt kommen wir zur problematischen Klasse... die CompsiteGate Klasse. Hier können Eingängen in einer ArrayList gesetzt werden und Outputs in einer ArrayList gesetzt werden. In der Outputs ArrayList wird an jedem Index, die Schaltung gespeichert, die dem entsprechenden Ausgang einer komplexen Schaltung zugrunde liegt. (Bei einem Halbaddierer beim ersten Ausgang (Index 0) für die Summe also die Schaltung für ein XOR und am Index 1 also ein einzelnes And Gate). Das Problem ist, dass ich keine Methode habe, die Eingänge in der Inputs Liste den Schaltungen in der Outputs Liste zur Verfügung zu stellen. Ich lasse bewusst alle meine Ansätze weg, die ich hatte, da mich sowas persönlich nur selbst verwirrt, wenn ich neue Ansätze suche.
Es folgt noch die CircuteManager-Klasse, die es ermöglicht eine Schaltung zu speichern und wieder zu laden. Dafür wird eine HashMap genutzt.
Zu guter letzt noch einige Anwendungsbeispiele. Beachte hierbei, dass zum Testen verbotenerweise System.out.println() genutzt wurde.
Dieser Code wird beim Laden der Quellcodedatei mit in die JShell geladen. Bei einem korrekt verschalteten fullAdder müsste die asserts Stimmen. Tue sie aber nicht. Der tatsächliche Output des Fulladders am Index 1 ist true, da dem ersten Halbdaddierer immer noch die InputGates x1 und x2 zugrunde liegen.
Falls jemand eine Idee für dieses Problem hat, gerne her damit. Ich persönlich bin mittlerweile komplett Überfragt, da alle meine ursprünglichen Ideen, einfach gar nichts getan haben.
in Rahmen einer Projektarbeit meines Studiums soll ich eine Anwendung schreiben, die die Simulation von Logikgattern ermöglicht. Dabei gibt es zwei Herausforderungen.
- Die Interaktion erfolgt rein über die JShell und System.out.println() ist verboten. Stattdessen wird ein von meinem Dozenten selbst geschriebenes Programm verwendet um die Ausgaben eines Logikgatters darzustellen. Das soll hier aber erstmal keine Rolle spielen. Ich stehe aktuell vor einer anderen Herausforderung, ich gebe dieses Information nur weiter, damit jedem klar ist, was genau gefordert ist und wie der Rahmen der Aufgabe ist.
- Ich habe bereits einen, meiner Meinung nach, guten Code entwickelt, der die fünf Grundlegenden Gatter, "And", "Or ", "Not", "Nand" und "Nor" umsetzt. Diese können zu komplexeren Schaltungen, wie einem "XOR" verschaltet werden und diese Bausteine können auch gespeichert und wiederverwendet werden. Dies wird bei späteren Aufgaben relevant. Ziel ist es nämlich aus mehreren solcher Bausteine einzelne Bestandteile einer CPU zu schalten. Dabei entsteht folgendes Problem: Schaltet man einen "Halbaddierer" aus einem XOR Baustein und einem And-Gatter und speichert diesen ab und schaltet dann zwei dieser Halbaddierer mit einem Or zusammen sollte ein Volladdierer entstehen, das scheitert aber an folgenden Punkt:
Bei einem komplexen Gatter (CompositeGate-Klasse) können zwar die Eingänge gesetzt werden, werden aber nicht der eigentlichen Logik als Eingang zur Verfügung gestellt. Diese bleiben weiterhin bei den bei der initial geschalteten Eingängen. Und für dieses Problem bräuchte ich eine Methode, die diese Verknüpfung herstellt. Sprich, die Eingänge, die sich eine komplexe Schaltung speichert, auch den Schaltungen, die ihr zu Grunde liegen, als Eingang sitzt.
An bei der Code mit einigen Ausführungen.
Java:
// Interface für allgemeines Logikgatter
interface LogicGate {
boolean output();
LogicGate copy();
}
// Abstrakte Klasse für Gatter mit zwei Eingängen
abstract class BinaryGate implements LogicGate {
LogicGate input1, input2;
public void setInput1(LogicGate input1) {
this.input1 = input1;
}
public void setInput2(LogicGate input2) {
this.input2 = input2;
}
public LogicGate getInput1() {
return input1;
}
public LogicGate getInput2() {
return input2;
}
}
Die nächsten Klassen implementieren alle das interface LogicGate und stellen die fünf grundlegend Gatter zur Verfügung. Beachtet hierbei die copy-Methoden, die dazu dienen, beim späteren Laden einer komplexen Schaltung eine Kopie dieses Gatters zu laden, da ansonsten, die selben Gatter, wie bei der initialen Verschaltung für jede geladen komplexe Schaltung genutzt werden.
Java:
// Klasse für Logisches UND
class AndGate extends BinaryGate {
AndGate(LogicGate input1, LogicGate input2) {
this.input1 = input1;
this.input2 = input2;
}
AndGate(boolean input1, boolean input2) {
this.input1 = new InputGate(input1, "input 1");
this.input2 = new InputGate(input2, "input 2");
}
@Override
public boolean output() {
return input1.output() && input2.output();
}
public AndGate copy() {
return new AndGate(input1.copy(), input2.copy());
}
}
// Klasse für Logisches ODER
class OrGate extends BinaryGate {
OrGate(LogicGate input1, LogicGate input2) {
this.input1 = input1;
this.input2 = input2;
}
OrGate(boolean input1, boolean input2) {
this.input1 = new InputGate(input1, "input 1");
this.input2 = new InputGate(input2, "input 2");
}
@Override
public boolean output() {
return input1.output() || input2.output();
}
public OrGate copy() {
return new OrGate(input1.copy(), input2.copy());
}
}
// Klasse für Logisches NICHT
class NotGate implements LogicGate {
LogicGate input;
NotGate(LogicGate input) {
this.input = input;
}
void setInput(LogicGate input) {
this.input = input;
}
LogicGate getInput() {
return input;
}
@Override
public boolean output() {
return !input.output();
}
public NotGate copy() {
return new NotGate(input.copy());
}
}
// Klasse für NAND
class NandGate extends BinaryGate {
NandGate(LogicGate input1, LogicGate input2) {
this.input1 = input1;
this.input2 = input2;
}
@Override
public boolean output() {
return !(input1.output() && input2.output());
}
public NandGate copy() {
return new NandGate(input1.copy(), input2.copy());
}
}
// Klasse für NOR
class NorGate extends BinaryGate {
NorGate(LogicGate input1, LogicGate input2) {
this.input1 = input1;
this.input2 = input2;
}
@Override
public boolean output() {
return !(input1.output() || input2.output());
}
public NorGate copy() {
return new NorGate(input1.copy(), input2.copy());
}
}
Die Klasse Inputgate dient dafür einen Eingang für logische Schaltungen zur Verfügung zu stellen.
Java:
// Klasse für statische Eingaben (Konstanten)
class InputGate implements LogicGate {
boolean value;
String name;
InputGate(boolean value, String name) {
this.value = value;
this.name = name;
}
void setValue(boolean value) {
this.value = value;
}
@Override
public boolean output() {
return value;
}
@Override
public String toString() {
return name;
}
public InputGate copy() {
return new InputGate(value, name);
}
}
Jetzt kommen wir zur problematischen Klasse... die CompsiteGate Klasse. Hier können Eingängen in einer ArrayList gesetzt werden und Outputs in einer ArrayList gesetzt werden. In der Outputs ArrayList wird an jedem Index, die Schaltung gespeichert, die dem entsprechenden Ausgang einer komplexen Schaltung zugrunde liegt. (Bei einem Halbaddierer beim ersten Ausgang (Index 0) für die Summe also die Schaltung für ein XOR und am Index 1 also ein einzelnes And Gate). Das Problem ist, dass ich keine Methode habe, die Eingänge in der Inputs Liste den Schaltungen in der Outputs Liste zur Verfügung zu stellen. Ich lasse bewusst alle meine Ansätze weg, die ich hatte, da mich sowas persönlich nur selbst verwirrt, wenn ich neue Ansätze suche.
Java:
class CompositeGate implements LogicGate {
List<LogicGate> inputs;
List<LogicGate> outputs;
CompositeGate(List<LogicGate> inputs, LogicGate... outputs) {
this.inputs = inputs;
this.outputs = new ArrayList<>();
for (LogicGate logicGate : outputs) {
this.outputs.add(logicGate);
}
}
CompositeGate(List<LogicGate> inputs, List<LogicGate> outputs) {
this.inputs = inputs;
this.outputs = outputs;
}
@Override
public boolean output() {
// assert outputs.size() == 1 : "Diese Methode funktioniert für komplexe Schaltungen mit mehr als einem Ausgang nicht";
return getOutput(0);
}
public boolean getOutput(int index) {
assert index >= 0 && index < outputs.size() : "Output nicht vorhanden";
return outputs.get(index).output();
}
public void setInputs(List<LogicGate> inputs) {
this.inputs = inputs;
}
void setOutputs(List<LogicGate> outputs) {
this.outputs = outputs;
}
public CompositeGate copy() {
List<LogicGate> copyedInputs = new ArrayList<LogicGate>();
for (LogicGate input : inputs) {
copyedInputs.add(input.copy());
}
List<LogicGate> copyeOutputs = new ArrayList<LogicGate>();
for (LogicGate output : outputs) {
copyeOutputs.add(output.copy());
}
return new CompositeGate(copyedInputs, copyeOutputs);
}
}
Es folgt noch die CircuteManager-Klasse, die es ermöglicht eine Schaltung zu speichern und wieder zu laden. Dafür wird eine HashMap genutzt.
Java:
class CircuitManager {
Map<String, CompositeGate> circuitMap;
CircuitManager() {
circuitMap = new HashMap<>();
}
void saveCircuit(String name, CompositeGate circuit) {
circuitMap.put(name, circuit);
}
CompositeGate loadCircuit(String name) {
return circuitMap.get(name).copy();
}
boolean circuitExists(String name) {
return circuitMap.containsKey(name);
}
}
Zu guter letzt noch einige Anwendungsbeispiele. Beachte hierbei, dass zum Testen verbotenerweise System.out.println() genutzt wurde.
Java:
CircuitManager manager = new CircuitManager();
// Beispiel: Halbaddierer
InputGate x1 = new InputGate(true, "x1");
InputGate x2 = new InputGate(false, "x2");
CompositeGate xor = new CompositeGate(Arrays.asList(x1, x2), new NandGate(x1, x2), new OrGate(x1, x2));
manager.saveCircuit("Xor", xor);
CompositeGate laodedXor = manager.loadCircuit("Xor");
CompositeGate halfAdder = new CompositeGate(Arrays.asList(x1, x2), laodedXor, new AndGate(x1, x2));
System.out.println("HalfAdder Sum = " + halfAdder.getOutput(0));
System.out.println("HalfAdder Carry = " + halfAdder.getOutput(1));
manager.saveCircuit("HalfAdder", halfAdder);
// Testen der gespeicherten Schaltung
CompositeGate loadedHalfAdder = manager.loadCircuit("HalfAdder");
assert loadedHalfAdder.getOutput(0) == true;
assert loadedHalfAdder.getOutput(1) == false;
// Beispiel: Volladdierer
InputGate a1 = new InputGate(false, "a1");
InputGate b1 = new InputGate(false, "b1");
InputGate cin = new InputGate(false, "cin");
CompositeGate halfAdder1 = manager.loadCircuit("HalfAdder");
halfAdder1.setInputs(Arrays.asList(a1, b1));
System.out.println("Halfadder 1 Inputs: " + halfAdder1.inputs);
for (LogicGate gate : halfAdder1.inputs) {
System.out.println(gate.output());
}
CompositeGate halfAdder2 = manager.loadCircuit("HalfAdder");
InputGate temp = new InputGate(halfAdder1.getOutput(0), "Sum HA 1");
System.out.println("Halfadder 2 Inputs: " + halfAdder2.inputs);
for (LogicGate gate : halfAdder2.inputs) {
System.out.println(gate.output());
}
System.out.println("Halfadder 1 Inputs: " + halfAdder1.inputs);
assert halfAdder1.inputs.equals(Arrays.asList(a1, b1));
System.out.println("\nHalfAdder 1:");
for (LogicGate gate : halfAdder1.inputs) {
System.out.println(gate.output());
}
System.out.println("halfAdder1 sum = " + halfAdder1.getOutput(0) + "\nhalfAdder1 carry = " + halfAdder1.getOutput(1));
LogicGate sum = new OrGate(halfAdder1.getOutput(0), halfAdder2.getOutput(0));
LogicGate carry = new OrGate(halfAdder1.getOutput(1), halfAdder2.getOutput(1));
CompositeGate fullAdder = new CompositeGate(Arrays.asList(a1, b1, cin), sum, carry);
assert fullAdder.getOutput(0) == false;
assert fullAdder.getOutput(1) == false;
Falls jemand eine Idee für dieses Problem hat, gerne her damit. Ich persönlich bin mittlerweile komplett Überfragt, da alle meine ursprünglichen Ideen, einfach gar nichts getan haben.