[Python] Qu

Pietro Battiston toobaz a email.it
Dom 14 Feb 2010 11:38:42 CET


Il giorno dom, 14/02/2010 alle 10.39 +0100, Enrico Franchi ha scritto:
> On Feb 13, 2010, at 2:58 PM, Pietro Battiston wrote:
> 
> 
> > 2) argomentare con il fatto che se un programmatore, che è poi l'utente
> > del linguaggio, si trova ad utilizzare oggetti matematici - e.g. numeri
> > - dovranno comportarsi, per quanto possibile, come lui si aspetta.
> 
> Sono d'accordo. Sotto ti faccio vedere come di fatto cose inaspettate
> salterebbero fuori comunque.
> 
> > A volte l'1 pone dei vincoli necessari al 2; ciononostante del Python mi
> > piace che pone molta attenzione al 2 (vedi passare automaticamente da
> > int a long, perché di nuovo, quando come programmatore penso a "int" in
> > Python penso agli interi, non agli interi modulo INT_MAX).
> 
> Come nota a margine, in C non hai sugli interi aritmetica modulare. Hai
> undefined behaviour quando sfori. L'aritmetica modulare la hai sugli
> unsigned (e quindi ocn UINT_MAX). Non che sia importante, solo una
> precisazione.
> 
> >> sono considerate *cattiva* pratica. Proporre di abbandonare la pratica
> >> considerata "buona" di
> >> 
> >> if []: 
> >> 
> >> IMHO non è particolarmente sensato.
> > 
> > 
> > Ma infatti non l'ho proposto, ed è la terza volta che cerco di
> > chiarirlo...
> 
> Su questo punto quindi siamo d'accordo. In contesti di questo tipo 0
> continuerebbe a valutare a False.
> Che poi, per dire, in effetti e' False che valuta a 0. Chi comanda e' 
> il metodo __nonzero__, ma ok.
> 
> >> Quindi spiegami meglio, tu proponi questa semantica.
> >> 
> >> 0 != False -> True
> >> 0 == False -> False
> > 
> > certo
> > 
> >> 
> >> Ora dimmi cosa vuoi fare in questi casi:
> >> 
> >> 10 * False  
> >> 10 + False 
> > 
> > il bool è convertito automaticamente in int ==> rispettivamente 0 e 10
> 
> Eh... poi ci troviamo con questo. Indichiamo con b un booleano, con n,m,...
> un intero e con a qualcosa che puo' essere indifferentemente un booleano
> o un intero. Il nostro obiettivo e' definire le operazioni su Bool U Int, rispettivamente
> l'insieme dei booleani e degli interi. 

... con Bool ⊂ Int ?

> 
> Normalmente ci aspettiamo che:
> 
> n + m - m == n
> 
> siamo d'accordo?

No, è qui il punto, io mi rendo perfettamente conto che questo si
perderebbe, ma non mi interessa, perché ... 


> Lascio perdere le parentesi, poiche' in Python, grazie al
> "salto" a long abbiamo la proprieta' associativa.
> 
> Supponiamo di estendere tale cosa agli "interi o booleani". Ricorda, vogliamo
> che i booleani si possano ancora usare al posto degli interi.
> 
> False + m - m != False

... perché una cosa così non la userei mai e poi mai: anche ammesso che
dei valori di verità si possano _contare_ (sempre perché torna comodo),
non li conto certamente con un booleano: prima che completamente
antipythonico, è estremamente controintuitivo. A parlare in generale si
rischia sempre, ma mi sento comunque di dire che _nessuno_ "conterebbe"
davvero con un booleano: tutt'al più, quel che facciamo è contare _i_
booleani - cosa che si fa benissimo semplicemente con la conversione al
volo di False in 0 e True in 1.


Questo è un motivo per cui quanto scrivi sopra è ben lontano dallo
scandalizzarmi. L'altro è che se veramente tu ritieni che
   n + m - m == n
sia una regola sacrosanta che anche i booleani dovrebbero rispettare,
allora

   In [1]: a = 5
   
   In [2]: False + a - a
   Out[2]: 0

per me è scandaloso. Io vorrei leggere "False". Poi tu mi dici che è la
stessa cosa, ma io sono convinto sia questione di abitudine e non si
finisce più...


