Introduction to Computer Science Using Python: A Computational Problem-Solving Focus
> Notare la recensione di Guido. Ah, sí, e pure il prezzo a dire il vero.
In USA costa meno. Tipo la meta'.
http://www.amazon.com/Introduction-Computer-Science-Using-Python/dp/0470555157/ref=cm_cr_arp_d_product_top?ie=UTF8e usato non si prende male.
> Qualcuno l'ha letto? Sembra molto molto interessante e stavo giustappunto ponderando la possibilità di vendere un rene (scherzi a parte, deve esserci un errore nel prezzo).
Sinceramente? No. E non intendo leggerlo. Non e' che sia un libro che mi possa essere vagamente utile. E' un peccato, ma fintanto che non si prendono la briga di mandarmeli a casa gratis, non posso stare a comprare ogni libro su Python. 10 anni fa si poteva anche fare, ora sono troppi.
EDIT: okay, sono riuscito a rimediarlo ed effettivamente è fatto apposta per un principiante come me. Analisi del problema, design, implementazione (il primo programma è il calcolo dell'equazione di Drake, 10 punti in più): decisamente interessante. Me lo leggerò dopo aver finito Learning Python.
Questo libro mi incuriosisce. Da quello che leggo sulle recensioni ci sono aspetti che approvo e aspetti che non approvo. Per esempio, approvo moltissimo il concetto di fare problem solving. Non approvo per nulla l'idea di fare, nel 2016, programmazione strutturata.
Onestamente, credo che la programmazione strutturata sia stata una sovra-reazione ad una serie di problemi che erano piuttosto diffusi all'epoca. Adesso e' un insieme di norme piuttosto antistoriche. Diciamo che oggigiorno la considero piu' dannosa che interessante.
1. fin dai fondamenti immagina un linguaggio essenzialmente imperativo. Anzi... il fatto stesso che abbia il concetto di ordinamento negli statement dice che parla di programmazione imperativa. Siccome oggigiorno quasi tutti i linguaggi hanno discreti aspetti di programmazione funzionale, gia' comincio a sentire puzza.
2. di fatto non gestisce uno degli aspetti piu' importanti della programmazione moderna, che e' la struttura ad oggetti. certo... alcuni linguaggi (specie funzionali) hanno un modello piuttosto diverso da quello ad oggetti (che comunque non e' coperto). insomma... in un mondo in cui i singoli metodi sono *gia'* considerati puzzolenti se vanno oltre poche decine di righe, il focus e' chiaramente quello sbagliato.
3. immagina un modello di gestione dell'errore completamente fuori di melone. poteva andare bene per i teorici che scrivono programmi "puri". Purtroppo, noialtri abbiamo un dominio molto piu' complesso e ambiguo. Hai chiamate a servizi esterni (che possono fallire per seicento motivi diversi) e un sacco di roba che ha lo stesso problema. La gestione dell'errore e' una delle parti piu' complicate di un programma moderno. La discussione si e' spostata su altri lidi... l'idea di un paradigma che di fatto proibisce le eccezioni (se non con estensioni ad hoc che spesso sfociano nel ridicolo: l'idea di Jim Bonang e' particolarmente assurda, ma la dice lunga su chi ami la programmazione strutturata) e' completamente assurda. E nota... personalmente non sono un gran fan delle eccezioni castrate comunemente diffuse; preferisco o eccezioni vere alla Lisp (con tutti i problemi e le complicazioni che portano) oppure la gestione degli errori di Go... la quale viola tipicamente il principio di un singolo punto di uscita.
4. Un solo punto di entrata? Ok, ci siamo giocati le coroutine. Brillante.
5. Un solo punto di uscita? Via al balletto delle ridicole flag.
6. concorrenza... qualcuno ha detto concorrenza? ah, si... gli accademici hanno certamente inventato qualche modello arricchito anche per questo.
No grazie. Non faccio programmazione strutturata. Devo scrivere codice leggibile per essere umani, non posso permettermi di attenermi a leggi arcaiche e discutibili. No. Un punto di uscita e un punto di entrata non serve a una beneamata mazza. O meglio, serve solo a quelli che fanno dimostrazioni formali *a mano*. Che se gia' le fai in modo automatico, puoi ampiamente applicare trasformazioni al programma per scriverlo come ti pare e ridurlo nella forma che vuoi.
Quindi non mi dai break e continue? Ma sei completamente pazzo?
Ma guarda, quando ci vuole ci ficco anche un bel goto. Anche perche' tanto i linguaggi "moderni" che ti concedono goto, non ti concedono il goto di Dijkstra. Ti concedono, di fatto, un goto locale. Poi si... in C ho anche setjump e compagnia. Che ci sa che ci sono sa quanto sono complicati da gestire e sa anche che sebbene in determinati contesti siano proprio una manna, nel caso generale hanno una tale complicazione che non ne vale la pena. Insomma... sono strumenti complicati da usare per fare cose complicate. Che vuole dire che la gente non li usa a sproposito.
Supponiamo di volere scrivere una funzione che itera una sequenza e mi risponde se contiene un determinato valore.
Scriviamola in Python.
[codice]
def has_item(seq, item):
for el in seq:
if item == el:
return True
return False
[/codice]
Non credo che ci sia un modo piu' coinciso e leggibile di scriverlo. E' *esattamente* come descriveremmo l'algoritmo a parole. Ha anche efficienza ottima: dobbiamo per forza fare una scansione lineare (per come e' fatto il problema), ma siamo in grado di uscire al primo momento in cui troviamo l'elemento.
Pensiamola ora in programmazione strutturata... abbiamo un po' di opzioni, una piu' demenziale dell'altra.
Opzione 1: lenta ma chiara
[codice]
def has_item(seq, item):
flag = False
for el in seq:
if item == el:
flag = True
return flag
[/codice]
Allora, siamo un po' piu' lunghi e un po' meno chiari. Ma soprattutto sta volta dobbiamo farci tutto quanto il ciclo, anche se troviamo subito l'elemento.
Opzione 2: non pythonica, confusa, ma efficiente (almeno dal punto di vista dei confronti)
[codice]
def has_item(seq, item):
notFound = True
index = 0
while notFound:
el = seq[index]
if el == item:
notFound = False
index += 1
return not notFound
[/codice]
Ora due cose... so gia' che contiene un baco (se la sequenza e' vuota). E non sono piu' convinto che questo codice sia corretto. La prima versione non l'ho nemmeno testata, e al di la dell'imbarazzo se ho ciccato un codice cosi' semplice scrivendolo di getto nel forum... sono abbastanza convinto che sia corretta. Quest'ultima versione... boh. Sembra corretta. Ma devo leggerla tre volte solo per convincermi che quel not notFound sia corretto. Forse sarebbe stato piu' chiaro usare una flag "found". vabbe'... vediamo
[codice]
def has_item(seq, item):
found = False
index = 0
while not found:
el = seq[index]
if el == item:
found = True
index += 1
return found
[/codice]
si, meglio. Piu' chiaro. Ora gestiamo la sequenza nulla... sarebbe bello scrivere if not seq: return False; ma non possiamo. Il MinCulPop ha detto che non si puo'.
Nel frattempo mi sono reso conto di un errore non da poco... scusate. Che non era quello che avevo immaginato.
Ho toppato, non sono perfetto. Non e' una finta, non e' una cosa didattica. Mentre ero consapevole del fatto che la prima versione non gestiva correttamente la sequenza vuota, non mi ero accorto che c'e' un baco grosso come una casa in quella funzione.
E io *onestamente* non lo ho visto subito... me ne sono accorto andando a pensare come risolvere il fatto che la sequenza sia vuota. Il baco e' che ovviamente se l'elemento non c'e', quella funzione esce con un index error. Bene... non va bene.
Oh... scrivo codice da troppo tempo. Ci mangio. Scrivo anche roba abbastanza complicata in genere. Eppure... ho infilato un baco da cretino in una funzione di 10 linee per cercare un elemento in una sequenza. Funzione che ti scrivo ad occhi chiusi nella maggior parte dei casi. Ma mi introduci tutte le complicazioni della programmazione strutturata ed ecco che involontariamente sbaglio.
[codice]
def has_item(seq, item):
found = False
stop = False
index = 0
if not seq:
stop = True
while not found and not stop:
el = seq[index]
if el == item:
found = True
index += 1
if index >= len(seq):
stop = True
return found
[/codice]
Pare funzionare. Al solito... non sono convinto. Non mi fido del mio codice. La probabilita' che abbia un off by one da qualche parte e' altissima. Potrei semplificarlo? Forse, pensandoci ancora.
Cioe' diciamola chiara... se volessi mandare in produzione, mi metterei li, farei tutti i test. Non credo sia utile. Posto quel codice dicendo: *davvero* non so se funziona. Potrebbero esserci bachi.
Quindi facciamo una breve analisi... siamo passati da 4 righe di codice a tipo 15. Siamo passati da un codice che non puo' essere piu' semplice ad un mezzo macello. Non siamo piu' sicuri che il codice faccia la cosa giusta. Abbiamo gia' scoperto che ci ho infilato un baco involontario. Ogni qualvolta qualcuno se lo deve capire, ci mettera' molto piu' tempo che l'istantanea occhiata al mio primo esempio. E tutto questo su una funzione *banale* che davvero non si dovrebbe scazzare e non puo' andare nulla male.
Sembra davvero una buona idea? A me sembra un idea veramente del ca**o.
Scusate... c'e' il terzo sistema. Che e' di fatto costruire un'iterabile su seq che in qualche modo si fermi prima. Ovvero in pratica ci stiamo andando ad infilare nel mondo della parte funzionale di Python... che sebbene in questo caso non violi nessun principio della programmazione strutturata. E viene fuori una roba tipo orribile:
[codice]def has_item(seq, item):
it = itertools.dropwhile(lambda el: el != item, seq)
found = True
try:
next(it)
except StopIteration:
found = False
return found[/codice]
Ancora una volta... dovrebbe essere corretto. Ma lo sai tu? Non davvero. E vallo a leggere. Cioe' l'unica cosa che concludi e' che se scrivo una cosa del genere sono pazzo nel cervello.
Quindi... questo non vuole dire che il libro sia cattivo. E' un libro *opinionato*. Prendi con spirito critico quello che ci leggi. Studialo. Sono sicuro che c'e' da imparare. Ma renditi molto chiaro quella che e' un'opinione dell'autore, e sentiti libero di formarti la tua (dopo esserti documentato sui pro e sui contro).
Boh, io ho dato un'occhiata al pocket reference di Lutz, ma mi è sembrato copiato pari pari dalla documentazione ufficiale. Non ne capisco il senso, francamente; cioè, capisco che a Lutz non dispiaccia avere 15€ in più in tasca, però... sembra fatto per persone che si sono scordate di pagare la bolletta telefonica e che pertanto non possono accedere a internet: un pubblico piuttosto circoscritto.
1. non ho presente affatto quel libro.
2. non e' che a Lutz arrivino 15 euro in tasca. secondo me se gliene arrivano 3 e' tanto.
3. posto che non posso parlare dello specifico libro, o'reilly ha sempre fatto vari pocket reference; qua sul mio tavolo a lavoro ne ho uno su sed&awk, uno su bash e dovrei averne uno su "linux". i primi due li uso spesso: onestamente a seconda di cosa stai "cercando" potrebbe essere che un libro compatto con un indice funzioni meglio (e.g., cercare ${foo,,} non e' esattamente banale). quello su "linux" lo uso meno spesso poiche' alla fine i comandi che uso spesso me li ricordo o ci faccio man, quelli che uso di rado non so come cercarli su quel libro, per cui non e' esattamente utile.