Programmierprobleme

    Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen zum Einsatz von Cookies
    Beachten Sie zudem unsere Datenschutzerklärung: Pirateboard.net - Datenschutzerklärung

    • Programmierprobleme

      Heyho,

      Ich habe da ein minimales Problem mit einer selbstdefinierten Bedingung in Robot Karol. Wäre schön, wenn mir da jemand helfen könnte.

      Welt:
      bilder-space.de/show_img.php?i…8399252.jpg&size=original

      Quellcode:
      Spoiler anzeigen

      {Start Bedingung IstESO}
      Bedingung IstESO
      Schnell
      RechtsDrehen
      Wenn IstWand Dann Falsch
      Sonst Wahr
      *Wenn
      LinksDrehen
      Wenn IstWand Dann Wahr
      Sonst Falsch
      *Wenn
      *Bedingung
      {Ende Bedingung IstESO}
      //IstESO = IstEckeSüdOst


      {Start Hauptprogramm}
      Wiederhole Immer
      Wenn IstWand Dann
      Wenn IstSüden Dann
      LinksDrehen
      Schritt
      LinksDrehen
      Sonst
      RechtsDrehen
      Schritt
      RechtsDrehen
      *Wenn
      Sonst
      Schritt
      Wenn IstZiegel Dann
      LinksDrehen
      Schritt
      RechtsDrehen
      Schritt
      LinksDrehen
      LinksDrehen
      *Wenn
      Wenn IstESO Dann Beenden
      *Wenn
      *Wenn
      *Wiederhole
      {Ende Hauptprogramm}


      Aufgabe:
      Karol soll nach unten gehen, links an den Ziegeln vorbei. Dann steht er rechts von den Ziegeln. Dort dreht er sich um und geht nach oben. Oben angekommen dreht er sich nach rechts, macht einen Schritt, dreht sich nochmal nach rechts und geht runter. Dann in der nächsten Spalte wieder hoch, dann wieder runter...
      Bis er in der Süd-Ost-Ecke angekommen ist. Dort soll er stoppen. Dazu habe ich (versucht) eine selbsdefinierte Bedingung zu schreiben, in der er prüfen soll, ob er in besagter Ecke ist.

      Problem:
      Die Bedingung (IstESO) kann man in der Pfeife rauchen, wenn ihr den Quellcode im Programm nutzt, wisst ihr warum.
      Kann mir jemand die selbstdefinierte Bedingung für IstEckeSüdOst geben?

      MfG. Faith
    • Versuchs mal damit:

      Bedingung IstESO
      Wenn istWand Dann
      dreheLinks
      Wenn ist Wand Dann Wahr
      Sonst Falsch
      *Wenn
      *Wenn
      *Bedingung

      Ich kenne Robot Karol selbst nicht, daher kann ich nicht für korrekte Formulierung garantieren. Der Grundgedanke müsste aber richtig sein. Funktioniert allerdings nur, wenn Robot Karol aus Norden kommt, d.h. nur bei ungerader Spaltenzahl.
      Dein Ansatz ist eigentlich nicht schlecht, allerdings trifft Deine Bedingung jedes Mal zu, wenn Karol auf eine südliche Wand trifft.
      Gutalala Sudalala~
    • Danke für den Vorschlag, aber so geht es auch nicht. Der Ansatz ist aber besser als meiner.
      Ich habe ihn etwas bearbeitet:

      Spoiler anzeigen
      {Start Bedingung IstESO}
      Bedingung IstESO
      Schnell
      LinksDrehen
      Wenn IstWand Dann Wahr
      Sonst Falsch
      *Wenn
      RechtsDrehen
      *Bedingung
      {Ende Bedingung IstESO}
      //IstESO = IstEckeSüdOst


      Dann gibt es aber ein neues Problem. Wenn er in der letzten Spalte von Norden nach Süden geht, bleibt er nach dem ersten Schritt stehen, da links von ihm eine Wand ist und er durch "IstESO" den Befehl zum Beenden bekommt.
    • Nope. Funktioniert nicht. Langsam denke ich, dass man aufgrund der Bahn, die man laufen muss, gar nicht die Bedingung IstESO nutzen kann.
      Wie auch immer, danke für deine Hilfe.

      EDIT: Habe die Lösung gefunden. Es ging nur mit zwei selbstdefinierten Bedingungen. Die Lösung lautet:

      Spoiler anzeigen
      {Start Bedingung IstLW}
      Bedingung IstLW
      Schnell
      LinksDrehen
      Wenn IstWand Dann Wahr
      Sonst Falsch
      *Wenn
      RechtsDrehen
      *Bedingung
      {Ende Bedingung IstLW}
      //IstLW = IstLinksWand

      {Start Bedingung IstESO}
      Bedingung IstESO
      Schnell
      Wenn IstWand Dann Wahr
      Sonst Falsch
      *Wenn
      *Bedingung
      {Ende Bedingung IstESO}
      //IstESO = IstEckeSüdOst

      {Start Hauptprogramm}
      Wiederhole Immer
      Wenn IstWand Dann
      Wenn IstSüden Dann
      LinksDrehen
      Schritt
      LinksDrehen
      Sonst
      RechtsDrehen
      Schritt
      RechtsDrehen
      *Wenn
      Sonst
      Schritt
      Wenn IstZiegel Dann
      LinksDrehen
      Schritt
      RechtsDrehen
      Schritt
      LinksDrehen
      LinksDrehen
      *Wenn
      Wenn IstLW Dann
      Wenn IstESO Dann Beenden
      *Wenn
      *Wenn
      *Wenn
      *Wiederhole
      {Ende Hauptprogramm}


      Kann geclosed werden, danke für die Hilfe.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Faith ()

    • Ich bezweifle dass jemand on ist der mir helfen kann, aber:

      Ich muss in Assembler mit Hilfe von SSE das Skalarproukt zweier Vektoren berechnen. Leider bin ich in Assembler eine null haha..
      FALLS das soweit richtig ist, habe ich die zwei Vektoren multipliziert und die Werte in einem Vektor gespeichert. Jetzt müsste ich diese Werte addieren, doch leider weiß ich nicht, wie man Werte innerhalb eines Vektors addiert.. kann mir da jemand helfen?


      1 .data
      2 floatout: .string "Wert %f\n"
      3 x: .float 12.0, 12.0, 3.0, 71.0 # vector 1
      4 y: .float 19.0, 10.0, 111.0, 12.0 # vector 2
      5 n: .long 4 #dimension
      6
      7 result: .float 0.0 #result
      8
      9 .text
      10
      11 .globl main
      12 main:
      13 movl x, %eax
      14 movl b, %ebx
      15
      16 movups (%eax), %xmm0
      17 movups (%ebx), %xmm1
      18 mulps %xmm1, %xmm0
      19
      20
      21
      22
      23
      24 fstpl (%esp) # output
      25 pushl $floatout
      26 call printf
      27
      28 # Exit
      29 movl $1, %eax
      30 int $0x80
      If I can't even protect my captain's dream, then whatever ambition I have is nothing but just talk.
    • Leider gibt es nicht den Assembler, deshalb wären Angaben wie der verwendete Assembler (Programm zum Übersetzen in Opcodes) und Architektur gut.
      Folgendes habe ich mir rekonstruiert:
      Linux (da "int 0x80")
      Gnu-Asm (da AT&T/Motorola Assembler verwendet wird + Linux + dieser dort verbreitet ist; ergo eher geraten)
      x86/AMD64 Architektur, min. Pentium 3 (da damit (I)SSE 1 eingeführt wurde)

      Auch wären Angaben, welche Rahmenbedingungen du hast - z.B. ob du zwangsweise SIMD(SSE)-Code verwenden musst - hilfreich.

      Ich habe mir deshalb mal ein Lubnuntu aufgesetzt und mit folgendem Befehl deinen Code (Datei PB.S genannt) kompiliert:
      g++ -g -m32 PB.S -o pb

      Allerdings baut der Code nicht, da "b" unbekannt ist (Zeile 14). Ich nehme einfach mal an, dass y gemeint war.
      Wenn man das Programm dann baut und ausführt stürzt es wie erwartet in Zeile 16 ab. Deine Zeile besagt, dass der Inhalt von EAX als Adresse aufgefasst werden soll und 128 Bit des Speichers ab dieser Adresse in das Register XMM0 geschrieben werden soll. Und was steht in EAX? Es ist 0x41400000, was der hexadezimalen Repräsentation von IEEE 754, einfacher Genauigkeit des Wertes 12,0 entspricht.
      Oder anders ausgedrückt, du kopierst den ersten Wert deines Vektors nach EAX und interpretierst diesen Wert dann allerdings als Zeiger. Und da du auf diese Speicheradresse nicht zugreifen darfst, knallt es.
      Stattdessen willst du den Zeiger auf deinen ersten Wert von X nach EAX speichern, für so etwas gibt es den Befehl LEA (Load Effective Address).

      Nun einige Anmerkungen zu deinem Code/Grundproblem. Du verwendest SSE-Code, dies hat auch immer Nebeneffekte. Wenn du reine Assembler-Programme schreibst kannst du das relativ leicht selber absichern, sofern dein Betriebssystem SSE unterstützt. Benutzt du allerdings Inline-Assembler wird das schon kompliziertes. Allgemein denke ich, dass SIMD-Erweiterungen wie SSE an dieser Stelle nicht notwendig sind - zumindest bei dem, was ich hier sehe. Ich persönlich habe schon einmal SIMD bei Vektor-Rechnungen verwendet und konnte sowohl Visual Studio C++, als auch Gnu C++-Compiler in Punkte Geschwindigkeit (wobei die ordentlich optimieren durften!) geschlagen, um ca. 2%. Das hat mich über 20 Stunden gekostet, heutige Compiler sind einfach verdammt gut im Optimieren und in meinem Fall hatten die Vektoren Millionen von Einträgen - nur so konnte ich den zusätzlichen Overhead eliminieren. Optimierung per Assembler lohnt sich bei den heutigen, ausgereiften Compilern nur noch sehr selten!
      Statt den SIMD-Code bietet sich wohl eher die Rechnung mit der FPU an.
      Dann wird "int 0x80" verwendet. Ich weiß, in der Linux-Welt kommt das immer noch ab und zu vor, als Fallback-Lösung. Doch heutzutage verwenden eigentlich nur noch Treiber und Schadsoftware Interrupt, Alternativen gibt es schon sehr lange. Wobei ich allerdings auch anmerken will, dass ich Assembler eigentlich nur unter Windows, bei Betriebssystem-Programmierung oder inline verwendet habe.
      Bei der Assembler-Programmierung ist es meiner Erfahrung nach immer gut eine entsprechende Dokumentation der Befehle parat zu haben und zu befragen. Bei x86/x64 sind das natürlich die Handbücher von Intel und AMD (unter Manuals, Vol. 4). Die von Intel finde ich besser, allerdings habe ich auch mit denen "gelernt" und es ist vermu7tlich nur eine Gewöhnungssache. Beide verwenden übrigens Intel-Syntax, nicht AT&T!

      Nun zum Schluss zu deiner eigentlichen Frage:
      Floatingpoint Operationen werden i.d.R. mit der FPU erledigt. Der von dir bereits verwendete Befehlt "fstpl" (Store Floating Point Value) ist bereits ein Befehl von/für diese Einheit. Die FPU ist als eine Art Stack aufgebaut. Es gibt 8 Register und die meisten Befehle gehen auf das oberste Element "ST(0)". Für die Addition musst du nun die Werte auf diesen Stack "pushen" und mit FADDP kannst du dann die obersten Elemente (ST(0) und ST(1)) miteinander addieren.
      Falls du ganz von SIMD wegkommen möchtest: mit FMULP kannst du ST(0) und ST(1) miteinander multiplizieren.
      Die Benutzung der FPU ist aufgrund des STACK-Konzeptes etwas gewöhnungsbedürftig, sind doch die anderen Befehle register-basierend.

      PS: Hier im Board gibt es den BBCode "code", mit dem man Quellcode gut darstellen kann:

      Quellcode: PB.S

      1. .data
      2. floatout: .string "Wert %f\n"
      3. x: .float 12.0, 12.0, 3.0, 71.0 # vector 1
      4. y: .float 19.0, 10.0, 111.0, 12.0 # vector 2
      5. n: .long 4 #dimension
      6. result: .float 0.0 #result
      7. .text
      8. .globl main
      9. main:
      10. movl x, %eax
      11. movl y, %ebx
      12. movups (%eax), %xmm0
      13. movups (%ebx), %xmm1
      14. mulps %xmm1, %xmm0
      15. fstpl (%esp) # output
      16. pushl $floatout
      17. call printf
      18. # Exit
      19. movl $1, %eax
      20. int $0x80
      Alles anzeigen

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Estocitus ()

    • Vielen vielen Dank!

      Zuerst einmal, ja das b sollte ein y sein, es war schon spät...
      Ich muss das Skalarprodukt zweier Vektoren berechnen und MUSS dafür SSE verwenden. Lerne Assembler gerade in der Uni und da ist das so vorgegeben.
      Deine Annahmen waren richtig, wir benutzen AT&T, deshalb kann ich die Intel-Anleitungen leider nicht benutzen, habe aber selbst einige.
      Mit FPU kenn ich mich aus, muss aber wie gesagt SSE benutzen.

      Das mit dem Register einlesen habe ich gemacht, weil auf den Vorlesungsfolien folgendes stand (Auszug):


      Zwei Vektorenadressen befinden sich in êx und ëx

      Quellcode

      1. 1. ... 2. movups (%eax), %xmm0 3. movups (%ebx), %xmm1 4. 5. addps %xmm1, %xmm0 6. movups %xmm0, result

      ( Weiß nicht wie eine neue Zeile anfängt,sorry )

      Also müsste es doch so gehen?

      PS: Mittlerweile habe ich HADDPS gefunden, mit dem ich an mein Ergebnis komme. In " result " steht dann allerdings das Ergebnis 4mal, da es ein Vektor ist. (Wenn ich das richtig verstanden habe.) Nun will ich nur EINS dieser Werte, egal welches, rausnehmen und pushen. Gibt es dafür einen Befehl?
      If I can't even protect my captain's dream, then whatever ambition I have is nothing but just talk.
    • Das Intel-Handbuch ist dennoch benutzbar. Die Unterschiede halten sich in Grenzen, besonders wichtig ist nur, dass die Reihenfolge der Parameter genau umgedreht ist. Für (aktuelle) x86/x64- und ISSE-Befehle gibt es keine besseren Referenzen als die Entwickler dieser, Intel und AMD. Aber es ist auch nicht schlimm, wenn die nicht benutzt werden. Wenn du eine Quelle mit AT&T-Syntax hast, ist auch gut. Das erspart einem das ständige Umdenken. Selbst mit einigen Erfahrungen kann doch mal eine Verwechslung geschehen. :S

      Lorenor D. Zorro schrieb:


      Das mit dem Register einlesen habe ich gemacht, weil auf den Vorlesungsfolien folgendes stand (Auszug):

      Zwei Vektorenadressen befinden sich in êx und ëx

      Quellcode

      1. ...
      2. movups (%eax), %xmm0
      3. movups (%ebx), %xmm1
      4. addps %xmm1, %xmm0
      5. movups %xmm0, result

      ( Weiß nicht wie eine neue Zeile anfängt,sorry )

      Also müsste es doch so gehen?

      (Der Code wird so direkt wiedergegeben, Zeilenumbrüche werden somit mit Zeilenumbrüchen dargestellt. Ich weiß allerdings gerade leider nicht, ob er unbedingt "Windows-Zeilenumbrüche" will.)

      Du hast selber geschrieben, dass auf der Folie "Vektorenadressen" steht. Deshalb muss du in EAX/EBX auch die Adressen und nicht das erste Element schreiben. Und das macht man, wie gesagt, mit LEA (auf Anhieb finde ich leider nur Quellen mit Intel-Syntax).

      Gibt es eine Vorgabe, welche SSE-Version es sein soll? Dein HADDPS ist beispielsweise SSE3. In SSE4.1 gibt es das Skalarprodukt sogar als einen direkten Befehl DPPS. SSE3 ist in späteren Pentium 4 Prozessoren 2004 vorhanden, SSE4.1 mit späteren Core2 Prozessoren Ende 2008. Die Wahl entscheidet also, wo der Code später laufen kann/soll.

      Dein HADDPS macht nicht das, was du denkst, zumindest, wenn ich dich richtig verstanden habe. Im Handbuch von Intel (tut mir Leid, dass ich damit schon wieder anfange) ist eine gute Abbildung, was dort berechnet wird.
      Wenn in xmm0 die Zahlen a,b,c,d stehen und in xmm1 die Zahlen w,x,y,z, dann ergibt sich mit

      Quellcode

      1. HADDPS %xmm1,%xmm0
      in xmm0 die Zahlen (w+x),(y+z),(a+b),(c+d).
      Du kannst den Befehl natürlich missbrauchen und ihn zweimal benutzen, im zweiten Register steht dann am Besten immer 0.

      Quellcode

      1. xorps %xmm1,%xmm1
      2. haddps %xmm1,%xmm0
      3. haddps %xmm1,%xmm0

      Nach der zweiten Anwendung befindet sich im untersten Element dein gewünschtes Ergebnis, der Rest ist leer. Du könntest den kompletten Ergebnisvektor (1x Skalarprodukt und drei mal 0) in x oder y schreiben und dann nur das richtige Element verwenden.
      Mit MOVSS kannst du wohl auch einen einzelnen Wert, allerdings nur den Untersten, herausholen.
    • Vielen Dank, habe es leider jetzt erst gesehen. Ich habe es mit LEA gelöst wie du richtig gesagt hast und haddps war der Schlüssel zur Lösung. Wir durften SSE benutzen aber nicht höher als SSE 3, deshalb ist das mit DDPS weggefallen.

      Nun habe ich Assembler zum Glück hinter mir und muss nun C++ lernen. In 90 Minuten wurde das durchgegangen..
      Da C++ aber verbreiteter ist im Forum, hoffe ich dass mir jemand helfen kann bei folgenden Fragen:


      Quellcode

      1. struct cond {bool operator() (char c) {return c == ’(’ || c == ’)’;}};std::string str("(STL und Strings)");str.erase(std::remove_if(str.begin(), str.end(), cond()), str.end());



      Das Idiom ist unter dem Namen erase-remove bekannt. Was passiert bei dieser Kombination von Aufrufen?
      Was ist der Inhalt von str nach diesen Aufrufen? (1 Punkt)

      So ist die Aufgabe. Soweit ich es erkenne, werden die Klammern ausgewählt und der Rest vorangeschoben. Anschließend wird alles was im end gespeichert ist gelöscht, also nach dem Letzten NICHT ausgewähltem Objekt. Demnach bleibt dann der String OHNE Klammern zurück, richtig?
      If I can't even protect my captain's dream, then whatever ambition I have is nothing but just talk.
    • Ich finde, man sollte Quellcode auch einfach mal ausprobieren.

      C-Quellcode

      1. #include <string>
      2. #include <iostream>
      3. #include <algorithm>
      4. struct cond {
      5. bool operator() (char c) {
      6. return c == '(' || c == ')';
      7. }
      8. };
      9. int main(int argc, char* argv[]){
      10. std::string str("(STL und Strings)");
      11. str.erase(std::remove_if(str.begin(), str.end(), cond()), str.end());
      12. std::cout << str << std::endl;
      13. return 0;
      14. }
      Alles anzeigen

      Quellcode

      1. a@b:~/Test$ g++ -g -m32 PB.cpp -o pb
      2. a@b:~/Test$ ./pb
      3. STL und Strings

      Ich habe den Code mal etwas angepasst, damit er ausführbar wird. Falls Fragen dazu sind, einfach heraus damit.
      Mit deiner Annahme, dass die Klammern entfernt werden, hast du also Recht. Doch was passiert genau? Dazu kann man sich die Hilfe von std::string::erase und std::remove_if ansehen. Bevor jetzt etwas Text kommt, es kann sein, dass du alles richtig verstanden hast und ich dich nur falsch verstanden habe. Aber so wie ich dich verstehe, hast du einen kleinen Fehler im Gedankengang.

      Beginnen wir einfach mit der äußeren Funktion, erease. Die Funktion gibt es in drei Varianten, die hier vorliegende ist die 3. Form. "Erases the sequence of characters in the range [first,last)." Die Funktion entfernt also alle ab "first", bis exklusive "end". Die Angaben werden als Iteratoren (beinhaltet grob das "aktuelle" Element und eine Möglichkeit zum nächsten Element zu kommen) vorgenommen.
      Im konkreten Fall sind der von remove_if erstellte Iterator und str.end, ein Iterator der auf das Zeichen hinter dem letzten Zeichen von str zeigt (ist also kein gültiges Zeichen!), angegeben. Mit str.end weiß man immer, wann die kompletten Zeichenkette durchlaufen ist.

      remove_if nimmt zwei Iteratoren (Anfang und Ende) und eine Bedingung an. Die Funktion entfernt alle Zeichen, die bei der angegebenen Bedingung "true" liefern, in unserem Fall ist das immer bei runden Klammern der Fall. Dies geschieht, indem die Zeichen, die behalten werden sollen, nach vorne geschoben werden. Die Größe des Containers - hier Zeichenkette - wird aber nicht verändert. Wenn also n Zeichen am Anfang da waren und m entfernt wurden, so sind es danach immer noch n Zeichen vorhanden. Allerdings sind die ersten (n-m) Zeichen, die, die behalten werden sollen. Wenn ich das richtig verstehe, so sind die letzten m-Zeichen undefiniert.
      Der von der Funktion zurückgegebene Iterator zeigt auf das erste Zeichen, welches weg kann. Im konkreten Fall könnte es so aussehen (ich hoffe, dass verwirrt jetzt nicht):

      Quellcode

      1. STL und Stringss)
      2. ^
      3. |

      "STL und Strings" ist gewünscht und "s)" ist zu viel da und der Iterator zeigt auf das s von "s)".
      Anmerkung
      Ob "s)" oder anderes im "zu viel da" steht hängt vermutlich vom verwendeten Compiler und Standard-Bibliothek ab (Gnu C++, oder Visual C++, oder ...). Ich denke, wie gesagt, dass in der Spezifikation undefiniert ist, was in dem "zu viel da" steht und es somit der Umsetzung dieser Funktion überlassen ist, was sie damit machen.


      Was ist also die Kombination?
      Zuerst werden die gewollten Zeichen nach vorne geholt und ein Iterator wird zurück gegeben, der auf den Anfang des Restes zeigt, welches entfernt werden soll. Danach wird dieser Rest entfernt.
      Ich hatte dich so verstanden, dass alles entfernt wird, was in "str.end()" steht, aber das ist nicht korrekt. "str.end()" zeigt immer auf das Element hinter dem letzten Element. Stattdessen wird alles entfernt ab dem ersten überflüssigen Rest, der noch in str ist. Denn remove_if hat es nicht real entfernt. Dies kann man auch leicht testen, indem erease entfernt wird, also nur noch

      Quellcode

      1. std::remove_if(str.begin(), str.end(), cond());

      ausgeführt wird. Das Ergebnis ist dann in meinem Fall mit Gnu C++:

      Quellcode

      1. STL und Stringss)

      str.end() zeigt dann immer noch auf ein Zeichen hinter ")" und str.begin() zeigt auf das erste "S", den Anfang. Iteratoren umfassen nie Bereiche, sondern immer nur einzelne Elemente.

      Ich hoffe, das war verständlich.
    • Super, ich habe es in einer Stunde geschafft aber gerade nochmal top erklärt, vielen Dank! Das mit dem str.end dachte ich zuerst auch :D
      Übrigens ich glaube statt dem zweiten " s " meinst du " ( " also Tippfehler, ansonsten alles richtig und verständlich!

      edit: Nein, meinte genau das, deine Überlegung scheint aber logisch zu sein. Heißt das aber dass die andere Klammer noch weiter hinten ist und nicht angezeigt wird weil der String seine Länge beibehält?
      If I can't even protect my captain's dream, then whatever ambition I have is nothing but just talk.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Lorenor D. Zorro ()

    • Bei mir steht dann in der Tat "STL und Stringss)" und nicht "STL und Strings()" in der Zeichenkette, nach remove_if. Die Hilfe zu der Funktion sagt nur, dass die Zeichen, die nicht entfernt werden sollen, nach vorne wandern. Was mit dem Rest geschieht, wird nicht gesagt, weshalb ich auch von "undefiniert" ausgehe. Und da die Zeichen sowieso entfernt werden sollen ist es überflüssig, diese extra zu merken und dann hinten anzustellen. Das Doppelte s ist dann einfach eine Folge davon, dass das "s" eins nach vorne gezogen wird, das Original aber nie überschrieben, da es in dem Bereich liegt, welcher entfernt werden soll.
      Den Vorgang im Speicher könnte man sich so vorstellen:

      Quellcode

      1. (STL und Strings) | Anfang
      2. (STL und Strings) | ( angesehen, soll entfernt werden
      3. SSTL und Strings) | S angesehen, wird behalten, eins vorgeschoben
      4. STTL und Strings) | T angesehen, wird behalten, eins vorgeschoben
      5. STLL und Strings) | L angesehen, wird behalten, eins vorgeschoben
      6. STL und Strings) | " " angesehen, wird behalten, eins vorgeschoben
      7. STL uund Strings) | u angesehen, wird behalten, eins vorgeschoben
      8. STL unnd Strings) | n angesehen, wird behalten, eins vorgeschoben
      9. STL undd Strings) | d angesehen, wird behalten, eins vorgeschoben
      10. STL und Strings) | " " angesehen, wird behalten, eins vorgeschoben
      11. STL und SStrings) | S angesehen, wird behalten, eins vorgeschoben
      12. STL und Sttrings) | t angesehen, wird behalten, eins vorgeschoben
      13. STL und Strrings) | r angesehen, wird behalten, eins vorgeschoben
      14. STL und Striings) | i angesehen, wird behalten, eins vorgeschoben
      15. STL und Strinngs) | n angesehen, wird behalten, eins vorgeschoben
      16. STL und Stringgs) | g angesehen, wird behalten, eins vorgeschoben
      17. STL und Stringss) | s angesehen, wird behalten, eins vorgeschoben
      18. STL und Stringss) | ) angesehen, soll entfernt werden
      19. fertig; 2 sollen entfernt werden, also Rückgabe Ende-2
      Alles anzeigen

      Das ganze habe ich mal etwas vereinfacht.

      Oder meinst du etwas anderes?
    • Nein, meinte genau das, deine Überlegung scheint aber logisch zu sein. Heißt das aber dass die andere Klammer noch weiter hinten ist und nicht angezeigt wird weil der String seine Länge beibehält?


      Nun muss ich bei einer andere Aufgabe einen int mithilfe von Bitoperationen shiften. Ich versuche das erste Bit ans Ende zu shiften, aber irgendetwas ist an diesem Befehl falsch..ich weiß nicht was in sizeof( .. ) rein muss, irgendeine Idee? Vielen Dank schonmal!


      std::vector<int> v{ 0, 0, 0, 1 };

      std::transform(v.begin(), v.end(), v.begin(), (v.begin() >>= v.sizeof() * 8));
      If I can't even protect my captain's dream, then whatever ambition I have is nothing but just talk.
    • Die Runde-Klammer-Auf ist bei GNU-C++ komplett verloren.
      Ich weiß nicht, wie weit dein Hintergrundwissen ist. Alle Informationen werden im (Arbeits-)Speicher abgelegt, so auch die Zeichenkette. Der Speicher besteht aus einzelnen, ein-Byte-großen Zellen, jede Zelle kann über eine Zahl direkt angesprochen werden. Bei 32 Bit gibt es 2^32 (2 hoch 32) solcher Zellen, also von 0 bis 4.294.967.295. Bei 64 Bit gibt es 2^64, also von 0 bis ((2^64)-1).
      Jedes Element der Zeichenkette, also jeder Buchstabe, steht in einer eigenen Zellen. Wird nun das erste S nach ( verschoben - eigentlich wird es im konkreten Fall kopiert - wird der Inhalt der Speicherzelle mit ( mit S überschrieben. Was vorher enthalten war, also die Klammer, ist verloren.
      Die Runde-Klammer-Zu bleibt nach remove_if erhalten. Was nach dem erease ist, kann ich dir nicht sagen. Ich vermute, dass die Klammer noch erhalten ist, das doppelte s aber ersetzt wurde durch eine \0 (nicht das Zeichen 0, sondern das Byte hat den Wert 0 mit der Bedeutung "Null character", was oft für das Ende von Zeichenketten verwendet wird). Ich könnte es herausfinden, das ist aber etwas aufwändiger und die Information nicht sonderlich sinnvoll.

      Es gibt Spezifikationen für die Sprachen und Standardbibliotheken. Dort ist festgelegt, wie groß die einzelnen Elemente sind, welche Aufgaben die einzelnen Funktionen haben und so weiter. Die Dokumente sind dann groß, das von C++ (Vorversion der aktuellen Fassung, Datum 2012) umfasst z.B. mehr als 1300 Seiten.
      Beispielausschnitt zu Zahlen von 3.9.1.2


      There are five standard signed integer types : “signed char”, “short int”, “int”, “long int”, and “long long int”. In this list, each type provides at least as much storage as those preceding it in the list. There may also be implementation-defined extended signed integer types. The standard and extended signed integer types are collectively called signed integer types. Plain ints have the natural size suggested by the architecture of the execution environment44; the other signed integer types are provided to meet special needs.
      [ Quelle]
      Es ist also nicht einmal in Standard festgelegt, wie groß die einzelnen Datentypen sind. Jeder Compiler kann das quasi selber entscheiden (meist gibt es das Betriebssystem vor und der Compiler muss sich an dessen Regeln halten).

      Alles, was nicht definiert wird oder ausgewiesen ist als "implementation-defined" kann der Entwickler des konkreten Compilers entscheiden.
      Und ich denke, beides, was mit dem zu entfernenden Teil von remove_if, als auch mit dem Entfernten Teil von erease ist, fällt in diese Kategorie. Nur weil es in meiner aktuell genutzten GNU-C++-Version so ist, heißt das nicht, dass alle anderen das auch so machen. Zudem könnte GNU das auch ändern, wenn für die eine andere Variante plötzlich besser ist.
      Es könnte auch sein, dass bei DevC++ nach remove_if nicht "STL und Stringss)" sondern "STL und Strings()" oder auch "STL und Strings\0\0" im Speicher steht.

      Ich nehme an, das ("k.A., ist auch nicht wichtig") klingt für dich jetzt vllt. blöd, zumindest wäre mir das früher so gegangen, aber eigentlich braucht man solches wissen nur, wenn man quasi am Standard vorbei arbeiten möchte. Ziel ist es ja eigentlich sich von der konkreten Implementierung zu lösen und auf einem höheren, abstrakteren Level (z.B. Zeichenketten, statt Speicherzellen) Level zu arbeiten. Und in solchen Fällen ist entscheidend, was die eigentliche Aufgabe der Funktion macht und das ist bei allem gleich. Was dann konkret im Müll ist, ist nicht wichtig.


      Deinen zweiten Teil verstehe ich leider nicht ganz. Wenn ich Zahlen mittels arithmetischem Shift verschieben möchte, dann mache ich das z.B. so:

      Quellcode

      1. int i,k;
      2. i = 10;
      3. k = i << 2; //danach ist k==40

      Wenn du das mit jedem Element eines Vektors machen möchtest, inkl. Nutzung von transform, dann verstehe ich deinen Ansatz nicht. Nach der Beschreibung auf dieser Seite ist das letzte beispielsweise Funktion, welches die Veränderung angibt. Auf der Seite gibt es unten übrigens auch ein Beispiel mit zwei verschiedenen Transformationen.

      Das normale sizeof gibt die Größe eines Elementes an, das wird allerdings noch vor der Übersetzung in ein Programm ersetzt. Soweit ich weiß besitzt std::vektor keine sizeof-Funktion.
      Beispiel:

      Quellcode

      1. #include <iostream>
      2. int main(){
      3. std::cout << sizeof(int) << std::endl;
      4. std::cout << sizeof(char) << std::endl;
      5. std::cout << sizeof(signed long long int) << std::endl;
      6. }

      ergibt bei mir

      Quellcode

      1. 4
      2. 1
      3. 8
    • Vielen vielen Dank für dieses ausführliche Antwort, die hat mir sehr weitergeholfen!!

      Zun zweiten:

      Die Aufgabe besagt dass man einen Vektor hat der Werte haben kann die entweder 1 oder -1 sind. Nun soll mittels transform jeder Wert -1 mit einer 1 und jede 1 mit einer 0 überschieben werden. Dazu dürfen wir ausschließlich bitoperationen benutzen. Das soll kein ganzes Programm seinsondern ich muss nur kurz 1 oder 2 Zeilen angeben, wie das zu lösen wäre.

      Meine Idee war, dass das Most Significant Byte von -1 1 ist und von 1 0. Diese erste Zahl nehm ich also und shifte sie einfach nach ganz rechts, von links wird mit Nullen aufgefüllt, schon habe ich meine gewünschte Zahl. Deshalb der Ausdruck. Irgendeine Idee dazu?

      EDIT: Lösung

      Quellcode

      1. int rightshift(const unsigned& i){ return (i >> 1) & 1;}//... std::transform(v.begin(), v.end(), v.begin(), rightshift);
      If I can't even protect my captain's dream, then whatever ambition I have is nothing but just talk.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Lorenor D. Zorro ()

    • Hallo! Übe mich jetzt an Java aber bin unsicher ob das was ich tue Sinn macht oder nicht..dachte mir vllt ist hier der ein oder andere Programmiercrack.

      1. Ich will eine Methode schreiben, die mir die Elemente einer Liste dupliziert. Sprich aus abc wird aabbcc.
      Dabei schreibe ich einmal eine iterative und einmal eine rekusive Version.



      2. Die zweite Methode soll jedes Tripel der Liste zyklisch permutieren lassen. Wenn am Ende kein volles Tripel da ist, soll es nicht verändert werden.
      Aus a b c d e f g h i j k wird also c a b f d e i g h j k.



      Folgenden Code habe ich:


      Quellcode

      1. package uebung1;
      2. public class List<T> { private ListItem<T> first; public List() { this.first = null; }
      3. public void duplicateEachElementIter() {
      4. for (ListItem<T> p = first; p != null; p = p.next){ ListItem<T> temp = new ListItem<T>(p.key, p.next); p.next = temp; } }
      5. public void duplicateEachElementRek() { duplicate(first); }
      6. private ListItem<T> duplicate(ListItem<T> t) { if (t == null) { return null; } else {ListItem<T> temp = new ListItem<T>(t.key, t.next); t.next = temp; return duplicate(temp.next); } }
      7. public void rotateTriplesIter() {
      8. for (ListItem<T> p = first; p != null; p = p.next.next){ if (p.next.next != null){ ListItem<T> temp = new ListItem<T>(p.next.next.key, p.next.next.next); p.next.next = temp.next; temp.next = p; } } }
      9. public void rotateTriplesRek() { rotate(first); }
      10. private ListItem<T> rotate(ListItem<T> t) {
      11. if (t == null) return null; else { if (t.next != null || t.next.next != null) { ListItem<T> temp = new ListItem<T>(t.next.next.key, t.next.next.next); t.next.next = temp.next; temp.next = t; } return rotate(t.next.next); } }
      12. }
      Alles anzeigen


      Die andere Klasse:

      Quellcode

      1. package uebung1;
      2. public class ListItem<T> { public T key; public ListItem<T> next; public ListItem(T key, ListItem<T> next) { this.key = key; this.next = next; } }


      Ich hoffe jemand kann mir sagen ob das so richtig ist..

      EDIT: Wow, das sieht hier richtig unübersichtlich aus, hab versucht es mit ein paar Leerstellen und Enter ein bisschen besser darzustellen, aber irgendwie bleibt das in einer Zeile...
      If I can't even protect my captain's dream, then whatever ambition I have is nothing but just talk.
    • Moin,

      ich hab jetzt leider nicht die große Zeit, mir das Geschwurbel da durchzulesen (Kommentare (!) und Einrückungen helfen, auch wenn letztere ja wohl wegen dem Kopieren fehlen), aber ich will dir einfach mal noch ne Idee im logischen Bereich geben, wie ich rangehen würde, nachdem ich deine Aufgabenstellung gelesen habe. Muss dazu aber sagen, dass mein letztes Mal Java programmieren auch bald 10 Jahre her ist, also kann es durchaus neuere Tricks und/oder bessere Methoden geben :D

      1. openbook.rheinwerk-verlag.de/j…a6292a2df6e31df7304ee0c9b
      Erstes Beispiel der ".add"-Befehl. Länge der Liste rausfinden, dann mit Schleife durchgehen und mit add das gleiche Element an die Stelle danach direkt einfügen. Dann brauchst du nur 1 Schleife.

      2. Ähnliches Vorgehen. Frage die Länge der gesamten Liste ab und mit modulo3 wie viele Tripple du hast. Das dient dir dann als Schleifenwert.
      Danach gehst du ins ((Schleifenzahl+1)*3-1)te Element, ziehst dir den Wert raus, löschst dieses Element und fügst das Element an die ((Schleifenzahl+1)*3-3)te Stelle wieder ein.
      (Für den ersten Durchgang dann also Schleifenzahl=0, nehme 2. Element [code=c], lösche es und füge es an die 0.te Stelle vor [a b] ein -> [cab]).
      Dann brauchst du auch hier keine großen Abfragen oder extra Listen/Schleifen.

      Solange dein Code funktioniert, wird der aber auch richtig sein. Und ich denke auf dem Level sind Zeitersparnisse dann auch noch nicht alles.
      Hoffe das konnte irgendwie leicht weiterhelfen, mehr ist heute leider net drin... :-/

      Gruss
    • Ja, irgendwie kommt da sowas komisches raus.. Danke!

      Zu 1: ich habe da nur eine Schleife.

      Zu 2: Das ist eine gute Idee! Wenn meins nicht klappt werdde ich das so machen ist wohl auch eleganter als immer p.next.next.next usw. :D
      If I can't even protect my captain's dream, then whatever ambition I have is nothing but just talk.
    • Ich habe mir deinen Code mal angesehen. Aber vorweg ein paar allgemeine Dinge als Hinweise/Empfehlungen.

      • Der Vorteil an Programmcode ist, dass man ihn auch gleich testen kann. Es ist auch gut dies zu tun. Versuch und Irrtum sind gute Lehrmeister.
      • Bei einem Programmierproblem ist es immer gut ein lauffähiges Minimalbeispiel zu liefern. Das minimiert den Aufwand für Personen, die dir helfen wollen. Wenn sie allerdings erst einmal Stunden damit zubringen müssen deinen Code zum Laufen zu bekommen, verlieren sie schnell die Lust.
      • Verwende keine Bezeichner der Standard-Java-Bibliothek. List ist beispielsweise ein Interface in java.util. Das könnte zu Verwirrungen führen, wenn man (an anderer Stelle im Quellcode) annimmt, dass es dieses List ist und nicht deine Klasse. Mir ist klar, dass es eine gern genommene Aufgabe aus der Uni ist, eine List-Klasse zu schreiben und prinzipiell geht es auch. Aber das heißt dennoch nicht, dass es schön ist und könnte dir, früher oder später, auf die Füße fallen.
      • Gib deinen Klassen eigene toString-Funktionen, dies hilft beim Debuggen immer wieder.


      So, nun zu deinem Code. Ich habe deinen Code etwas abgeändert. Zuerst habe ich ein "COwn" für deine beiden Klassennamen gepackt - das C steht übrigens für Class, ein Teil meinen Code-Styles. Ferner habe ich COwnList drei neue Funktionen geschenkt: "addItems", "clear" und "toString".
      Im Anschluss habe ich eine weitere Klasse, CMain, geschaffen, um den Code ausführen zu können:

      Java-Quellcode

      1. import java.io.IOException;
      2. public class CMain {
      3. public static void main(String [] args){
      4. COwnList<String> aMyList = new COwnList<String>();
      5. String[] aMyStrings = {"Ada", "Bernhard", "Charles", "Domian", "Eduard", "Ferdinant", "Gérald", "Helga", "Ida", "Johann", "Katrin", "Lilja", "Mohandas", "Norbert", "Oedipus", "Peter", "Quintus", "Rénè", "Sigríður", "Titus", "Umut", "Violet", "Werner", "Xenia", "Yasen", "Zora"};
      6. aMyList.addItems(aMyStrings);
      7. System.out.println(aMyList);
      8. System.out.println("duplicateEachElementIter");
      9. aMyList.duplicateEachElementIter();
      10. System.out.println(aMyList);
      11. aMyList.clear();
      12. aMyList.addItems(aMyStrings);
      13. System.out.println("duplicateEachElementRek");
      14. aMyList.duplicateEachElementRek();
      15. System.out.println(aMyList);
      16. aMyList.clear();
      17. aMyList.addItems(aMyStrings);
      18. System.out.println("rotateTriplesIter");
      19. aMyList.rotateTriplesIter();
      20. System.out.println(aMyList);
      21. aMyList.clear();
      22. aMyList.addItems(aMyStrings);
      23. System.out.println("rotateTriplesRek");
      24. aMyList.rotateTriplesRek();
      25. System.out.println(aMyList);
      26. System.out.println("Ende");
      27. try {
      28. System.in.read();
      29. } catch (IOException e) {
      30. // TODO Auto-generated catch block
      31. e.printStackTrace();
      32. }
      33. }
      34. }
      Alles anzeigen

      Quellcode

      1. public class COwnList<T> {
      2. //...
      3. public void addItems(T[] aArray){
      4. COwnListItem<T> elem = first;
      5. int i=0;
      6. if(elem==null){
      7. elem = new COwnListItem<T>(aArray[i], null);
      8. first=elem;
      9. }else{
      10. while(elem.next!=null){
      11. elem = elem.next;
      12. }
      13. elem.next = new COwnListItem<T>(aArray[i], null);
      14. elem = elem.next;
      15. }
      16. i=1;
      17. while(i<aArray.length){
      18. elem.next = new COwnListItem<T>(aArray[i], null);
      19. ++i;
      20. elem = elem.next;
      21. }
      22. }
      23. public void clear(){
      24. first=null;
      25. }
      26. public String toString(){
      27. String res = "COwnListItem[";
      28. COwnListItem<T> elem = first;
      29. while(elem!=null){
      30. res += elem.key.toString();
      31. elem = elem.next;
      32. if(elem!=null){
      33. res+=", ";
      34. }
      35. }
      36. return res+"]";
      37. }
      38. //...
      39. }
      Alles anzeigen


      Also auf und laufen lassen! Doch oh weh - das Programm wird nicht fertig und konsumiert immer mehr Speicher. Zu diesem Zeitpunkt steht folgendes auf der Konsole:

      Quellcode

      1. COwnListItem[Ada, Bernhard, Charles, Domian, Eduard, Ferdinant, Gérald, Helga, Ida, Johann, Katrin, Lilja, Mohandas, Norbert, Oedipus, Peter, Quintus, Rénè, Sigríður, Titus, Umut, Violet, Werner, Xenia, Yasen, Zora]
      2. duplicateEachElementIter

      Also ist es wahrscheinlich, dass etwas in der duplicateEachElementIter-Funktion nicht stimmt. Da wir eine Debug-Ausgabe haben, können wir dies einfach in die Funktion einbauen. Also flugs ein "System.out.println(this);" ans Ende (innerhalb!) der Schleife packen und wieder laufen lassen. Ich gebe nur mal den Anfang der Ausgabe auf der Konsole aus:

      Quellcode

      1. COwnListItem[Ada, Bernhard, Charles, Domian, Eduard, Ferdinant, Gérald, Helga, Ida, Johann, Katrin, Lilja, Mohandas, Norbert, Oedipus, Peter, Quintus, Rénè, Sigríður, Titus, Umut, Violet, Werner, Xenia, Yasen, Zora]
      2. duplicateEachElementIter
      3. COwnListItem[Ada, Ada, Bernhard, Charles, Domian, Eduard, Ferdinant, Gérald, Helga, Ida, Johann, Katrin, Lilja, Mohandas, Norbert, Oedipus, Peter, Quintus, Rénè, Sigríður, Titus, Umut, Violet, Werner, Xenia, Yasen, Zora]
      4. COwnListItem[Ada, Ada, Ada, Bernhard, Charles, Domian, Eduard, Ferdinant, Gérald, Helga, Ida, Johann, Katrin, Lilja, Mohandas, Norbert, Oedipus, Peter, Quintus, Rénè, Sigríður, Titus, Umut, Violet, Werner, Xenia, Yasen, Zora]
      5. COwnListItem[Ada, Ada, Ada, Ada, Bernhard, Charles, Domian, Eduard, Ferdinant, Gérald, Helga, Ida, Johann, Katrin, Lilja, Mohandas, Norbert, Oedipus, Peter, Quintus, Rénè, Sigríður, Titus, Umut, Violet, Werner, Xenia, Yasen, Zora]

      Es fällt auf, dass immer wieder Ada hinzugefügt wird. Wenn man sich den Code ansieht, fällt auch auf, wieso. Wir beginnen bei first und fügen danach direkt dahinter ein neues Element mit dem Wert von first ein. Anschließend nehmen wir das Element hinter first und wiederholen den Vorgang damit. Dieses Element ist allerdings das neu eingefügte. Wir wollten allerdings mit dem weiter machen, was ursprünglich hinter first war und nun hinter first.next liegt. Also kurz eine Zeile abgewandelt und nun scheint der Teil zu klappen. Dann können wir nun unsere Debug-Ausgabe wieder entfernen

      Nachdem das erste Problem behoben ist, führen wir unserer Programm erneut aus. Doch was ist das, es stürzt ab:

      Quellcode

      1. COwnListItem[Ada, Bernhard, Charles, Domian, Eduard, Ferdinant, Gérald, Helga, Ida, Johann, Katrin, Lilja, Mohandas, Norbert, Oedipus, Peter, Quintus, Rénè, Sigríður, Titus, Umut, Violet, Werner, Xenia, Yasen, Zora]
      2. duplicateEachElementIter
      3. COwnListItem[Ada, Ada, Bernhard, Bernhard, Charles, Charles, Domian, Domian, Eduard, Eduard, Ferdinant, Ferdinant, Gérald, Gérald, Helga, Helga, Ida, Ida, Johann, Johann, Katrin, Katrin, Lilja, Lilja, Mohandas, Mohandas, Norbert, Norbert, Oedipus, Oedipus, Peter, Peter, Quintus, Quintus, Rénè, Rénè, Sigríður, Sigríður, Titus, Titus, Umut, Umut, Violet, Violet, Werner, Werner, Xenia, Xenia, Yasen, Yasen, Zora, Zora]
      4. duplicateEachElementRek
      5. COwnListItem[Ada, Ada, Bernhard, Bernhard, Charles, Charles, Domian, Domian, Eduard, Eduard, Ferdinant, Ferdinant, Gérald, Gérald, Helga, Helga, Ida, Ida, Johann, Johann, Katrin, Katrin, Lilja, Lilja, Mohandas, Mohandas, Norbert, Norbert, Oedipus, Oedipus, Peter, Peter, Quintus, Quintus, Rénè, Rénè, Sigríður, Sigríður, Titus, Titus, Umut, Umut, Violet, Violet, Werner, Werner, Xenia, Xenia, Yasen, Yasen, Zora, Zora]
      6. rotateTriplesIter
      7. COwnListItem[Ada, Bernhard, Domian, Eduard, Gérald, Helga, Johann, Katrin, Mohandas, Norbert, Peter, Quintus, Sigríður, Titus, Violet, Werner, Yasen, Zora]
      8. rotateTriplesRek
      9. Exception in thread "main" java.lang.NullPointerException
      10. at COwnList.rotate(COwnList.java:93)
      11. at COwnList.rotate(COwnList.java:98)
      12. at COwnList.rotate(COwnList.java:98)
      13. at COwnList.rotate(COwnList.java:98)
      14. at COwnList.rotate(COwnList.java:98)
      15. at COwnList.rotate(COwnList.java:98)
      16. at COwnList.rotate(COwnList.java:98)
      17. at COwnList.rotate(COwnList.java:98)
      18. at COwnList.rotate(COwnList.java:98)
      19. at COwnList.rotateTriplesRek(COwnList.java:85)
      20. at CMain.main(CMain.java:32)
      Alles anzeigen

      Eine NullPointerException in rotate in Zeile 93. Meine Zeilen 92-94 sehen wie folgt aus:

      Quellcode

      1. if (t.next != null || t.next.next != null) {
      2. COwnListItem<T> temp = new COwnListItem<T>(t.next.next.key,
      3. t.next.next.next);

      Was passiert hier? Wir haben eine Oder-Verknüpfung, die ist dann wahr, wenn eines von beiden wahr ist. Zudem bricht die Überprüfung ab, sobald wahr erreicht ist - besser wird es eh nicht. Also nehmen wir an, wir sind bei unserem vorletzten Element angekommen, t.next!=null, aber t.next.next==null. Dann ist die Bedingung erfüllt, wir gehen rein und bei t.next.next.key knallt es.
      Also kurz den Fehler behoben und nun läuft das Programm durch:

      Quellcode

      1. COwnListItem[Ada, Bernhard, Charles, Domian, Eduard, Ferdinant, Gérald, Helga, Ida, Johann, Katrin, Lilja, Mohandas, Norbert, Oedipus, Peter, Quintus, Rénè, Sigríður, Titus, Umut, Violet, Werner, Xenia, Yasen, Zora]
      2. duplicateEachElementIter
      3. COwnListItem[Ada, Ada, Bernhard, Bernhard, Charles, Charles, Domian, Domian, Eduard, Eduard, Ferdinant, Ferdinant, Gérald, Gérald, Helga, Helga, Ida, Ida, Johann, Johann, Katrin, Katrin, Lilja, Lilja, Mohandas, Mohandas, Norbert, Norbert, Oedipus, Oedipus, Peter, Peter, Quintus, Quintus, Rénè, Rénè, Sigríður, Sigríður, Titus, Titus, Umut, Umut, Violet, Violet, Werner, Werner, Xenia, Xenia, Yasen, Yasen, Zora, Zora]
      4. duplicateEachElementRek
      5. COwnListItem[Ada, Ada, Bernhard, Bernhard, Charles, Charles, Domian, Domian, Eduard, Eduard, Ferdinant, Ferdinant, Gérald, Gérald, Helga, Helga, Ida, Ida, Johann, Johann, Katrin, Katrin, Lilja, Lilja, Mohandas, Mohandas, Norbert, Norbert, Oedipus, Oedipus, Peter, Peter, Quintus, Quintus, Rénè, Rénè, Sigríður, Sigríður, Titus, Titus, Umut, Umut, Violet, Violet, Werner, Werner, Xenia, Xenia, Yasen, Yasen, Zora, Zora]
      6. rotateTriplesIter
      7. COwnListItem[Ada, Bernhard, Domian, Eduard, Gérald, Helga, Johann, Katrin, Mohandas, Norbert, Peter, Quintus, Sigríður, Titus, Violet, Werner, Yasen, Zora]
      8. rotateTriplesRek
      9. COwnListItem[Ada, Bernhard, Domian, Eduard, Gérald, Helga, Johann, Katrin, Mohandas, Norbert, Peter, Quintus, Sigríður, Titus, Violet, Werner, Yasen, Zora]
      10. Ende


      Doch was ist das? Die rotate-Funktionen liefern zwar das gleiche Ergebnis, aber die Liste wird irgendwie - nunja - kleiner. Und Ada, Bernhard und der nicht mehr vorhandene Charles sind auch nicht umgedreht.
      Sehen wir uns einfach rotateTriplesIter am Beispiel von A B C D an. Ändern wir unsere Main und nehmen nur die vier Werte. Zudem lassen wir uns von der Funktion jeden Schleifendurchlauf ausgeben.

      Quellcode

      1. public void rotateTriplesIter() {
      2. System.out.println(this);
      3. for (COwnListItem<T> p = first; p != null; p = p.next.next) {
      4. if (p.next.next != null) {
      5. COwnListItem<T> temp = new COwnListItem<T>(p.next.next.key, p.next.next.next);
      6. p.next.next = temp.next;
      7. temp.next = p;
      8. System.out.println(this);
      9. }
      10. }
      11. }
      Alles anzeigen

      Quellcode

      1. COwnListItem[A, B, C, D]
      2. COwnListItem[A, B, D]
      3. Exception in thread "main" java.lang.NullPointerException
      4. at COwnList.rotateTriplesIter(COwnList.java:76)
      5. at CMain.main(CMain.java:39)

      Wir sehen, C verschwindet beim ersten Schritt und in Zeile 76 steht "if (p.next.next != null) {".
      Bevor wir uns dem Absturz widmen, fangen wir vorne an. Unser Ausgang ist A->B->C->D
      Wir beginnen den Schleifendurchlauf mit A und schauen, ob next.next existiert, das ist aktuell C, also alles OK. Nun nehmen wir ein neues Element haben damit temporär 3 Listen (first, p und temp):
      first: A->B->C->D
      p: A->B->C->D
      temp: C->D
      Nun ändern wir von B was als nächstes und zwar auf temp.next, also D:
      first: A->B->D
      p: A->B->D
      temp: C->D
      Und nun gehen wir zwei Elemente weiter, also :
      first: A->B->D
      p: D
      Das temp existiert nicht mehr, da es innerhalb des if-Blockes war, wir jetzt aber einen neuen Durchlauf der Schleife haben, also außerhalb sind.
      Nun testen wir ob p.next.next null ist. Dummerweise ist bereits p.next null und es knallt.

      Ich vermute, deine Grundidee ist es, die einzelnen Listenelemente zu vertauschen und nicht deren Inhalt. Da T auch sehr komplex sein kann, ist das eine gute Idee. Doch leider änderst du nur zwei Zeiger, du willst aber zwei Elemente mit je zwei Zeigern - einen eingehenden und einen ausgehenden - ändern. Und deine Prüfung, ob du weiter machen kannst oder am Ende bist, funktioniert leider auch nicht in jedem Fall.
      Ich mache an dieser Stelle mal nicht weiter. Du hattest ja schon angedeutet, dass du den Ansatz von Totenkopfgitarrist weiter verfolgen willst.

      Zum Schluss noch ein paar Anmerkungen. Einige sind nur, damit keine Fragen über meinen Code aufkommen.
      • In Java und anderen Sprachen werden gerne Iteratoren verwendet. Dann ist es nicht notwendig manuell über die Elemente zu laufen, sondern kann sich einfach die Elemente nacheinander geben lassen. In diesem Fall ist das wohl zu viel des Guten, aber ich wollte es für die Zukunft erwähnen.
      • Es hat keine besondere Bedeutung, dass ich while-Schleifen verwende. Das ist einfach ein Relikt aus alten Tagen defekter Compiler. ;)
      • Bei Eclipse und evtl. anderen IDEs mit integrierter Konsole ist das "System.in.read();" samt Umbau überflüssig. Auch wenn man direkt auf der Konsole arbeitet ist das überflüssig. Das ist ein Relikt aus meiner Visual Studio-Zeit. Hmm, ziemlich viele Relikte :rolleyes: .