Liste mit wiederholten Objekten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
BlackJack

Goswin: Verkettete Listen sind in Python kein Problem und sehen im Grunde so aus wie in C, nur das man statt einem `struct` und Funktionen eine Klasse schreibt, weil das in Python der Verbunddatentyp ist. Man hat nur keine Pointer als *Datentyp* in der Sprache.

Code: Alles auswählen

class Node(object):

    def __init__(self, data, next_node=None):
        self.data = data
        self.next_node = next_node


class LinkedList(object):

    def __init__(self):
        self.items = None

    def __iter__(self):
        node = self.items
        while node:
            yield node.data
            node = node.next_node

    def __len__(self):
        return sum(1 for _ in self)

    def prepend(self, item):
        self.items = Node(item, self.items)


def main():
    linked_list = LinkedList()
    linked_list.prepend(23)
    linked_list.prepend(42)
    linked_list.prepend(4711)
    print(len(linked_list))
    for item in linked_list:
        print(item)


if __name__ == '__main__':
    main()
Wie man sieht sind verkettete Listen in Python kein Problem. Eher selten in der Praxis so zu sehen, weil der eingebaute `list`-Typ in der Regel alles mitbringt was man benötigt. Ausser wenn man wirklich mal auch am Anfang der Datenstruktur effizient hinzufügen oder entfernen möchte, dann nimmt man `collections.deque`. Was dann noch fehlt ist ein Datentyp bei dem man auch in der Mitte effizient Elemente einfügen oder entfernen kann. Dann könnte man sich in der Tat eine doppelt verkette Liste einfach selbst implementieren. Und natürlich Graphen, die man ähnlich mit eigenen Knotenklassen erstellen kann.

Ich denke was Sirius3 meinte war der Code den Du da geschrieben hast. Denn so etwas habe ich in Python noch nicht gesehen und sehe auch nicht wirklich wo man das braucht. Wenn ich mal *vermuten* dürfte was Du da tust, dann sieht das eher nach einer eigenen Koordinaten-Klasse und einer Polygon-Klasse aus und nach Code um zwischen der besch…eidenen API von Tk hin und her zu wandeln.

Zu Deinen beiden Codebeispielen: Namen sind in Python nicht gleichzusetzen mit Adressen oder Schachteln wo man Dinge/Werte hinein tut sondern Haftnotizen die man an Objekte klebt. Das ist eine Zuweisung. Nicht etwas in eine Schachtel mit dem Namen `x` stecken, sondern eine Haftnotiz wo `x` drauf steht an ein Objekt kleben. Die Adressen gehören in Python fest zu den Werten.

Dein erstes Codebeispiel:

a=[2]: Es wird eine Liste mit dem Element 2 erstellt und da wird ein Zettel mit `a` draufgeklebt.
b=a: Dann wird ein Zettel mit `b` auf das Objekt geklebt wo der Zettel mit `a` dran klebt. An der Liste kleben nun zwei Haftnotizen.
a=[3]: Es wird eine Liste mit dem Element 3 erstellt und da wird der Zettel mit `a` dran geklebt, wofür er von der anderen Liste entfernt werden muss, weil es (in einem Namensraum) immer nur einen Zettel mit dem gleichen Namen gibt, und der immer nur maximal an einem Objekt kleben kann.
print(b): Es wird das Objekt an dem der Zettel `b` klebt ausgegeben, also die Liste mit der 2 darin. Denn das umkleben der Zettels mit `a` hat keinen Einfluss auf irgend etwas anderes. Keines der beiden Objekte, weder das wo der Zettel vorher klebte, noch das wo er dann drauf geklebt wird, hat irgend einen Einfluss auf die beteiligten Objekte.

Zweites Codebeispiel:
a=[2]: Wie im ersten Beispiel.
b=a: Ebenfalls wie im ersten Beispiel.
a[0]=3: Hier wird das Objekt mit den Zettel `a` genommen und dessen erstes Element referenziert nun das Objekt 3. Und nicht mehr das Objekt 2, weil an einem Index immer nur *ein* Objekt referenziert werden kann.
print(b): Wie im ersten Beispiel wird das Objekt ausgegeben auf dem Zettel `b` klebt. Das ist die Liste auf der beide Zettel kleben, weil Zettel `a` ja nicht zu einem anderen Objekt bewegt wurde, also die Liste in der wegen der Zeile hiervor das Element 3 steckt.

Das ist ein ”Riesenunterschied” weil die vorletzte Zeile in den beiden Beispielen etwas unterschiedliches macht. Es wird nicht der Name neu gebunden, an ein *anderes* Listenobjekt, sondern es wird die eine Liste an die beide Namen gebunden sind *verändert*. Und riesig ist der Unterschied IMHO auch nur weil Du im Denkmuster von C (oder einer ähnlichen Sprache) fest hängst, wie man an den Semikolons erahnen kann.

Wobei ich auch sagen würde das Java im grossen und ganzen auch wie Python funktioniert. Bei den Wertdatentypen unter den Grunddatentypen macht es keinen Unterschied das Java die anders behandelt, weil die unveränderlich sind, also vom Effekt her kein Unterschied besteht. Wenn man diese Art des Objektmodells nicht versteht, dann kann man weder Java noch Python sinnvoll programmieren.

