Nur glatt teilbare Zahlen erzeugen (random)

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.
chris5790
User
Beiträge: 70
Registriert: Mittwoch 19. Februar 2014, 19:25

Hallo liebes Python-Forum,

ich besuche den Infokurs der 9. Klasse auf einem Gymnasium. Unser Thema momentan ist (wer hätte es gedacht) Python. Ich schreibe das nur, damit sich keiner über meinen "Programmierstil" wundert. Dementsprechend kenne ich mich auch natürlich nicht sehr gut in Python aus usw.

Ich habe ein Programm geschrieben, das dem Benutzer eine Matheaufgabe mit beliebeigen Operator stellt. Die beiden Zahlen dafür werden zufällig erzeugt. Jedoch sollten die gestellten Matheaufgaben immer eine glatte Lösung ergeben. Das Problem tritt natürlich nur bei der Division auf. Mein bisheriges Programm sieht so aus:

Code: Alles auswählen

# Python 3.2.3

# Import

from math import *
import sys
import random


############################################################### DEF ###############################################################


def plus(zahl1,zahl2):
    print(zahl1,"+",zahl2,"=")
    global ergebnis
    ergebnis = zahl1+zahl2
    durchlauf()


#################################


def mal(zahl1,zahl2):
    print(zahl1,"*",zahl2,"=")
    global ergebnis
    ergebnis = zahl1*zahl2
    durchlauf()

    

#################################


def durch(zahl1,zahl2):
    print(zahl1,"/",zahl2,"=")
    global ergebnis
    ergebnis = zahl1/zahl2
    durchlauf()

    

#################################


def minus(zahl1,zahl2):
    print(zahl1,"-",zahl2,"=")
    global ergebnis
    ergebnis = zahl1-zahl2
    durchlauf()

    



#################################################################################


def quizfrage(zahl1,zahl2,operator):
    
    ##########################
    if operator=="*":
        mal(zahl1,zahl2)
               
    elif operator=="/":
        durch(zahl1,zahl2)
        
    elif operator=="+":
        plus(zahl1,zahl2)

    elif operator=="-":
        minus(zahl1,zahl2)
    ###########################
    
    
def durchlauf():
    antwort = input()
    antwort = int(antwort)
        
    if antwort == ergebnis:
        print("Richtig!")
        
        global punkte
        punkte=punkte+1
          
          
    else:
        print("Falsch!")
 
 
    
########################################################## Hauptprogramm ##########################################################


name=input("""Hallo, dies ist ein Quiz, wie heisst du?
""")
print("Hallo", name)

operator=input("Welchen Rechenoperator moechtest du benutzen?(+ - * /): ")

print("Hier kommen deine 10 Fragen:")
      
punkte = 0
for i in range(10):
    quizfrage(random.randint(1,20),random.randint(1,20),operator)



Ich bin grundsätzlich natürlich offen für Verbesserungen, ich bin ja nur ein Schüler :P

Nun, es ist ja durchaus möglich zu jeder zahl von 2 - 20 die entsprechenden Teilbaren Zahlen aufzuschreiben und zufällig auswählen zu lassen, das ist aber (leider) selbst bei 20 Zahlen ein ziemlich großer Code. Ich hoffe mal, ich werde jetzt nicht gehated, weil ich Schüler mit einem Problem ohne Python-Sonderkenntnisse bin, der fragt, aber ich bin zuversichtlich :D
Bin für jede Hilfe dankbar

Grüße
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@chris5790: Du hast ja schon richtig erkannt, dass bei anderen Operationen, zum Beispiel Multiplizieren, es kein Problem gibt, mit nicht ganzen Zahlen. Wie kann man jetzt eine Division in eine Multiplikation umwandeln ;-)?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Noch ein paar Anmerkungen zum Code: "global" ist ein Schlüsselwort, das Du am besten gleich wieder vergisst. Es macht mehr Probleme als es löst und sollte in normalen Programmen nicht vorkommen. In Deinem Fall kannst Du »ergebnis« einfach als Argument an »durchlauf« übergeben.
Auf Modulebene dürfen keine Befehle stehen, also alles von Zeile 94 solltest Du auch in eine Funktion schreiben. Diese Funktion heißt normalerweise »main« und wird durch diese beiden Zeilen am Ende des Programms aufgerufen:

