While-Schleife funktioniert nicht wie gewollt

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
TheChiller
User
Beiträge: 6
Registriert: Freitag 25. Mai 2012, 18:48

Hallo!
Anlässlich einer Programmierhausaufgabe, die ich schon zu 60% erledigt habe, muss ich mich mit dem Programmcode mal an Euch wenden.
Es geht darum, dass der Nutzer ein Wort angibt, das in einem Korpus, das als Datei reingeladen wird, gesucht wird. Dieses Wort, auch Token genannt, soll zusammen mit den fünf vorhergehenden und den fünf folgenden Wörtern (=Kollokation) ausgegeben werden.
Kommt das Wort aber häufiger vor, so soll das nächste Auftreten des Wortes im Korpus unter dem vorherigen mit einem Zeilenumbruch dazwischen ausgegeben werden.
Ich habe das dementsprechend programmiert und meine While-Schleife scheint die weiteren Befehle auch auszuführen (bis die Anzahl Zeilenumbrüche im abschließenden String, der ausgegeben wird, gleich der Vorkommen des gesuchten Wortes ist), aber wenn ein Wort mehrmals auftritt, wird es nicht an den String angefügt, der String enthält trotzdem nur die allererste Kollokation. Warum?
Das hier ist mein Programmcode und einen Ausschnitt aus der Textdatei, die das Korpus darstellt, habe ich angehängt (Wenn man für das zu suchende Wort 'supervision' eingibt, hat man nur 3 Treffer, daher ist das ganz passend).

Code: Alles auswählen

# coding=utf-8
from __future__ import division
import codecs

Korpus_original = codecs.open("poe.txt","r").read().split() #eventuell nach "poe.txt" noch ,"utf-8", anfügen
Korpus=Korpus_original[:]
suchwort=raw_input(u"Welches Wort soll im Korpus gesucht werden?\n")
Kollokationen=""
Fuenfvor=[]
Fuenfhinter=[]
list_suchwoerter= []
while (Kollokationen.count("\n"))<=Korpus.count(suchwort):
    #print "Anzahl Suchwörter im Korpus: ",Korpus.count(suchwort)
    # print "Nummer von Zeilenumbrüchen in Kollokationen",Kollokationen.count("\n")
    suchwortPos=Korpus.index(suchwort.lower())
    
    list_suchwoerter.append(suchwortPos)
    print list_suchwoerter
    #print Korpus[suchwortPos-5:suchwortPos] 
    for i in range (suchwortPos-5,suchwortPos):
        if Korpus[i] not in Fuenfvor and len(Fuenfvor)<5:
            Fuenfvor.append(Korpus[i])
            
        #    print "i", Korpus[i], Fuenfvor
    for i in range(suchwortPos,suchwortPos+5):                     
        if Korpus[i] not in Fuenfhinter and len(Fuenfhinter)<6:                                    
            Fuenfhinter.append(Korpus[i])
        #    print Fuenfhinter
    if suchwortPos in list_suchwoerter:
        del Korpus[suchwortPos]
        #print "EIN SUCHWORT GELÖSCHT"
    Kollokationen=" ".join(Fuenfvor)+" "+suchwort+" "+" ".join(Fuenfhinter[1:])+"\n" 
    
    #print Fuenfvor, suchwort, Fuenfhinter
    #Kollokationen=" ".join(Kollokationen)
print Kollokationen


Vielen Dank im Voraus :-)

Poe.txt (Das Korpus):
In the inmost recesses of this coppice not far from the eastern
or more remote end of the island Legrand had built himself a small
hut which he occupied when I first by mere accident made his
acquaintance This soon ripened into friendship for there was much
in the recluse to excite interest and esteem I found him well
educated with unusual powers of mind but infected with misanthropy
and subject to perverse moods of alternate enthusiasm and melancholy
He had with him many books but rarely employed them His chief
amusements were gunning and fishing or sauntering along the beach
and through the myrtles in quest of shells or entomological
specimens his collection of the latter might have been envied by a
Swammerdamm In these excursions he was usually accompanied by an old
negro called Jupiter who had been manumitted before the reverses of
the family but who could be induced neither by threats nor by
promises to abandon what he considered his right of attendance upon
the footsteps of his young Massa Will It is not improbable that
the relatives of Legrand conceiving him to be somewhat unsettled in
intellect had contrived to instil this obstinacy into Jupiter with
a view to the supervision and guardianship of the wanderer

