python 3.5 ja / nein Abfrage

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.
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@codemode89: Ganze 50% wiederholter Code in jeder der drei Funktionen ist keine Code-Duplizierung für Dich? :-o

Ein Beispiel für nicht-ASCII-Ziffern? Wie wär's mit 698:

Code: Alles auswählen

In [25]: ds = list(filter(str.isdigit, (chr(i) for i in range(0x110000))))

In [26]: len(ds)
Out[26]: 708
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@__backjack__: `int` mag auch arabische Ziffern:

Code: Alles auswählen

int("۵۷")
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Stimmt. Also noch ein bisschen mehr filtern:

Code: Alles auswählen

In [59]: def f(x):
    ...:     try:
    ...:         int(x)
    ...:         return True
    ...:     except:
    ...:         return False
    ...:     

In [60]: ds2 = [x for x in ds if not f(x)]

In [61]: len(ds2)
Out[61]: 128
Ums mal praktisch zu machen: ich hatte es mit etwas getestet was auf deutschen Tastaturlayouts einfach einzugeben ist: ² (Alt Gr + 2). Das mag `int()` beispielsweise nicht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
codemode89
User
Beiträge: 12
Registriert: Mittwoch 26. September 2018, 17:39

__blackjack__ hat geschrieben: Mittwoch 26. September 2018, 22:09 @codemode89: Ganze 50% wiederholter Code in jeder der drei Funktionen ist keine Code-Duplizierung für Dich? :-o
Das ist korrekt.

Code: Alles auswählen

i=0
while i <10
	i+=1
return i
Hast du da auch 50% Dopplung?
Das ist ein Standardkonstrukt und für jeden leicht lesbar.

Ich verstehe deinen Standpunkt ja, es wird ja auch teilweise in Python Fachliteratur so angeraten mit dem while true , aber ich sehe es halt anders.
Ich empfehle dir mal das Buch Clean Code von Robert C. Martin zu lesen. (Soll weder einen Angriff darstellen, noch deine Expertise in Frage stellen)

Der Tipp mit isdigit() ist super! Vielen Dank dafür :) wieder was dazu gelernt.

Aber ich würde trotzdem kein Int cast mit try/catch machen, sondern würde dann eher ein pattern matching verwenden.

Code: Alles auswählen

def is_valid_number(number_string):
    return not re.fullmatch('\d+', number_string, re.A) is None
Genauso in Java: try catch finally alles unötige code Aufblähung.
Das oberste Ziel sollte sein den Code gut lesbar und somit leicht wartbar zu machen.

Code: Alles auswählen

private static boolean isValidNumber(String validationObject) {
	return validationObject.matches("[0-9]+");		
}

private static int askForAge(String prompt) {
	String consoleInput;
	do {
		System.out.println(prompt);
		consoleInput = SCANNER.nextLine();
	}while(!isValidNumber(consoleInput));
	return Integer.parseInt(consoleInput);
}
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Idiome aus einer Sprache unhinterfragt in andere zu uebertragen ist keine gute Idee. So gut Clean Code ist, es bezieht sich auf die bei MS zu der Zeit genutzten Sprachen. Explizite Zaehlvariablen sind in Python verpoent, weil sich die meisten Probleme in denen sie verwandt werden eh besser durch Iteratoren darstellen lassen. Eine Lektion, die inzwischen auch andere Sprachen gelernt haben, Java inklusive.

Und damit definiert sich die Lesbarkeit nicht absolut, sondern relativ zur Sprache, und da sind while True: break und try/excepts eben das Mittel der Wahl. Und erst recht nichth so ein i=0, while-Konstrukt wie du es oben praesentierst. Natuerlich geht es auch anders, was ja das unterliegende Grundproblem aller solcher Diskussionen ist: es tut ja. GOTO-Wuesten und ungarische Notation tun es auch. Will aber keiner mehr sehen.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@codemode89: das Patternmatching ist entweder nicht ausreichend oder schränkt zu sehr ein. Ausnahmen mit try-Block sind ja gerade dazu da, bei Falscheingaben entsprechend reagieren zu können. Das Pattern zu verstehen ist um einiges komplizierter, als der fast schon literarische Text: "versuche: wandle Text in Zahl. Falls das nicht klappt, sage, 'das war keine Zahl' ".

Code: Alles auswählen