Code: Alles auswählen

if __name__ == '__main__':
    main()
Dadurch kann man das Programm auch als Modul in ein anderes Programm importieren, um es z.B. zu testen, oder Teile daraus wiederzuverwenden.
chris5790
User
Beiträge: 70
Registriert: Mittwoch 19. Februar 2014, 19:25

Sirius3 hat geschrieben:@chris5790: Du hast ja schon richtig erkannt, dass bei anderen Operationen, zum Beispiel Multiplizieren, es kein Problem gibt, mit nicht ganzen Zahlen. Wie kann man jetzt eine Division in eine Multiplikation umwandeln ;-)?
Du hast mich auf eine Idee gebracht, jedoch ist die Frage ob mir das was bringt?
Ich möchte ja den Anwender eine Divisionsaufgabe lösen lassen. Ich könnte ja die beiden Zahlen miteinander so lange teilen lassen, bis es keine Nachkommastelle gibt. Erst dann wird die Schleife beendet und das Programm läuft weiter. Ich bin mir nur unsicher, wie ich prüfen lassen kann, ob das Ergebnis Nachkommastellen hat.

Mein Wissensstand ist bei Python für KIDS Kapitel 7, nach der Zeit gingen mir die ganzen Grafischen Sachen so dermaßen auf den Zeiger... Objektorientiert mag ich ja, aber nicht auf dem Niveau von 10-Jährigen... :D

Nun, entweder stehe ich auf dem Schlauch oder das ist außerhalb meines Wissens: was bedeutet Argument? Ich habe nun alles aus der Modulebene in eine Definition kopiert und jetzt bekomme ich Probleme mit dem Punktezähler. Hier mal das neu geschriebene Programm. Ich habe global überall entfernt, wie kann ich nun das Ergebnis von mal() usw. an durchlauf() weitergeben?

Code: Alles auswählen

# Python 3.2.3

# Import

from math import *
import sys
import random


############################################################### DEF ###############################################################


def plus(zahl1,zahl2):
    print(zahl1,"+",zahl2,"=")
    
    ergebnis = zahl1+zahl2
    durchlauf()


#################################


def mal(zahl1,zahl2):
    print(zahl1,"*",zahl2,"=")
    
    ergebnis = zahl1*zahl2
    durchlauf()

    

#################################


def durch(zahl1,zahl2):
    print(zahl1,"/",zahl2,"=")
    
    ergebnis = zahl1/zahl2
    durchlauf()

    

#################################


def minus(zahl1,zahl2):
    print(zahl1,"-",zahl2,"=")
    
    ergebnis = zahl1-zahl2
    durchlauf()

    



#################################################################################


def quizfrage(zahl1,zahl2,operator):
    
    ##########################
    if operator=="*":
        mal(zahl1,zahl2)
               
    elif operator=="/":
        durch(zahl1,zahl2)
        
    elif operator=="+":
        plus(zahl1,zahl2)

    elif operator=="-":
        minus(zahl1,zahl2)
   
#####################################################################################################################
    
    
def durchlauf():
    antwort = input()
    antwort = int(antwort)
        
    if antwort == ergebnis:
        print("Richtig!")
        
        
        punkte=punkte+1
          
          
    else:
        print("Falsch!")
 
 
    
def main():
  
    name=input("""Hallo, dies ist ein Quiz, wie heisst du?
    """)
    print("Hallo", name)

    operator=input("Welchen Rechenoperator moechtest du benutzen?(+ - * /): ")

    print("Hier kommen deine 10 Fragen:")
 
    punkte = 0
    
    while True:
        punkte = 0
        for i in range(10):
            quizfrage(random.randint(1,20),random.randint(1,20),operator)


