Matrix mit Strings dynamisch füllen

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
maschimaen
User
Beiträge: 1
Registriert: Donnerstag 23. November 2006, 15:42

Hallo Leute,

ich bin neu im Programmieren und habe folgendes Problem: Das Skript soll eine Datei einlesen und aus deren Inhalt einen Array füllen (nur Strings), den ich dann in Schleifen weiter nutzen kann.

Die Input-Datei sieht folgendermaßen aus:

Code: Alles auswählen

####################################################################
#
C=A01_e02_a01_n100
2p20
2p10
#
C=A01_e02_a01_n79
1p40	
1p42
1p44
1p46
1p48
1p50
1p52
1p55
1p57
#
C=A02_e03_a02_n100
2p20
2p10
#
C=A02_e03_a02_n79
1p40
1p57
1p50
Steht am Anfang "#" soll die Zeile übersprungen werden, steht "C=", soll eine neue Zeile der Matrix begonnen werden und der erste Eintrag ist der Ausdruck ohne C=. Die erste Zeile sollte also folgendermaßen aussehen:

RUN=['A01_e02_a01_n100','2p20','2p10']

Optimalerweise sollte man hinter den Strings in der Inputdatei noch Kommentare einfügen können.

Mein Code sieht jetzt so aus, geht aber nicht:

Code: Alles auswählen

import os

runs = open('./AL_runs.dat','r')

i=0   
k=0   

RUN=[]

for line in runs:
   
   if not line.startswith('#'):
      if line.startswith('C='):
      
         print  line.rstrip('\n') + '\r'
	 
	 RUN[k][0]=line
	 k=k+1
	 i=0
	 print RUN
      else:
         RUN[k][i]=line
         i=i+1
         print RUN

print RUN

Wäre toll wenn mir damit jemand helfen könnte! :D

edit (jens): Bitte http://www.python-forum.de/faq.php#21 mal ansehen ;)
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Also dein code sieht übel aus ;)

Korrigiere als erstes mal das "Einrücken", denn das macht man eigentlich immer mit vier Zeichen. Man kann nämlich eigentlich nicht erkennen zu welchem if das letzte else steht ;)

Ansonsten: [wiki]FAQ#WieFangeIchAlsEinsteigerAn[/wiki]

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hallo maschimaen, willkommen im Forum!
maschimaen hat geschrieben:Steht am Anfang "#" soll die Zeile übersprungen werden, steht "C=", soll eine neue Zeile der Matrix begonnen werden und der erste Eintrag ist der Ausdruck ohne C=. Die erste Zeile sollte also folgendermaßen aussehen:

RUN=['A01_e02_a01_n100','2p20','2p10']
Wo wird da eine neue Zeile eingefügt? Du hänst einfach die Zeilen ohne Kommentar linear hintereinander, ohne jegliche Verschachtelung. Ist das so geplant oder willst du nicht eher etwas anderes? Und: Hat die Reihenfolge der 'Überschriften' eine Bedeutung? Ansonsten kann man einfach ein Dict verwenden.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Und noch ein Tipp von mir... Man sollte IMHO Schleifen immer recht flach halten. Ich würde das mit dem "#"-Zeilen anders machen:

Also nicht so:

Code: Alles auswählen

for line in runs:
   if not line.startswith('#'):
      if line.startswith('C='):
      ...
sondern:

Code: Alles auswählen

for line in runs:
    if line.startswith('#'):
        continue # Mit der nächsten Zeile weitermachen
       
    if line.startswith('C='):
        ...
btw. ich würde "runs" nicht als Namen für das Datei-Objekt verwenden. Ist nicht wirklich passend. Vielleicht einfach "f" oder "AL_file" oder so...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Hauptproblem des Programms dürfte das setzen von nichtvorhandenen Elementen sein. Also ``xs[n] = irgendwas`` funktioniert nur, wenn die Liste schon ein n-tes Element besitzt. In Python benutzt man statt Arrays mit fester Grösse, Listen. Da Du immer ein neues Element ans Ende anfügen möchtest, bietet sich die `append()`-Methode an.