def frage_nach_zahl(type="Zahl"):
    while True:
        eingabe_von_console = input('Bitte {} eingeben: '.format(type))
        try:
            return int(eingabe_von_console)
        except ValueError:
            pass # keine Zahl eingegeben
Bei

Code: Alles auswählen

i=0
while i <10
	i+=1
return i
gibt es keine Code-Dopplung, weil i ja zwingend initialisiert werden muß um dann bei i+=1 damit weiter rechnen zu können. Dagegen ist es einfach nur das falsche Konstrukt, da das durch eine for-Schleife ausgedrückt werden kann

Code: Alles auswählen

for i in range(10):
    pass
return i
Und da die for-Schleife nichts macht, außer den Zähler hochzuzählen, kann man das zu

Code: Alles auswählen

return 9
verwandeln.
codemode89
User
Beiträge: 12
Registriert: Mittwoch 26. September 2018, 17:39

__deets__ hat geschrieben: Donnerstag 27. September 2018, 09:02Und erst recht nichth so ein i=0, while-Konstrukt wie du es oben praesentierst.
Ähm das war nur ein Beispiel wie eine Standard while schleife definiert ist.

Code: Alles auswählen

condition
while(!condition)
	do something
	
Mir ist schon klar das es mit Integern schönere Wege gibt, man hat aber nicht immer Integer.
Und der Grundzustand einer condition kommt nicht aus der Luft gefallen sondern bedarf einer Zuweisung.

Und ob es nun nicht der Pythonic way ist steht auf einen anderen Blatt. Das habe ich ja auch eingeräumt, und es ist ja völlig legitim
codemode89 hat geschrieben: Donnerstag 27. September 2018, 08:30 Ich verstehe deinen Standpunkt ja, es wird ja auch teilweise in Python Fachliteratur so angeraten mit dem while true , aber ich sehe es halt anders.
Clean Code ist schön älter ja, aber es behandelt etliche zeitlose Konzepte unabhängig von jeglicher Programmiersprache.


Sirius3 hat geschrieben: Donnerstag 27. September 2018, 09:16 gibt es keine Code-Dopplung, weil i ja zwingend initialisiert werden muß um dann bei i+=1 damit weiter rechnen zu können.
i muss schon initialisiert werden für die condition, das ist exakt das was ich bei meiner Funktion auch gemacht habe.
Sirius3 hat geschrieben: Donnerstag 27. September 2018, 09:16 @codemode89: das Patternmatching ist entweder nicht ausreichend oder schränkt zu sehr ein. Ausnahmen mit try-Block sind ja gerade dazu da, bei Falscheingaben entsprechend reagieren zu können. Das Pattern zu verstehen ist um einiges komplizierter, als der fast schon literarische Text: "versuche: wandle Text in Zahl. Falls das nicht klappt, sage, 'das war keine Zahl' ".
Wie machst du es denn das er nur 0-9 als Input akzeptiert?
Was ist wenn die Zahl dann nur zwischen 0 bis 130 sein darf?
Kommen wir dann nicht wieder zu try if else except?

Naja mir soll es im Endeffekt auch egal sein :P.
Ich denke ich habe meinen Standpunkt klar gemacht. Ihr habt euren Standpunkt klar gemacht.
Und wir werden in der Hinsicht wohl nicht zueinander finden.

Zum try, except als mittel der Wahl, siehe den Javacode.
Wenn du

Code: Alles auswählen

while true
	try
		if
		continue
	catch
		continue
	finally
leserlicher findest als

Code: Alles auswählen

do
while(condition)
dann ist das wohl so und das akzeptiere ich ja auch.

In Python ist es auch weniger performant jedes mal in try except zu gehen und wie bereits erwähnt,
ist es MEINER Meinung weniger leserlich als eine konkrete condition.

Da ich neu im Forum bin wollte ich abschließend nur nochmal klar stellen, das ich hier niemanden persönlich angreifen wollte.
Ich denke auch nicht das mein Weg der einzig richtige ist, sondern habe nur meine Meinung kund getan, wofür das Forum ja nun auch da ist.

Ich will mir hier ja keine Feinde machen sondern Freunde für einen tollen Austausch :P
Miranda
User
Beiträge: 23
Registriert: Sonntag 23. September 2018, 21:45

