ubuntu--anfaenger
Anmeldungsdatum: 12. Oktober 2013
Beiträge: 1088
Wohnort: Belgien
|
Hallo, Ich habe bei einer Übung ein Verständnis Problem:Kann mir vielleicht jemand erklären wieso ich bei diesem code Schnipsel die Ausgabe t4,t3,t2,t1(Rückwerts bekomme) und nicht t1,t2,t3,t4.. Würde mir helfen ein paar zusammenhänge besser zu verstehen. lg, 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | #include <iostream>
using namespace std;
struct Link {
string value;
Link* prev;
Link* succ;
Link(const string& v, Link* p = 0, Link* s = 0)
: value(v), prev(p), succ(s){}
};
Link* insert(Link* p, Link* n)
{
n->succ = p;
return n;
}
void print_mich(Link* p)
{
cout << "{ ";
while (p) {
cout << p->value;
if (p=p->succ) cout << ", ";
}
cout << " }";
}
int main()
{
Link* test = new Link("t1");
test = insert(test,new Link("t2"));
test = insert(test,new Link("t3"));
test = insert(test,new Link("t4"));
print_mich(test);
cout<<"\n";
}
|
|
Cranvil
Anmeldungsdatum: 9. März 2019
Beiträge: 990
|
Machen wir hier eine Hausaufgabe? Versuch mal, deinen Quelltext auf dem Papier nachzuahmen. Wenn nicht dasselbe wie im Programm passiert, schau dir deine insert-Methode an. Mehr dann später. Edit: Das war unverständlich geschrieben. Wenn du auf dem Papier dasselbe Ergebnis wie im Programm erreichst, sollte dir bewusst werden, was da falsch läuft. Sollte nicht dasselbe passieren wie im Programm, dann musst du dir deine insert-Methode anschauen, weil dein Fehler dort am schnellsten erkennbar ist.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Zeile 36, main, sagt:
| test = insert (test, new Link("t4"));
|
und ruft auf:
| Link* insert (Link* p, Link* n)
{
n->succ = p;
return n;
}
|
Die Aufrufreihenfolge ist nicht intuitiv. In der Methode steht n links, p rechts, aber die Parameter werden p links, n rechts übergeben.
p ist also test, n ist "t4", zurückgeben wird n also t4, das wird test zugewiesen. print_mich (test) wird also zuerst t4 ausgeben, dann t4.succ, dann t4.succ.succ usw. Ich würde die Parameter insert (wem, was) übergeben, nicht (was, wem).
|
ubuntu--anfaenger
(Themenstarter)
Anmeldungsdatum: 12. Oktober 2013
Beiträge: 1088
Wohnort: Belgien
|
Machen wir hier eine Hausaufgabe?
Nee 😬 ich mache das privat,so wie es die Zeit hergibt versuche ich ein bisschen was zu lernen.
Edit: Das war unverständlich geschrieben. Wenn du auf dem Papier dasselbe Ergebnis wie im Programm erreichst, sollte dir bewusst werden, was da falsch läuft. Sollte nicht dasselbe passieren wie im Programm, dann musst du dir deine insert-Methode anschauen, weil dein Fehler dort am schnellsten erkennbar ist.
Ich weiss nicht ob ich mich ganz verständlich ausgedrückt habe, es geht mir nicht darum das ein Fehler im Programm ist,sollte kein Fehler drinn sein. Ich möchte nur verstehen warum der compiler bei der Ausgabe zuerst t4,t3,t2,t1 anstatt t1,t2,t3,t4 Link* test = new Link("t1"); steht in main oben,und die anderes t2,t3 usw kommen später das ist was ich nicht verstehe
Die Aufrufreihenfolge ist nicht intuitiv. In der Methode steht n links, p rechts, aber die Parameter werden p links, n rechts übergeben
Bis hier komme ich noch mit.
also t4, das wird test zugewiesen. print_mich (test) wird also zuerst t4 ausgeben, dann t4.succ, dann t4.succ.succ usw.
Hier verstehe ich gar nix mehr,aber trotzdem danke für die Erklärung. lg,
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12829
|
ubuntu--anfaenger schrieb:
Ich weiss nicht ob ich mich ganz verständlich ausgedrückt habe, es geht mir nicht darum das ein Fehler im Programm ist,sollte kein Fehler drinn sein.
Wenn Du etwas anderes erwartest als, was Du siehst, ist es wohl ein Fehler.
Ich möchte nur verstehen warum der compiler bei der Ausgabe zuerst t4,t3,t2,t1 anstatt t1,t2,t3,t4 Link* test = new Link("t1"); steht in main oben,und die anderes t2,t3 usw kommen später das ist was ich nicht verstehe
Dann tue mal, was Cranvil geraten hat und spiel das auf Papier durch. Dann wirst Du verstehen, warum der Code genau diese Ausgabe produziert. Konkret liegt es daran, wie insert() und print_mich() implementiert sind.
|
ubuntu--anfaenger
(Themenstarter)
Anmeldungsdatum: 12. Oktober 2013
Beiträge: 1088
Wohnort: Belgien
|
Wenn Du etwas anderes erwartest als, was Du siehst, ist es wohl ein Fehler.
Ich habe das Programm gar nicht selber geschrieben, es ist aus einem Lehrbuch von Stroustrup Ja es ist ein Fehler in meinem Kopf das zu verstehen 😀
Dann tue mal, was Cranvil geraten hat und spiel das auf Papier durch. Dann wirst Du verstehen, warum der Code genau diese Ausgabe produziert. Konkret liegt es daran, wie insert() und print_mich() implementiert sind.
Zur Zeit verstehe ich das noch nicht richtig,wie das mit den Zeigern funktioniert,ich bin total Anfänger Ok ich versuche das Morgen mal.
|
Cranvil
Anmeldungsdatum: 9. März 2019
Beiträge: 990
|
ubuntu--anfaenger schrieb: Zur Zeit verstehe ich das noch nicht richtig,wie das mit den Zeigern funktioniert,ich bin total Anfänger
Kleiner Tipp: Der Algorithmus bestimmt hier das Verhalten, nicht die Frage, ob die Variablen Pointer darstellen. 😉
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
Finde den Logikfehler:
| Link* insert (Link* previous_node, Link* next_node)
{
next_node->successor = previous_node;
return next_node;
}
|
Mit sprechenden Variablen tut es mehr weh das, was in Zeile 3 steht zu lesen: Weise dem nächsten Knoten den Vorgänger als Nachfolger zu.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Zeile 36 erzeugt rechts einen Link t4, der als zweiter Parameter der Funktion "insert" übergeben wird:
| test = insert (test, new Link ("t4"));
|
Der erste Parameter ist test, und der Variablen test wird das Ergebnis der Funktion zugewiesen.
| Link* insert (Link* p, Link* n)
{
n->succ = p;
return n;
}
|
Link p (vielleicht für Pointer?) ist der erste Parameter, n, vielleicht für Node der zweite. n-> succ = p; Also in der Kette folgt p, obwohl früher erzeugt, n. n=>p.
Dann wird n zurückgegeben, nicht etwa p, so dass jetzt t4 im aufrufenden Kontext als "test" bekannt ist, nicht mehr das alte test. Da das sukzessiv mit den vorherigen Parametern genauso war haben wir eine Kette von Links; test wird jeweils durch den neuen Knoten ersetzt. t4 ⇒ t3 ⇒ t2 ⇒ t1, wobei der ⇒ für "succ" stehen soll. print_mich (test) fängt also mit dem an, was zuletzt test zugewiesen wurde, also t4. Insert kann man als Einfügen-vor, im Gegensatz zu Einfügen-nach auffassen, wie etwa auch bei den Sed-Kommandos i/a, i fügt davor, ein, a danach.
|
ubuntu--anfaenger
(Themenstarter)
Anmeldungsdatum: 12. Oktober 2013
Beiträge: 1088
Wohnort: Belgien
|
Danke schonmal für die vielen Antworten, Ich bin bis Kapitel 17 eigentlich ganz gut durchgekommen..hierbei tue ich mir sehr schwer zu verstehen.
Kleiner Tipp: Der Algorithmus bestimmt hier das Verhalten, nicht die Frage, ob die Variablen Pointer darstellen.
Ja danke.
Der erste Parameter ist test, und der Variablen test wird das Ergebnis der Funktion zugewiesen.
Wenn ich nun Link* p mit n tausche: Link* insert(Link* n, Link* p)dann bekomme ich die Ausgabe{t1,t4} t2 u t3 fehlen Wie müsste ich schreiben damit er die Liste komplett Ausgibt{t1,t2,t3,t4} das würde mir zum vergleichen helfen den Kram zu verstehen
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
ubuntu--anfaenger schrieb: Ich bin bis Kapitel 17 eigentlich ganz gut durchgekommen..
Ist das zufällig "Programming Principles and Practice using C++"? hierbei tue ich mir sehr schwer zu verstehen.
C++ ist IMHO nicht unbedingt die beste Sprache, wenn man als Einsteiger mit Algorithmen spielen will - da tut man sich mit einer Sprache wie Python3 wesentlich leichter, weil viel Komplexität vor einem versteckt wird und
die Introspektion zur Laufzeit besser möglich ist.
Wenn ich nun Link* p mit n tausche: Link* insert(Link* n, Link* p) dann bekomme ich die Ausgabe{t1,t4} t2 u t3 fehlen
Ich würde die Verwendung von "sprechenden" Variablennamen empfehlen, dann fällt es viel leichter zu Lesen, was der Code tut. Variablen mit nur einem Buchstaben stammen noch aus einer Zeit, als Platz auf Lochkarten rar war. Die Reihenfolge der Argumente ist Geschmackssache, wichtig ist, dass man die richtige Variable an der richtigen Stelle nutzt. Statt einer freien Funktion könnte man eine Methode für das Link-Struct schreiben, das Knoten hinzufügen kann - dadurch muss man nur ein Argument übergeben und kommt weniger leicht durcheinander. In der nächsten Ausbaustufe könnte man sich dann dem Vector weiter annähern und den Begin und Ende der verketteten Liste speichern, damit die Handhabung komfortabler wird und man mit einer Methode push_back(Link* link) oder etwas ähnlichem neue Links an die Kette anhängen kann. Wie müsste ich schreiben damit er die Liste komplett Ausgibt{t1,t2,t3,t4} das würde mir zum vergleichen helfen den Kram zu verstehen
Du hast da ja momentan noch eine einfach verkettete Liste (auch wenn der Code dafür vorbereitet zu sein scheint, dass es mal eine doppelt verkette Liste werden soll) - die kann man nur in einer Richtung vom ersten Element aus vollständig durchlaufen, also musst du dir einen Pointer auf das erste Link -Element in der Kette merken. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 | #include <iostream>
using namespace std;
struct Link {
string value;
Link* prev;
Link* succ;
Link(const string& v, Link* p = 0, Link* s = 0)
: value(v), prev(p), succ(s){}
};
Link* insert(Link*current_node, Link* next_node)
{
current_node->succ = next_node;
return next_node;
}
void print_link_and_successors(Link* current_node)
{
cout << "{ ";
while (current_node) {
cout << current_node->value;
if (current_node = current_node->succ) cout << ", ";
}
cout << " }";
}
int main()
{
Link* first_node;
Link* current_node;
first_node = current_node = new Link("t1");
current_node = insert(current_node, new Link("t2"));
current_node = insert(current_node, new Link("t3"));
current_node = insert(current_node, new Link("t4"));
print_link_and_successors(first_node);
cout<<"\n";
}
|
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17552
Wohnort: Berlin
|
Wenn ich nun Link* p mit n tausche: Link* insert(Link* n, Link* p)dann bekomme ich die Ausgabe{t1,t4} t2 u t3 fehlen
Ja, an t1 wird erst t2 angehängt, aber dann wird t2 durch t3, t4 ersetzt.
Wie müsste ich schreiben damit er die Liste komplett Ausgibt{t1,t2,t3,t4} das würde mir zum vergleichen helfen den Kram zu verstehen
Du könntest an den Knoten nur anhängen, wenn dessen Nachfolger noch leer ist, ansonsten rekursiv an den Nachfolger anhängen: | // next ist der, der an die Liste hinten angehängt werden soll:
Link* append (Link* p, Link* next)
{
if (p->succ == null)
p->succ = next;
else
append (p->succ, next);
return p;
}
|
|
Cranvil
Anmeldungsdatum: 9. März 2019
Beiträge: 990
|
seahawk1986 schrieb: C++ ist IMHO nicht unbedingt die beste Sprache, wenn man als Einsteiger mit Algorithmen spielen will - da tut man sich mit einer Sprache wie Python3 wesentlich leichter, weil viel Komplexität vor einem versteckt wird und
die Introspektion zur Laufzeit besser möglich ist.
Ich habe im Studium Introduction to Algorithms kennengelernt und kann es sehr empfehlen, wenn es wirklich um Algorithmen gehen sollte. Der dort zu findende Pseudocode ließ sich auch recht gut in verschiedene Programmiersprachen umsetzen, sobald man deren Syntax grob im Griff hatte. ☺
|
rleofield
Anmeldungsdatum: 14. September 2008
Beiträge: 779
Wohnort: Görlitz
|
ubuntu--anfaenger schrieb: Hallo, Ich habe bei einer Übung ein Verständnis Problem:Kann mir vielleicht jemand erklären wieso ich bei diesem code Schnipsel die Ausgabe t4,t3,t2,t1(Rückwerts bekomme) und nicht t1,t2,t3,t4..
In meinem Debugger in der print funktion schaut das so aus: prev habe ich weggelassen, da das immer 0 ist. | Lokale Variablen
p @0x55d097ef8340 Link
succ @0x55d097ef8300 Link
succ @0x55d097ef82c0 Link
succ @0x55d097ef8280 Link
succ 0x0 Link*
value "t1" std::__cxx11::string
value "t2" std::__cxx11::string
value "t3" std::__cxx11::string
value "t4" std::__cxx11::string
|
D.h. print bekommt 't4', schaut dann nach succ und bekommt t3 ... Der letzte Wert in der Liste ist 't1'. Also alles so, wie es programmiert wurde.
Ein Tipp: Wirf das Buch, das solche 'Ideen hat oder woher das auch immer stammt, sofort weg. Es ist grottenschlecht, damit lernst Du nie C++. nie pointer kopieren, man verliert die Kontrolle nie pointer auf 0 setzen, dafür gibt es nullptr nie new ohne ein delete schreiben mit C++ ist new/delete überflüssig mit C++ nimmt man für Listen die STL
So könnte das aussehen, ohne new/delete: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | #include <iostream>
#include <list>
#include <string>
#include <algorithm>
using std::string;
using std::list;
void pp(string const& s){
std::cout << s << std::endl;
}
int main()
{
std::list<string> test1;
test1.push_back("t1");
test1.push_back("t2");
test1.push_back("t3");
test1.push_back("t4");
for_each( test1.begin(), test1.end(), pp );
std::cout << "ende" << std::endl;
}
|
Happy C++ rleofield
|
ubuntu--anfaenger
(Themenstarter)
Anmeldungsdatum: 12. Oktober 2013
Beiträge: 1088
Wohnort: Belgien
|
Ist das zufällig "Programming Principles and Practice using C++"?
Nee das Buch heisst Einführung in die Programmierung mit c++ von Bjarne Stroustrup
C++ ist IMHO nicht unbedingt die beste Sprache, wenn man als Einsteiger mit Algorithmen spielen will - da tut man sich mit einer Sprache wie Python3 wesentlich leichter, weil viel Komplexität vor einem versteckt wird und die Introspektion zur Laufzeit besser möglich ist.
Das höre ich nicht zum ersten mal,
In der nächsten Ausbaustufe könnte man sich dann dem Vector weiter annähern und den Begin und Ende der verketteten Liste speichern, damit die Handhabung komfortabler wird und man mit einer Methode push_back(Link* link) oder etwas ähnlichem neue Links an die Kette anhängen kann
Ich glaube das ist auch das Ziel von diesem Kapitel, zu verstehen wie <vector> funktioniert.
Du hast da ja momentan noch eine einfach verkettete Liste (auch wenn der Code dafür vorbereitet zu sein scheint, dass es mal eine doppelt verkette Liste werden soll) - die kann man nur in einer Richtung vom ersten Element aus vollständig durchlaufen, also musst du dir einen Pointer auf das erste Link-Element in der Kette merken.
So sieht der eigentliche Beispielcode aus dem Buch aus,ich hatte den so vereinfacht um halt besser verstehen zu können wie die Ausgaben zu Stande kommen: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 | #include <iostream>
using namespace std;
//------------------------------------------------------------------------------
struct Link {
string value;
Link* prev;
Link* succ;
Link(const string& v, Link* p = 0, Link* s = 0)
: value(v), prev(p), succ(s) { }
};
//------------------------------------------------------------------------------
Link* insert(Link* p, Link* n) // f�gt n vor p ein; liefert n zur�ck
{
if (n==0) return p;
if (p==0) return n;
n->succ = p; // p folgt auf n
if (p->prev) p->prev->succ = n;
n->prev = p->prev; // der "ehemalige" Vorg�nger von p wird zum Vorg�nger von n
p->prev = n; // n wird zum Vorg�nger von p
return n;
}
//------------------------------------------------------------------------------
Link* add(Link* p, Link* n) // f�gt n hinter p ein; liefert n zur�ck
{
// �hnlich wie insert (siehe �bung 11)
return insert(p,n);
}
//------------------------------------------------------------------------------
Link* erase(Link* p) // l�scht *p aus der Liste; liefert den Nachfolger von p zur�ck
{
if (p==0) return 0;
if (p->succ) p->succ->prev = p->prev;
if (p->prev) p->prev->succ = p->succ;
return p->succ;
}
//------------------------------------------------------------------------------
Link* find(Link* p, const string& s) // sucht in der Liste nach s;
// liefert 0 zur�ck, wenn die Suche erfolglos war
{
while(p) {
if (p->value == s) return p;
p = p->succ;
}
return 0;
}
//------------------------------------------------------------------------------
Link* advance(Link* p, int n) // r�ckt n Positionen in der Liste vor oder zur�ck
// liefert 0 zur�ck, wenn die Suche erfolglos war
// positives n r�ckt vor, negatives n zur�ck
{
if (p==0) return 0;
if (0<n) {
while (n--) {
if (p->succ == 0) return 0;
p = p->succ;
}
}
if (n<0) {
while (n++) {
if (p->prev == 0) return 0;
p = p->prev;
}
}
return p;
}
//------------------------------------------------------------------------------
void print_all(Link* p)
{
cout << "{ ";
while (p) {
cout << p->value;
if (p=p->succ) cout << ", ";
}
cout << " }";
}
//------------------------------------------------------------------------------
int main()
{
Link* norse_gods = new Link("Thor");
norse_gods = insert(norse_gods,new Link("Odin"));
norse_gods = insert(norse_gods,new Link("Zeus"));
norse_gods = insert(norse_gods,new Link("Freya"));
Link* greek_gods = new Link("Hera");
greek_gods = insert(greek_gods,new Link("Athena"));
greek_gods = insert(greek_gods,new Link("Mars"));
greek_gods = insert(greek_gods,new Link("Poseidon"));
Link* p = find(greek_gods, "Mars");
if (p) p->value = "Ares";
// In gleicher Manier versetzen wir Zeus in seinen korrekten Pantheon:
{
Link* p = find(norse_gods,"Zeus");
if (p) {
erase(p);
insert(greek_gods,p);
}
}
// Bereinigung des subtilen Fehlers aus der obigen Version
{
Link* p = find(norse_gods, "Zeus");
if (p) {
if (p==norse_gods) norse_gods = p->succ;
erase(p);
greek_gods = insert(greek_gods,p);
}
}
print_all(norse_gods);
cout<<"\n";
print_all(greek_gods);
cout<<"\n";
}
|
Ja, an t1 wird erst t2 angehängt, aber dann wird t2 durch t3, t4 ersetzt.
Ich schaue mir das diese Woche mal in Ruhe an,ich hab leider heute die Zeit nicht dazu.
Ich habe im Studium Introduction to Algorithms kennengelernt und kann es sehr empfehlen, wenn es wirklich um Algorithmen gehen sollte. Der dort zu findende Pseudocode ließ sich auch recht gut in verschiedene Programmiersprachen umsetzen, sobald man deren Syntax grob im Griff hatte. ☺
Ich habe leider nie Studiert, es geht weniger um Algorithmen.. Um das dann mal kurz zu Erklären,ich habe nie vor ein professioneller Programierer zu werden, ich bin da eher reingerutscht,hab mal einen Netzwerktechnik Kurs bei cisco gemacht,und dort waren Windows Rechner verboten, So bin ich dann zu linux gekommen und hab sehr viel Spass daran gefunden was mann mit linux alles so machen kann..hab dann später mir ein Buch über c Programmierung gekauft und nach kurzer Zeit abgebrochen..weil ich nix verstanden habe. Das aktuelle Buch von Stroustrup dagegen fand ich bisher sehr gut,weil das halt so erklärt wurde das ich etwas verstehe(bisher)ich fande die ersten Kapitel sogar sehr leicht..die Theoretische Einführung von diesem Kapitel hab ich auch eigentlich verstanden..Wie das mit dem Freispeicher funktioniert. Nur jetzt bei diesem Teil sehe ich alles irgendwie vernebelt ich blicke da noch nicht durch. Auf jedenfall danke für euer bemühen 😳
|