IndexError warum?

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
efix
User
Beiträge: 43
Registriert: Samstag 7. Dezember 2019, 20:59

Hallo Leute,

ich wollte anfangen eine Übungsaufgabe zu machen und bekomme einen seltsamen IndexError. Kann mir jemand vielleicht erklären warum und was ich falsch mache an der stelle?

Code: Alles auswählen

seq = [[1,1,2]]

print(seq[0]) # hier gehts

for i in range(len(seq.pop())):
    n = len(seq)
    print(seq[0]) # hier bekomme ich list index out of range
Warum bekomme ich in der for Schleife einen Error wenn ich auf seq[0] zugreifen will und vor der Schleife funktioniert es... irgendwie seltsam.

Grüße
ganja
User
Beiträge: 189
Registriert: Mittwoch 3. Dezember 2014, 07:44

Hallo,
für mein Verständnis, hast du es in "n" versuch print n
einfachTobi
User
Beiträge: 491
Registriert: Mittwoch 13. November 2019, 08:38

Schau dir mal an, was list.pop() macht: https://docs.python.org/3/tutorial/data ... e-on-lists und lass dir in deiner for-Schleife i, seq und n ausgeben, bevor du auf seq[0] zugreifen willst. Dann solltest du schnell erkennen, was falsch läuft.
efix
User
Beiträge: 43
Registriert: Samstag 7. Dezember 2019, 20:59

Ja stimmt, danke für den Tipp. Dann habe ich die pop() Funktion falsch verstanden. Ich dachte die gibt mir nur das letzte Element in einer Liste...
Jetzt geht es. Ich möchte das letzte Element durchlaufen und mit der Zahlenreihe etwas machen...

Code: Alles auswählen

seq = [[1,1,2]]

for i in range(len(seq[len(seq)-1])):
    print(i)

Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum hast Du eine Liste in eine Liste gesteckt?
Wenn man auf das letzte Element einer Liste zugreifen will, dann benutzt man einfach `seq[-1]`.
Wenn man mit Listen arbeitet, benutzt man keinen Index:

Code: Alles auswählen

seq = [[1, 1, 2]]
for number in seq[-1]:
    print(number)
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

efix hat geschrieben: Mittwoch 19. Januar 2022, 08:47 Ich möchte das letzte Element durchlaufen und mit der Zahlenreihe etwas machen...
Dann verwendest du aber derzeit einen umständlichen Weg.

Code: Alles auswählen

seq = [[3, 4], [5, 23, 42]]
for i in range(len(seq[len(seq)-1])):
    print(i)
Dein Code ermittelt das letzte Element mit `seq[len(seq)-1]`, wendet darauf `len` an um die Größe der Liste zu bekommen und verpackt das dann in einem `range`. Im Schleifenrumpf wird dann der Index der enthaltenen Werte ausgegeben, nicht einmal die Zahl selber. Du erhältst also die Werte 0, 1, und 2..

Das letzte Element einer Liste erhält man einfacher über den Index -1. Und da eine Liste iterierbar ist kannst du auch direkt die Werte dort herausholen.

Code: Alles auswählen

seq = [[3, 4], [5, 23, 42]]
for value in seq[-1]:
    print(value)
Das liefert dir direkt 5, 23 und 42.

Solltest du aus irgendeinem Grund zusätzlich zu den Werten den Index benötigen, dann kannst du enumerate verwenden.

Code: Alles auswählen

for index, value in enumerate(seq[-1]):
    print(f'{index}: {value}')
efix
User
Beiträge: 43
Registriert: Samstag 7. Dezember 2019, 20:59

Ich wollte die zweite Aufgabe machen von:

https://trainyourprogrammer.de/index.pl ... llposition

Code: Alles auswählen

seq = [[1,1,2]]

nseq = []

temp = []


for x in range(0, 1):
    del nseq[:]
    for i,v in enumerate(seq[-1]):
        if len(seq[-1]) == i+1:
            nseq.append(len(temp))
            nseq.append(temp[-1])
            del temp[:]
            temp.append(v)
            nseq.append(len(temp))
            nseq.append(temp[-1])
            del temp[:]    
        elif not temp:
            temp.append(v)
        elif temp[-1] == v:
            temp.append(v)
        elif temp[-1] != v:
            nseq.append(len(temp))
            nseq.append(temp[-1])
            del temp[:]
            temp.append(v) 
    seq.append(nseq)