########################################################## Hauptprogramm ##########################################################


if __name__ == '__main__':
    main()
Jetzt bekomme ich grade einen Fehler, dass Ergebnis nicht definiert ist
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

chris5790 hat geschrieben:Ich habe global überall entfernt, wie kann ich nun das Ergebnis von mal() usw. an durchlauf() weitergeben?
Wie gibst du denn Werte an deine Funktionen wie plus oder mal weiter?
chris5790
User
Beiträge: 70
Registriert: Mittwoch 19. Februar 2014, 19:25

Über die definierte Funktion per Variable, aber wie soll ich das auf das Ergebnis übertragen? Die Varibale ist ja nur in der jeweiligen Definition existent (mal(), plus()), also lokal, global soll ich ja nicht verwenden. Gibt es einen Befehl um lokale Varibalen für die ganze Definition verfügbar zu machen oder etwas in der Art?

BTW: geiler Name :D
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@chris5790: was ist »zahl1« und »zahl2« bei »plus«?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

chris5790 hat geschrieben:Über die definierte Funktion per Variable, aber wie soll ich das auf das Ergebnis übertragen? Die Varibale ist ja nur in der jeweiligen Definition existent (mal(), plus()), also lokal, global soll ich ja nicht verwenden. Gibt es einen Befehl um lokale Varibalen für die ganze Definition verfügbar zu machen oder etwas in der Art?
Schau mal, beim Aufruf von mal gibst du zwei Parameter (oder auch Argumente) mit: zahl1 und zahl2. Warum sollte das bei anderen Funktionen nicht auch gehen?

Wirf mal einen Blick auf den folgenden Code:

Code: Alles auswählen

def do_something(thing, number):
    result = thing * number
    show_me(result)

def show_me(value):
    print(value)

def main():
    do_something(7, 3)
    do_something('-', 40)

if __name__ == '__main__':
    main()
chris5790
User
Beiträge: 70
Registriert: Mittwoch 19. Februar 2014, 19:25

Ich glaub ich habs:

Code: Alles auswählen

def minus(zahl1,zahl2):
    print(zahl1,"-",zahl2,"=")
    
    ergebnis = zahl1-zahl2
    durchlauf(ergebnis)

    



#################################################################################

def durchlauf(ergebnis):
    antwort = input()
    antwort = int(antwort)
        
    if antwort == ergebnis:
        print("Richtig!")
        
        
       # punkte=punkte+1
          
          
    else:
        print("Falsch!")
Richtig? Beim Durchlauf funktioniert es zumindest ;)

Nun zu den Punkten: so wie es bisher war, funktioniert es natürlich nicht. Die Punkte werden bei durchlauf() addiert, vorher einmal in der while True Schleife auf 0 gesetzt, falls man das Programm neu starten möchte. Muss ich das mit den Punkten genau so wie mit dem ergebnis grade machen? Oder geht das auch anders?
BlackJack

@chris5790: Auf zur nächsten Eigenschaft von Funktionen: Die können Rückgabewerte haben. Lass die `quizfrage()` zurückgeben ob die Antwort richtig oder falsch war. Oder Alternativ die Anzahl der Punkte die durch die Beantwortung dazu kommen. Also zum Beispiel 0 wenn falsch geantwortet wurde und 1 wenn richtig geantwortet wurde. Das kannst Du dann auf die Gesamtpunkte addieren.

Die `durchlauf()`-Funktion ist eigenartig. Funktionsnamen sollten beschreiben was die Funktion tut, und das macht der Name `durchlauf` nicht wirklich. Normalerweise sollten es auch Tätigkeiten sein die durch den Namen beschrieben werden. Also zum Beispiel `stelle_quizfrage()` statt `quizfrage`. So kann man besser Namen für Funktionen, und Namen für ”Dinge” auseinander halten.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Lass doch mal die ganzen Leerzeilen und ``************``-Kommentare weg; die stören nur; insbesondere hier im Forum ;-)

