Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Hallo zusammen,
ich finde den Fehler im folgenden Programm nicht. Das Programm soll die größte Anzahl der zwischen 2 gleichen Buchstaben liegenden Buchstaben finden.
Beispiel mit 'a' und 'z': "Das Haus ist schön im Wald ztestz"
"Das Haus ist schön im Wald ztestz"
"Das Haus ist schön im Wald ztestz"
Das Programm sollte dann ausgeben:
"a Ergebnis: 17"
"z Ergebnis: 4"
Aber es gibt aus:
"a Ergebnis: 17"
"z Ergebnis: 26"
Das Programm zählt 'z' von Anfang an schon +1.
Ich weiß nicht woran das liegt.
Ich bin für jede Hilfe dankbar.
Gruß
Unten der Code
Java:
public static void main(String[] args) {
String text = "Das Haus ist schön im Wald ztestz";
String textklein = text.toLowerCase();
System.out.println(text);
char[] buchstaben = textklein.toCharArray();
int zahl_a = 0;
int zahl_z = 0;
int hoechste_a = 0;
int hoechste_z = 0;
boolean a = false;
boolean z = false;
for(int i = 0; i<textklein.length(); i++){
switch(buchstaben[i]){
case 'a':while(hoechste_a<zahl_a){
hoechste_a = zahl_a;
}
zahl_a = 0;
a = true;
break;
case 'z':while(hoechste_z<zahl_z){
hoechste_z = zahl_z;
}
zahl_z = 0;
z = true;
break;
}
if(a = true){
zahl_a = zahl_a +1;
}
if(z = true){
zahl_z = zahl_z +1;
}
}
System.out.println("a Ergebnis: "+(hoechste_a-1));
System.out.println("z Ergebnis: "+(hoechste_z-1));
}
Also ich muss gestehen, dass ich nicht verstehe, wie Du da eigentlich vorgehen willst.
Bei den while Schleifen reicht ein einfaches if. Die Zuweisung wird ja nicht mehrfach durchlaufen.
Und wenn ich das richtig sehe, dann zählt er sofort eins hoch. Also er findet das erste a. Daher wird dann zahl_a = 0 und a=true gesetzt. Und dann wird zahl_a = zahl_a+1 gerechnet. Das Gleiche bei z.
Dann auch noch das if (a = true) - das ist immer wahr. Das ist kein vergleich sondern eine Zuweisung. Vergleich ist mit ==.
Da es aber schon ein Boolean ist, kannst Du einfach if(a) und if(z) sagen.
@SuppressWarnings("resource")
public static void main(String[] args) {
String s = new Scanner(System.in).nextLine();
int first = 0;
int second = 0;
int max = 0;
for (int i = 0; i < s.length(); i++) {
for (int j = i + 1; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j)) {
if ((j - i) > max) {
first = i;
second = j;
max = j - i;
}
j = s.length();
}
}
}
System.out.println(s.charAt(first) + " " + s.charAt(second) + " " + first + " " + second + " " + max);
}
Also ich muss gestehen, dass ich nicht verstehe, wie Du da eigentlich vorgehen willst.
Bei den while Schleifen reicht ein einfaches if. Die Zuweisung wird ja nicht mehrfach durchlaufen.
Und wenn ich das richtig sehe, dann zählt er sofort eins hoch. Also er findet das erste a. Daher wird dann zahl_a = 0 und a=true gesetzt. Und dann wird zahl_a = zahl_a+1 gerechnet. Das Gleiche bei z.
Dann auch noch das if (a = true) - das ist immer wahr. Das ist kein vergleich sondern eine Zuweisung. Vergleich ist mit ==.
Da es aber schon ein Boolean ist, kannst Du einfach if(a) und if(z) sagen.
@SuppressWarnings("resource")
public static void main(String[] args) {
String s = new Scanner(System.in).nextLine();
int first = 0;
int second = 0;
int max = 0;
for (int i = 0; i < s.length(); i++) {
for (int j = i + 1; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j)) {
if ((j - i) > max) {
first = i;
second = j;
max = j - i;
}
j = s.length();
}
}
}
System.out.println(s.charAt(first) + " " + s.charAt(second) + " " + first + " " + second + " " + max);
}
public class start {
public final static int INIT_COUNT = -1;
public static void main(String[] args) {
char[] letters = { 'a', 'i', 'z' };
int[] distance = countSameLetterDistanceMulti("Das Haus ist schön im Wald ztestz", letters);
for (int i = 0; i < letters.length; i++)
System.out.println("'" + letters[i] + "' maximum diastance\t" + distance[i]);
}
public static int[] countSameLetterDistanceMulti(String text, char[] c) {
int[] distances = new int[c.length];
for (int i = 0; i < c.length; i++)
distances[i] = countSameLetterDistance(text, c[i]);
return distances;
}
public static int countSameLetterDistance(String text, char c) {
int distance = INIT_COUNT;
int distanceMax = 0;
for (int i = 0; i < text.length(); i++) {
if (distance > INIT_COUNT)
distance++;
if (text.charAt(i) == c) {
if (distanceMax < distance)
distanceMax = distance;
distance = 0;
}
}
return distanceMax - 1;
}
}
@Tobias-nrw Allerdings sehe ich gerade, dass für jeden Buchstaben das Maximum ausgegeben werden soll. Trotzdem finde ich countSameLetterDistance etwas schwer zu lesen.
Außerdem ist die Aufgabe Schrott: "Anzahl der zwischen 2 gleichen Buchstaben liegenden Buchstaben finden." - und dann sollen lt. Beispiel die Leerzeichen mitgezählt werden...
So, sehe ich das auch, klar countSameLetterDistanceMulti( und countSameLetterDistance( fallen ins Auge, aber trotzdem brauche ich doch einige Zeit um zu erkennen, wie diese Methoden zusammenspielen. Oder ist das nur bei mir so?
Ist wohl Geschmackssache und vielleicht nicht die beste Wortwahl.
Besser wäre wohl getSameLetterMaxDistance.
Das Wesentliche für den Hilfesuchenden ist die Verdeutlichung, dass das Problem in suche einen Abstand und suche mehrere Abstände zerlegt wurde.
PS: Ich denke aber, dass
Java:
String s = new Scanner(System.in).nextLine();
int first = 0;
int second = 0;
int max = 0;
for (int i = 0; i < s.length(); i++) {
for (int j = i + 1; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j)) {
if ((j - i) > max) {
first = i;
second = j;
max = j - i;
}
j = s.length();
}
}
}
Ja. Ich kann Dir nicht mal genau sagen, warum. Vermutlich liegt es an INIT_COUNT und distance-1. Da muss ich erst anfangen, nachzuvollziehen, wofür das gut sein soll. Hinzu kommt, dass ich ursprünglich eine etwas andere Aufgabenstellung im Kopf hatte.
„Teile und herrsche“ ist eines der wichtigsten Prinzipien für effiziente Algorithmen. Dabei wird ausgenutzt, dass bei vielen Problemen der Lösungsaufwand sinkt, wenn das Problem in kleinere Teilprobleme zerlegt wird. Dies lässt sich meist durch Rekursive Programmierung umsetzen, bei der die Teilprobleme wie eigenständige Probleme gleichzeitig parallel oder sequenziell (einzeln nacheinander) behandelt werden
Du ließt den von Dir geposteten Text sehr ungenau und benutzt diesen aus dem Zusammenhang gerissen. "Dabei wird ausgenutzt, dass bei vielen Problemen der Lösungsaufwand sinkt"
Das ist die Kernaussage Deines obigen Post.
Dies lässt sich meist durch Rekursive Programmierung umsetzen.
Das ist hingegen nur ein Beispiel wo es häufig vorkommt.
Der Lösungsaufwand sinkt, wenn man sich zuerst auf das Problem suche einen Abstand fokussiert.
Dadurch sinkt der Lösungsaufwand beim eigentlichen Problem suche mehrere Abstände.
siehe Code:
Java:
public static int[] countSameLetterDistanceMulti(String text, char[] c) {
int[] distances = new int[c.length];
for (int i = 0; i < c.length; i++)
distances[i] = countSameLetterDistance(text, c[i]);
return distances;
}
Lösungsaufwand ist nicht nur Aufwand vom Algorithmus (Also die Laufzeit oder wir man das sonst nennen möchte) sondern zu dem Lösungsaufwand gehört der ganze Aufwand, der zur Erstellung der Lösung notwendig ist. Und da sinkt der Aufwand durch Aufteilung massiv. Spätestens wenn es zum Testen kommt, sind kleine Methoden deutlich einfacher und mit geringerem Aufwand zu testen.
Dann wäre alleine schon auf Grund der Tiefe der Verschachtelungen ein Refactoring der Lösung aus #3 angebracht. Man mag die Lösung aus #7 für verbesserungswürdig halten (die eine Methode hat auch noch eine gewisse Tiefe der Verschachtelungen) aber es ist durchaus ein Schritt in die richtige Richtung.
Aber ich frage mich, ob es nicht günstiger wäre, universellere Methoden zu schreiben. Das würde dann z.B. darauf hinaus laufen, dass Methoden der String Klasse, geschrieben würden (die man wohl nicht nutzen darf, sonst würde ich die halt nutzen).
==> Wiederverwendung von Code.
Und dann entsteht auch schnell ein - aus meiner Sicht - besser lesbarer Code.
Lösungsaufwand ist nicht nur Aufwand vom Algorithmus (Also die Laufzeit oder wir man das sonst nennen möchte) sondern zu dem Lösungsaufwand gehört der ganze Aufwand, der zur Erstellung der Lösung notwendig ist. Und da sinkt der Aufwand durch Aufteilung massiv. Spätestens wenn es zum Testen kommt, sind kleine Methoden deutlich einfacher und mit geringerem Aufwand zu testen.
Das ist Deine Meinung, die ich Dir auch nicht nehmen will.
Sachliche Argumente werden da auch nicht wirklich erwünscht sein, oder? Wobei eins vielleicht: Schreib mal für Deinen Algorithmus Unit Tests ohne Refactoring. Oder sind Unit Tests auch nur etwas für Leute, die nicht Programmieren können?
Nicht wirklich, weil ich das schon durch ein Anti Pattern begründet habe und der Rest trivial ist. Wenn ich ein Methode nicht sinnvoll aufteilen kann, ist es logischerweise ein Mehr an Schreibarbeit - ergo ist es aufwändiger. Außerdem versteht es sich eigentlich auch von selbst, Methoden nicht sinnlos aufzuteilen... Naja aber das lernt man mit etwas mehr Erfahrung.
@mihe7 hatte mir bereits zugestimmt, das im Vergleich meine Methode in jedem Fall lesbarer ist. Einzig das ich das in die main geschrieben habe ist etwas ungünstig. Aber auch das versteht sich von selbst. Ich weiß nicht, wieso immer noch versucht wird etwas Falsches zu verteidigen.
Das stimmt. Ich tue mich bei bei Deiner leichter, allerdings löst sie auch eine andere Aufgabe, so dass man die beiden Codes nicht direkt vergleichen kann.
Das stimmt. Ich tue mich bei bei Deiner leichter, allerdings löst sie auch eine andere Aufgabe, so dass man die beiden Codes nicht direkt vergleichen kann.
Wobei man doch bitte unterscheiden sollte. So ging es doch nicht mehr um die konkrete Lösung von @Blender3D in #7, sondern im Folgenden um eine Lösung, bei der die Problematik in Teilprobleme zerlegt wird (und das rekursiv so lange, bis alles in kleine, einfach lösbare Probleme zerteilt ist) um so eine gut lesbare Lösung zu haben.
Eine solche Lösung zu bauen sollte für jeden trivial sein. Wie ich vorgehen würde, habe ich etwas in #18 anklingen lassen.
Und Fakt ist, dass eine Methode mit zwei verschachtelten Schleifen und da drin zwei verschachtelte if Anweisungen direkt ein Kandidat für eine Überprüfung ist bei einem Code Review. Das muss man nicht so sehen - Ich kenne aber genau so ein Vorgehen in der Praxis.
Nicht desto trotz habe ich gewisse Probleme, dies hier nachzuvollziehen:
Ja. Ich kann Dir nicht mal genau sagen, warum. Vermutlich liegt es an INIT_COUNT und distance-1. Da muss ich erst anfangen, nachzuvollziehen, wofür das gut sein soll. Hinzu kommt, dass ich ursprünglich eine etwas andere Aufgabenstellung im Kopf hatte.
Also das alles als "in jedem Fall lesbarer" zu werten finde ich schon lustig - aber das soll mir auch alles egal sein, denn es ging ja nicht mehr um den konkreten Vergleich, wie ich ja im ersten Teil noch einmal verdeutlichen wollte.
Und ich bezweifle, dass mihe7 den Code als lesbar erachten würde. Aber das können wir ja später mal testen - dann reichen wir ihm nur den Code und dann soll er sagen, was der Code genau macht. Das ist halt öfter mal die Problematik: Man hat halt nicht immer direkt Aufgabenstellung und Code zusammen ...
Hm... ich dachte, das hätte ich gemacht. Der Code von @Tobias-nrw kommt mir eher bekannt vor als der von @Blender3D. Das ist ein rein subjektiver Eindruck.
Java:
public static int countSameLetterDistance(String text, char c) {
int distanceMax = -1;
int charPos = text.indexOf(c);
while (charPos != -1) {
int lastCharPos = charPos;
charPos = text.indexOf(c, lastCharPos + 1);
int distance = charPos - lastCharPos - 1;
if (distance > distanceMax) {
distanceMax = distance;
}
}
return distanceMax;
}
Nur einmal aus reiner Langeweile mal, wie ich es implementiert hätte.
Die StringUtils mit indexOf implementationen poste ich erst einmal nicht. Aber das zeigt, dass es das um einiges aufbläht...
Mein Code ist erst einmal in einer Klasse. Da es nur eine Aufgabe mit einem String ist habe ich dafür nicht wirklich etwas sinnvolles gefunden, daher ist es dann einfach die Klasse StringTask und die arbeitet auf einem source String. Des Gerüst sieht also so aus:
Java:
/**
* Class to solve the following task on a given String:
* Get the number of maximum characters between 2 equal characters.
*/
public class StringTask {
/**
* String source to work on.
*/
private String source;
/**
* Gets the source string to work on.
* @return String to work on.
*/
public String getSource() { return source; }
/**
* Sets the source string to work on.
* @param source New String to work on.
*/
public void setSource(String source) { this.source = source; }
/**
* Creates a new instance of StringTask.
* @param source Source string to use in methods.
*/
public StringTask(final String source) {
this.source = source;
}
}
Dann gab es mehrere Einzelne Schritte, die ich für wichtig empfand um es leserlich zu handhaben. So ist schon die Berechnung der Differenz etwas spezielles. Da hat man halt noch ein zusätzliches "-1" - daher wurde dies eine Methode:
Java:
/**
* Get the maximum distance between two occurences of the ch character.
* @param ch - character to check.
* @return Maximum or NO_MAXIMUM if no character pair is found.
*/
public int getMaxDistanceForCharacter(final char ch) {
return getMaxDistanceForCharacter(ch, 0);
}
Dann musste man natürlich von einem Startpunkt aus diese Differenzen berechnen können. Das wurde dann so eine Schleife:
Java:
/**
* Gets the maximum distance between two occurences of the ch character.
* @param ch - character to check.
* @return Maximum or NO_MAXIMUM if no character pair is found.
*/
public int getMaxDistanceForCharacter(final char ch) {
return getMaxDistanceForCharacter(ch, 0);
}
/**
* Gets the maximum distance between two occurrences of the ch character.
* @param ch - character to check.
* @param startPoint - start point for search.
* @return Maximum or NO_MAXIMUM if no character pair is found.
*/
public int getMaxDistanceForCharacter(final char ch, final int startPoint) {
int start = StringUtils.indexOf(source, ch, startPoint);
int max = NO_MAXIMUM;
int end;
while ((end=StringUtils.indexOf(source, ch, start+1)) > -1) {
if (max < getDistance(start, end)) {
max = getDistance(start, end);
}
start = end;
}
return max;
}
Und dann weil dann im Vergleichscode auch für jedes Zeichen nach der Differenz geschaut wurde:
Java:
/**
* Get the maximum Distance between two equal Characters.
* @return Maximum found or NO_MAXIMUM if none found.
*/
public int getMaxDistanceForAnyCharacter() {
int max = NO_MAXIMUM;
for (int index = 0; index < source.length(); index++) {
int charMax = getMaxDistanceForCharacter(source.charAt(index), index);
if (charMax > max) {
max = charMax;
}
}
return max;
}
Das Ganze kann man dann auch gerne einmal mit einem Programm ausführen:
Java:
public static void main(String[] args) {
StringTask task = new StringTask("Das Haus ist schön im Wald ztestz");
System.out.printf("Max-Distance for a: %d\n", task.getMaxDistanceForCharacter('a'));
System.out.printf("Max-Distance for z: %d\n", task.getMaxDistanceForCharacter('z'));
System.out.printf("Max-Distance for any char: %d\n", task.getMaxDistanceForAnyCharacter());
}
Damit habe ich dann mal ein krassen Gegenpol gesetzt.
Schon die StringUtil Methoden haben ja fast die Zeilen vom Vorschlag von @Tobias-nrw aus #3.
Was sind aus meiner Sicht die Punkte, die mich diesen Code bevorzugen lassen:
a) Testbarkeit - Ich finde, dass hier jede Methode relativ wenig macht, so dass diese einfach zu testen ist. Die Testfälle für jede Methode sind auch überschaubar und es ist unkompliziert, die Testfälle zu finden. (Mit den Unit Tests bläht sich der Code übrigens noch einmal deutlich auf ) Der Unterschied, den diese Lösung an LoC hat gegenüber #3 ist damit wirklich extrem!
b) Die Methoden sind auf Wieder-Verwertbarkeit ausgelegt. Daher auch die JavaDoc Hinweise, die ich aber eigentlich nur etwas angedeutet habe. (Die dienen also nicht der Erklärung des Codes sondern sollen einem Nutzer oder Interessenten helfen, die Methoden zu nutzen ohne eben in den Code selbst hinein schauen zu müssen. Für die Code Lesbarkeit spielen diese also explizit keine Rolle! In der Realität finden sich dann dort auch weniger Hinweise zum Codeablauf sondern mehr Hinweise, was das Ziel des Codes ist und ist damit etwas Bindeglied zwischen der Dokumentation der Anforderung und dem Code. Hier gibt es viele Wege - in einem früheren Team wurden neben dem Code in Word noch Dokumente zur Implementation gepflegt.)
Der Code bietet aber natürlich auch noch viel Anregung zu Diskussionen. Wie hätte man denn gerne das eine oder andere? Ich nenne einfach mal zwei Punkt, die aus meiner Sicht hervor stechen:
a) NO_MAXIMUM Konstante. Hier hätte man natürlich Lösungen wie Integer (erlaubt ein null) oder gar ein Optional verwenden können. Diese gehen beide mehr in eine Richtung, die evtl. lesbarer wird. Muss sich jeder selbst überlegen, wie er dies bevorzugt.
b) while ((end=StringUtils.indexOf(source, ch, start+1)) > -1)
Dies ist ein Konstrukt, das in meinen Augen umstritten ist. Die Zuweisung in einer Bedingung fordert geradezu Kritik heraus.
Dies ist aber dennoch ein Pattern, das man relativ oft sieht - Bestes Beispiel ist das Lesen einer Datei Zeile für Zeile mit dem typischen Zuweisen der nächsten Zeile in die Variable und dem Prüfen des Inhalts.
Generell habe ich versucht, bei dem ganzen Code nur auf Befehle, die Anfängern zur Verfügung stehen, zuzugreifen.
Vom ersten Gefühl her würde ich bei getMaxDistanceForCharacter wohl eher auf Streams zurück greifen wollen. Dazu fehlte mir aber bisher die Zeit. (Das ist alles in der Nacht entstanden während ich auf meinen Einsatz als "Fahrservice" wartete. Nur das Posten hat letzte Nacht nicht geklappt und um 3 Uhr hatte ich dann keine Lust mehr, den Post zu Ende zu schreiben.... Daher kommt der Post erst jetzt nach dem Aufstehen.)
Anmerkung noch von meiner Seite:
Interessant fand ich gestern Abend eine Sache: Die Aufgabe selbst ist abstrakt und erscheint erst einmal wenig sinnvoll. Evtl. trifft ein "Die Problemdomäne, in die man 'eintauchen' kann, fehlt". Ich hatte StringUtil zu implementieren und da kamen zwei Dinge zusammen:
- klare Problemdomäne, denn hier ging es wirklich rein um das Thema String, welches gut erfassbar war.
- Klare, bekannte Algorithmen.
Dies traf auf die anderen Implementationen nicht zu. Dies fehlte mir, was dann z.B. das Finden von sinnvollen Namen erschwerte. Die Algorithmen selbst sind natürlich auch 08/15: Jeweils eine Schleife über Werte um ein Maxima zu finden (=> Schreit nach einer .stream().max() Lösung, die dann kürzer und im Code lesbarer wird ...).
Ja, das ist auch ein Problem in der Argumentation für solche Lösungen. Hier kann man tatsächlich von einem Einzelfall ausgehen, so dass man das getrost als Overkill bezeichnen kann aber in der Praxis wird das schon schwieriger. Für Leute, die damit nichts oder weniger zu tun haben, ist es nicht einfach nachzuvollziehen, wieso es besser sein kann, mehr Code zu schreiben.
Macht wirklich Spaß. Besonders weil dein Verzicht auf sachliche Argumente es einem so leicht macht. #27 von @JustNobody war dagegen sehr differenziert, eröffnet unterschiedliche Blickwinkel und ermöglicht die individuelle Bewertung der verschiedenen Aspekte. Ein nützlicher Diskussionsbeitrag. Das ist viel konstruktiver als pure Rechthaberei.
Da hast Du was missverstanden. Es ist ja nichts neues, dass ich grundsätzlich für die Aufteilung von Code in kleine Einheiten bin - erstens weil es (in der Regel) lesbarer wird und zweitens, weil sich der Grad der Wiederverwendung erhöht (so habe ich im Code in #26 z. B. indexOf verwendet, statt diesen Part direkt in der Methode zu übernehmen).
Das ändert aber nichts daran, dass im konkreten Fall Dein Code aus #3 für mich einfacher nachzuvollziehen war als der Code aus #7. Darum ging es hier aber nicht und damit hier nicht weiter was missverstanden wird:
Hier wollte ich insbesondere Anfänger, die den Thread (mit)lesen, dazu ermutigen, sich von der Länge des Kommentars von @JustNobody nicht abschrecken zu lassen. Außerdem bleibt die Frage, ob der Vorteil kleinerer Einheiten angesichts des vielen Codes überhaupt erkennbar ist.
Ja, das ist auch ein Problem in der Argumentation für solche Lösungen. Hier kann man tatsächlich von einem Einzelfall ausgehen, so dass man das getrost als Overkill bezeichnen kann aber in der Praxis wird das schon schwieriger. Für Leute, die damit nichts oder weniger zu tun haben, ist es nicht einfach nachzuvollziehen, wieso es besser sein kann, mehr Code zu schreiben.
Hier geht es um das allgemeine Problem, dass die Aufteilung des Codes zunächst einmal oft mit mehr LOCs einhergeht, die sich durch die Wiederverwendung rechnen. Da es hier um eine abgeschlossene Übungsaufgabe geht, wird auch nichts wiederverwendet -> over engineered. Ein ähnliches Problem hat man in der Praxis, wenn man
a) erklären soll, wie (scheinbar) mehr Code zu weniger Aufwand führen kann oder
b) vorab nicht weiß, ob der Code jemals wiederverwendet werden wird
Verzichtet man bei b) in die Aufteilung, wird man ggf. später Code mehrfach schreiben, teilt man bei b) auf, schreibt man ggf. zu viel Code, wenn er nie wiederverwendet wird.
ja, du hast Recht: Für kleine Aufgaben im Bereich von Schule oder Studium gebe ich dir recht. Eine Hello World Applikation benötigt all sowas nicht. Hinzu kommt, dass es um rein temporäre Lösungen geht. Die Hausaufgaben interessieren nach der Abgabe nun einmal nicht mehr. Und dabei ist es dann vollkommen egal, ob es nur wenige Stunden/Tage sind oder paar Monate für die Diplomarbeit...
Daher ist so ein Ansatz übertrieben. Unit Tests sind da übertrieben - ein einfacher Durchlauf reicht und wenn da ein kleiner Fehler drin ist, dann gibt es halt Punktabzug (Wenn es dem Lehrer / Dozenten überhaupt auffällt!).
Aber wenn man mit der Einstellung an wirkliche Projekte heran geht, dann ist das schnell fatal. (Ich würde vermuten, dass Du bisher in keinem wirklichen Projekt mitgearbeitet hast, oder? Du dürftest noch im Studium sein ... das wäre jetzt meine Einschätzung. Wobei das aber auch unerheblich ist. Es gibt genug "Profis" die da extrem schlampig Arbeiten. Sind oft die Lieblinge von Projektmanagern - die sehen nur die Kosten und das Projekt und wenn da schnell etwas irgendwie fertig wird, ist es ok. Die Probleme mit der Gewährleistung bekommen diese dann oft nicht mehr mit und müssen diese oft nicht tragen... Aber ich habe mehrfach so schnelle, günstige Lösungen erlebt, die dann im Müll landeten und die auch entsprechende Konsequenzen nach sich gezogen hat.)
Wobei die Unterteilung in mehrere Methoden in anderen Thread mehrfach angemerkt wurde und irgendwie habe ich in Erinnerung, dass dies nie wirklich aufgenommen wurde. Daher sehe ich dies nur als Zeitvertreib - irgendwie musste ich ja die Zeit totschlagen bis ich da Kameraden von Ihrer Weihnachtsfeier abholen durfte.
Ja, das ist auch ein Problem in der Argumentation für solche Lösungen. Hier kann man tatsächlich von einem Einzelfall ausgehen, so dass man das getrost als Overkill bezeichnen kann aber in der Praxis wird das schon schwieriger. Für Leute, die damit nichts oder weniger zu tun haben, ist es nicht einfach nachzuvollziehen, wieso es besser sein kann, mehr Code zu schreiben.
Ja, die Notwendigkeit kommt halt - wie ich Tobias oben schon geschrieben habe - erst bei größeren Projekten. Und da ist dann tatsächlich die Frage, in wie weit die Leute dann bereit sind, sowas ein zu sehen.... Die Problematik ist ja (leider) sehr real wenn man sich diverse Projekte anschaut und die Zahlen waren zumindest damals katastrophal, wenn man sich Software Entwicklungs Projekte angesehen hat. Da war der Prozentsatz an erfolgreichen Projekten (Ziel erreicht innerhalb der vorgegebenen Zeit, Budget und Qualität) extrem gering. Mag sich gebessert haben in den letzten 15-20 Jahren, aber da habe ich keine aktuellen Zahlen mehr ...
Wo hast Du denn bitte gelesen, dass wir die Lines of Code verringern wollten? Es ging nie um die LoC des Projektes. Wenn, dann wäre es um die LoC einer Methode gegangen, aber auch die wurden nie kritisiert. Verschachtelungstiefe habe ich angesprochen. Aber sonst?
Also Du scheinst gewisse Dinge total zu missverstehen. Ich habe bei Dir regelmäßig den Verdacht, dass Du andere Threads zu lesen bekommst, als ich. Deine Interpretation von den Aussagen habe ich ja mal versuch in #25 zu hinterfragen ...
Und du hast noch keine Begründung gelesen?
- Dann bring doch endlich mal Unit Tests deiner tollen main Methode! Sorry, aber du kannst gerne beliebige Positionen vertreten, aber wenn Du faktisch falsche Dinge schreibst, dann kann ich nur den Kopf schütteln.
- Verschachtelungstiefe hast Du auch nichts von gelesen in diesem Thread?
Was sind das denn? Evtl. Begründungen für eine Meinung?
Nein, das macht absolut keinen Spaß, denn irgendwie scheint es komplett bei der Kommunikation zu hapern. Daher ist es auch kein wirkliches widersprechen in dem Sinne, dass ich Dich überzeugen möchte. Es ist lediglich eine Darstellung einer anderen Position. Diese versuche ich etwas zu belegen und dann darf sich jeder überlegen, was er wie für gut und richtig hält. Die Welt ist nicht schwarz / weiss ... Jeder soll einfach das machen, was er für richtig hält.
Lol, nein, das siehst du falsch. Das Taxi spielen beruht auf Gegenseitigkeit. Die Stadtteile arbeiten zusammen und wenn wir feiern, dann fährt einer von denen....
Scheinbar, weil durch Wiederverwendung Code eingespart wird.
Mal ein Beispiel:
Java:
FacesContext fc = FacesContext.getCurrentInstance();
if (someIllegalStateDetetected) {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_ERROR,
"Nix gut, weil ...");
fc.addMessage(null, msg);
fc.renderResponse();
return;
}
Das sind jetzt 6 LOC, wenn ich mich nicht verzählt habe. Würde man das in eine Klasse auslagern, hat man erstmal mehr Code, sagen wir mal das Doppelte, also 12 LOC. Die 12 LOCs sind einmalig, dagegen spare ich bei jeder Verwendung der Klasse x LOCs ein, so dass mit der (Math.floor(12d/x)+1)-ten Verwendung der Klasse Code gespart wird.
Ein anderes Beispiel ist, wenn Leute anfangen, ihre Methoden in kommentierte Abschnitte einzuteilen. Statt dem Kommentar kann man auch einfach eine Methode schreiben, das sieht zwar nach mehr aus, ist aber nicht mehr Arbeit und man hat keinen toten Text im Quelltext sondern echten Code.
In #30 hattest du ja noch das vermeintliche Fehlen von Begründungen kritisiert und nicht deren Stichhaltigkeit in Frage gestellt. Darüber kann man natürlich unterschiedlicher Meinung sein. Einfache Gegenteilsbehauptungen bringen der Diskussion aber nichts. Zum Auslagern von Schleifen in Methoden könnte man beispielsweise sagen, dass man zwar einerseits durch gute Namensvergabe deutlich machen kann, was dort überhaupt geschieht, man aber andererseits nicht mehr an Ort und Stelle sieht, ob es korrekt gemacht wird. Das Eine erhöht die Lesbarkeit, das Andere mindert sie. So läuft es ja meistens. Es gibt Vorteile und Nachteile, die man bei seiner Entscheidung abwägen muss.
Falls das als Pluralis Majestatis gemeint war, wäre aber Großschreibung anmessen gewesen. Oder wen meinst du mit "eurer"? Ich habe nichts gegen break und continue einzuwenden, solange man ohne Labels auskommt. Ich glaube, die wurden mal kritisiert. Die Manipulation von Laufvariablen gefällt mir aber nicht.
Naja das war ja eher eine relativierende nennen wir es Richtigstellung zu einer aus meiner Sicht "falschen" Behauptung. Also nicht Schwarz oder Weiß, sondern Grau...
Ich bin weiterhin der Auffassung, dass man als "programmieraffine" Person zwei geschachtelte Schleifen, zwei if's und ein break (ohne Name) verstehen muss und dieses Konstrukt von der horizontalen Länge noch nicht zu tief ist.
Als extremes Gegenbeispiel könnte man sich überlegen, jede einzelne Zeile in eine Methode zu splitten... Aber wäre das sinnvoll?
Ich bin weiterhin der Auffassung, dass man als "programmieraffine" Person zwei geschachtelte Schleifen, zwei if's und ein break (ohne Name) verstehen muss und dieses Konstrukt von der horizontalen Länge noch nicht zu tief ist.
Nur eben sagt dies absolut nichts aus. Jemand, der programmieren kann, muss auch schlecht lesbaren Code lesen können. Daher ist das Argument hier im Zusammenhang schlicht sinnlos.
Aber Punkte wie schnelles verstehen, Möglichkeit der Wiederverwendung und Anpassung sind da schlicht nicht so gut gegeben. Aber das wurde schon breit ausgeführt und eine Wiederholung ist kaum zielführend.
Ja, man kann auch einzelne Teile einer Zeile in eine Methode packen wenn dies sinnvoll scheint. Habe ich sogar auch gemacht, weil im Thread jemand über eine "-1" gestolpert ist und ich damit zu einer besseren Lesbarkeit kommen wollte...
Ja, man kann auch einzelne Teile einer Zeile in eine Methode packen wenn dies sinnvoll scheint. Habe ich sogar auch gemacht, weil im Thread jemand über eine "-1" gestolpert ist und ich damit zu einer besseren Lesbarkeit kommen wollte
Das ist nur ein Sonderfall, der i.A. nicht gilt. Natürlich kann das manchmal sinnvoll sein.
Das ist genau so, wie auf in Notwehr zu handeln. I.d.R. ist es verboten, auf jemanden zu schießen, aber in Ausnahmefällen ist es erlaubt... (weit hergeholtes Beispiel, aber etwas anderes fiel mir gerad nicht ein)
Meine Hilfestellungsversuch war. Teile und Herrsche mit einer ordentlichen Benennung zu demonstrieren.
Die Benennung war von mir nicht wirklich günstig gewählt, wie bereits von @mihe7 erwähnt.
Meine Zielsetzung war es nicht Clean Code zu präsentieren.
Aber hier noch einmal eine etwas verbesserte Variante gelesen.
Das Problem: Ermittle die maximale Abstände des selben Buchstaben in einem Text aus einer Menge von Buchstaben
Das Programm gelesen:
Java:
public final static int NO_DISTANCE = -1;
public static void main(String[] args) {
char[] letter = { 'a', 'i', 'H', 'b' };
int[] distance = getSameLetterMaxDistance("bbDas Haus ist schön im Wald ztestz", letter);
printLetterMaxDistance(letter, distance);
}
Es gibt eine Konstante mit der Semantik kein Abstand.
1) Ein char Array mit Buchstaben wird angelegt.
Dann 2 Funktionsaufrufe.
2) Eine Funktion ermittle vom gleichen Buchstaben den maximalen Abstand gibt ein int Array zurück.
3) Eine Funktion gib den Buchstaben maximal Abstand einer Anzahl von Buchstaben aus.
Ein Testaufruf ergibt.
An der Stelle weiß man bereits was das Programm macht. Der Benutzer muss sich nicht mehr für den eigentlichen Algorithmus interessieren.
Die Lösung von @Tobias-nrw in Post #3. Muss vom A-Z verstanden werden, um zur Selben Einsicht zu gelangen. Wenn es jetzt um das Verständnis des Algorithmus selbst geht.
Liest man die Funktion ermittle vom gleichen Buchstaben den maximalen Abstand aus einem Array von Buchstaben
Java:
public static int[] getSameLetterMaxDistance(String text, char[] letter) {
int[] distances = new int[letter.length];
for (int i = 0; i < letter.length; i++)
distances[i] = getSameLetterMaxDistance(text, letter[i]);
return distances;
}
1) Ein int Array mit der Länge des Buchstaben Array wird erzeugt.
2) Eine Zählvariable über die Länge des Texts wird durchlaufen.
3) Jedem Buchstaben aus dem Array Buchstaben wird ein maximaler Abstand anhand des übergebenen Textes zugewiesen
4) Das ermittelte Abstands Array wird zurückgegeben.
Somit hat sich das Problem: Ermittle die maximale Abstände des selben Buchstaben in einem Text aus einer Menge von Buchstaben auf das Problem: Ermittle den maximalen Abstande eines bestimmten Buchstaben in einem Text
reduziert.
Jetzt liest man die Funktion ermittle vom gleichen Buchstaben den maximalen Abstand
Java:
public static int getSameLetterMaxDistance(String text, char c) {
int distance = NO_DISTANCE; // value is -1
int distanceMax = 0;
for (int i = 0; i < text.length(); i++) {
if (distance != NO_DISTANCE)
distance++;
if (text.charAt(i) == c) {
if (distanceMax < distance)
distanceMax = distance;
distance = 0;
}
}
return distanceMax - 1; // distanceMax = 0 -> NO:DISTANCE
}
1) Einer Variable Abstand wird der Wert -1 mit der Bedeutung kein Abstand zugewiesen.
2) Die Variable maximaler Abstand wird auf 0 gesetzt.
3) Eine Zählvariable über die Länge des Texts wird durchlaufen.
4) Falls die Variable Abstand nicht mehr die Bedeutung kein Abstand hat wird sie um eins erhöht.
5) Falls der gegenwärtig betrachtete Buchstabe des Textes der Gesuchte ist, dann
setze die Variable maximaler Abstand auf den größeren Wert und die Variable Abstand auf 0.
6) Gib maximaler Abstand um 1 reduziert zurück. Grund die letzte gefundene Variable war die Gesuchte.
Zum Abschluss die Funktion gib den Buchstaben maximal Abstand einer Anzahl von Buchstaben aus.
Java:
public static void printLetterMaxDistance(char[] letter, int[] distance) {
for (int i = 0; i < letter.length; i++) {
String distanceStr = distance[i] != NO_DISTANCE ? " " + distance[i] : "NO DISTANCE";
System.out.println("'" + letter[i] + "' maximum diastance =\t" + distanceStr);
}
}
1) Eine Zählvariable über die Anzahl der Buchstaben wird durchlaufen.
2) Dem Abstandstring wird wenn der gegenwärtige Abstand die Bedeutung kein Abstand hat "NO DISTANCE" zugweisen
sonst wird er auf 3 führende Leerstellen und den Wert des jeweilige Abstands gesetzt.
3) Der Buchstabe und der Abstandstring wird ausgegeben.
Über die Lesbarkeit von Code gibt es viele verschiedene Sichtweisen. Ich nehme auch nicht in Anspruch, dass diese Variante die am beste zu lesende ist.
Aus meiner Sicht ist mit Lesbarkeit nicht nur das Verstehen des jeweiligen Algorithmus gemeint. Betrachtet man das eigentliche Programm sollte man an der Stelle erkennen können was es macht ohne in die Tiefe gehen zu müssen.
Durch Teile und Herrsche das Programm auf das Wesentliche zu reduzieren ist daher essenziell.
In diesem Kontext sehe der Vergleich dann so aus.
Was ist besser lesbar?
Java:
public final static int NO_DISTANCE = -1;
public static void main(String[] args) {
char[] letter = { 'a', 'i', 'H', 'b' };
int[] distance = getSameLetterMaxDistance("bbDas Haus ist schön im Wald ztestz", letter);
printLetterMaxDistance(letter, distance);
}
versus
Java:
@SuppressWarnings("resource")
public static void main(String[] args) {
String s = new Scanner(System.in).nextLine();
int first = 0;
int second = 0;
int max = 0;
for (int i = 0; i < s.length(); i++) {
for (int j = i + 1; j < s.length(); j++) {
if (s.charAt(i) == s.charAt(j)) {
if ((j - i) > max) {
first = i;
second = j;
max = j - i;
}
j = s.length();
}
}
}
System.out.println(s.charAt(first) + " " + s.charAt(second) + " " + first + " " + second + " " + max);
}
Genau. Weiß doch jeder, dass es ein unter Arbeitgebern verbreitetes Verhaltensmuster ist, es zu akzeptieren, wenn der hoch bezahlte Programmierer die Pflege des Codes seines Vorgängers verweigert, weil der schlecht lesbar ist. Die sind da immer sehr verständnisvoll und verzichten dann auch gerne mal auf die Weiterentwicklung ihrer Produkte.
Wo siehst du Wiederholungen, also den Verstoß gegen dry? Und auch yagni / KISS ist kein Thema aus meiner Sicht.
Es wurde ein Algorithmus schlicht runter gebrochen und jeweils in minimaler Weise ohne Optimierungen entwickelt.
Eine einfache Lösung des Problem ist doch (jetzt mal einfach für einen vorgegebenen Buchstaben) den ersten Index des Buchstabens zu finden. Und dann die folgenden Indizes um dann den Abstand zu bestimmen.
Daraus folgt dann:
- String.indexOf bzw. mein StringUtils.indexOf
- die Methode, die indexOf nutzt in einer while Schleife.
==> Ich sehe keinen doppelten Code
==> Ich sehe keine nicht benötigte Funktionalität.
==> Ich sehe keine Optimierungen und so
Das Teilergebnis wird dann halt für alle Zeichen benutzt, was dann wieder eine einfache Schleife in einer Methode ist.
Daher sehe ich im Augenblick keine Problematik bezüglich der von Dir gebrachten Prinzipien.
Und Lines of Code einzusparen ist kein wirkliches Ziel. Zeitliche Optimierungen sind kein vorrangiges Ziel. (Wenn es Probleme mit der Laufzeit gibt, dann wird eh eher die Datenspeicherung oder der Algorithmus als Ganzes ersetzt. Da geht es dann nicht mehr um eine optimierte Schreibweise des Algorithmus.)
Wenn Du die Probleme, die Du siehst, etwas ausführst, dann kann man das gerne auch noch im Detail erörtern.
Der Vorwurf des Overkills kann man durchaus bringen. Aber das ist aus meiner Sicht leider wirklich notwendig. Was ich nicht mit gepostet habe, sind ja auch noch die Unit Tests! Das war also auch nur ein Teil der Lösung. Der Overkill kostet Zeit, aber da ist viel Tipparbeit und man prüft sich ja durch die Tests direkt selbst. Und der Code ist für andere änderbar, denn die Tests stellen die Funktionalität weiter sicher.
Bei so kleinen Mini Projekten oder Übungsaufgaben spielt es keine Rolle, aber sobald es etwas größer wird, ist es nach meiner Erfahrung extrem wichtig. Und ich habe in meiner Laufbahn schon einige Projekte komplett in die Tonne kicken müssen incl. Diskussionen mit Managern, wieso da etwas nicht brauchbar ist.
Ganz am Rande mal die Anmerkung: Ich finde https://clean-code-developer.de/ recht gut. (Wobei ich es einige Zeit nicht mehr betrachtet habe, hoffe es ist noch so wie damals ....) Da fängt es an mit absoluten Grundlagen, die existenziell sind (oder sein sollen). Also da kommen so Dinge wie Sourcecode Verwaltung und Tests und so ... Und dann geht es mit Clean Code Prinzipien los ... und man kann nach und nach mehr dazu nehmen - dazu haben die Grade entwickelt... Hat mir in der Vergangenheit sehr gut gefallen.
Naja, ich wäre an Deiner Stelle etwas vorsichtig mit so absoluten Aussagen. Bisher ist das auf Stack Exchange eine Einzelmeinung und primär ging es auch um den Methodennamen. Gut möglich, dass da noch weitere Antworten kommen (insofern ich bei der Fragestellung alle Regeln bedacht habe).