Einen String rekursiv umdrehen

Hallo liebe Community, eine letzte Frage von mir

wir sollen einen String rekursiv (das heißt ohne Schleifen etc) umdrehen, also sprich dass z.B. aus “Hallo” “ollaH” wird.
Ich habe auch schon einen Quellcode, der allerdings aus mir unerfindlichen Gründen nicht funktionieren möchte:

		System.out.println("Geben Sie ein Wort ein: ");
		String wort = scan.next();
		System.out.println(wort);
		scan.close();
		
}
		public static String dreheWortUm(String wort){
			if (wort.length() > 0){
				System.out.println(wort.charAt(wort.length()-1));
				dreheWortUm(wort.substring(0, wort.length()-1));
			}
		return wort;
	}
		
}```

Mein Ansatz ist der, dass ich quasi erstmal überprüfen lasse ob die Länge des Strings über null liegt (in der Aufgabe vorgegeben) und dann die letzte Stelle des Strings ausgegeben wird. Danach ruf ich dreheWortUm erneut auf und er schneidet quasi so lange die Buchstaben ab, bis das Wort umgedreht ist.

Sieht einer was eventuell fehlen könnte oder wo der Fehler ist?

Danke und schöne Weihnachtszeit :)

Hm, ich werde aus dem Code nicht so ganz schlau.
Sollt ihr das Wort lediglich rekursiv ausgeben auf der Konsole, oder soll die Methode den String tatsächlich umdrehen? Momentan ist der Rückgabewert der Methode sinnlos.

Was genau funktioniert an deinem Code denn nicht?

Kleines Edit: Ich habe den Code etwas modifizert jetzt gibt er das Wort rückwärts aus, zwar untereinander aber immerhin es funktioniert.

Habe vergessen oben unter System.out.println die Funktion dreheWortUm nochmal aufzurufen.

Jemand noch eine Idee, wie ich die chars die ja untereinander nun auftauchen in einen String umwandeln kann?
Bei String.valueOf(wort); tut sich leider gar nichts :x

Am einfachsten wäre es die einzelnen Buchstaben in einer Klassenvariablen (static) zu “sammeln” oder der Methode eine Variable durchzureichen und so Schritt für Schritt den Rückwärts String aufzubauen.
Besser wäre es den String Buchstabe für Buchstabe zu durchlaufen und diese passend zurück zu geben und rückwärts zu verketten.

  Scanner scan = new Scanner(System.in);
  System.out.println("Geben Sie ein Wort ein: ");
  String wort = scan.next();
  scan.close();
  String trow = dreheWortUm(wort);
  System.out.println(trow);
}

public static String dreheWortUm(String wort){
  //Rekursionsende
  if (wort.length() <= 0){
    return "";
  }
  else //Rekursionsschritt
  { 
    char letzter = wort.charAt(wort.length()-1);
    String rest = wort.substring(0, wort.length()-1);
    return letzter + dreheWortUm(rest);
  }
}

Ausgaben ala System.out.println() sind in der Methode dreheWortUm eher Kontraproduktiv.

Zuerst ein Rekursionsende definieren. Wenn der String keine Buchstaben enthält, dann gib den leeren String zurück.
Ein Rekursionsschritt besteht nun darin, den letzten Buchstaben zu nehmen und ihn zu Verknüpfen mit, dem String der aus allen Buchstaben, bis auf den letzten besteht und umgedreht wurde.

Da es rekursiv sein soll würde die Klassenvariable wegfallen. Ich würds so in etwa machen:

private static String dreheUm(String wort, int position)
{
    if (position == wort.length()) {
        return wort;
    }

    String prefix = wort.substring(0, position);
    char charToSwap = wort.charAt(wort.length() - 1);
    String affix = wort.substring(position, wort.length() - 1);

    return dreheUm(prefix + charToSwap + affix, position + 1);
}

EDIT:
Majoras Lösung ist aber schöner :wink:

Danke an alle Helfer, habe nun meinen Code komplett umgeschrieben weil es mir einfach zu kompliziert war.
Habe nun einen ähnlichen (aber wesentlich kürzeren) Code, der im Prinzip dasselbe macht (er trennt quasi die Buchstaben vorne ab, um sie hinten wieder zu einem String zusammenzufügen)

Wer an der Lösung interessiert ist, hier ist der fertige Code. Falls euch noch was auffallen sollte, raus damit:

		System.out.println(dreheWortUm(wort));
}
        public static String dreheWortUm(String wort){
        	  if (wort.length() == 0) {
        		  return wort;
        	  }
        	  return dreheWortUm(wort.substring(1)) + wort.charAt(0);
        		   }
         }```

Das ist übrigens ein gutes Beispiel, um einen Unittest dafür zu schreiben. Dann findest Du auch ziemlich leicht den Fehler. :slight_smile:

Sym du bist doch nicht etwa in meinem OOP-Kurs? ;D
Nein Spaß - die darauf folgende Aufgabe war tatsächlich, einen jUnit-Test dafür zu schreiben :slight_smile:
Und der funktioniert sogar :>

Dann noch ein kleiner Tipp.

public void test() {
  StringBuffer buffer = new StringBuffer();
  for(int i = 0; i< 3000; i++) {
    buffer.append("a");
  }
  String s = buffer.toString();
  assertEquals(s, dreheWortUm(s));
}

Was wird hier passieren?