The winters in the latitude of Sullivan 's Island are seldom very
severe and in the fall of the year it is a rare event indeed when a
fire is considered necessary About the middle of October 18- there
occurred however a day of remarkable chilliness Just before sunset
I scrambled my way through the evergreens to the hut of my friend
whom I had not visited for several weeks my residence being at
that time in Charleston a distance of nine miles from the Island
while the facilities of passage and re-passage were very far behind
those of the present day Upon reaching the hut I rapped as was my
custom and getting no reply sought for the key where I knew it was
secreted unlocked the door and went in A fine fire was blazing upon
the hearth It was a novelty and by no means an ungrateful one I
threw off an overcoat took an arm-chair by the crackling
logs and awaited patiently the arrival of my hosts
BlackJack

@TheChiller: Falls ich richtig verstanden habe was Du haben möchtest, dann ist das alles superkompliziert gelöst. Das sollte man mit *einer* ``for``-Schleife zusammen mit der `enumerate()`-Funktion lösen können. In der Schleife prüft man das jeweilige Wort und wenn man einen Treffer hat holt man sich das Wort + Umgebung mittels „slicing” aus der Korpus-Liste.

Die einzelnen Treffer kann man schon zusammenfügen, aber dann in einer Liste sammeln. In Schleifen wiederholt Zeichenketten erweitern kann ineffizient werden. Der idiomatische Weg ist das Sammeln von Teil-Zeichenketten in einer Liste und am Ende ein zusammenfügen mittels der `join()`-Methode auf Zeichenketten.

Mach Dir mal klar wie oft bei Dir Sequenzen linear immer wieder und wieder durchlaufen werden. Bei jedem `index()`, Test mit ``in``, und ``del`` um ein Listenelement zu löschen wird viel unnötige Arbeit verrichtet.

Zu Deiner eigentlichen Frage eine Gegenfrage:

Code: Alles auswählen

spam = 'Hallo'
spam = ' Welt'
print spam
Was würdest Du hier als Ausgabe erwarten?

Edit: 'supervision' kommt nur einmal vor ist also kein gutes Beispiel für den Textausschnitt.
TheChiller
User
Beiträge: 6
Registriert: Freitag 25. Mai 2012, 18:48

Danke schon mal für deine Nachricht!

Die Ausgabe deines Beispiels ist ja klar. Und ich habe mir auch schon gedacht, dass sich meine Lösung sehr vereinfachen lässt. Habe es eben nur erstmal so aufgebaut, wie ich mir das Programm vorgestellt habe, ohne es zu vereinfachen. Kannst du mir ein Beispiel für die enumerate-Funktion geben? Die habe ich noch nie verwendet, daher habe ich es ja auf diesem Wege programmiert. Danke voraus!
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Ein Beispiel mit enumerate:

Code: Alles auswählen

>>> shopping_list = ['apples', 'flour', 'honey', 'bread']
>>> for i, item in enumerate(shopping_list):
...     print '{}: {}'.format(i, item)
... 
0: apples
1: flour
2: honey
3: bread
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@BlackJack: Man muss die "Text"-Liste aber dann noch um fünf leere Einträge zu Beginn erweitern, damit das auch bei Suchwörtern klappt, die in den ersten fünf Positionen stehen... oder gibt es da einen Slicing-Trick, den ich hier übersehe?

Ok, so was ginge wohl:

Code: Alles auswählen

text[max(0, index-5):index+5]
:-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Hyperion: Stimmt, hatte ich gar nicht dran gedacht. Dafür ist bei Dir der obere Index um 1 zu klein. :-)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:Dafür ist bei Dir der obere Index um 1 zu klein. :-)
Pedant :mrgreen: Ob nun vier oder fünf Wörter danach ist doch egal... :oops:
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

