Types werden bei Arrays kopiert - was kann ich tun?

Previous topic - Next topic

Foo

Hallo,

ich habe gemerkt, dass Types beim einfügen in ein Array wohl kopiert werden. Warum ist das so? Kann ich das irgendwie ändern?
Beispielcode
Code (glbasic) Select

LIMITFPS 10


TYPE TBar
text$ = ""
x = 0
y = 0
ENDTYPE


LOCAL a AS TBar
LOCAL b AS TBar
LOCAL c AS TBar
LOCAL d AS TBar

LOCAL list[] AS TBar
DIM list[4]

FOR i = 0 TO 3
list[i].x = RND(1000)
list[i].y = RND(1000)
NEXT

a.text$ = "A"
b.text$ = "B"
c.text$ = "C"
//d.text$ = "D"
//Direct try
list[3].text$ = "D"

x = 10
WHILE TRUE
y = 10
FOR i = 0 TO 3
PRINT "Text: " + list[i].text$ + " x: " + list[i].x + " y: " + list[i].y, x, y
INC y, 10
NEXT
SHOWSCREEN
KEYWAIT
WEND

Erwartet:
Gesetzte Variable text$ bei allen Types im Array. Das ist aber leider nicht so. Nur die eine Typeinstanz d, bei der klappt es. Wohl, weil die Types beim Array kopiert werden. :(


PS. Sollte man die Fragen eigentlich eher ins Englische Forum posten, wenn man dieser mächtig ist, oder doch dann hier?

Quentin

ähem, was treibst du eigentlich da? Das programm macht genau das, was du ihm sagst. Du ordnest nur dem letzten Element aus list[] einen Text zu. Wozu sollen die Instanzen a, b, c, und von TBar gut sein?

Was siehst du denn jetzt als Fehler an?

Foo

Hallo,

aus den Types werden ja beim kompilieren Klassen. Von anderen Programmiersprachen kenn ich das so, das Klassen meistens als Referenz übergeben werden.

Wenn ich nun zum Beispiel die Variable a ändern würde, würde sich auch der Inhalt der Variable an der Stelle list[0] ändern. Das passiert aber bei GLBasic nicht. Gibt es in GLBasic vielleicht einen speziellen Referenzoperator dafür?

Quentin

ich befürchte, du wirfst da etwas durcheinander. Deine Variablen a-d sind Variablen vom Typ TBar. list[] ist ein Array vom Typ TBar. Beide haben erst einmal gar nichts miteinander zu tun, nur daß sie den gleichen Datentyp haben.

Wenn du den text$ von einem Element der Liste ändern möchtest, dann tu das doch einfach, wie du es schon mit den anderen Werten für x und y getan hast

list[0].text$ = "text1"
list[1].text$ = "text2"

usw.
Warum meinst du, daß in deinem Beispiel a auf list[0] zeigt, b auf list[1] etc.?

Foo

Der unten stehende Code wäre das Gleiche in Java, vielleicht ist es dann verständlicher:

Code (glbasic) Select

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Main m = new Main();
        m.test();
    }

    public void test() {
        ArrayList<Bar> barArray = new ArrayList<Bar>();

        Bar a = new Bar();
        Bar b = new Bar();

        barArray.add(a);
        barArray.add(b);

        for (int i = 0;i < 2; ++i) {
          barArray.get(i).x = 10;
        }
        // Variable a wird direkt verändert
        a.text = "A";
        // Variable b innerhalb des Arrays verändert
        barArray.get(1).text = "B";

        for (int i = 0;i < 2; ++i) {
          System.out.println(barArray.get(i).text + ": " + barArray.get(i).x);
        }
    }


    class Bar {
      public String text = "";
      public int x = 0;
      public int y = 0;
    }
}



Ausgabe:

Quote
run:
Text: A x:10
Text: B x:10
BUILD SUCCESSFUL (total time: 0 seconds)

Variable a und b wurden korrekt manipuliert. Bei GLBasic wäre das $text bei a nicht gesetzt.

Schranz0r

Jow, es wird mit DIMPUSH übergeben/kopiert, ist schon richtig.

Machen würde ich es aber so:

FOR i = 0 TO 3
    LOCAL t AS TBar
    t.x = RND(1000)
    t.y = RND(1000)
    DIMPUSH TBar[], t 
NEXT

...da das Array ja ein offenes Array ist, somit brauchst dein DIM list[4] nicht.


I <3 DGArray's :D

PC:
AMD Ryzen 7 3800X 16@4.5GHz, 16GB Corsair Vengeance LPX DDR4-3200 RAM, ASUS Dual GeForce RTX™ 3060 OC Edition 12GB GDDR6, Windows 11 Pro 64Bit, MSi Tomahawk B350 Mainboard

