Python

Python

Riprendo la mia rubrica sulla programmazione sul mio amato linguaggio di programmazione Python. Prendete questo articolo come un piccolo tutorial al contrario, ovvero come non programmare in Python; sbatterete la testa contro costrutti illogici, impossibili e controproducenti, ma alla fine ne uscirete rinati in finissimi programmatori.

Non posso. Non ce la faccio. Non posso crederci. Ho riguardato tante volte il codice, ma mi stupisco a ogni passaggio. Sarà stato intenzionale oppure è opera di un troll, un utente che vuole fare scherzi? In ciascun caso si tratterrebbe di estrema malvagità oppure di estrema stupidità. E non riesco a credere né all’una né all’altra. Su ActiveState Code è stato rinvenuto un frammento di codice la cui didascalia recita:

This is a python program that can convert bianry number( binary is a language that computers use to comunicate with other) to a normal digital number and the other way around.

Questo è un programma python che converte i numeri binari (il binario è un linguaggio che i computer usano per comunicare con altro) in un normale numero digitale e il contrario.

La prima domanda sorge spontanea: cosa sono i numeri digitali?

E’ già chiaro che l’autore (o chi impersona tale essere) non ha capito niente: i numeri in base 2 sono i numeri binari, ma i numeri in base 10, normalmente usati dagli esseri umani, si chiamano numeri decimali, non numeri digitali. Questa definizione ha scatenato in me un gusto grottesco di irrisione nei confronti dell’autore, che ho soddisfatto leggendo a fondo tutto il codice che ha scritto. Ho poi tramutato questa sensazione in qualcosa di buono: scrivere una sorta di tutorial su come non scrivere in Python. Un HOW NOT TO. Per godere della seguente lettura è bene conoscere un po’ di programmazione, anche se forse basta solo la curiosità.

Per chi dunque non avesse mai visto un programma Python gli basti sapere questo: i blocchi non sono delimitati da parentesi graffe o altro, ma sono semplicemente indentati, e le funzioni vengono definite con il costrutto def nomefunzione(parametri):.

def mainmenu():
    while True:
        os.system('cls')
        menu = """What du you want to do?

1) Convert digital to binary
2) Convert binary to digital
3) Credits
4) Exit

pick choice [1-4] : """
        choice = raw_input(menu)
        if choice == "1":
            digitalToBinary()
        if choice == "2":
            binaryToDigital()
        if choice == "3":
            credit()
        if choice == "4":
            sys(exit)
mainmenu()

Questa è la prima funzione chiamata, che si mette in un ciclo infinito (while True) in attesa di input dall’utente (raw_input). Non è il massimo, ma siamo ancora nella norma. Un grave errore è invece “os.system(‘cls’)”: questa è una chiamata diretta alla shell di sistema con un comando (cls) tipicamente Windows. Eseguendo questo script su Linux o Os X abbiamo già un grosso problema.

Va bene, diciamo che vogliamo “56″, convertire un numero da decimale a binario.

def binaryToDigital():
    os.system('cls')
    while True:
        print "To go back to the main menu type 'e' and press enter"
        print
        print """Binary numbers consist of only '1's and '0's
        """
        binary = raw_input('Type in the binary number: ')
        if binary == "e":
            mainmenu()
        else:
            binarylist = list(binary)
            no = len(binarylist)
            no = no -1
            diginum = 0
            i = 0
            while no > -1:
                if binarylist[no] == '1':
                    kj = 2**i
                    diginum = diginum + kj
                no = no -1
                i = i +1
            print diginum
            raw_input()

Alla riga 12 prende il numero inserito (che viene passato da raw_input come stringa) e lo trasforma in lista. Errore! Le stringhe sono già iterabili, cioè le posso scorrere e posso indicizzarle direttamente con la sintassi [indice]. Poi legge la lunghezza di questa lista, e successivamente toglie uno. Poi apre un contatore (i) e il numero che poi restituirà. Parliamo della riga 17: non poteva fare direttamente così?

while len(binary) > 0:

Ma andiamo avanti: siamo al ciclo while, quello che scorre le cifre del numero che hai inserito, da sinistra verso destra. E qui c’è il colpo di genio: solo se la cifra è uguale a 1 (r.18), somma a diginum (r.20) 2 elevato alla i (r.19). Ho fatto prima a scriverlo io a parole che lui a scriverlo in python! Se proprio proprio io avrei fatto:

if binarylist[no] == "1":
    diginum += 2**i

Ma tutto il ciclo avrebbe potuto essere:

diginum = sum(2**i for i, c in enumerate(binary.reverse()) if c == "1")

Ovvero: prendi binary (che è una stringa), la giri (reverse), la scorri (enumerate) prendendo l’indice (i) e la cifra (c). Se questa non è “1″, fai due alla i. Questa cosa, dentro a sum, è una espressione che crea (in sostanza) una lista di numeri. Questa viene sommata (sum) e attribuita infine a diginum.

Ora che avete circa capito come funziona python, e come NON usarlo, fatevi un giro sulla recipe originale:

http://code.activestate.com/recipes/496778/

E guardate queste righe (nell’originale 54:57):

                    if bx == '0' or bx == '2' or bx == '4' or bx == '6' or bx == '8':
                        binarynum.append('0')
                    if bx == '1' or bx == '3' or bx == '5' or bx == '7' or bx == '9':
                        binarynum.append('1')

Avete capito bene: è un controllo per vedere se un numero è pari o dispari.  Anzi: sono due controlli. il primo per vedere se è pari. Il secondo per controllare, comunque, che sia dispari.

Se proprio volessi controllare se un carattere fa parte di una lista posso usare l’operatore “in”, e soprattutto mi basterebbe fare un semplice else per il caso contrario:

if bx in ('0','2','4','6','8'):
    binarynum.append('0')
else:
    binarynum.append('0')

Ma poi, che diamine, è questo il modo per controllare se un numero è pari?? Ovviamente no!

if int(bx) % 2 == 0:
    binarynum.append('0')
else:
    binarynum.append('0')

Ora che abbiamo scorso questo codice potete capire come io non possa credere né alla teoria della stupidità né in quella della malvagità; in compenso avrete sicuramente condiviso con me la totale bellezza del python! Infine, vi lascio con una versione un po’ più ristretta dello stesso programma qui sopra:

# da binario a decimale
int("101",2)

oppure

# da decimale a binario
bin(5)