codemode89 hat geschrieben: Mittwoch 26. September 2018, 17:58 Und als weiterer Tipp nutze Englische Namen. Du wirst auf lange Sicht eh nicht ums englische drum herum kommen.
Das dachte ich auch mal :lol:
Bis ich dann das erste mal in einem deutsch, weißrussisch, spanischen Team gearbeitet habe und deutsch tatsächlich die einzige Sprache war, die alle verstanden haben.
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@codemode89: Bei der ``while``-Schleife sehe ich keine Code-Dopplung. Du etwa? Welchen Teilausdruck davon könnte man denn nur einmal schreiben wenn man das anders ausdrückt?

Diese Schleife ist so auch kein Standardkonstrukt, wie ja bereits gesagt wurde. Auch in vielen anderen Programmiersprachen würde man hier standardmässig eine ``for``-Schleife verwenden. Ansonsten könnte man ja auch GOTO in einem leicht verständlichen Standardkonstrukt verwenden (braucht das externe `goto-statement`-Package):

Code: Alles auswählen

from goto import with_goto


@with_goto
def f():
    i = 0
    label .loop_start
    if i >= 9: goto .loop_end
    i += 1; goto .loop_start
    label .loop_end
    return i
Das ist aber genau wie die ``while``-Schleife kein idiomatisches Python.

In „Clean Code“ hatte ich schon mal geblättert, und ich kann mir schwer vorstellen, dass Uncle Bob empfiehlt das DRY-Prinzip zu verletzen. Das ist eines der zeitlosen und ziemlich grundlegenden Konzepte.

Was ist denn in *Python* an einer Ausnahmebehandlung aufgeblähter als an einer Fallunterscheidung? Bei Java stimmt das, da ist das sowohl vom Code her unhandlicher weil es kein ``else`` gibt, als auch von der Laufzeit, weil Ausnahmebehandlung dort verhältnismässig teuer ist, was sie in Python nicht ist! Da sind Funktionsaufrufe mindestens genau so teuer. Deswegen soll man in Java Ausnahmen auch nicht für normale Steuerung des Programmflusses verwenden. In Python ist das dagegen normal. Jede ``for``-Schleife wird in Python durch eine `StopIteration`-Ausnahme beendet. Das explizit angeforderte Programmende wird durch eine `SystemExit`-Ausnahme geregelt.

Das ist eben idiomatisches Python. Dazu mal zwei Einträge aus dem Glossar der Python-Dokumentation:
EAFP
Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

[…]

LBYL
Look before you leap. This coding style explicitly tests for pre-conditions before making calls or lookups. This style contrasts with the EAFP approach and is characterized by the presence of many if statements.
Und wirklich aufgebläht finde ich das hier auch in Java nicht:

Code: Alles auswählen

    private static int askPositiveInteger(String prompt) {
        while (true) {
            try {
                return Integer.parseUnsignedInt(askString(prompt));
            } catch (NumberFormatException e) {
                System.out.println("Bitte eine positive ganze Zahl eingeben!");
            }
        }
    }
Der Code mit dem ``continue`` ist in der Tat schrecklich, aber das hätte man in Python so ja nicht, denn da gibt es das ``else`` was es in Java nicht gibt *und* eine Quelle von Komplexität ist hier ja auch das man in jedem Fall den Scanner noch das Zeilenende schlucken lassen muss. Sonst kann man sich den ``finally``-Zweig sparen. Wie man an dem letzten Codebeispiel gut sehen kann. Ich finde das jedenfalls nicht unleserlicher als eine ``do``/``while``-Schleife. In Java kann man dann überlegen was man nehmen will, in Python nicht. Denn eine ``do``/``while``-Schleife gibt es als Syntaxkonstrukt nicht.

Zum `is_valid_number()` fällt auf das da ``not`` gefolgt von einem recht langem Ausdruck und dann ein ``is None`` steht. Das kann man sehr leicht übersehen und sollte deshalb besser als ``[…] is not None`` geschrieben werden. Oder man lässt das einfach komplett weg, denn ein `Match`-Objekt ist ja immer ”wahr” und `None` ist immer ”unwahr” in boole'schen Kontexten.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
codemode89
User
Beiträge: 12
Registriert: Mittwoch 26. September 2018, 17:39

__blackjack__ hat geschrieben: Donnerstag 27. September 2018, 10:54 Diese Schleife ist so auch kein Standardkonstrukt, wie ja bereits gesagt wurde. Auch in vielen anderen Programmiersprachen würde man hier standardmässig eine ``for``-Schleife verwenden. Ansonsten könnte man ja
War wie jetzt zum dritten mal gesagt nur ein Beispiel um den Aufbau