5 oder 4, das ist ja ein konstanter Faktor c und fällt unter den Tisch :D
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
TheChiller
User
Beiträge: 6
Registriert: Freitag 25. Mai 2012, 18:48

Okay, ich frage mich nun nur, wie genau mir die enumerate-Funktion helfen soll. Reicht mir nicht schon das Slicing?
Und irgendwie versuche ich immer noch zu verstehen, warum meine allererste, dieses Thema eröffnende While-Schleife nicht funktioniert.
BlackJack

@TheChiller: Für das Slicing brauchst Du doch einen Index. Der kann vom `enumerate()` kommen.

Wenn die Ausgabe von meinem Beispiel klar ist, dann sollte auch klar sein warum Du nur eine Kollokation in Deiner ``while``-Schleife hast. Übrigens die *letzte* und nicht die *erste*. Du hast ja nicht gesagt *was* mein Beispiel ausgibt, darum hoffe ich mal Du gehst nicht davon aus, dass es 'Hallo Welt' ausgibt.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich bin mal nett und poste eine Lösung, die ich in einer Python-Shell entwickelt habe:

Code: Alles auswählen

In [33]: with open("poe.txt") as f:
   ....:     text = f.read().split()
   ....:     

In [35]: search = "is"

In [36]: result = list()

In [37]: for index, word in enumerate(text):
   ....:     if word == search:
   ....:         result.append(text[max(0, index-5):index+6])
   ....:         

In [51]: for item in result:
   ....:     print(" ".join(item))
   ....:     
his young Massa Will It is not improbable that the relatives
fall of the year it is a rare event indeed when
event indeed when a fire is considered necessary About the middle
Man sollte das alles natürlich noch hübsch in Funktionen verpacken. Die Suchfunktion ließe ich prima als Generatorfunktion schreiben :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

Mein Testprogramm sah so aus:

Code: Alles auswählen

#!/usr/bin/env python


def main():
    with open('test.txt') as korpus_file:
        korpus = korpus_file.read().split()
    
    needle = raw_input('Welches Wort soll im Korpus gesucht werden? ')
    
    print '\n'.join(
        ' '.join(korpus[max(0, i-5):i+6])
        for i, w in enumerate(korpus)
        if w == needle
    )


if __name__ == '__main__':
    main()
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ja gut, ich wollte es Stück für Stück demonstrieren :-D
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
TheChiller
User
Beiträge: 6
Registriert: Freitag 25. Mai 2012, 18:48

Ich hätte wirklich am Anfang, als ich begonnen habe, ein Programm für diese Aufgabe zu erstellen, nicht gedacht, dass es so viel kürzer und einfacher geht.
Und dank der Lösung von Blackjack habe ich endlich auch mal verstanden, auf welche Weise sich die enumerate-Funktion einbauen lässt (die Verbindung zum Suchwort (needle) und das ersetzen der index-funktion) :-)
Meine einzige letzte Frage, die ich noch habe, bezieht sich auf die letzten beiden Zeilen deines Programmcodes, Blackjack.
Da ich in Python noch nie eine main-Funktion gesehen habe (lediglich in C), frage ich mich, was genau die if-Abfrage if __name__ == '__main__': main() erwirkt. Wird dadurch die Main-Funktion erst ausgeführt?

Nochmals vielen Dank an alle, das bringt mich in Python wirklich weiter!
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

TheChiller hat geschrieben: Und dank der Lösung von Blackjack habe ich endlich auch mal verstanden,
Dir ist schon klar, dass BlackJacks und meine Lösung quasi identisch sind? ;-)
TheChiller hat geschrieben: Da ich in Python noch nie eine main-Funktion gesehen habe (lediglich in C), frage ich mich, was genau die if-Abfrage if __name__ == '__main__': main() erwirkt. Wird dadurch die Main-Funktion erst ausgeführt?
Wenn Du noch keine `main`-Funktion gesehen hast, dann hast Du Dir bisher noch nicht viele Programme angeschaut ;-)