Du könntest doch ``durchlauf`` (doofer Name übrigens! Was *tut* denn diese Funktion?) einfach ``berechne_ergebnis`` nennen diese das richtige Ergebnis berechnen und zurückliefern lassen (``return ergebnis``). Dann kannst Du *aus* Deiner Schleife erst die Zahlen generieren lassen, das richtige Ergebnis dazuholen, die Benutzerabfrage machen und danach die Auswertung vornehmen. Im Anschluss kannst Du immer noch in der Schleife die Punkte einfach hochzählen.

Also mal im Pseudocode:

Code: Alles auswählen

def run_quiz(operator_):
    while True:
        # ``berechne_ergebnis`` weiß intern, welche Funktion es
        # aufrufen muss (also Dein altes ``quizfrage`` in etwa)
        ergebnis = berechne_ergebnis(operator_)

        # in dieser Funktion holst Du dir die Antwort
        benutzer_antwort = hole_benutzer_antwort()
        
        # und hier einfach die Auswertung
        if(ergebnis == benutzer_antwort):
            # ausgabe eines Textes; Punkte hochsetzen
        # irgend wann abbrechen
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
chris5790
User
Beiträge: 70
Registriert: Mittwoch 19. Februar 2014, 19:25

BlackJack hat geschrieben:@chris5790: Auf zur nächsten Eigenschaft von Funktionen: Die können Rückgabewerte haben. Lass die `quizfrage()` zurückgeben ob die Antwort richtig oder falsch war.

Code: Alles auswählen

def antwort(ergebnis):
    antwort_benutzer = input()
    antwort_benutzer = int(antwort_benutzer)
        
    if antwort_benutzer == ergebnis:
        print("Richtig!")
                
        punkte=punkte+1
        
        return(punkte)
          
          
    else:
        print("Falsch!")
Meinst du in etwa so? Mein Buch sagt, dass return() verwendet wird, um Werte auch außerhalb der Funktion benutezn zu können
BlackJack

@chris5790: So nicht, denn wo kommt denn `punkte` her? Und was gibt die Funktion zurück wenn die Antwort falsch war?

Diese Funktion für *eine* Antwort braucht doch gar nichts über die Punkte insgesamt wissen, sondern dem Aufrufer nur mitteilen ob die eine Frage, für die sie Aufgerufen wurde, richtig oder falsch beantwortet wurde, beziehungsweise wie viele Punkte es für die Beantwortung der Frage gibt.
BlackJack

Ich habe nicht widerstehen können und das mal (für Python 2) umgesetzt:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from functools import partial
from operator import add, mul, sub
from random import randint


QUESTION_COUNT = 10


def create_question_data(operation_function, max_operand_value=20):
    operand_a = randint(1, max_operand_value)
    operand_b = randint(1, max_operand_value)
    return (operand_a, operand_b, operation_function(operand_a, operand_b)) 


def create_div_question_data():
    # ...


def ask_for_operator_symbol(symbols):
    prompt = 'Welchen Rechenoperator ({0}) moechtest Du benutzen? '.format(
        ' '.join(symbols)
    )
    while True:
        symbol = raw_input(prompt)
        if symbol in symbols:
            return symbol
        else:
            print 'Falsche Eingabe!'


def ask_result(prompt):
    while True:
        try:
            return int(raw_input(prompt))
        except ValueError:
            print 'Fehler: Eingabe ist keine Zahl!'


def ask_question(symbol, create_data_function):
    operand_a, operand_b, expected_result = create_data_function()
    result = ask_result('{0} {1} {2} = '.format(operand_a, symbol, operand_b))
    is_correct = result == expected_result
    print 'Richtig!' if is_correct else 'Falsch!'
    return 1 if is_correct else 0