print(seq)
Gibt bestimmt noch bessere Ansätze...
Aber jetzt habe ich das Problem das es mit zwei Durchgängen nicht mehr geht. Ich verstehe nicht warum...
Bei einem Durchgang bekomme ich:

[[1, 1, 2], [2, 1, 1, 2]]

als Ausgabe.
Bei zwei:

[[1, 1, 2], [], []]

Warum?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Ich habe jetzt nicht versucht den Algorithmus zu verstehen, aber du hast da häufiger ein del mit dem du Listeninhalte löschst. Du operierst häufiger auf nseq und diese Löschoperationen betreffen nseq natürlich überall, egal ob es direkt mit Namen angesprochen wird oder ob du es zweimal in eine andere Liste gesteckt hast.

Schau dir mal Folgendes an:

Code: Alles auswählen

>>> outer = []
>>> inner = [999, 999]
>>> outer.append(inner)
>>> outer
[[999, 999]]
>>> del inner[:]
>>> outer
[[]]
Und wenn du das so weit verstanden hast, dann mach hiermit weiter:

Code: Alles auswählen

>>> outer.append(inner)
>>> outer
[[], []]
>>> inner[:] = [1, ]
>>> outer
[[1], [1]]
>>> inner = [2, ]
>>> outer
[[1], [1]]
efix
User
Beiträge: 43
Registriert: Samstag 7. Dezember 2019, 20:59

Also das Problem muss mit dem del nseq[:] zusammenhängen.

Code: Alles auswählen

seq = [[1,1,2]]

nseq = []

temp = []


for x in range(0, 1):
    for i,v in enumerate(seq[-1]):
        if len(seq[-1]) == i+1:  
            nseq.append(len(temp)) 
            nseq.append(temp[-1])
            del temp[:]
            temp.append(v)
            nseq.append(len(temp))
            nseq.append(temp[-1])
            del temp[:]    
        elif not temp:
            temp.append(v)
        elif temp[-1] == v:
            temp.append(v)
        elif temp[-1] != v:
            nseq.append(len(temp))
            nseq.append(temp[-1])
            del temp[:]
            temp.append(v) 
    
    seq.append(nseq)
    print(nseq)
    print(seq)
    del nseq[:]

print(seq)

Ausgabe:
[2, 1, 1, 2] # nseq vor dem del Befehl
[[1, 1, 2], [2, 1, 1, 2]] # seq vor dem del Befehl
[[1, 1, 2], []] # seq nachem del von nseq - seltsam?
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

`del` braucht man fast nie. Wobei das "fast" für Anfänger "wirklich niemals, auch wenn Du denkst, es wäre so, nein Du brauchst es nicht" heißen sollte.

Wenn man eine leere Liste möchte, dann legt man eine neue leere Liste an.
Wenn die elif-Bedingung exakt das Gegenteil der vorangegangenen if-Bedingung ist, dann ist das ein Fall für `else`.
Gerade bei längeren elif-Ketten sollte es immer einen `else`-Block geben, damit der Leser weiß, was in Fällen passieren soll, die nicht schon vorher behandelt worden sind.

Ohne den Algorithmus verstanden zu haben, komme ich dann auf sowas:

Code: Alles auswählen

seq = [[1,1,2]]
temp = []
for x in range(5):
    nseq = []
    for i,v in enumerate(seq[-1]):
        if len(seq[-1]) == i+1:
            nseq.append(len(temp))
            nseq.append(temp[-1])
            nseq.append(1)
            nseq.append(v)
            temp = []
        elif not temp or temp[-1] == v:
            temp.append(v)
        else:
            nseq.append(len(temp))
            nseq.append(temp[-1])
            temp = []
            temp.append(v)
    seq.append(nseq)

print(seq)
Dort frage ich mich dann, warum `temp` vor der Schleife initialisiert wird, ob es also sein kann, ob `temp` am Anfang des inneren Schleifendurchlaufs nicht leer sein kann, und ob das Absicht ist, oder nicht.
Dann fällt mir die erste if-Abfrage auf, und ich frage mich, wann kann die `wahr` werden. `enumerate` zählt bis zur Länge der Liste - 1. Also wird die Bedingung immer beim letzten Schleifendurchgang war. Warum ist dann dieser Fall überhaupt innerhalb der Schleife und steht nicht nach der Schleife?
Das vereinfacht den Code erheblich:

Code: Alles auswählen