Der Code bewirkt, dass Du ein unterschiedliches Verhalten des Moduls bekommst, je nach dem, wie es aufgerufen wird.

Wird es als Programm ausgeführt, so wird die `if`-Anweisung wahr und der Code im Rumpf entsprechend ausgeführt. Per Konvention ruft man dort eine `main`-Funktion auf, bei kleineren Dingen schreibt man den Code auch mal direkt in den Rumpf. Ich bevorzuge meist ersteres.

Wird das Modul hingegen aus einem anderen Modul importiert, so wird die Bedingung nicht wahr und es wird demzufolge auch nichts ausgeführt.

Du kannst das ganz einfach testen. Lege Dir ein Modul wie folgt an:

Code: Alles auswählen

#!/usr/bin/env python

print(__name__)
Nun führe es einmal aus:

Code: Alles auswählen

python main_hook.py
__main__
Aha. An den Namen `__name__` bindet der Interpreter in diesem Falle den String "__main__".

Nun importiere ich das Modul einmal in einer Python-Shell:

Code: Alles auswählen

>>> import main_hook
main_hook
In diesem Falle wird der Modulname an `__name__` gebunden.

Durch den "Trick" mit dem `if`-Statement kann ich also auf diese beiden unterschiedlichen Situationen reagieren. Bei einem Import will ich (idR.) keinen Code ausführen, starte ich ein Modul als "Programm", so will ich ja etwas ausführen.

Du solltest diesen "Hook" immer in Deine Module einbauen - außer, ein Modul soll nicht ausführbar sein, dann ist es natürlich unnötig.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
TheChiller
User
Beiträge: 6
Registriert: Freitag 25. Mai 2012, 18:48

Ah, sowas muss ja schließlich erst mal wissen, um mit Main-Funktionen umgehen zu können :)
Stimmt, ich habe noch nciht so viele Programme gesehen, da im Kurs im letzten Semester nur eine grobe Einführung gemacht wurde und jetzt erst richtige Programmierkenntnisse gefragt sind ;-)
Klar habe ich gemerkt, dass sich eure beiden Programme mehr als ähneln, nur finde ich die Version von Blackjack übersichtlicher (da ich u.a. das "In[33]" auch noch nie gesehen habe) :)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Du bist also mit Generator-Ausdrücken vertraut? Interessant... ;-)
TheChiller hat geschrieben: (da ich u.a. das "In[33]" auch noch nie gesehen habe) :)
Das ist nur der Prompt der Python Shell IPython.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
TheChiller
User
Beiträge: 6
Registriert: Freitag 25. Mai 2012, 18:48

Generator-Ausdrücke? Schon mal gehört :D

Achso, dachte schon, das "In [30]" sei eine unübliche Form der Zeileniteration :P
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hyperion hat geschrieben:
TheChiller hat geschrieben: (da ich u.a. das "In[33]" auch noch nie gesehen habe) :)
Das ist nur der Prompt der Python Shell IPython.
Deswegen würde ich abraten, IPython in Beispielen zu nutzen. Das verwirrt Anfänger und bis auf dass es zeigt "Hey, guckt mal was ich für ne tolle Shell habe" bringt es im Kontext von Foren nichts und man muss oft erklären was das überhaupt ist. Ich copypaste meine Beispiele meist aus ner Shell daher als Python-Quellcode, nicht als Transcripts.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Mal mit einem anderen Ansatz

Code: Alles auswählen

>>> from collections import deque
>>> def kolok(it, word, pos=5, length=11):
...   ring = deque([None]*length)
...   for el in it:
...     ring.append(el)
...     ring.popleft()
...     if ring[pos]==word:
...       yield list(ring)
...   for _ in xrange(length-pos):
...     ring.append(None)
...     ring.popleft()
...     if ring[pos]==word:
...       yield list(ring)
Antworten