Benutzereingabe, zweistellige Zahlen, while-Schleife?

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
Mary86
User
Beiträge: 9
Registriert: Donnerstag 24. März 2011, 14:29

Hi!
Ich hab folgendes Problem:
Der Nutzer soll etwas eingeben und dass soll dann gespeichert werden und nachher verrechnet!
Aber wenn ich jetzt 22 eingebe dann speichert er mir 2 , 2 und nicht 22. Wichtig ist noch dass es sich um ein integer handeln muss weil ich das für die weitere Rechnung so brauche.
Hier mal mein Code:

Code: Alles auswählen

#!/usr/bin/env python3

import csv
import math
from collections import defaultdict


pMDict = defaultdict(list) #pM = physikalische Massen
zeile = []

with open ("isotopes.csv") as pM:
  for line in pM:
    line = line.rstrip()
    data = line.split(',')
    print(data)
    element, n = data[:2]
    pMDict[element].append(n)
print (pMDict)

formel = input ("Bitte geben Sie das Molekül ein: ")
formel = formel.upper()
formel_g = formel.split() #Formel aufsplitten
formel_e = [] #Liste aus einzelnen Bestandteilen der Formel

for element in formel_g:
  for e in element:
    formel_e.append(e) #Liste erstellen
print(formel_e)

for i in range(len(formel_e)):
  try:
    formel_e[i] = int(formel_e[i])
  except ValueError:
    pass

print(formel_e)
 
if type(formel_e[0]) == int:
  print("Please check your input")
  exit(1)



Zahlen = []

for i in range(len(formel_e)):
  if formel_e[i] in pMDict:
    print(formel_e[i], "zur Berechnung geeignet")
    Zahlen.append(int(pMDict[formel_e[i]][0]))
  if formel_e[i] not in pMDict:
    Zahlen.append(int(int(pMDict[formel_e[i-1]][0])*int(formel_e[i])))
    Zahlen.remove(int(pMDict[formel_e[i-1]][0]))
    print("Berechnung mit ",e," nicht möglich")
print(Zahlen)

Ergebnis = 0
for z in Zahlen:
  Ergebnis += z

print(Ergebnis)
Ich könnte jetzt natürlich irgendwie mit einer while-Schleife arbeite und so die Eingabe schritt für schritt durchgehen und immer wenn eine Zahl kommt überprüfen ob die nächste auch eine Zahl ist und die dazu addieren, aber 1. weiß ich nicht so genau wie ich das anstellen soll und 2. müsste es doch eine elegantere Lösung geben oder?

Vielen Dank schon mal für eure Hilfe!
Mary
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Zunächst einmal gibt es hier spezielle Python-Code-Tags. Bei zu langen Code-Blöcken solltest Du das in ein Paste-Bin auslagern, wie das hier im Board oder z.B. paste.pocoo.org.

Ohne Deinen Code auszuführen wird man Deinen Fehler nicht nachvollziehen können! Also poste doch mal die exakte Fehlermeldung inkl. der Schritte, die man benötigt dahin zu kommen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@Mary86: Das Problem liegt in der Schleife, in der Du die Elemente von `formel_g` in einzelne Zeichen aufteilst und in `formel_e` speicherst.

Code: Alles auswählen

In [101]: for x in '42':
   .....:     print x
   .....: 
4
2
Du importierst die Module `csv` und `math` ohne sie zu verwenden.

Die Namensgebung ist nicht immer so toll. Wenn man in einem Kommentar erklärt was ein Name bedeutet, sollte man überlegen den Namen so zu wählen, dass man sich den Kommentar sparen kann. Insbesondere bei Abkürzungen die im Kommentar ausgeschrieben werden, könnte man auch einfach den Namen selbst nicht abkürzen. Auch sollte man keine Typen in den Namen "einbauen" -- Namen sollten Anhaltspunkte dafür geben was die Daten dahinter bedeuten und nicht in was für einem Datentyp sie gespeichert sind. Bei Abbildungen ist eine übliche Konvention die Bedeutung von Schlüssel und Wert durch eine `2` zu trennen, weil das im Englischen wie "to" ausgesprochen wird. Dein `pMDict` sollte zum Beispiel besser `element_zu_physikalische_massen` heissen -- oder englisch `element2physical_masses`. Das `physikalisch` könnte man aus dem Namen heraus lassen und tatsächlich in einem Kommentar unterbringen, sofern es im Modul nur eine Art von Massen gibt.