Und JavaScript funktioniert an der Stelle auch so wie Python, Ruby, Perl, Java, …. Das ist also wirklich nichts ”exotisches” was ausgerechnet Python anders machen würde als alle anderen Programmiersprachen.

Da pillmuncher Lisp ins Spiel brachte: Auch dort gibt es keinen Pointer-Datentyp aber verkettete Listen sind *der* Grunddatentyp und sie werd aus Cons-Zellen erstellt, also im Grunde so etwas wie Tupel die aus einem Wert und einer nächsten Cons-Zelle oder einem ”Endkennzeichen” bestehen, letztlich also so etwas wie die `Node`-Klasse im Python-Beispiel weiter oben.

Mal als Beispiel in Hy (in Python implementierter Lisp-Dialekt):
[codebox=clojure file=Unbenannt.txt]#!/usr/bin/env hy
(require [hy.contrib.loop [loop]])

(defn length [a-list]
(if (empty? a-list) 0 (inc (length (cdr a-list)))))

(defmain [&rest args]
(setv a-list '())
(setv a-list (cons 23 a-list))
(setv a-list (cons 42 a-list))
(setv a-list (cons 4711 a-list))
(print (length a-list))
(loop [[items a-list]]
(unless (empty? items)
(print (car items))
(recur (cdr items)))))[/code]
Also so könnte `length` in Lisp aussehen wenn man es selber implementieren müsste, natürlich gibt es das schon und da solche Listen *die* Grunddatenstruktur in Lisp sind in der sogar die Programme repräsentiert werden, gibt es auch eine syntaktische Möglichkeit solche Listen zu schreiben: Mit Klammern, wie das Programm selbst auch. Das hier wäre dann etwas näher an dem was man in Lisp schreiben würde:
[codebox=clojure file=Unbenannt.txt]#!/usr/bin/env hy
(require [hy.contrib.loop [loop]])

(defmain [&rest args]
(let [a-list '(4711 42 23)]
(print (len a-list))
(loop [[items a-list]]
(unless (empty? items)
(print (car items))
(recur (cdr items))))))[/code]
Und da Hy auf Python abgebildet wird, gibt es die `for`-Form, die das `loop`-Makro ersetzen kann und man bei folgendem landet:
[codebox=clojure file=Unbenannt.txt]#!/usr/bin/env hy

(defmain [&rest args]
(let [a-list '(4711 42 23)]
(print (len a-list))
(for [item a-list] (print item))))[/code]
Benutzeravatar
Goswin
User
Beiträge: 363
Registriert: Freitag 8. Dezember 2006, 11:47
Wohnort: Ulm-Böfingen
Kontaktdaten:

@pillmuncher & BlackJack:
Vielen Dank für die ausführlichen Erklärungen und Beispiele. :D Ich werde mir Lisp einmal etwas genauer anschauen.
BlackJack hat geschrieben: Wenn ich mal *vermuten* dürfte was Du da tust, dann sieht das eher nach einer eigenen Koordinaten-Klasse und einer Polygon-Klasse aus und nach Code um zwischen der besch…eidenen API von Tk hin und her zu wandeln.
Genau. Falls du meine Bemerkung weiter oben nicht gelesen hast, bin ich über deine korrekte Vermutung extrem erstaunt

Beim Code "a=[3]", wenn ich dich richtig verstanden habe, läuft folgendes ab:
Es werden zwei Objekte erzeugt. Erstens ein Listenobjekt, auf dem der Zettel mit Namen 'a' klebt und an welchem ein Bindfaden angebunden ist, an dem ein weiterer Zettel mit einem neuen, mir unbekannten internen Namen hängt, zum Beispiel '_a0_'. Im allgemeinen wären Listenobjekte wie Kipus vorzustellen, woran viele Bindfäden mit Namenszetteln hängen. Zweitens wird noch ein Zahlenobjekt erstellt, auf den der Zettel mit dem neuen Namen '_a0_' geklebt wird. Ein anderes Objekt, auf dem der Zettel 'a0' vorher klebte, könnte frei werden.

Beim Code "a[0]=3" würde hingegen nur ein einziges Objekt erstellt, und zwar ein Zahlenobjekt, auf welches der Zettel '_a0_' geklebt wird. Ein anderes Objekt, auf dem der Zettel '_a0_' vorher klebte, könnte frei werden.
BlackJack

@Goswin: Ja genau so kann man sich den Unterschied vorstellen. Das mit den Bindfäden ist gut. Muss ich mir merken. :-)
Benutzeravatar
pyHoax
User
Beiträge: 84
Registriert: Donnerstag 15. Dezember 2016, 19:17

So würde ichs machen...

Code: Alles auswählen

pt = [1,2,3]
lst = [0, 1, 2, 1, 2, 0, 1, 0, 1]
print [ pt[i] for i in lst ]
pt[0] = 5
print [ pt[i] for i in lst ]
Antworten