> 
> Infatti la prima parte viene convertita ad intera. False + m -> m 
> ed m - m va a 0. Essenzialmente il problema e' che il nostro insieme
> Bool U Int si trova con due elementi neutri. L'algebra ci dice che
> se questi sono distinti non abbiamo piu' un gruppo, ovvero l'insieme
> (Bool U Int, +) non e' un gruppo.


Assolutamente.


> Questo e', in essenza, uno degli assurdi di cui parlavo. Per eliminare una
> cosa sgradevole matematicamente come avere False == 0, perdiamo
> alcune proprieta' fondamentali sulla struttura algebrica delle operazioni.
> 

Pensa che per me invece è assurdo (anche se formalmente impeccabile) che
tu continui a chiamare
   (Bool U Int, +)
una cosa che, finché parliamo matematicamente, è semplicemente
   (Int, +)
dato che per come la vedi tu Bool ⊂ Int...

> Nota... per non dovere definire le operazioni su Bool U Int dobbiamo
> proibire l'aritmetica mista (come dicevo) oppure cambiare le proprita'
> di False e True, che non dovrebbero comportarsi come elementi
> cosi' particolari algebricamente come 0 ed 1.
> 
> Il problema e' che non riusciamo a trovare nessun altro buon valore.
> Non possiamo metterli "in mezzo" fra due interi. Dobbiamo lasciare
> loro un valore tutto particolare e distinto da quello degli interi.
> 
> Matematicamente si fa, voglio dire. A naso direi che ci imbatteremmo
> in qualche altro comportamento contro-intuitivo. Un'altra possibilita'
> e' intendere tutte le operazioni come operazioni su UxT dove U
> e' l'insieme di tutti i valori e T quello di tutti i tipi. Ma accidenti... IMHO
> viene proprio una cosa tosta.
> 
> In questo senso mi sembra che la semplicita' di False e' praticamente
> un altro nome per 0 sia difficile da battere.
> 

Quel che dico io è UxT per T = {bool, altri_numeri}.

Tu mi dici "ma non è giusto, i bool sono numeri come gli altri!".
Io dico di no.
E questa è una differenza di opinione che non ha giustificazioni
formali: a seconda di come sei abituato a concepire il concetto di
booleano, alcune cose ti sembrano più controintuitive di altre.


> 
> >> 0 < False
> >> 0 > False
> >> 0 < True
> >> 0 > True
> >> 
> > 
> > Per come l'ho sostenuta sinora, semplicemente l'ordering tra tipi
> > diversi, ovvero int > bool in python 2.*, eccezione in python 3.* (se
> > non sbaglio). Poi preferisco la prima, ma questo è un altro discorso.
> 
> Anche perche' se lanci eccezione, di fatto non e' vero che hai veramente
> "aritmetica" mista. Alcune operazioni "banali" che fai con gli interi (confronto)
> non puoi farle se uno dei due e' un booleano.
> 
> > Sì. Non che veda niente di proibito in
> > 
> > int < bool
> >    definito come
> > int < int(bool)
> > 
> > , ma non stavo pensavo a quello (che poi potrebbe essere pure più
> > comodo, non so).
> 
> Sarebbe ovviamente l'unica scelta consistente con il "mischiare" interi
> e booleani. Quale e' il problema?
> 
> A noi piace il principio del terzo escluso. Se due elementi non sono 
> uno minore dell'altro (comunque li prendi), allora sono uguali. 
> 
> Eppure tu siccome vuoi che False "valuti" a 0 non puoi prenderlo ne
> minore ne maggiore di 0, ma di fatto non hai nemmeno 0 == False.

Infatti, il "problema" a cui pensavo è proprio questo, per cui per
tenere le cose pulite sarebbe più semplice che l'ordinamento tra Int e
Bool come li penso io rimanesse un ordinamento dei tipi.

Ciò detto, se posti i bool come li vorrei io ed un tale ordinamento
"stupido" poi qualcuno mi dicesse "il fatto che
   False > -1
   False < 1
ma
   False != 0
sembri controintuitivo è un sacrificio che vale la pena fare per poter
confrontare Int con Bool in modo interessante", io gli darei ragione.


> Ovviamente, se per esempio prendi False > 0 poi ti saltano cose tipo
> 
> 10 * False > 2 * False
> 
> che a rigore dovresti avere. Questo perche' False continua a comportarsi
> come 0 e questo dovrebbe riflettersi nei confronti.
> 