Die Suffixe bei `formel_g` und `formel_e` sind auch nicht gerade selbsterklärend.

``for i in range(len(obj))`` ist ein "anti pattern". Man kann in Python direkt über die Elemente einer Liste iterieren. Falls man zusätzlich einen Index benötigt, gibt es die `enumerate()`-Funktion. Nur den Index alleine braucht man wirklich ganz selten.

`type()` zu Typtesten ist unschön. Da sollte man besser `isinstance()` verwenden.

Wenn man ein ``if`` hat um in nächsten ``if`` genau die gegenteilige Bedingung angibt, dann will man in aller Regel ein ``else`` statt dem zweiten ``if`` verwenden.

Aufteilen in Funktionen macht vielleicht Sinn. Dann kann es einem nicht passieren, dass man am Ende aus versehen irgendwelche Namen verwendet, die weiter vorne bei einer unabhängigen Aktion mal gebunden wurden. Das `e` in ``print("Berechnung mit ",e," nicht möglich")`` scheint mir nämlich an der Stelle keinen sinnvollen Wert zu haben.

Schau Dir mal die `sum()`-Funktion an.

Warum speicherst Du in `pMDict` eigentlich Listen als Werte, wenn Du dann doch nur immer das erste Element daraus verwendest!?
Mary86
User
Beiträge: 9
Registriert: Donnerstag 24. März 2011, 14:29

Hallo Black Jack!
Danke für deine Ausführlichen Anregungen!
Das ich die beiden Module importiert habe ohne sie zu verwenden, weiß ich.
Es ist auch noch nicht der endgültige Code, es ist quasi mein erster größerer Code in Python und ich probier da so einiges aus. So dass ich nachher etwas besser Programmieren kann. Deshalb bin ich dir für deine Antwort sehr dankbar.

Ich werde mir über meine Namensgebung nocheinmal ein paar Gedanken machen!

Mit den Indizes habe ich gearbeitet weil ich die Liste an jeder Stelle durchgehen muss und gucken muss ob es eine Zahl ist, damit er mir dann den Wert zu dem Buchstaben der vor der Zahl steht mit der Zahl multipliziert und addiert. Es gibt mit Sicherheit eine bessere Lösung.

Wie funktioniert das mit dem `isinstance()`, genauso wie mit type, kannte bisher nur das.

Mit else bzw. elif hast hast natürlich recht... irgendwie ist mir das entgangen.

Das 'e' im letzten print ist noch aus einer vorherigen Version übrig in der ich nur über die Liste iterriert hab und nicht über den index... danke für den Hinweis.

Im Moment möchte ich immer nur den ersten Wert verwenden, aber später soll es möglich sein, dass der Benutzer angeben kann welches Isotop er hat und dann der entsprechende Wert dafür genommen wird.

Wie kann ich denn jetzt möglichst elegant mein Problem mit der zweistelligen Zahl lösen?


Hallo Hyperion!
Danke für deine schnelle Antwort!
Der Fehler ist eigentlich ziemlich logisch den er mir dann ausgibt. Er sagt:
Traceback (most recent call last):
File "./chemie3.py", line 51, in <module>
Zahlen.append(int(int(pMDict[formel_e[i-1]][0])*int(formel_e)))
IndexError: list index out of range
Weil er jetzt ja auf keinen Buchstabenwert zugreifen kann, weil keiner vor der Zahl steht.
Also wenn ich zum Beispiel H22 eingebe, sollte 22 also Ergebnis ausgeben werden.
Er speichert aber so:
['H', '2', '2']
['H', 2, 2]
Und er soll aber so speichern ['H',22]

Viele Grüße
Mary
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Verwende doch auch für Fehlermeldungen Code-Tags. Das grenzt die Meldung vom übrigen Posting besser ab!

BlackJack hat Dir doch schon den Hinweis gegeben, woran es genau liegt.

Ich kenne ja Deine genauen Datenstrukturen und das exakte Problem nicht, daher kann ich Dir jetzt nur den Tipp geben, dass Du einen Parser benötigst, der die Buchstaben von den Zahlen trennt und letztere auch zu einer Zahl umwandelt.

Das ganze ist viel zu lang, um so einfach von jemandem überblickt zu werden, der nicht sehr viel Zeit investieren will. Also versuche das einfach mehr in Problem-Häppchen aufzusplitten.