Quentin

Aha, ok, bin zwar nicht so wirklich firm in Java, aber ich denke, dass kann ich noch nachvollziehen.

Ich versuche mal, das gegenüberzustellen:

ArrayList<Bar> barArray = new ArrayList<Bar>();
entspricht in etwas
LOCAL list[] AS TBar
in GLBasic

Bar a = new Bar();
Bar b = new Bar();
wäre dann
LOCAL a AS Tbar
LOCAL b AS Tbar

barArray.add(a);
barArray.add(b);
zum hinzufügen von Elementen an das Array wäre in GLBasic
DIMPUSH list[], a
DIMPUSH list[], b

allerdings wären noch keine Inhalte zugeordnet. Sinnvollerweise würde man hier zunächst a und b Werte zuordnen und dann in das Array schreiben

for (int i = 0;i < 2; ++i) {
    barArray.get(i).x = 10;
}
wäre dann
FOREACH l IN list[]
    l.x = 10
NEXT

Der Knackpuntk wäre dann wohl folgende Zeile
a.text = "A";
So wie ich das verstanden habe, würde damit auch das entsprechende Element in der Liste geändert, weil a darauf zeigt. Richtig?
Das geht dann in GLBasic so nicht, hier müsstest du schon gezielt das entsprechende Element aus dem Array ansprechen
list[0].text$ = "A"

Ich vermute, in Java, wird lediglich ein Zeiger auf a in der Liste gesichert, sodaß eine Zuweisung a.text = "test" auch den gewünschten Effekt hat. In GLBasic wird jedoch der Inhalt von a komponentenweise nach list[0] kopiert, sodaß eine Änderung an a nicht automatisch den Inhalt von list[0] mit ändert.

Ich hoffe, das war jetzt einigermaßen verständlich :)






Foo

@Schranz0r
Das ist richtig, stimmt. Nur sollte man bedenken, dass das nächste Element bei einem Push erst berechnet werden muss. Wenn man die Anzahl kennt z.B. 128 - ist das je nach Verwaltungsalgorythmus um einiges schneller. So ist es zumindest in den meisten Sprachen, denke auch in C++.

@Quentin
QuoteIn GLBasic wird jedoch der Inhalt von a komponentenweise nach list[0] kopiert
Genau kopiert werden die Variablen an dieser Stelle bei GLBasic, leider. Nur vielleicht liesse sich das ja durch einen Referenzoperator steuern? Oder hat das einen bestimmten Grund, dass die Variablen kopiert werden?

Quotewäre dann
FOREACH l IN list[]
    l.x = 10
NEXT
Das mit dem Foreach würde so aber oder so nicht klappen, weil die bei einem Foreach ja kopiert werden. Deswegen habe ich ja die normale For-Schleife genommen.


Quentin

na zum ersten Punkt würde ich mal sagen "That's why it's Basic" :))
Zwar gibt es auch in etlichen Basic-Dialekten mittlweile Zeiger, aber wirklich Basic-like ist das nicht. Nee im Ernst, warum GLBasic so arbeitet, wie es es scheinbar tut, kann ich dir nicht sagen.

die FOREACH-Schleife wird sehr wohl so funktionieren, weil l hier auf das jeweilige Elemte aus list[] zeigt.

Schranz0r

@Foo:

Wieso sollte das langsamer sein?
Gehst du bei C++ und Vectoren her und definierst die größe vor?
Für was wären dann blabla.push_back() gut? ;)

Ne mal im ernst, die Types sind hier schneller als in anderen mirbekannten Basicdialekten.

Quote from: Quentin on 2009-Oct-05
die FOREACH-Schleife wird sehr wohl so funktionieren, weil l hier auf das jeweilige Elemte aus list[] zeigt.


Eben, dafür ist es auch gemacht worden ;)
I <3 DGArray's :D

PC:
AMD Ryzen 7 3800X 16@4.5GHz, 16GB Corsair Vengeance LPX DDR4-3200 RAM, ASUS Dual GeForce RTX™ 3060 OC Edition 12GB GDDR6, Windows 11 Pro 64Bit, MSi Tomahawk B350 Mainboard

Kitty Hello

AAAALSO.

Wenn man mit FOREACH arbeitet, dann ist die FOREACH Variable ein Zeiger! Echter Zeiger.

Wenn man unbedingt das Kopieren beim Dimpush übergehen will, kann man's so machen:

Code (glbasic) Select

LOCAL a[] AS foo

locain id% = LEN(a[])
REDIM a[ id% +1 ]

a[id].xy = Wert