certo, una volta che si è deciso di introdurre i bool nell'ordinamento
degli int questo deriva naturalmente

> > Ma ti rendi conto che per nessuno di questi valori in Python si è
> > ritenuto necessario introdurre un nuovo letterale (o una variabile)? Ti
> > sembra veramente un caso?!
> 
> No, tutti questi valori hanno un nuovo letterale! I long li fai aggiungendo
> la L in fondo, i float con il punto. Per il parser sono tutti nuovi letterali.
> In Python 3 finalmente anche True e False sono letterali (o forse sono
> Keywords... boh).


Via, non è la stessa cosa...


In [1]: False + False
Out[1]: 0

In [2]: 0L + 0L
Out[2]: 0L


... e poi è "False", non "0b". Di nuovo, non è una differenza di
rilevanza formale, ma concettuale.


> 
> > 
> > A me sembra un suggerimento, casomai ce ne fosse bisogno, del fatto che
> > il programmatore comune considera i bool prima come valori di verità e
> > solo in seguito come cose che possono "giocare" con/come i numeri.
> > 
> > 
> >> 
> >> ----
> >> [0] fintanto che ci sono i long, ma possiamo estendere il concetto
> >> a razionali e complessi e altri tipi numerici eventualmente definiti in seguito.
> >> 
> >>> Per me puoi chiamare "numero" anche una mela, se ti fa comodo, riesci a
> >>> infilarlo in un certo numero di bit e riesci ad infilarci qualche
> >>> operazione aritmetica.
> >> 
> >> No, davvero, ripeto. Di quanti bit hai bisogno per considerarlo numero? 
> > 
> > No, ho bisogno che sia un numero per considerarlo numero. Vorrei dire
> > "nella realtà", ma ovviamente mi farei ridere dietro: diciamo nella
> > mente del programmatore medio, quello per cui è stato deciso che
> > esistessero False e True, nonostante la loro attuale ridondanza rispetto
> > a 0 e 1.
> 
> Esatto. Sono ridondanti. Semanticamente si considera piu' chiaro scrivere
> False che 0. Posso anche essere d'accordo. Poi per dire, per certe ottimizzazioni,
> in Python 2.x devi usare 1.
> 
> Guarda il codice generato da 
> 
> while 1:
> ...
> 
> e quello da 
> 
> while True:
> ...
> 
> 
> E no, non mi piace. Ma fintanto che si puo' ridefinire True... il giorno che mi
> voglio fare buttare fuori a calci lo faro' scrivendo codice tipo
> 
> In [5]: i = 4
> 
> In [6]: while True:
>    ...:   if i < 0: True = False
>    ...:   else: i -= 1
> 

argh

> > Non mi sembra ci sia _nessun_ altro caso, in qualsiasi tipo numerico in
> > python, in cui si ha
> > 
> > b = a + a
> > type(b) != type(a)
> > a + a == b
> 
> Comunque type(bool) == type(int). Che ovviamente discende dall'essere
> un bool un int. Comunque riprendo... poi stacco che mi sa che il pezzo i
> interessante del messaggio lo ho gia' scritto.
> 
> Credo che la chiave sia accettare che == e' un uguaglianza *semantica*
> in Python, non un'uguaglianza sintattica. In un linguaggio ad oggetti,
> siamo abbastanza liberi di avere == che ci dice ok su oggetti di tipi diversi.
> 
> Io poi confesso che questa parte di programmazione ad oggetti non la amo.
> Ma puo' avere un suo senso, dopotutto.
> 

Tecnicamente, == è certamente un'uguaglianza semantica, ma io sono
convinto - il discorso sarebbe lungo e forse inutile - che lo scopo
prefissato è sempre l'aderenza all'== sintattica che un programmatore ha
in mente.

> > Poi in matematica puoi dire che le ha, ma in un modo tale che, ristrette
> > ai booleani, siano assolutamente stupide (e non per questo sbagliate
> > formalmente). Ossia _non_ le considero operazioni sui booleani.
> 
> Non sono operazioni sui booleani. Come dicevo prima, un'operazione e'
> AxA -> A, qui invece siamo 
> Bool x Bool -> Int
> 
> *MA* sia A = Int U Bool, allora sono operazioni
> AxA -> A
> 
> Allo stesso modo in cui in effetti le operazioni sugli interi, in Python,
> sconfinano nei long. Di fatto sono definite su (IntULong^2)->IntULong
> 