Also:

* was hast Du als Datenstruktur vorliegen (ggf. wieso ist das so? Kann man evtl. die Eingabe gleich sinnvoll strukturieren?)
* was willst Du mit den Daten erreichen?

Ich habe bisher so verstanden, dass Du eine Formel als String vorliegen hast und diese parsen und interpretieren willst. Du hast dieses Beispiel gegeben:

Code: Alles auswählen

"H22" -> ["H", 22]
Das ist natürlich recht simpel. Da wäre als erstes die Frage zu klären, wie die Grammatik für solche Formeln aussieht. Ist es immer nur ein Buchstabe und zwei Ziffern? Immer ohne Leerzeichen? Kommen noch andere Zeichen drin vor? usw.

Wenn man das geklärt hat, kann man sich Gedanken über einen Parser machen. In diesem simplen Fall sähe der stumpf so aus:

Code: Alles auswählen

formel = "H22"
name, value = formel[0], int(formel[1:])
Vermutlich ist dem aber ja nicht so.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Ich rate mal:

Code: Alles auswählen

>>> import re
>>> exp = re.compile("([A-Z][a-z]?)([0-9]+)")
>>> exp.findall("C12H4Cl4O2")
[('C', '12'), ('H', '4'), ('Cl', '4'), ('O', '2')]
Das zum Matching zu erweitern ist dann nicht mehr die Schwierigkeit.
Das Leben ist wie ein Tennisball.
BlackJack

@Mary86: Statt beim Ausrechnen immer schauen zu müssen ob man eine Zahl oder einen Buchstaben vor sich hat, sollte man die Benutzereingabe schon so normalisieren, dass man immer die gleiche Struktur beim Ausrechnen hat. Also zum Beispiel grundsätzlich Tupel aus Buchstabe und Zahl. Wenn der Benutzer einzelne Buchstaben ohne dazugehörige Ziffern eingibt, dann kann man an der Stelle die Zahl auf 1 setzen. Also die Eingabe 'H2O' könnte man als ``[('H', 2), ('O', 1)]`` darstellen. Das ist eine schön regelmässige Struktur, mit der man viel einfacher weiter arbeiten kann.

Falls Du übrigens wirklich Eingaben wie 'H2O' ohne Leerzeichen dazwischen haben möchtest, dann kommst Du wohl um einen geringfügig aufwändigeren Parser nicht herum. Dein `split()`-Aufruf bewirkt bei solchen Eingaben gar nichts, der kann dann also weg fallen. Am einfachsten kann man solche Eingaben mit regulären Ausdrücken zerlegen. Siehe den Beitrag von EyDu. Nur das man wahrscheinlich auch 'H2O' eingeben können soll und nicht zu 'H2O1' gezwungen wird. Wie man die Ziffern optional macht, überlasse ich Dir mal als Übung. :-)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

BlackJack hat geschrieben:Nur das man wahrscheinlich auch 'H2O' eingeben können soll und nicht zu 'H2O1' gezwungen wird. Wie man die Ziffern optional macht, überlasse ich Dir mal als Übung. :-)
Schlimm wirds ja dann, wenn mehrere "einser"-Elemente nacheinander stehen... Denn Elemente können ja auch 2-stellig sein, wenn mich mein ziemlich altes Chemiewissen jetzt nicht täuscht ;-)

Hatte nicht PyParsing als Tutorial so einen Molekülrechner? Oder wars ne andere Parser-Generator Lib...
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Mary86
User
Beiträge: 9
Registriert: Donnerstag 24. März 2011, 14:29

Hi!
Danke für eure schnellen Antworten!
Ich hab den Code jetzt nochmal etwas verändert. Hier:

Code: Alles auswählen

#!/usr/bin/env python3
# this is a programm to calculate the pysikal masses of an molecule
from collections import defaultdict
import math

element2masses = defaultdict(list)

with open ("isotopes.csv") as masses:
  for line in masses:
    line = line.rstrip()
    data = line.split(',')
    print(data)
    element, n = data[:2]
    element2masses[element].append(n)
print(element2masses)
#build the dictionary

molecule = input ("Please enter the molecule: ")
molecule = molecule.upper()
single_molecule = []

for element in molecule:
  for e in element:
    single_molecule.append(e) 