Code: Alles auswählen

condition
while (!condition)
	do something
zu verdeutlichen. (Rede ich gegen Wände?)

Eine for Schleife sollte eingesetzt werden wenn das Ende vor dem Beginn absehbar ist.
Trifft hier nicht zu da der User 100 oder auch 1000 mal falsche Eingaben machen kann.

Eine while Schleife sollte man einsetzen wenn das Ende vorher nicht absehbar ist.

DRY Prinzip verletzt in dem ich eine Variable initialisiere? Ich denke nicht.
Nochmal DRY Prinzip nachlesen und verstehen was da der ausschlaggebende Punkt ist.

Diese Methode

Code: Alles auswählen

    private static int askPositiveInteger(String prompt) {
        while (true) {
            try {
                return Integer.parseUnsignedInt(askString(prompt));
            } catch (NumberFormatException e) {
                System.out.println("Bitte eine positive ganze Zahl eingeben!");
            }
        }
    }
macht 2 Sachen, sie liest von der Console ein und prüft den Input.

Jede Methode sollte genau eine Sache machen.
Wenn das Alter jetzt zwischen 30 und 100 sein soll, wie sieht es dann aus?

Code: Alles auswählen

	    private static int askPositiveInteger(String prompt) {
	        while (true) {
	            try {
	                int number = Integer.parseUnsignedInt(askString(prompt));
	                if(number>30 && number<100)
	                	return number;
	            } catch (NumberFormatException e) {
	                System.out.println("Bitte eine positive ganze Zahl eingeben!");
	            }
	        }
	    }
Jetzt bitte einmal extrahieren und bei deiner while true schleife bleiben.
__blackjack__ hat geschrieben: Donnerstag 27. September 2018, 10:54 Zum `is_valid_number()` fällt auf das da ``not`` gefolgt von einem recht langem Ausdruck und dann ein ``is None`` steht. Das kann man sehr leicht übersehen und sollte deshalb besser als ``[…] is not None`` geschrieben werden. Oder man lässt das einfach komplett weg, denn ein `Match`-Objekt ist ja immer ”wahr” und `None` ist immer ”unwahr” in boole'schen Kontexten.
Hast du zu 100% recht.
codemode89
User
Beiträge: 12
Registriert: Mittwoch 26. September 2018, 17:39

__blackjack__ hat geschrieben: Donnerstag 27. September 2018, 10:54 Das ist eben idiomatisches Python. Dazu mal zwei Einträge aus dem Glossar der Python-Dokumentation:
EAFP
Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

[…]

LBYL
Look before you leap. This coding style explicitly tests for pre-conditions before making calls or lookups. This style contrasts with the EAFP approach and is characterized by the presence of many if statements.
Darauf gehe ich nicht weiter ein mehr als 2 mal zu bestätigen das ihr Recht habt, das es nicht der pythonic way ist kann ich auch nicht tun ^^
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

codemode89 hat geschrieben: Donnerstag 27. September 2018, 11:47
DRY Prinzip verletzt in dem ich eine Variable initialisiere? Ich denke nicht.
Nochmal DRY Prinzip nachlesen und verstehen was da der ausschlaggebende Punkt ist.
Das bezog sich auf deine drei Input-Methoden, die alle bis auf Text & Praedikat das gleiche machen. Und in einer Sprache, in der Funktionen Mitbuerger erster Klass sind, ist beides problemlos loesbar ohne sich so viel zu wiederholen. Und die while-Schleife ist nun schon mehrfach nicht wegen DRY, sondern unidiomatischem Python-Code angesprochen worden. Auch anderswo stehen augenscheinlich Waende rum.
codemode89
User
Beiträge: 12
Registriert: Mittwoch 26. September 2018, 17:39

__deets__ hat geschrieben: Donnerstag 27. September 2018, 11:53 Und die while-Schleife ist nun schon mehrfach nicht wegen DRY, sondern unidiomatischem Python-Code angesprochen worden. Auch anderswo stehen augenscheinlich Waende rum.
siehe mein letzten Post
__deets__ hat geschrieben: Donnerstag 27. September 2018, 11:53 Das bezog sich auf deine drei Input-Methoden, die alle bis auf Text & Praedikat
und return type.
__deets__ hat geschrieben: Donnerstag 27. September 2018, 11:53 das gleiche machen.
Jede get_input Methode hat eine eigene condition sowie ein anderen return value das einzige was sie sich teilen ist das in jeder eine while schleife läuft