sì, ovvero sugli interi...

> 
> > OK, ammetto che come quoting può essere stato equivoco, ma non è un
> > "trucchetto misero"... io ho tirato in ballo xor e compagnia per fare
> > _notare_ che le operazioni classiche dei bool sono diverse da quelle
> > degli altri numeri. 
> 
> Si, ma per un *informatico* xor e compagnia sono assolutamente operazioni
> definite e definibili sugli interi, comunque. Proprio sfruttando la loro natura
> binaria a basso livello.

Non intendo certo arrogarmi il diritto di definirmi *informatico* (non
lo sono né di formazione né di professione)... quindi i casi sono 2:

1) io lo sono, e nonostante ciò per me xor e compagnia sono operazioni
sui booleani estendibili alla rappresentazione binaria degli interi (io
una mask di gtk la penso sempre come una sequenza di bool, non come un
intero!)

2) io non lo sono, eppure uso python, e molti altri che usano python lo
sono probabilmente ancora meno di me...

> 
> Oggi ci si gioca meno, in univerista' non ci si lavora tantissimo. Forse le usano di
> piu' ad ingegneria e magari non vengono naturali agli altri, ma di per se
> sono tutte cose che ci aspettiamo da una qualunque ALU. Anzi.. conosco
> ALU che hanno xor e non hanno la moltiplicazione generica. Non scherzo. :)
> 
> Roba vecchia, eh.Avevi moltiplicazione solo per due
> (visto che era uno shift) e la moltiplicazione generica era una procedura.
> La scrittura della quale e' lasciata per esercizio a chi voleva scriverci.
> 
> Dalla mia memoria avevo estratto che forse la CPU del game boy aveva questa
> caratteristica, ma wikipedia non conferma e non smentisce, *ma* sembra piu' 
> vicino a smentire.


Io non la vedo affatto come "roba vecchia" in generale, ma semplicemente
come roba vecchia sulla mailing list di un linguaggio ad alto livello.

Davvero, quando dico "abitudine" non intendo offendere nessuno: anch'io
vengo dal C e non lo disprezzo affatto.

Solo, non mi sembra giusto che chi _usa_ (non _implementa_) il Python
debba pensare in C.

(ma ovviamente il punto del dibattito non è questo, perché tu sei
convinto che _non_ sia un problema di abitudine)

> 
> > Mi spiego meglio: ovviamente non stavo dicendo che, data A sottoclasse
> > di B, mi aspetterei
> > 
> > is_instance(B(), A) --> True
> > 
> > , ma che se A non introduce _niente_ di nuovo rispetto a B (e mi sembra
> > questo il caso per i booleani nei confronti degli int - a parte la
> > rappresentazione diversa, ovviamente), tanto vale abolirla...
> > 
> > ... a meno che non siamo tanto affezionati alla rappresentazione proprio
> > perché per noi False e True _non_ sono 0 e 1.
> 
> 1. se False e True fossero solo nomi per 0 e 1 sarei d'accordissimo. 
> 2. la differenza "semantica" la vedi per esempio su __str__ che e' ridefinito per
> stampare False invece di 0
> 3. possibile ci siano altre cose che non mi vengono in mente, essenzialmente.
> 
> Chiaramente se in C(99|++) i bool sono motivati anche dal consumo di spazio,
> questo non vale in Python, che io sappia. Spiace.
> 
> 
> 
> > No, ti dicevo solo che per me in questo caso Ruby è OT perché sul fatto
> > che
> > if [] : print "ciao"   ->   ...
> > 
> > è una bella cosa sono perfettamente d'accordo con te (e se ho capito
> > bene - ma ci avrò scritto 10 righe in vita mia - in Ruby non è così).
> 
> Beh, Ruby propone un modello in cui interi e booleani non sono interoperabili.
> Come avevo scritto 0 + false da eccezione. Era uno dei modelli proposti.
> Io personalmente preferisco quello di Python, ma preferisco quello di Ruby
> a quello che proponi tu (ovvero aritmetica con booleani ma False != 0).
> Il perche' lo ho essenzialmente spiegato sopra.
> 


ciao, taglia pure dove siamo ormai d'accordo

Pietro



Maggiori informazioni sulla lista Python