seq = [[1,1,2]]
temp = []
for x in range(5):
    nseq = []
    for v in seq[-1][:-1]:
        if not temp or temp[-1] == v:
            temp.append(v)
        else:
            nseq.append(len(temp))
            nseq.append(temp[-1])
            temp = []
            temp.append(v)
    nseq.append(len(temp))
    nseq.append(temp[-1])
    nseq.append(1)
    nseq.append(seq[-1][-1])
    temp = []
    seq.append(nseq)
Jetzt wird auch klar, dass `temp` am Anfang immer leer ist. Im if und im else-Block ist der letzte Befehl der selbe, kann also außerhalb der Blöcke stehen. Damit ist der erste if-Block leer, man dreht also die if-Bedingung um und hat keinen else-Block mehr:

Code: Alles auswählen

seq = [[1,1,2]]
for x in range(5):
    nseq = []
    temp = []
    for v in seq[-1][:-1]:
        if temp and temp[-1] != v:
            nseq.append(len(temp))
            nseq.append(temp[-1])
            temp = []
        temp.append(v)
    nseq.append(len(temp))
    nseq.append(temp[-1])
    nseq.append(1)
    nseq.append(seq[-1][-1])
    seq.append(nseq)
Wenn ich mir jetzt die Aufgabe durchlese, frage ich mich gerade, ob die Sonderbehandlung des letzten Elements überhaupt gewollt ist.

Code: Alles auswählen

seq = [[1,1,2]]
for x in range(5):
    nseq = []
    temp = []
    for v in seq[-1]:
        if temp and temp[-1] != v:
            nseq.append(len(temp))
            nseq.append(temp[-1])
            temp = []
        temp.append(v)
    nseq.append(len(temp))
    nseq.append(temp[-1])
    seq.append(nseq)
efix
User
Beiträge: 43
Registriert: Samstag 7. Dezember 2019, 20:59

Warum machst du:

Code: Alles auswählen

if temp and temp[-1] != v:
Damit fragst du ob temp nicht leer ist und das letzte Element nicht gleich v ... oder?
efix
User
Beiträge: 43
Registriert: Samstag 7. Dezember 2019, 20:59

Aber Genial, danke. Hab eine Menge gelernt...
Viel besser deine Lösung :)
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mit `more_itertools` kann man sich das Programmieren der Lauflängenkodierung sparen, da gibt's was für:

Code: Alles auswählen

#!/usr/bin/env python3
from itertools import islice
from more_itertools import run_length, iterate


def do_step(digits):
    """
    Bildet aus einer gegebenen Ziffernfolge `digits` nach folgenden Regeln eine
    neue Ziffernfolge:
    
    Die nacheinander folgenden gleichen Ziffern werden gezählt und die Anzahl
    zusammen mit der Ziffer in die Ergebnisziffernfolge übernommen.
    
    Beispiele:
    
    >>> do_step("112")
    '2112'
    >>> do_step("2112")
    '122112'
    
    Mehrstellige Anzahlen werden auch mehrstellig im Ergebnis angegeben:
    
    >>> digits = "1" * 12
    >>> digits
    '111111111111'
    >>> do_step(digits)
    '121'
    
    """
    return "".join(
        f"{count}{digit}" for digit, count in run_length.encode(digits)
    )


def main():
    start_value = "112"
    target_step_number = 15
    
    step_values = iterate(do_step, start_value)
    for step_count, digits in enumerate(
        islice(step_values, target_step_number), 1
    ):
        print(f"{step_count:3d}. {digits}")


if __name__ == "__main__":
    main()
Ausgabe:

Code: Alles auswählen

  1. 112
  2. 2112
  3. 122112
  4. 11222112
  5. 21322112
  6. 121113222112
  7. 11123113322112
  8. 3112132123222112
  9. 132112111312111213322112
 10. 11131221123113111231121123222112
 11. 3113112221121321133112132112211213322112
 12. 1321132132211211131221232112111312212221121123222112
 13. 1113122113121113222112311311221112131221123113112211322112211213322112
 14. 31131122211311123113322112132113212231121113112221121321132122211322212221121123222112
 15. 1321132132211331121321232221121113122113121122132112311321322112111312211312113221133211322112211213322112
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Eigentlich soll man ja laut Aufgabe nicht mit Zahlen arbeiten, sondern mit Ziffern.

Die Lösung könnte dann so aussehen:

Code: Alles auswählen

text = "112"
for _ in range(5):
    text=re.sub(r'(.)\1*', lambda m: f"{len(m.group(0))}{m.group(1)}", text)
    print(text)
Antworten