Eine andere Möglichkeit die ``if``-Struktur flach zu halten ist es, die Kommentarzeilen vorher schon zu entfernen. Ich benutze dazu eine `strip_comment()`-Funktion die Kommentare, auch in Zeilen mit Inhalt, und "whitespace" Zeichen komplett entfernt und dann `ifiler()` mit `None`, um alle Zeilen die danach gar nichts mehr enthalten zu entfernen. Wenn in der Quelldatei Leerzeilen vorkommen, werden die also auch ignoriert.

Code: Alles auswählen

from itertools import ifilter, imap
from pprint import pprint


def strip_comment(line):
    return line.split('#', 1)[0].strip()


def parse_runs(lines):
    result = list()
    run = None
    for line in ifilter(None, imap(strip_comment, lines)):
        if line.startswith('C='):
            if run is not None:
                result.append(run)
            run = [line[2:]]
        else:
            run.append(line)
    return result


def main():
    run_file = open('test.txt', 'r')
    runs = parse_runs(run_file)
    run_file.close()
    pprint(runs)


if __name__ == '__main__':
    main()
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

BlackJack hat geschrieben:Ich benutze dazu eine `strip_comment()`-Funktion...
Das ist aber für ein Anfänger starker Stoff :)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

jens hat geschrieben:
BlackJack hat geschrieben:Ich benutze dazu eine `strip_comment()`-Funktion...
Das ist aber für ein Anfänger starker Stoff :)
Das stimmt. Besonders dies schöne Konstruktion 'line.split('#', 1)[0].strip()' mit der Indizierung nach dem Split und dann darauf ein stripe () :twisted:
Solche Konstrukte sind bei mir in letzter zeit auch häufiger anzutreffen (BlackJack hat die schuld :twisted: ^^ *gg). Auch LCs kommen mittlerweile seit dieser Woche bei mir sehr oft vor ^^

Ja Python ist ne schöne Sprache, kann aber auch mal recht böse sein und zuschnappen ^^

Sorry 4 OT.

lg
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Zur Verteidigung von BJ:
1. Ich schreibe auch oft so und - wenn kommentiert - halte ich das sogar für guten Stil.
2. Anfänger können davon nur lernen. Schließlich sollte man nicht anfangen ein Programm in winzigkleinen Schritten zu entwickeln, sondern so ein kurzes Progrämmchen kann ja auch in einem Schritt erstellt werden - das ist nicht so frustig.

Gruß,
Christian
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

CM, das war keineswegs von mir Ironisch gemeint. Ich benutze auch gerne selber LCs und solche Konstrukte wie 'line.split('#', 1)[0].strip()'.

Aber dennoch ist das für einen Anfänger erstmal recht schwer zu verstehen was aber __nicht__ heißen sol das man den Anfängern sowas nicht zeigen soll :) Also keines wegen schonen, sondern immer sowas Zeigen damit man daraus auch lernt ;)

Also, ich bin froh das BlackJack so drauf ist, weil ich schon viel von ihm gelernt habe. Wenn ich was nicht versteh dann -->print. print ist dein Freund ;) Und wenn ichs dann immer noch nicht habe nach stunden, dann frage ich nach und bekomme immer eine gute Erklärung :)

Deshalb, das war nicht Böse oder so gemeint ;)

lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

XtraNine hat geschrieben: Ja Python ist ne schöne Sprache, kann aber auch mal recht böse sein und zuschnappen ^^
Hehe, Ja ok, das könnte man falsch auffasen ^^ Aber eigentlich sollte klar sein das ich das so nicht meine ;) Es gibt da so ein par Strange Sachen am Anfang wo ich nicht so durchgestiegen bin. Dazu gehören LCs oder Sachen wie Methode()[0].Methode()[::-1] ;) Sind halt für einen der eigentlich von C kamm eher ungewohnte Konstrukte. Aber wenn man es mal drauf hat, möchte man darauf nicht verzichten (ich zumindestens nicht).
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Sorry, aber ich finde in dem Falle mein if line.startswith('#'): continue besser. Weil wesentlich einfacher und nachvollziehbarer...

Bei BlackJack Variante muss man echt überlegen, was da jetzt passiert. Nicht nur bei der split-strip Zeile, sondern auch bei dem ifilter-imap Ding...

Zu Studienzwecke ist das natürlich interessanter...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