Bitte einmal Code Beispiel wie das sonst zu erreichen ist. Ich bin ja lernwillig
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@codemode89: Der Aufbau war in deinem praktischen Beispiel

Code: Alles auswählen

condition = [code und daten die die bedingung ausmachen]
while (!condition)
    do something
    condition = [code und daten die die bedingung ausmachen]
Und das in den eckigen Klammern stand da zweimal identisch im Code und war nicht nur ein trivialer Aufruf, müsste also auch an beiden Stellen gleich geändert werden, wenn man etwas Ändern oder einen Fehler beheben möchte, und verletzt damit das DRY-Prinzip.

`askPositiveInteger()` macht genau eine Sache: Den Benutzer solange nach einer Eingabe fragen bis der eine positive, ganze Zahl eingegeben hat. Das ist auf dieser Abstraktionsebene ein Schritt. Ansonsten würde dieses Argument ja gegen jede Methode sprechen die mehr als eine Operation beinhaltet und `main()`-Methoden komplett verboten machen, denn die machen ja *alles*!

Eine Methode die eine ganze Zahl mit Unter- und Obergrenze abfragt, würde in der Tat so ähnlich aussehen. Ungefähr so:

Code: Alles auswählen

    private static int askAge(int lowerLimit, int upperLimit) {
        if (lowerLimit > upperLimit) {
            throw new IllegalArgumentException("lower limit must not be greater than upper limit");
        }
        while (true) {
            try {
                final var result = Integer.parseInt(askTrimmedNonEmptyString("Alter eingeben: "));
                if (lowerLimit <= result && result <= upperLimit) {
                    return result;
                }
                System.out.printf(
                        "Bitte eine Zahl zwischen %d und %d (inklusive) eingeben!",
                        lowerLimit, upperLimit);
            } catch (NumberFormatException e) {
                System.out.println("Bitte eine ganze Zahl eingeben!");
            }
        }
    }
Was ich da jetzt extrahieren soll ist mir allerdings nicht klar‽ Und warum?
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ein Beispiel wie man eine wiederholte Eingabe ohne Code-Duplizierung und mit klarer Abtrennung von Fehlerbedingung gestalten koennte:

Code: Alles auswählen

def ask_until_correct(message, errormessage, conversion):
    while True:
        try:
            return conversion(input(message))
        except ValueError:
            print(errormessage)


def alterspraedikat(value):
    alter = int(value)
    if 1 <= alter <= 120:
        return alter
    raise ValueError


def namenspraedikat(value):
    if not len(value):
        raise ValueError
    return value


def ja_nein_frage(value):
    try:
        return dict(j=True, y=True, n=False)[value.lower()[0]]
    except (IndexError, KeyError):
        raise ValueError


def main():
    alter = ask_until_correct(
        "Bitte Alter angeben: ",
        "Falsche Eingabe, Alter zwischen 1 und 120",
        alterspraedikat,
    )
    name = ask_until_correct(
        "Bitte Namen angeben: ",
        "Falsche Eingabe. Ein Name kann nicht leer sein",
        namenspraedikat,
    )
    raucher =  ask_until_correct(
        "Sind Sie Raucher?",
        "Bitte mit Ja/ja/yes/j/y oder Nein/nein/no/n antworten",
        ja_nein_frage,
    )
    print("hallo", name, "im Alter von", alter)
    print("Rauchen ist ungesund" if raucher else "Nichtraucher haben bessere Zaehne")


if __name__ == '__main__':
    main()
Zuletzt geändert von __deets__ am Donnerstag 27. September 2018, 13:13, insgesamt 1-mal geändert.
Grund: Fehler in namens-test repariert
codemode89
User
Beiträge: 12
Registriert: Mittwoch 26. September 2018, 17:39

__blackjack__ hat geschrieben: Donnerstag 27. September 2018, 12:58 @codemode89: Der Aufbau war in deinem praktischen Beispiel

Code: Alles auswählen

condition = [code und daten die die bedingung ausmachen]
while (!condition)
    do something
    condition = [code und daten die die bedingung ausmachen]
Und das in den eckigen Klammern stand da zweimal identisch im Code und war nicht nur ein trivialer Aufruf
Nein es war beides mal eine Zuweisung mittels eines trivialen Aufrufs der Input Methode und das do somethin wird durch diese Zuweisung ersetzt nicht erweitert.
hier nochmal von Seite 1

Code: Alles auswählen

def nochmal():
    eingabe_von_console = input('Nochmal y oder n : ').lower()
    while eingabe_von_console != "y" and eingabe_von_console != "n":
        eingabe_von_console = input('Nochmal y oder n : ').lower()
    return eingabe_von_console == "y"
Was daran nicht so schön ist das die input Methode nicht gekapselt ist.
__blackjack__ hat geschrieben: Donnerstag 27. September 2018, 12:58 `askPositiveInteger()` macht genau eine Sache: Den Benutzer solange nach einer Eingabe fragen bis der eine positive, ganze Zahl eingegeben hat. Das ist auf dieser Abstraktionsebene ein Schritt. Ansonsten würde dieses Argument ja gegen jede Methode sprechen die mehr als eine Operation beinhaltet und `main()`-Methoden komplett verboten machen, denn die machen ja *alles*!

Eine Methode die eine ganze Zahl mit Unter- und Obergrenze abfragt, würde in der Tat so ähnlich aussehen. Ungefähr so:

Code: Alles auswählen

    private static int askAge(int lowerLimit, int upperLimit) {
        if (lowerLimit > upperLimit) {
            throw new IllegalArgumentException("lower limit must not be greater than upper limit");
        }
        while (true) {
            try {
                final var result = Integer.parseInt(askTrimmedNonEmptyString("Alter eingeben: "));
                if (lowerLimit <= result && result <= upperLimit) {
                    return result;
                }
                System.out.printf(
                        "Bitte eine Zahl zwischen %d und %d (inklusive) eingeben!",
                        lowerLimit, upperLimit);
            } catch (NumberFormatException e) {
                System.out.println("Bitte eine ganze Zahl eingeben!");
            }
        }
    }
Was ich da jetzt extrahieren soll ist mir allerdings nicht klar‽ Und warum?
Nein deine Methode macht 2 Sachen und die sollen nun beide voneinander getrennt werden.
Welche 2 Sachen? siehe vorangegangen Post ich habe keine Lust in jedem Post exakt das zu wiederholen was ich im vorangegangen geschrieben habe.

Siehe Code von __deets__ dieser macht genau eine Sache pro Methode und ist wohl die beste hier gepostete Lösung in der Hinsicht.
Chapeau! Mr __deets__ ;)

Auch wenn hier als Zahlen als Namen zugelassen werden. Aber dies kann an einer Zentralen stelle gefixed werden genau aus dem Grund, das jede Methode nur eine Aufgabe hat.
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

``eingabe_von_console = input('Nochmal y oder n : ').lower()`` kommt in der Funktion zweimal vor. Das ist Code- und Datenwiederholung die man trivial vermeiden kann in dem man das mit ``while True`` und ``break`` oder ``return`` löst.

Die Java-Methode macht genau eine Sache. Wie gesagt, es kommt auf die Abstraktionsebene an was als eine Sache betrachtet wird. Für die gegebene Aufgabe sollte das nicht getrennt werden. Für die gegebene Aufgabe ist allerdings auch die Begrenzung auf Ober- und Untergrenze schon nicht gegeben, eine Begrenzung auf positive Werte macht bei Altersangaben aber schon Sinn.

Mit dem „,macht nur eine Sache“ dürfte man ja schon `input()` in Python schon nicht benutzen weil das Ausgabe *und* Eingabe macht. Und was `print()` so alles macht… Deswegen wird doch jetzt niemand ernsthaft Argumentieren wollen man muss alles über `sys.stdout.write()` und `sys.stdin.readline()` an jeder Stelle wo ein `input()` benutzt wurde, selber regeln muss. Oder man dürfte sich in Java die `askString()`-Methode nicht selber schreiben.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@__deets__: Deine Aufteilung des Codes lässt sich ja mittlerweile auch ganz gut in Java machen, ohne das man dafür Klassen schreiben muss. Den `ValueError` habe ich als `Optional<T>` umgesetzt um weniger Ausnahmebehandlung zu haben:

Code: Alles auswählen

import java.util.Optional;
import java.util.Scanner;
import java.util.function.Function;

final class Main {

    private static final Scanner SCANNER = new Scanner(System.in);

    private Main() {}