def main():
    symbol2create_data_function = {
        '+': partial(create_question_data, add),
        '-': partial(create_question_data, sub),
        '*': partial(create_question_data, mul),
        '/': create_div_question_data,
    }
    symbol = ask_for_operator_symbol(symbol2create_data_function.keys())
    create_data_function = symbol2create_data_function[symbol]
    print 'Hier kommen Deine {0} Fragen:'.format(QUESTION_COUNT)
    points = sum(
        ask_question(symbol, create_data_function)
        for _ in xrange(QUESTION_COUNT)
    )
    print 'Du hast {0} Punkt(e).'.format(points)


if __name__ == '__main__':
    main()
Die zwei Zeilen von `create_div_question_data()` habe ich hier raus genommen um chris5790 nicht das Erfolgsgefühl zu nehmen selbst eine Lösung für die Frage im Betreff dieses Themas zu finden. :-)
chris5790
User
Beiträge: 70
Registriert: Mittwoch 19. Februar 2014, 19:25

Boah, der Code überfordert mich jetzt schon in gewisser Weise.

Code: Alles auswählen

def ask_for_operator_symbol(symbols):
    prompt = 'Welchen Rechenoperator ({0}) moechtest Du benutzen? '.format(
        ' '.join(symbols)
    )
    while True:
        symbol = raw_input(prompt)
        if symbol in symbols:
            return symbol
        else:
            print 'Falsche Eingabe!'
das '.format(' '.join(symbols), was ist das genau? Und woran erkennt er in der Schleife, ob ein falsches Symbol eingegeben wurde? den Teil nach 'while True' versteh ich nicht, ist symbol eine Variable? Oder prüft das, ob ein Rechensymbol eingegeben wurde? Ich denke mich verwirren nur die englischen Variablen oder ich bin immer noch von den heutigen Eindrücken bei unserem neuen Projekt über Python geschockt. Aber dazu mach ich lieber einen eigenen Thread auf ;)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@chris5790: symbol ist eine Variablem genauso wie symbols. Wenn Dir etwas unklar ist, dann kannst Du Dir einfach einzelne Teile in der Python-Konsole ausgeben lassen:

Code: Alles auswählen

In [1]: symbols = ['+', '-', '*', '/']

In [2]: ' '.join(symbols)
Out[2]: '+ - * /'

In [3]: prompt = 'Welchen Rechenoperator ({0}) moechtest Du benutzen? '.format(
   ...:         ' '.join(symbols)
   ...:     )

In [4]: prompt
Out[4]: 'Welchen Rechenoperator (+ - * /) moechtest Du benutzen? '

In [5]: symbol = '-'

In [6]: symbol in symbols
Out[6]: True
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@chris5790: Der Code von BlackJack ist auch imho (noch) nicht für Dich gedacht; schön, wenn Du versuchen willst, diesen zu verstehen, aber eigentlich ist das eher eine Fingerübung von BlackJack gewesen, der eine pythonische Lösung des Problems aufzeigt. Darin werden natürlich auch weit fortgeschrittene Konzepte der Sprache verwendet, die Du vermutlich noch nicht kennst ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

Jo, was Hyperion gesagt hat. Wobei jetzt gerade die Funktion die den Benutzer nach dem Symbol fragt ja eigentlich nichts wirklich fortgeschrittenes macht. Das müsste alles irgendwo im Tutorial vorkommen, oder?
chris5790
User
Beiträge: 70
Registriert: Mittwoch 19. Februar 2014, 19:25

In welchem Tutorial? In der Documentary? Unser Python-Buch verliert da vielleicht 3 Wörter drüber, am Ende, irgendwo in Kapitel 14. Der Schwerpunkt ist turtle, was zum zeichnen halt, wirkoches programmieren ist das nicht...
BlackJack

Ja, ich meinte das in der Dokumentation.
Antworten