jens hat geschrieben:[...]sondern auch bei dem ifilter-imap Ding...
[...]
:? Ja ich frage mich auch gerade was das sein soll :? ifilter() ist was aus der funktionale Programierung. Damit kann ich noch was anfangen (hab zu ifilter() letzte Woche was dazu gelesen, aber noch nicht getestet).
Aber imap()? :? Hmm, kA was das sein soll. Vielleicht ne andere Form von map().

lg
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Kann man alles nachlesen:

http://docs.python.org/lib/itertools-fu ... l#l2h-1057
ifilter(): Make an iterator that filters elements from iterable returning only those for which the predicate is True.

http://docs.python.org/lib/built-in-funcs.html#l2h-49
map(): Apply function to every item of list and return a list of the results.

:wink:

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

ifliter() (Aber noch nicht getestet) und map() kenne ich doch ;) Steht doch im Post. Aber imap() kenne ich nicht, werde ich mir aber nachher mal ansehen.

lg
BlackJack

Gnarf. Die Parse-Funktion hat einen Fehler: Der letzte `run` wird nicht mehr an die Liste angehängt. Hier die Korrektur, ist sogar etwas einfacher geworden:

Code: Alles auswählen

def parse_runs(lines):
    result = list()
    run = None
    for line in ifilter(None, imap(strip_comment, lines)):
        if line.startswith('C='):
            run = [line[2:]]
            result.append(run)
        else:
            run.append(line)
    return result
Zu meiner Verteidigung:

Die `strip_comment()`-Funktion ist nicht kompliziert. Da passieren letztendlich nur 3 Sachen nacheinander. Wenn das jetzt irgendwo mitten im Quelltext steht und man es im Zusammenhang verstehen muss, okay, aber so als einzelne Funktion sollte so etwas "erlaubt" sein. In einem "echten" Programm hätte ich noch einen Doctstring mit kurzer Beschreibung und einem Doctest mit '', ' # comment', ' spam ' und ' spam # comment #' hinzugefügt, und dann sollte es keine Verständnisprobleme mehr geben.

Ein einfaches ``line.startswith('#')`` reicht ja nicht, weil in der "Aufgabenstellung" steht, dass eigentlich auch Kommentare am Ende jeder Zeile möglich sein sollen. Und den Ausdruck um den Kommentar zu entfernen sollte man in eine eigene Funktion auslagern und damit nicht die Lesbarkeit Schleife belasten.

Beim Rest ist eigentlich nur `None` beim `ifilter()` etwas gewöhnungsbedürftig. Ansonsten sind `map()` und `filter()` IMHO ziemlich grundlegende Bausteine um Programme lesbarer zu gestalten, man braucht weniger explizite Schleifen. Ohne die beiden hat man mehr Quelltext in der Schleife und eine Einrücktiefe mehr:

Code: Alles auswählen

def parse_runs(lines):
    result = list()
    run = None
    for line in lines:
        line = strip_comment(line)
        if line:
            if line.startswith('C='):
                run = [line[2:]]
                result.append(run)
            else:
                run.append(line)
    return result
Grundgedanke bei solchen Skripten ist bei mir immer `data driven programming`, also dass man den Datenstrom in den Mittelpunkt stellt und überlegt wie man mit den "Verbindungsbausteinen" `map`, `filter` und `reduce` zum gewünschten Ergebnis kommt. Ich schaue mir an was habe ich, was möchte ich und wie komme ich durch Transformation, Filtern und Zusammenfassen zum gewünschten Ergebnis. So kommt man in der Regel zu relativ kurzen, übersichtlichen Funktionen die eine klar definierte Aufgabe haben, z.B. ein Element des Datenstroms verändern, es auf etwas Testen oder mehrere aufeinanderfolgende Elemente zu jeweils einem neuen zusammenfassen.

Diese einzelnen Funktionen kann man einzeln Testen und dann mit den `itertools` zu einem Programm zusammenstecken.

Ich habe mich sogar etwas zurückgehalten. Die Kommentare hätte ich in eigenen Programmen vor der Parse-Funktion entfernt und aus der Parse-Funktion einen Generator gemacht. :-)

@XtraNine: `imap()` und `ifilter()` sind die Gegenstücke zu `filter()` und `map()` und liefern Iteratoren statt Listen. Sparen also Speicherplatz.
Antworten