Mhm, ist ein Dreizeiler:

        if (s.isEmpty())
            return "";
        return rev(s.substring(1)) + s.charAt(0);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        System.out.println(rev("Hallo"));
    }```

Zeile 2: Abbruchbedingung, das Umgekehrte von nix ist nix
Zeile 4: Reduzierung des Parameters, rekursiver Aufruf, Erweiterung der Rückgabe

Mit einem StringBuilder, dessen Referenz du als Argument mit schleppst (u. a.), wäre alles viel effektiver-

Hm ja mit einer leeren Menge kann man es natürlich auch machen, das stimmt.
Das mit dem StringBuffer musste ich erstmal nachlesen haben wir noch nicht verwendet sorry :slight_smile:

Mein jUnit-Test sieht so aus:


import org.junit.Test;
public class Aufgabe5Test {
	String wort = "Haus";
	String umgedreht;
@Test
	public void testDreheWortUm() {
		umgedreht = "suaH";
			assertEquals(umgedreht,Aufgabe5.dreheWortUm(wort));
		
	}


}```

Hier ein paar Tips:

[ul]
[li] Die beiden Variablen gehören in Deinem Fall in die Methode, da sie nirgends sonst verwendet werden.[/li][li] Was passiert mit dem Leerstring “”? Warum ist das nicht getestet?[/li][li] Was passiert mit null? Warum ist das nicht getestet?[/li][/ul]

Da solltest du aber mehr als einen Test schreiben. Insbesondere auch den oben erwähnten mit den 3000 'a’s.

Danke nochmal für eure weiter Hilfe, ich habe den Test nun umgeschrieben und er prüft nun auch, ob der String leer ist. Also habe es so realisiert dass ich im Test dem leeren String den Wert “” zugewiesen habe und das dann geprüft wird.

Hier könnt ihr euch den TEst nochmal anschauen :


import org.junit.Test;
public class Aufgabe5Test {
	String wort = "Haus";
@Test
	public void testDreheWortUm() {
	String umgedreht = "suaH";
			assertEquals(umgedreht,Aufgabe5.dreheWortUm(wort));
}
{	
	String leer = "";
			assertEquals(leer,Aufgabe5.dreheWortUm(""));
		
	}

}

[QUOTE=Majora]Dann noch ein kleiner Tipp.

public void test() {
  StringBuffer buffer = new StringBuffer();
  for(int i = 0; i< 3000; i++) {
    buffer.append("a");
  }
  String s = buffer.toString();
  assertEquals(s, dreheWortUm(s));
}

Was wird hier passieren?[/QUOTE]

Steh ich gerade auf dem Schlauch? Oder spielst du damit auf die “Dauer” bzw. die String- Generierung im Pool an ?

[QUOTE=Max1992]Danke nochmal für eure weiter Hilfe, ich habe den Test nun umgeschrieben und er prüft nun auch, ob der String leer ist. Also habe es so realisiert dass ich im Test dem leeren String den Wert “” zugewiesen habe und das dann geprüft wird.

Hier könnt ihr euch den TEst nochmal anschauen :


import org.junit.Test;
public class Aufgabe5Test {
	String wort = "Haus";
@Test
	public void testDreheWortUm() {
	String umgedreht = "suaH";
			assertEquals(umgedreht,Aufgabe5.dreheWortUm(wort));
}
{	
	String leer = "";
			assertEquals(leer,Aufgabe5.dreheWortUm(""));
		
	}

}
```[/QUOTE]

Da fehlt sowas wie
```    @Test
    public void emptyInput()```

Recursion is the goto of functional programming - Erik Meijer

Rekursion ist ein mächtiges Werkzeug, dessen gebrauch weise erfolgen sollte.
Wenn die Rekursionstiefe zu Groß wird, dann gibt es einen Stackoverflow. 3000 sollte oftmals ausreichen.

Im Gegensatz zu Scala macht Java von Haus aus keine Tail Call Optimization um beliebige Stacktiefen erreichen zu können.
Tail Call Optimization funktioniert allerdings nur, wenn die Rückgabe einer Funktion nur aus dem Aufruf selbiger besteht. Folgende rekursive Implementierung könnte Scala optimieren

  return dreheWortUm(new StringBuffer(), wort);
}

private static String dreheWortUm(StringBuffer acc, String wort){
  //Rekursionsende, gibt den Akkumulator zurück
  if (wort.length() <= 0){
    return acc.toString();
  } else {
    acc.append(wort.charAt(wort.length()-1));
    String rest = wort.substring(0, wort.length()-1);
    return dreheWortUm(acc, rest);
  }
}```

Hier würde Scala daraus automatisch eine iterative Implementierung generieren, die vorerst keine Stackprobleme bekommt.

Dies würde dann in etwa so aussehen

```private static String dreheWortUm(StringBuffer acc, String wort){
  StringBuffer acc_1 = acc;
  String wort_1 = wort;
  while(true) {
    if (wort_1.length() <= 0){
      return acc_1.toString();
    } else {
      acc_1.append(wort_1.charAt(wort_1.length()-1));
      String rest = wort_1.substring(0, wort_1.length()-1);
      acc_1 = acc_1;
      wort_1 = rest;
    }
  }
}```

Habe die volle Punktzahl bei der Vorstellung ergattern können, danke an alle die mir geholfen haben (:

Jetzt erstmal besinnliche Weihnachten und frohes Neues :slight_smile: