Seite 1 von 1

Matrix mit Strings dynamisch füllen

Verfasst: Donnerstag 23. November 2006, 15:56
von maschimaen
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 ;)

Verfasst: Donnerstag 23. November 2006, 16:26
von jens
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]

Re: Matrix mit Strings dynamisch füllen

Verfasst: Donnerstag 23. November 2006, 16:31
von Leonidas
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.

Verfasst: Donnerstag 23. November 2006, 16:41
von jens
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...

Verfasst: Donnerstag 23. November 2006, 17:03
von 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()

Verfasst: Donnerstag 23. November 2006, 17:21
von jens
BlackJack hat geschrieben:Ich benutze dazu eine `strip_comment()`-Funktion...
Das ist aber für ein Anfänger starker Stoff :)

Verfasst: Donnerstag 23. November 2006, 17:35
von sape
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

Verfasst: Donnerstag 23. November 2006, 17:49
von CM
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

Verfasst: Donnerstag 23. November 2006, 18:02
von sape
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

Verfasst: Donnerstag 23. November 2006, 18:07
von sape
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).

Verfasst: Donnerstag 23. November 2006, 18:10
von jens
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...

Verfasst: Donnerstag 23. November 2006, 18:14
von sape
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

Verfasst: Donnerstag 23. November 2006, 18:27
von jens
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:

Verfasst: Donnerstag 23. November 2006, 18:42
von sape
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

Verfasst: Donnerstag 23. November 2006, 18:52
von 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.