print(single_molecule)
#build a List out of the input

for i in range(len(single_molecule)):
  try:
    single_molecule[i] = int(single_molecule[i])
  except ValueError:
    pass
print(single_molecule)
#convert numbers from strings in intergers

if isinstance(single_molecule[0], int):
  print("Please checke your input!")
  exit(1)
#check if the first is a Number, it must be a Letter

single_values = []

for i in range(len(single_molecule)):
  if single_molecule[i] in element2masses:
    print(single_molecule[i], "computation possible")
    single_values.append(int(element2masses[single_molecule[i]][0]))
  elif single_molecule[i] not in element2masses:
    single_values.append(int(int(element2masses[single_molecule[i-1]][0])*int(single_molecule[i])))
    single_values.remove(int(element2masses[single_molecule[i-1]][0]))
    print(single_molecule[i],"computation not possible")
print(single_values)
#compares the input with the dictionary and saves the masses

result = math.fsum(single_values)
print(result)
#sum each mass an returns the result
Ich hoffe er ist jetzt etwas verständlicher.
Also, ich habe eine Datei in der einzelne Moleküle und ihre einzelnen Massen gespeichert sind.
Ziel des Programms soll es jetzt sein, dass der Benutzer ein Molekül wie zum Beispiel H2O oder HO oder CO2 oder C22S3 oder so etwas eingeben kann und dann die korrekte Masse dafür ausgespuckt wird.
Im ersten schritt würde ich jetzt gern mein Problem mit dem zweistelligen Zahlen lösen.
@EyDu: Was verwendest du da genau?
Ach ja und ohne range(len... komm ich auch nicht weiter...

Viele Grüße
Mary
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ein wenig Herausforderung wollte ich in der Aufgabe schon noch lassen. Eigentlich ist es damit getan, dass ein Zeichen verändert wird.

@Hyperion: Da der erste Buchstabe ein Großbuchstabe ist und der zweite ein kleiner, wird der Fall mit abgedeckt.
Das Leben ist wie ein Tennisball.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Mary86 hat geschrieben: Ich hoffe er ist jetzt etwas verständlicher.
;-) Bzw. mag sein... aber für die Frage spielt der Quellcode ja eigentlich keine Rolle.
Mary86 hat geschrieben: Also, ich habe eine Datei in der einzelne Moleküle und ihre einzelnen Massen gespeichert sind.
Ziel des Programms soll es jetzt sein, dass der Benutzer ein Molekül wie zum Beispiel H2O oder HO oder CO2 oder C22S3 oder so etwas eingeben kann und dann die korrekte Masse dafür ausgespuckt wird.
Na also! Das ist doch mal eine gute und anschauliche Beschreibung des eigentlichen Problems.
Mary86 hat geschrieben: Im ersten schritt würde ich jetzt gern mein Problem mit dem zweistelligen Zahlen lösen.
Ich glaube nicht, dass Du das möchtest. Du solltest Dir überlegen, wie so ein Ausdruck aufgebaut sein kann. BlackJack nannte ja schon Regular Expressions (sogar in Bezug auf EyDus Posting!). Allerdings kommt es dabei schon darauf an, im Vorfeld möglichst alle Eventualitäten zu kennen oder sich bewusst zu beschränken. Auch da nannten BlackJack und ich ja schon Schwierigkeiten.

Evtl. wäre es ja auch eine Option, die Eingabe zu entschärfen, also alle Elemente nacheinander abzufragen:

Code: Alles auswählen

> Element?
H
> Anzahl?
2
> Noch ein Element?
ja
> Element?
O
> Anzahl?
1
> Noch ein Element?
nein
...
-> [('H', 2), ('O', 1)]
Oder aber man baut sich künstliche Trenner ein:

Code: Alles auswählen

"H-2+O+NA".split("+")
# nächster Schritt
"H-2".split("-")
# hier muss man gucken, wie man die nicht explizit genannte '1' handelt.
"O-".split("-")
"NA-".split("-")
Das kann man halt einfachst splitten und dann entsprechend auswerten.
Mary86 hat geschrieben: @EyDu: Was verwendest du da genau?
Reguläre Ausdrücke. s.o. Gibt in der Doku einen Verweis auf ein gutes Tutorial diesbezüglich.

@EyDu: Ah, das hatte ich übersehen! Stimmt, so war das damals :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten