Python – The Collatz Sequence (Italiano)
Su Dicembre 1, 2020 da adminPosso avere una revisione del mio codice per la sequenza di Collatz dal capitolo tre di Automatizza le cose noiose con Python ?
La sequenza di Collatz
Scrivi una funzione chiamata collatz () che abbia un parametro chiamato number. Se il numero è pari, collatz () dovrebbe stampare il numero // 2 e restituire questo valore. Se il numero è dispari, allora collatz () dovrebbe stampare e restituire 3 * numero + 1.
Quindi scrivere un programma che consenta allutente di digitare un numero intero e che continui a chiamare collatz () su quel numero fino al restituisce il valore 1. (Sorprendentemente, questa sequenza funziona effettivamente per qualsiasi numero intero: prima o poi, usando questa sequenza, arriverai a 1! Anche i matematici non sono sicuri del perché. Il tuo programma sta esplorando quella che viene chiamata la sequenza di Collatz , a volte chiamato “il più semplice problema di matematica impossibile.”)
Ricorda di convertire il valore restituito da input () in un numero intero con la funzione int (); altrimenti, sarà un valore stringa.
Suggerimento: un numero intero è pari se numero% 2 == 0, ed è dispari se numero% 2 == 1.
Loutput di questo programma potrebbe essere simile a questo:
Enter number: 3 10 5 16 8 4 2 1
Convalida input
Aggiungi le istruzioni try e tranne al progetto precedente per rilevare se lutente digita una stringa non intera. Normalmente, la funzione int () solleverà un errore ValueError se viene passata una stringa non intera, come in int (“puppy”). Nella clausola tranne, stampa un messaggio allutente dicendo che deve inserire un numero intero.
Mi chiedo principalmente se esista un modo più pulito per scrivere il mio soluzione.
def collatz(num): while num > 1: if num % 2 == 0: print(num//2) num = num //2 elif num % 2 ==1: print(3*num+1) num = 3*num+1 else: print(num) def getNum(): global num num = (input("> ")) try: num = int(num) except ValueError: print("plese enter a number") getNum() getNum() collatz(num)
Commenti
- Aggiungi una descrizione che indichi cosa consiglia questo capitolo 3 con esempi di input e output, nessuno ‘ indovinerà di cosa si tratta
- @Edmad Broctor e @ Carcigenicate Grazie per il vostro feedback. Sono andato avanti e ho rivisto il mio post.
- ” Abbastanza sorprendentemente, questa sequenza funziona effettivamente per qualsiasi numero intero: prima o poi, usando questa sequenza, arriverai a 1! ” In realtà non ‘ sappiamo che funziona per qualsiasi numero intero, lidea che raggiungerà sempre 1 è una congettura .
- @PierreCath é Ho una dimostrazione davvero meravigliosa di questa proposizione che questa casella di commento è troppo piccola per contenere.
- @Acccumulation Wow ! ‘ è impressionante, in tal caso dovresti aggiornare larticolo di Wikipedia e contattare lUnione Matematica Internazionale per ritirare la tua medaglia Fields.
Risposta
Innanzitutto, nota come “stai duplicando i calcoli:
print(num//2) num = num //2
Questo potrebbe non causa problemi con questo codice specifico, ma non è “una buona pratica. Stai facendo il doppio del lavoro necessario, il che può causare problemi di prestazioni una volta che inizi a scrivere codice più complicato. Fai il calcolo una volta e salva il risultato. In questo caso, però, tutto ciò che devi fare è invertire quelle righe e utilizza num
:
num = num // 2 print(num)
Inoltre, assicurati di avere una spaziatura corretta attorno agli operatori e sii coerente .
I tuoi if
e elif
i casi si escludono a vicenda e il tuo else
non dovrebbe mai verificarsi. Se la prima condizione è vera, laltra deve essere falsa e viceversa. Non è necessario secondo controllo. Una volta riscritto, vedrai che la stampa in ogni caso non è necessaria. Puoi stampare solo dopo:
while num > 1: if num % 2 == 0: num = num // 2 else: num = 3 * num + 1 print(num)
Dal momento che “stai solo riassegnando num
una delle due opzioni basate su un condizione, unespressione condizionale può essere utilizzata anche qui in modo pulito:
while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) print(num)
Le parentesi graffe non sono necessarie, ma penso che “siano utili qui a causa del numero di operatori coinvolti.
La stampa dei numeri non è lideale qui. Nella maggior parte del codice, devi essere in grado di utilizzare i dati che produci. Se volessi analizzare la sequenza prodotta, dovresti fare qualcosa per intercettare lo stdout, che è costoso ed eccessivamente complicato. Rendila una funzione che accumuli e restituisca un elenco.Negli esempi seguenti, ho anche aggiunto alcuni suggerimenti sul tipo per rendere più chiaro il tipo di dati:
from typing import List def collatz(starting_num: int) -> List[int]: nums = [starting_num] num = starting_num while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) nums.append(num) return nums
Oppure, un approccio molto più pulito è quello di renderlo un generatore che restituisca i numeri :
# Calling this function returns a generator that produces ints # Ignore the two Nones, as they aren"t needed for this kind of generator def collatz_gen(starting_num: int) -> Generator[int, None, None]: yield starting_num num = starting_num while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) yield num >>> list(collatz_gen(5)) [5, 16, 8, 4, 2, 1]
Ci sono” alcune cose importanti su getNum
:
Python usa “snake_case” , non “camelCase”.
Il tuo uso di global num
qui non è necessario e confonde . Proprio come prima, in modo esplicito return
qualsiasi dato prodotto dalla funzione:
def get_num() -> int: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number") return get_num()
Nota come invece di riassegnare un global num
, stiamo solo restituendo il numero. Ho anche distanziato un po le cose e ho usato nomi più appropriati. Concettualmente, direi che num = input("> ")
è sbagliato. Nel momento in cui viene eseguito, num
non contenere un numero (contiene una stringa).
Questo non è un buon uso della ricorsione. probabilmente non ti causerà alcun problema, ma se il tuo utente è davvero stupido e inserisce dati sbagliati ~ 1000 volte, il tuo programma andrà in crash. Usa semplicemente un ciclo:
def get_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
In linguaggi come Python, fai attenzione a usare la ricorsione nei casi in cui non hai garanzie su quante volte la funzione si ripeterà.
I “d anche probabilmente chiamalo qualcosa di più vicino a ask_for_num
. “get” non “rende molto chiaro da dove provengono i dati.
Complessivamente, ti ritroverai con:
from typing import Generator def collatz_gen(starting_num: int) -> Generator[int, None, None]: yield starting_num num = starting_num while num > 1: num = (num // 2) if num % 2 == 0 else (3 * num + 1) yield num def ask_for_num() -> int: while True: raw_num = input("> ") try: return int(raw_num) except ValueError: print("Please enter a number")
Che può essere utilizzato come:
num = ask_for_num() for n in collatz_gen(num): print(n)
Risposta
Prompt
La cattiva pratica più ovvia qui è luso di una variabile globale. Invece di impostare num
come effetto collaterale, la tua funzione dovrebbe return
il risultato.
getNum()
non è un buon nome per la funzione. PEP 8 , la guida di stile ufficiale per Python, afferma che i nomi delle funzioni dovrebbero essere lower_case_with_underscores
. Inoltre, “get” implica che la funzione stia recuperando un pezzo di dati che è già memorizzato da qualche parte, cosa che non è qui. Infine, “Num” dovrebbe essere più specifico.
Luso della ricorsione non è appropriato. Se vuoi un ciclo, scrivi un ciclo.
def ask_integer(): """ Return an integer entered by the user (repeatedly prompting if the input is not a valid integer). """ while True: try: return int(input("> ")) except ValueError: print("Please enter an integer") num = ask_integer()
collatz
funzione
Rigorosamente parlando, non hai seguito le istruzioni. La tua soluzione non è sbagliata o cattiva – semplicemente non hai implementato la funzione collatz
secondo le specifiche fornite, che dice che dovresti stampare e restituire un singolo numero.
def collatz(num): """ Given a number, print and return its successor in the Collatz sequence. """ next = num // 2 if num % 2 == 0 else 3 * num + 1 print(next) return next num = ask_integer() while num > 1: num = collatz(num)
Commenti
- Forse invertire il test ed eliminare il confronto con 0:
next = 3 * num + 1 if num % 2 else num // 2
- @RootTwo Onestamente, trovo che questo offuschi lintento. ‘ ri non verifica se
num % 2
è falso; ‘ non è un predicato. ‘ re controllando se ‘ è uguale a 0. Accade così che ‘ siano equivalenti in Python. -
next = ...
mette in ombra ilnext
. In questo contesto ‘ probabilmente non è pericoloso, ma è solo opportuno esserne consapevoli - Anche se tecnicamente potresti utilizzare
if num % 2 else
, sono daccordo che mostrare lintento qui è più importante. eif num % 2 == 0 else
comunica meglio tale intento. Tuttavia, sono daccordo che dovresti rinominare la tua variabilenext
. ‘ è una cattiva abitudine. - @ 200_success Grazie per aver sottolineato il mio errore nel restituire il numero, non semplicemente stamparlo. È stato molto utile.
Risposta
Per completezza, unimplementazione ricorsiva per collatz
(hai già abbastanza buoni suggerimenti per inserire num
):
def collatz(num): print(num) if num == 1: return num if num % 2 == 0: return collatz(num // 2) return collatz(3 * num + 1) collatz(3)
Output
3 10 5 16 8 4 2 1
Commenti
- Non ‘ t penso che questa risposta aiuti OP. Sebbene una definizione ricorsiva sia certamente possibile, ‘ non si adatta alle specifiche che stanno cercando di soddisfare. Inoltre, questo codice ha una certa ridondanza: la funzione restituisce sempre 1 (quando restituisce affatto), quindi perché anche avere un valore di ritorno?
- @MeesdeVries
the function always returns 1 (when it returns at all), so why even have a return value
‘ non sono sicuro di seguirlo. La ricorsione deve avere un caso base. Accade così che il caso base in questo esempio sia ifnum == 1
. Loutput di esempio mostra che la funzione non restituisce sempre 1. - La funzione stampa altri valori, ma restituisce solo il valore 1.
- Non sono sicuro di quale sia il tuo punto . Il mio commento originale ha notato che non è necessario che la funzione che hai scritto restituisca qualcosa, perché sarà sempre il numero 1, indipendentemente dallinput. Pertanto una migliore implementazione eviterebbe di restituire alcun valore, per evitare il suggerimento che sia significativo. Ad esempio, direi che unimplementazione migliore (della parte di ricorsione) è
if num % 2 == 0: collatz(num//2); elif num > 1: collatz(3 * num + 1)
. - Ti invito a provare il codice che ho scritto per te stesso per vederlo non lo fa (almeno, per un input intero positivo). Se desideri discuterne ulteriormente, ti suggerisco di portarlo in chat.
Lascia un commento