    private static <T> T askUntilCorrect(String message, String errorMessage,
                                         Function<String, Optional<T>> converter) {
        while (true) {
            System.out.print(message);
            final var result = converter.apply(SCANNER.nextLine());
            if (result.isPresent()) {
                return result.get();
            }
            System.out.println(errorMessage);
        }
    }

    private static Optional<Integer> convertAge(String value) {
        try {
            final var age = Integer.parseInt(value);
            return 1 <= age && age <= 120 ? Optional.of(age) : Optional.empty();
        } catch (NumberFormatException e) {
            return Optional.empty();
        }
    }

    private static Optional<String> convertName(String value) {
        return (value.isEmpty()) ? Optional.empty() : Optional.of(value);
    }

    private static Optional<Boolean> convertYesNo(String value) {
        if (value.isEmpty()) {
            return Optional.empty();
        } else {
            switch (value.toLowerCase().charAt(0)) {
                case 'y':
                    case 'j': return Optional.of(true);
                case 'n': return Optional.of(false);
                default: return Optional.empty();
            }
        }
    }

    public static void main(String... args) {
        final var age = askUntilCorrect("Bitte Alter angeben: ",
                "Falsche Eingabe, Alter zwischen 1 und 120", Main::convertAge);
        final var name = askUntilCorrect("Bitte Namen angeben: ",
                "Falsche Eingabe. Ein Name kann nicht leer sein.", Main::convertName);
        final var doesSmoke = askUntilCorrect("Sind sie Raucher? ",
                "Bitte mit Ja/ja/yes/j/y oder Nein/nein/no/n antworten", Main::convertYesNo);

        System.out.printf("Hallo %s im Alter von %d.\n", name, age);
        System.out.println((doesSmoke) ? "Rauchen ist ungesund." : "Nichtraucher haben bessere Zähne.");
    }
}
Was mir an der Aufteilung nicht so gefäll,t ist das Wissen was in der Fehlernachricht *und* in den Konvertierungsmethoden steckt, also das man beispielsweise um die Altersgrenzen zu ändern sowohl die `convertAge()` als auch an ganz anderer Stelle die Zahlen in der Fehlermeldung anpassen muss.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

@__blackjack__ das Altersprädikat kann man ja auch problemlos als closure definieren, welcher die Grenzen an der Einsatzstelle bekommt. Somit liegen Definition und passende Fehlermeldung nahe beieinander. Und dann gibt’s ja auch noch zu guter letzt Konstanten, die man zentral definiert für beides nutzen kann.
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Na dann mal in Kotlin, da kann man die Prädikatsfunktion auch als anonyme Funktion beim Aufruf angeben und ist so ziemlich nah an der Zeichenkette mit der Fehlermeldung:

Code: Alles auswählen

private tailrec fun <T> askUntilCorrect(message: String, errorMessage: String,
                                        convert: (s: String) -> T?): T {
    print(message)
    val result = convert(readLine() ?: error("Can't read from stdin"))
    return if (result == null) {
        println(errorMessage)
        askUntilCorrect(message, errorMessage, convert)
    } else result
}

fun main(args: Array<String>) {
    val age = run {
        val minAge = 1
        val maxAge = 120
        askUntilCorrect(
                "Bitte Alter angeben: ",
                "Falsche Eingabe, Alter zwischen $minAge und $maxAge."
        ) { string -> string.toIntOrNull()?.takeIf { it in minAge..maxAge } }
    }

    val name = askUntilCorrect(
            "Bitte Namen angeben: ",
            "Falsche Eingabe. Ein Name kann nicht leer sein."
    ) { it.trim().takeUnless(String::isEmpty) }

    val doesSmoke = askUntilCorrect(
            "Sind sie Raucher? ",
            "Bitte mit Ja/ja/yes/j/y oder Nein/nein/no/n antworten"
    ) {
        when (it.getOrNull(0)?.toLowerCase()) {
            'y', 'j' -> true
            'n' -> false
            else -> null
        }
    }

    println("Hallo $name im Alter von $age.")
    println(if (doesSmoke) "Rauchen ist ungesund." else "Nichtraucher haben bessere Zähne.")
}
Ist auch keine ``while (true)``-Schleife mehr drin, das ist durch Endrekursion ersetzt. Closures wären auch möglich wenn man eine Prädikatsfunktion als benannte Funktion erstellen wollen müsste. :-)
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten