View Full Version : [PYTHON] Stile pythonico ed efficienza del codice
HoldenCaulfield1987
03-02-2013, 11:46
Ciao,
ieri mi sono divertito ad implementare in python un esercizio di un corso di algoritmi.
Il testo dell'esercizio è:
Dato un vettore di n numeri reali, progettare un algoritmo iterativo efficiente che posizioni tutti gli elementi negativi prima di tutti gli elementi positivi.
Ovviamente il vettore è stato sostituito da una lista, ma ho provato ad essere il più rispettoso possibile delle specifiche.
La mia prima soluzione é:
lista = [1, -2, 3, -7, -10]
lista2 = [0 for i in lista]
x = 0
y = len(lista) - 1
for i in lista:
if i < 0:
lista2[x] = i
x += 1
else:
lista2[y] = i
y -= 1
La seconda, e molto più pythonica, è:
lista = [1, -2, 3, -7, -10]
lista2 = [i for i in lista if i < 0]
lista2.extend([i for i in lista if i >= 0])
Ora, mi chiedevo è meglio la prima soluzione dal momento che itero solo una volta sulla lista, oppure è meglio la seconda visto che tutto il codice è più breve più pythonico anche se itero due volte?
Sto sbagliando tutto? :D
Grazie
Vincenzo1968
03-02-2013, 12:28
Volevo provare a fare eseguire le due versioni per 100000 volte e prendere i tempi col comando time su Linux.
time python nonpythonico.py
Ma, il codice mi da errore:
vincenzo@Ubuntu12-10-AMD64:~/Contest19/Python$ time python nonpythonico.py
Traceback (most recent call last):
File "nonpythonico.py", line 13, in <module>
lista2[y] = i
IndexError: list assignment index out of range
Codice:
lista = [1, -2, 3, -7, -10]
lista2 = [0 for i in lista]
x = 0
y = len(lista) - 1
k = 0
while k < 100000:
for i in lista:
if i < 0:
lista2[x] = i
x += 1
else:
lista2[y] = i
y -= 1
x = 0
k += 1
Come lo debbo aggiustare?
Vincenzo1968
03-02-2013, 12:34
Ci sono:
http://img163.imageshack.us/img163/5966/pythonicoenon.jpg
La versione non pythonica è leggermente più veloce. ;)
Versione pythonica:
k = 0
while k < 1000000:
lista = [1, -2, 3, -7, -10]
lista2 = [i for i in lista if i < 0]
lista2.extend([i for i in lista if i >= 0])
k += 1
Versione non pythonica:
lista = [1, -2, 3, -7, -10]
lista2 = [0 for i in lista]
x = 0
y = len(lista) - 1
k = 0
while k < 1000000:
for i in lista:
if i < 0:
lista2[x] = i
x += 1
else:
lista2[y] = i
y -= 1
x = 0
y = 0
k += 1
Vincenzo1968
03-02-2013, 13:20
Anche su Windows la versione non pythonica risulta più veloce:
http://img59.imageshack.us/img59/7635/pythonicoenonwin.jpg
Per misurare i tempi su Windows devi scaricare ptime.exe da qui:
http://www.pc-tools.net/win32/ptime/
ptime python pythonico.py
;)
HoldenCaulfield1987
03-02-2013, 13:31
Beh si me lo aspettavo, vista una singola iterazione contro due.
Mi chiedevo però se in ogni caso è comunque preferibile tenere sempre uno stile più pythonico anche se si perde qualcosina in tempi di esecuzione.
Vincenzo1968
03-02-2013, 13:51
In generale è meglio lo stile pythonico, ma se l'esercizio richiede la versione più efficiente...
Tu presentagliele tutt'e due le versioni al professore e vedi che ti dice(e facci sapere).
;)
HoldenCaulfield1987
03-02-2013, 16:22
No non devo presentarlo al prof.
Ho preso solo spunto da quell'esercizio per fare pratica con python.
Vincenzo1968
03-02-2013, 16:24
Ah, allora in generale è meglio usare lo style non pythonico. Vedi Contest 19 (http://www.hwupgrade.it/forum/showpost.php?p=38952810&postcount=426).
Comunque io ho riscritto il codice cercando di renderlo più "pythonico", e sfruttando quello che offre il linguaggio e la libreria, anche se sono rimasto sorpreso dal risultato del tuo pezzo più "C-esco".
;)
HoldenCaulfield1987
03-02-2013, 16:43
eheheh, il C è sempre il C :cool:
Vincenzo1968
03-02-2013, 17:01
eheheh, il C è sempre il C :cool:
No no, non dicevo questo. Nel contest 19 abbiamo visto che la versione pythonica di cdimauro risuta più lenta della versione C-tonica(c-tonica ma sempre in python ;) ).
Tieni conto anche che, nelle versioni che abbiamo testato qui, quelle che hai scritto tu, su windows il tempo della versione C-tonica è quasi dimezzato rispetto alla versione pythonica.
;)
HoldenCaulfield1987
03-02-2013, 18:15
Ah, interessante!
A questo punto mi chiedo se ci sia davvero convenienza nello scrivere tutto pythonico.
Vincenzo1968
03-02-2013, 18:41
Ah, interessante!
A questo punto mi chiedo se ci sia davvero convenienza nello scrivere tutto pythonico.
Secondo me non c'è convenienza. Io addirittura passerei a Ruby, che nell'ultima versione, la 1.9, è risultato più prestante(e su Windows molto più prestante) di python, almeno per quanto riguarda l'input-output su file.
;)
HoldenCaulfield1987
03-02-2013, 18:43
Se ti sentisse cdimauro....:D :D :D
Vincenzo1968
03-02-2013, 18:45
Se ti sentisse cdimauro....:D :D :D
Lascia pure che mi senta. Io esprimo le mie opinioni, mica verità assolute. Appena s'arricampa sentiamo pure la sua di opinione.
E la sua opinione equivale alla mia come a quella di chiunque altro. Non è mica il figlio della gallina bianca.
;)
HoldenCaulfield1987
03-02-2013, 18:52
Si certo, ma non volevo dire che la tua opinione non conta o vale meno della sua ;)
Che poi, va a sapere perchè è più lento codice pythonico che C-esco...
Vincenzo1968
03-02-2013, 18:56
Che poi, va a sapere perchè è più lento codice pythonico che C-esco...
Nel caso particolare di cui ci siamo occupati nel contest 19, il motivo è dovuto alla lettura/scrittura di blocchi da 4096 byte, come spiegato nei testi sui DBMS.
;)
Nel caso particolare di cui ci siamo occupati nel contest 19, il motivo è dovuto alla lettura/scrittura di blocchi da 4096 byte, come spiegato nei testi sui DBMS.
;)
No, dico in generale... Li ho seguito!
Ma quello che non capisco è perchè scrivere codice python in stile python sembra generi codice meno efficiente rispetto a scrivere codice python in stile C
HoldenCaulfield1987
03-02-2013, 19:15
Tempi:
time python3 pythonico.py
real 0m0.039s
user 0m0.028s
sys 0m0.008s
time python3 non_pythonico.py
real 0m0.035s
user 0m0.028s
sys 0m0.004s
-------------
time python3 pythonico.py
real 0m0.035s
user 0m0.028s
sys 0m0.004s
time python3 non_pythonico.py
real 0m0.038s
user 0m0.032s
sys 0m0.004s
--------------
time python3 pythonico.py
real 0m0.038s
user 0m0.028s
sys 0m0.008s
time python3 non_pythonico.py
real 0m0.035s
user 0m0.024s
sys 0m0.008s
È anche vero che li si tratta di spiccioli eh..
Vincenzo1968
03-02-2013, 21:00
Su Windows 8:
http://img51.imageshack.us/img51/3079/pythnonpyth.jpg
Vincenzo1968
03-02-2013, 21:01
È anche vero che li si tratta di spiccioli eh..
Certamente non è questo il caso di ottimizzare. Qui è possibile utilizzare tranquillamente la versione pythonica. Qui, ma non in generale. ;)
HoldenCaulfield1987
03-02-2013, 21:13
Però la versione pythonica di Vincenzo non è proprio uguale uguale all'altra.
All'interno del ciclo c'è sempre la creazione della lista di partenza, cosa che non accade nella versione non pythonica.
Se modifichiamo leggermente il codice di pythonico.py levando dal ciclo la dichiarazione di lista otteniamo dei tempi molto diversi:
pythonico.py
real 0m1.434s
user 0m1.420s
sys 0m0.012s
real 0m1.384s
user 0m1.368s
sys 0m0.012s
real 0m1.389s
user 0m1.372s
sys 0m0.012s
non_pythonico.py
real 0m1.740s
user 0m1.732s
sys 0m0.004s
real 0m1.751s
user 0m1.732s
sys 0m0.012s
real 0m1.736s
user 0m1.728s
sys 0m0.004s
k = 0
lista = [1, -2, 3, -7, -10]
while k < 1000000:
lista2 = [i for i in lista if i < 0]
lista2.extend([i for i in lista if i >= 0])
k += 1
Vincenzo1968
03-02-2013, 21:46
Però la versione pythonica di Vincenzo non è proprio uguale uguale all'altra.
...
Nonsi! Quella è la tua versione. Io ho solo aggiunto i cicli while a entrambe le tue versioni per provare i tempi ciclando un milione di volte.
;)
Vincenzo1968
03-02-2013, 22:04
E comunque, su Windows 8, la tua nuova versione pythonica, continua a essere meno veloce rispetto alla tua versione non pythonica:
http://img577.imageshack.us/img577/2481/pythnew.jpg
HoldenCaulfield1987
03-02-2013, 22:18
Io ho solo preso il codice che avevi postato e spostato la dichiarazione della lista, niente di più.
Non prenderlo come un attacco...non lo è!
Comunque a me su linux da tempi diversi.
Vincenzo1968
03-02-2013, 22:24
Questa è la mia versione, in Ruby:
lista = [1, -2, 3, -7, -10]
lista2 = [0, 0, 0, 0, 0]
x = 0
y = 5
k = 0
while k < 1000000
for i in lista do
if i < 0 then
lista2[x] = i
x += 1
else
lista2[y] = i
y -= 1
end
end
x = 0
y = 0
k += 1
end
http://img824.imageshack.us/img824/1361/rubyonica2.jpg
Vincenzo1968
03-02-2013, 22:30
Io ho solo preso il codice che avevi postato e spostato la dichiarazione della lista, niente di più.
Non prenderlo come un attacco...non lo è!
Comunque a me su linux da tempi diversi.
No no, quale attacco! Sempre meglio precisare però. Io ho postato il tuo codice nel quale ho semplicemente aggiunto a entrambe le versioni i cicli while. Tutto qui. ;)
Vedi che Ruby risulta più veloce?
Sono già due casi: qui e nel contest 19.
Una rondine non fa primavera, e nemmeno due, però...
:D
Domani prendo i tempi su Linux(ché la sera mi debbo arrangiare con Windows 8 sul portatilino) ;)
Vedi che Ruby risulta più veloce?
module Main where
reorder :: [Int] -> [Int]
reorder [] = []
reorder (x:xs)
| x < 0 = x : (reorder xs)
| otherwise = (reorder xs) ++ [x]
main :: IO ()
main = print $ reorder [1, -2, 3, -7, -10]
Più compatto e comprensibile (a patto ovviamente di conoscere il linguaggio) di Python e Ruby e...
nico159@nico159-300E4A-300E5A-300E7A:~/Scrivania/eclipse/k/TestingHaskell/src$ time ./Main
[-2,-7,-10,3,1]
real 0m0.004s
user 0m0.000s
sys 0m0.000s
Un compilatore che fa il suo dovere :cool:
Vincenzo1968
04-02-2013, 11:01
module Main where
reorder :: [Int] -> [Int]
reorder [] = []
reorder (x:xs)
| x < 0 = x : (reorder xs)
| otherwise = (reorder xs) ++ [x]
main :: IO ()
main = print $ reorder [1, -2, 3, -7, -10]
Più compatto e comprensibile (a patto ovviamente di conoscere il linguaggio) di Python e Ruby e...
nico159@nico159-300E4A-300E5A-300E7A:~/Scrivania/eclipse/k/TestingHaskell/src$ time ./Main
[-2,-7,-10,3,1]
real 0m0.004s
user 0m0.000s
sys 0m0.000s
Un compilatore che fa il suo dovere :cool:
Il mio compilatore, invece, fa i capricci col tuo codice:
vincenzo@Ubuntu12-10-AMD64:~/RubyPython$ python Nico.py
File "Nico.py", line 1
module Main where
^
SyntaxError: invalid syntax
Ho la versione 2.7.3 ;)
Non è Python, è Haskell
sudo apt-get install ghc
ghc Main.hs -O2
Era una provocazione, come un linguaggio statico potesse risultare più compatto, veloce e leggibile di uno dinamico
Vincenzo1968
04-02-2013, 11:05
Che poi non ho capito 'sta cosa delle versioni. Perché ne mantengono 2, la 2.7 e la 3.3?
Why? :confused:
Vincenzo1968
04-02-2013, 11:06
Non è Python, è Haskell
sudo apt-get install ghc
ghc Main.hs -O2
Minnale, ragazzi, ditelo prima che sennò mi si fonde il cervello(più di quanto non sia già fuso). :D
Che poi non ho capito 'sta cosa delle versioni. Perché ne mantengono 2, la 2.7 e la 3.3?
Why? :confused:
http://docs.python.org/3/whatsnew/3.0.html
Vincenzo1968
04-02-2013, 11:13
Nico, dovresti racchiudere il tuo codice all'interno di un ciclo che itera per un milione di volte, così confrontiamo i tempi con le versioni Ruby/Python.
Intanto sto installando Haskell.
;)
EDIT: Fatto: "The Glorious Glasgow Haskell Compilation System, version 7.4.2"
:yeah: :winner: :yeah:
Rispondendo in ordine sparso :D (dopo un bel ciao perché manco da tanto)
Il mio compilatore, invece, fa i capricci col tuo codice
È Haskell ;)
La versione non pythonica è leggermente più veloce.
Credo che il benchmark sia mooolto "dolce" nel suo iterare 1000000 su una lista così piccola; probabilmente usando liste di grandi dimensioni generate casualmente la versione "non-pythonica" sarebbe ancora meno prestante.
Ora, mi chiedevo è meglio la prima soluzione dal momento che itero solo una volta sulla lista, oppure è meglio la seconda visto che tutto il codice è più breve più pythonico anche se itero due volte?
Io sono per la versione pythonica. Personalmente lavoro poco in Python ma molto in Ruby e il codice che fa uso massiccio di iteratori (built in e user defined) è gradevole e semplice da capire, e solo raramente è davvero più lento.
Dal benchmark di Vincenzo è evidente che per casi di liste relativamente piccole, la differenza nel tempo di esecuzione è ininfluente (se fosse stata di un fattore 10, ne potevamo riparlare).
Un programma che lavora su una lista iterandola una volta sola e svolgendo più operazioni è quasi sempre più performante, e spesso è una buona riscrittura per un pezzo di codice che usa troppi iteratori, ma è anche meno leggibile al primo sguardo.
Di contro, la tua versione pythonica "dice" tutto al primo sguardo: è persino più chiara del testo! Quando sai in partenza di lavorare con liste piccole, scrivere così è un ottimo compromesso che ti permette, nella pratica, di comprendere e modificare quel codice anche a distanza di tempo senza problemi, oltre ad essere perfettamente comprensibile per un lettore esterno che non sa che algoritmo stai implementando e non ha il testo a disposizione (questo non pesa ovviamente per questo esercizio semplice, ma può diventare, e in effetti lo diventa, un fattore per algoritmi più complessi).
Cioè pazzesco, perchè diamine hanno cambiato il print?? Perchè???? :muro:
Era una delle cose che mi piaceva di più..
Vincenzo1968
04-02-2013, 11:22
Ciao Albi, bentornato ;)
Questi benchmarck che stiamo facendo qui non sono molto significativi, lo so. Ma tieni conto che su Windows i tempi della versione non pythonica sono quasi dimezzati rispetto alla versione pythonica. Su Linux, invece, la versione non pythonica è solo di poco più veloce rispetto alla versione pythonica.
Sembra che Linux ottimizzi meglio il codice python. E questa è un'altra brutta notizia per cdimauro che ha in antipatia Linux, Stallman, Mac, etc. :D
Vincenzo1968
04-02-2013, 11:32
Ruby mon amour!
http://img341.imageshack.us/img341/2982/rubypythonnew.jpg
:D
Vincenzo1968
04-02-2013, 11:53
Non è Python, è Haskell
sudo apt-get install ghc
ghc Main.hs -O2
Era una provocazione, come un linguaggio statico potesse risultare più compatto, veloce e leggibile di uno dinamico
vincenzo@Ubuntu12-10-AMD64:~/RubyPython$ ghc Nico.hs -O2
Linking Nico ...
/usr/bin/ld: --hash-size=31: opzione sconosciuta
/usr/bin/ld: usare l'opzione --help per le informazioni sull'uso
collect2: error: ld returned 1 exit status
vincenzo@Ubuntu12-10-AMD64:~/RubyPython$ ghc Nico.hs
Linking Nico ...
/usr/bin/ld: --hash-size=31: opzione sconosciuta
/usr/bin/ld: usare l'opzione --help per le informazioni sull'uso
collect2: error: ld returned 1 exit status
:confused:
vincenzo@Ubuntu12-10-AMD64:~/RubyPython$ ghc Nico.hs -O2
Linking Nico ...
/usr/bin/ld: --hash-size=31: opzione sconosciuta
/usr/bin/ld: usare l'opzione --help per le informazioni sull'uso
collect2: error: ld returned 1 exit status
vincenzo@Ubuntu12-10-AMD64:~/RubyPython$ ghc Nico.hs
Linking Nico ...
/usr/bin/ld: --hash-size=31: opzione sconosciuta
/usr/bin/ld: usare l'opzione --help per le informazioni sull'uso
collect2: error: ld returned 1 exit status
:confused:
Che ti risponde ld --version
Dovrebbe essere:
ld di GNU (GNU Binutils for Ubuntu) 2.22.90.20120924
Vincenzo1968
04-02-2013, 12:30
È carinissimo il codice Haskell:
http://img35.imageshack.us/img35/5421/haskellnice.jpg
:D
Gliel'ho sempre detto a Vicius ma non vuol darmi retta :O
http://www.hwupgrade.org/public/style_emoticons/default/coolface.png
Haskell, lista da 10000 interi (più loro stamp su console)
real 0m0.928s
user 0m0.920s
sys 0m0.004s
https://gist.github.com/raw/4706492/dd4f999ce17d8a88f82ee86d810a333aae4bd45f/gistfile1.txt
Vincenzo1968
04-02-2013, 12:31
Che ti risponde ld --version
Dovrebbe essere:
ld di GNU (GNU Binutils for Ubuntu) 2.22.90.20120924
vincenzo@Ubuntu12-10-AMD64:~/RubyPython$ ld --version
GNU gold (GNU Binutils for Ubuntu 2.22.90.20120924) 1.11
Copyright 2011 Free Software Foundation, Inc.
Questo programma è software libero; siete liberi di ridistribuirlo secondo i termini della
GNU General Public License versione 3 o (a scelta) una versione più recente.
Questo programma non ha assolutamente alcuna garanzia.
:confused:
Ruby mon amour!
http://img341.imageshack.us/img341/2982/rubypythonnew.jpg
:D
Tieni conto però che quello che hai scritto è codice Ruby assolutamente non idiomatico, e se da una parte la dice lunga sul lavoro fantastico fatto con YARV (immagino che stai usando una versione 1.9, che in certi casi è davvero veloce), è anche vero che arrivati a questo punto tanto valeva scriverlo direttamente in C.
Ad esempio in Ruby idiomatico non si itera quasi mai con for .. in .., a meno che non si vogliano lasciare definite le variabili che hai usato (inclusa la variabile del loop).
Una versione idiomatica che concatena due sottoparti sulla base di una condizione potrebbe essere:
list = [1, -2, 3, -7, -10]
list.partition {|e| e < 0}.reduce(:concat)
e stiamo sempre facendo finta di dimenticarci che un semplice list.sort sarebbe stato altrettanto risolutivo ^^"
Lavorando con liste di 10 o 20 numeri resta il pregio di leggere in una sola riga "partiziono sulla base di questa condizione, e poi concateno le partizioni" e di sapere "a occhio" che il codice è corretto, e che qualsiasi programmatore con una conoscenza della piattaforma Ruby e degli Enumerable può capire anche se non capisce il tuo algoritmo (essì, magari è un collega tonto, capita).
Per questo stresso il valore del codice idiomatico, non necessariamente pythonico, visto che anche Ruby forza un certo stile, per non parlare dell'esempio in Haskell che pur semplice fa bella mostra di pattern matching e guards, oltre all'eredità Lisp "del nonno" :D
Vincenzo1968
04-02-2013, 12:36
Eh Albi, tieni conto che Ruby lo uso da tre/quattro giorni. Vengo da vent'anni di C.
:D
Vincenzo1968
04-02-2013, 12:42
Raga', sarebbe interessante vedere le soluzioni in Python, Ruby e Haskell per il contest 19(kwb, hai provato a risolvere il problema con python 3.3?).
Se non avete tempo per il punto B.1, fornite almeno le soluzioni per i punti B.2 e B.3. Io me la sono cavata con poche righe di codice C. Figuriamoci quindi in haskell, python e ruby.
Go!
:D
EDIT: Se proprio siete superimpegnati, almeno il punto B.2. Go go go!
Vincenzo1968
04-02-2013, 12:53
Tieni conto però che quello che hai scritto è codice Ruby assolutamente non idiomatico, e se da una parte la dice lunga sul lavoro fantastico fatto con YARV (immagino che stai usando una versione 1.9, che in certi casi è davvero veloce), è anche vero che arrivati a questo punto tanto valeva scriverlo direttamente in C.
Ad esempio in Ruby idiomatico non si itera quasi mai con for .. in .., a meno che non si vogliano lasciare definite le variabili che hai usato (inclusa la variabile del loop).
Una versione idiomatica che concatena due sottoparti sulla base di una condizione potrebbe essere:
list = [1, -2, 3, -7, -10]
list.partition {|e| e < 0}.reduce(:concat)
e stiamo sempre facendo finta di dimenticarci che un semplice list.sort sarebbe stato altrettanto risolutivo ^^"
Lavorando con liste di 10 o 20 numeri resta il pregio di leggere in una sola riga "partiziono sulla base di questa condizione, e poi concateno le partizioni" e di sapere "a occhio" che il codice è corretto, e che qualsiasi programmatore con una conoscenza della piattaforma Ruby e degli Enumerable può capire anche se non capisce il tuo algoritmo (essì, magari è un collega tonto, capita).
Per questo stresso il valore del codice idiomatico, non necessariamente pythonico, visto che anche Ruby forza un certo stile, per non parlare dell'esempio in Haskell che pur semplice fa bella mostra di pattern matching e guards, oltre all'eredità Lisp "del nonno" :D
Si si:
vincenzo@Ubuntu12-10-AMD64:~/RubyPython$ ruby --version
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
vincenzo@Ubuntu12-10-AMD64:~/RubyPython$ python --version
Python 2.7.3
Leggevo che, passando dalla 1.8 alla 1.9, gli sviluppatory Ruby hanno migliorato do molto le prestazioni. Adesso il codice risulta due volte più veloce. http://www.hwupgrade.it/forum/images_hwu/smilies/icon_mrgreen.gif
vincenzo@Ubuntu12-10-AMD64:~/RubyPython$ ld --version
GNU gold (GNU Binutils for Ubuntu 2.22.90.20120924) 1.11
Copyright 2011 Free Software Foundation, Inc.
Questo programma è software libero; siete liberi di ridistribuirlo secondo i termini della
GNU General Public License versione 3 o (a scelta) una versione più recente.
Questo programma non ha assolutamente alcuna garanzia.
:confused:
Strano che usi gold come linker, che versione di Ubuntu hai?
Devi tornare al linker classico di binutils altrimenti GHC 7.4 non va
Raga', sarebbe interessante vedere le soluzioni in Python, Ruby e Haskell per il contest 19(kwb, hai provato a risolvere il problema con python 3.3?).
Sono semi riuscito a venire a capo alla situazione.
Mi faccio sentire di la
Vincenzo1968
04-02-2013, 12:58
Strano che usi gold come linker, che versione di Ubuntu hai?
Devi tornare al linker classico di binutils altrimenti GHC 7.4 non va
:confused:
Boh! Sono niubbo su Linux. Mi sono trovato con questa versione e questa uso. Non è che tentando d'installare il link classico poi mi s'incasina gcc e non mi compila più un accidente?
:confused:
Vincenzo1968
04-02-2013, 12:59
Sono semi riuscito a venire a capo alla situazione.
Mi faccio sentire di la
Ottimo! ;) Eventualmente cerca di mettare in atto i suggerimenti di cdimauro su unicode. Oppure fregatene di unicode: togli dai file di input "descartes_discorso_sul_metodo.txt" che è quello cha da problemi. ;)
:confused:
Boh! Sono niubbo su Linux. Mi sono trovato con questa versione e questa uso. Non è che tentando d'installare il link classico poi mi s'incasina gcc e non mi compila più un accidente?
:confused:
No, avrai più problemi a tenere gold :D
Vincenzo1968
04-02-2013, 13:04
No, avrai più problemi a tenere gold :D
Ah ok! Ma che è 'sto gold? :confused:
:D
EDIT: ho Ubuntu 12.10 a 64 bit. ;)
Vincenzo1968
04-02-2013, 13:14
http://www.hwupgrade.it/forum/showpost.php?p=38973712&postcount=529
http://www.hwupgrade.org/public/style_emoticons/default/coolface.png
Vincenzo1968
04-02-2013, 13:28
Ragazzi la cosa bella di Linux(tra le tante) sapete qual è? Che ti lascia libertà di scelta.
Ho installato Linux nel vecchio computer dove ho due dischi fissi. In uno c'ho Windows XP; nell'altro disco ho installato Ubunto 12.10 a 64 bit.
Ebbene, il programma d'installazione di Ubuntu s'è accorto da solo che avevo due dischi e m'ha installato automaticamente il dual boot.
Adesso, quando avvio la macchina, mi spunta una schermata che mi chiede se voglio avviare Ubuntu o Windows.
Non è meraviglioso? :D
Adesso mi installo Ubuntu anche nella macchina nuova. Ho Windows 8. Creerò(anzi il programma di installazione di Ubuntu creerà per me, al posto mio :D ) una partizione per Ubuntu. E li mi installo la versione classica(non gold) delle BinUtils.
;)
cdimauro
04-02-2013, 22:52
Ho letto tutto, ma sono rientrato da un viaggio e sono a pezzi, per cui rispondo soltanto a questo per adesso.
Ciao,
ieri mi sono divertito ad implementare in python un esercizio di un corso di algoritmi.
Il testo dell'esercizio è:
Ovviamente il vettore è stato sostituito da una lista, ma ho provato ad essere il più rispettoso possibile delle specifiche.
Normalmente in Python si usa la lista, che è molto simile a un vettore, ma eventualmente è presente anche il tipo array, che si trova nel modulo array, e che consente di gestire vettori di elementi omogenei dei tipi "base" che si trovano in altri linguaggi, che ovviamente è più efficiente rispetto alla lista, che però risulta un "contenitore" più generale e flessibile.
Comunque non è questo il caso, ma ti spiegherò dopo il perché.
La mia prima soluzione é:
lista = [1, -2, 3, -7, -10]
lista2 = [0 for i in lista]
x = 0
y = len(lista) - 1
for i in lista:
if i < 0:
lista2[x] = i
x += 1
else:
lista2[y] = i
y -= 1
La seconda, e molto più pythonica, è:
lista = [1, -2, 3, -7, -10]
lista2 = [i for i in lista if i < 0]
lista2.extend([i for i in lista if i >= 0])
Ora, mi chiedevo è meglio la prima soluzione dal momento che itero solo una volta sulla lista, oppure è meglio la seconda visto che tutto il codice è più breve più pythonico anche se itero due volte?
Sto sbagliando tutto? :D
Grazie
A mio avviso sì, perché non hai soddisfatto i requisiti dell'esercizio. Dal testo evinco che il vettore debba essere, alla fine dell'esecuzione, costituito da tutti gli elementi negativi nella sua prima parte, e da tutti gli altri (non solo positivi: c'è anche lo zero da considerare, che non è né negativo né positivo) nella seconda.
Entrambi i codici che hai fornito non ottengono il risultato desiderato.
Ciò detto, rimane da chiarire il significato di "efficiente". In Algoritmi sappiamo e ci interessiamo delle misure asintotiche di spazio e tempo per un determinato algoritmo. In misura minore della complessità dell'implementazione (vedi, ad esempio, gli algoritmi di Kruskal e Prim: il primo è meno efficiente, ma spesso viene preferito al secondo perché ha un'implementazione molto più semplice).
Per cui, parlando di efficienza, devi intanto analizzare le soluzioni dal punto di vista asintotico per spazio e tempo. Nello specifico, essendo l'algoritmo molto semplice, val la pena anche stimare in qualche modo il valore di K. Dovresti sapere che, se hai trovato che un algoritmo è lineare, ma richiede 3 "passate", il tempo stimato è di 3*N, che in forma asintotica diventa O(N), perdendo il valore della costante. Nello specifico, dicevo, puoi prendere in considerazione di scovare quanto vale K, perché l'esercizio è molto semplice.
Riguardo al "pythonico" / "non pythonico", direi che la questione è semplice: dovresti cercare di usare gli strumenti che ti offre il linguaggio, per quanto possibile, senza quindi stravolgere l'obiettivo dell'esercizio, che è quello di trovare un'implementazione "passo passo" dell'algoritmo. Detto in altri termini, se un esercizio ti chiede di ordinare un vettore, non è che, usando Python, tu possa usare il metodo sort, e lavartene le mani. L'algoritmo va implementato nei suoi elementi caratteristici.
Poi se il linguaggio ti consente di sfruttare degli strumenti che aiutano nell'espressione del codice, dovresti farlo, a mio avviso, perché qui parliamo più che altro di una questione stilistica / estetica, e se hai deciso di usare Python, allora non mi pare il caso di scriverne il codice come se stessi lavorando con C o Java.
Infine, tornando all'obiettivo del corso, i professori non usano i comandi time o ptime. Pretendono che tu gli fornisca il codice (in genere pseudocodice, ma ormai ha preso piede di usare un linguaggio "didattico": Pascal, C, Java, C#, Python) e le formule asintotiche sullo spazio occupato, e il tempo richiesto. Nient'altro.
Dimenticavo: bisogna anche vedere se il vettore debba essere ordinati "in-place", oppure se ne possa generare uno nuovo col risultato, e poi copiarne il contenuto su quello di partenza.
HoldenCaulfield1987
04-02-2013, 23:30
Il codice in questione non è per un corso, ma ho solo preso spunto dal testo per scrivere qualcosa in py, quindi i costi in termini asintotici e un ordinamento in loco o meno non è proprio un aspetto che ho considerato.
Non sapevo esistesse anche un vettore tradizionale.
Lo andrò a studiare.
Grazie a tutti!
cdimauro
05-02-2013, 09:07
No no, non dicevo questo. Nel contest 19 abbiamo visto che la versione pythonica di cdimauro risuta più lenta della versione C-tonica(c-tonica ma sempre in python ;) ).
Ma non mi pare proprio. Hai "dimenticato" di postare questo (http://www.hwupgrade.it/forum/showpost.php?p=38950196&postcount=407), dove la mia seconda versione è leggermente migliore della tua, e in particolare questa (http://www.hwupgrade.it/forum/showpost.php?p=38952775&postcount=424), dove tutte le mie versioni hanno risultati migliori della tua.
Su Linux, invece, vale quanto hai scritto prima.
Però la versione pythonica di Vincenzo non è proprio uguale uguale all'altra.
All'interno del ciclo c'è sempre la creazione della lista di partenza, cosa che non accade nella versione non pythonica.
Esattamente. Se ogni volta la lista di partenza viene ricreata, mi pare logico che il tempo d'esecuzione aumenti. Tranne se usi la mia versione di Python, WPython, perché quella lista non viene ricreata completamente da zero, mentre se fosse una tupla non verrebbe mai ricreata. :cool:
E comunque, su Windows 8, la tua nuova versione pythonica, continua a essere meno veloce rispetto alla tua versione non pythonica:
http://img577.imageshack.us/img577/2481/pythnew.jpg
Già, ma credo ci sia qualche problema. Non è normale che ricreare continuamente la lista iniziale, che ha un costo non indifferente (visto quante iterazioni ci sono), risulti più veloce della versione che quella identica lista la crei una sola volta, prima del ciclo.
No no, quale attacco! Sempre meglio precisare però. Io ho postato il tuo codice nel quale ho semplicemente aggiunto a entrambe le versioni i cicli while. Tutto qui. ;)
Vedi che Ruby risulta più veloce?
Sono già due casi: qui e nel contest 19.
Una rondine non fa primavera, e nemmeno due, però...
:D
Domani prendo i tempi su Linux(ché la sera mi debbo arrangiare con Windows 8 sul portatilino) ;)
Ne abbiamo già discusso qui (http://www.hwupgrade.it/forum/showpost.php?p=38956107&postcount=440).
module Main where
reorder :: [Int] -> [Int]
reorder [] = []
reorder (x:xs)
| x < 0 = x : (reorder xs)
| otherwise = (reorder xs) ++ [x]
main :: IO ()
main = print $ reorder [1, -2, 3, -7, -10]
Più compatto e comprensibile (a patto ovviamente di conoscere il linguaggio) di Python e Ruby e...
Non mi pare sia più compatto e più comprensibile. Certamente bisogna conoscere la sintassi del linguaggio per comprendere quel codice, ma di sicuro non puoi dire che sia più compatto. ;)
Credo che il benchmark sia mooolto "dolce" nel suo iterare 1000000 su una lista così piccola; probabilmente usando liste di grandi dimensioni generate casualmente la versione "non-pythonica" sarebbe ancora meno prestante.
E' possibile, infatti.
Io sono per la versione pythonica. Personalmente lavoro poco in Python ma molto in Ruby e il codice che fa uso massiccio di iteratori (built in e user defined) è gradevole e semplice da capire, e solo raramente è davvero più lento.
Assolutamente d'accordo. Gli iteratori sono nati apposta per evitare di creare sempre nuove liste, consumando memoria e CPU.
Dal benchmark di Vincenzo è evidente che per casi di liste relativamente piccole, la differenza nel tempo di esecuzione è ininfluente (se fosse stata di un fattore 10, ne potevamo riparlare).
Un programma che lavora su una lista iterandola una volta sola e svolgendo più operazioni è quasi sempre più performante, e spesso è una buona riscrittura per un pezzo di codice che usa troppi iteratori, ma è anche meno leggibile al primo sguardo.
Per questo non ne faccio un abuso: quando posso, scelgo di usare gli iteratori, altrimenti le liste.
Fra prestazioni e leggibilità, prediligo quest'ultima.
Di contro, la tua versione pythonica "dice" tutto al primo sguardo: è persino più chiara del testo! Quando sai in partenza di lavorare con liste piccole, scrivere così è un ottimo compromesso che ti permette, nella pratica, di comprendere e modificare quel codice anche a distanza di tempo senza problemi, oltre ad essere perfettamente comprensibile per un lettore esterno che non sa che algoritmo stai implementando e non ha il testo a disposizione (questo non pesa ovviamente per questo esercizio semplice, ma può diventare, e in effetti lo diventa, un fattore per algoritmi più complessi).
Ancora una volta, sono assolutamente d'accordo. Anche per questo Python è divenuto il mio strumento preferito.
Cioè pazzesco, perchè diamine hanno cambiato il print?? Perchè???? :muro:
Era una delle cose che mi piaceva di più..
Perché era un'eccezione rispetto al resto del linguaggio. Anche a me inizialmente non piaceva, ma alla fine sono un paio di parentesi da aggiungere, mentre la nuova funzione ha delle interessanti e utili caratteristiche.
Ciao Albi, bentornato ;)
Questi benchmarck che stiamo facendo qui non sono molto significativi, lo so. Ma tieni conto che su Windows i tempi della versione non pythonica sono quasi dimezzati rispetto alla versione pythonica. Su Linux, invece, la versione non pythonica è solo di poco più veloce rispetto alla versione pythonica.
Sembra che Linux ottimizzi meglio il codice python.
Detta così suona molto strano: non è che il kernel si metta a manipolare la virtual machine per farla funzionare meglio. :D
E questa è un'altra brutta notizia per cdimauro che ha in antipatia Linux, Stallman, Mac, etc. :D
Guarda che un Mac ce l'ho, anche se al momento è fuori sede. :O
Poi a me sta antipatico principalmente il fanatismo. Uno come Stallman si farebbe disprezzare lo stesso anche se fosse un fanatico sostenitore dell'Amiga.
Che poi, guarda come si vogliono bene Torvalds e Stallman. :asd:
Tieni conto però che quello che hai scritto è codice Ruby assolutamente non idiomatico, e se da una parte la dice lunga sul lavoro fantastico fatto con YARV (immagino che stai usando una versione 1.9, che in certi casi è davvero veloce), è anche vero che arrivati a questo punto tanto valeva scriverlo direttamente in C.
[...]
Per questo stresso il valore del codice idiomatico, non necessariamente pythonico, visto che anche Ruby forza un certo stile, per non parlare dell'esempio in Haskell che pur semplice fa bella mostra di pattern matching e guards, oltre all'eredità Lisp "del nonno" :D
Infatti. Che senso ha usare Python (o Ruby, o altro) come fosse il C?
Ragazzi la cosa bella di Linux(tra le tante) sapete qual è? Che ti lascia libertà di scelta.
Ho installato Linux nel vecchio computer dove ho due dischi fissi. In uno c'ho Windows XP; nell'altro disco ho installato Ubunto 12.10 a 64 bit.
Ebbene, il programma d'installazione di Ubuntu s'è accorto da solo che avevo due dischi e m'ha installato automaticamente il dual boot.
Adesso, quando avvio la macchina, mi spunta una schermata che mi chiede se voglio avviare Ubuntu o Windows.
Non è meraviglioso? :D
Non ti seccare, ma XP è un s.o. del 2001. Da Vista, se non ricordo male, non vengono rasi al suolo eventuali altri s.o. installati.
Il codice in questione non è per un corso, ma ho solo preso spunto dal testo per scrivere qualcosa in py, quindi i costi in termini asintotici e un ordinamento in loco o meno non è proprio un aspetto che ho considerato.
Ma rimane aperta la questione che avevo posto nel mio precedente messaggio. T'interessa risolvere il problema mantenendo le "caratteristiche" dell'algoritmo (iterazione, confronti, ecc.) oppure semplicemente risolvere il problema con qualunque strumento che Python mette a disposizione?.
Perché se vale il secondo caso, una soluzione immediata è quella suggerita da Albi:
lista = [1, -2, 3, -7, -10]
lista.sort()
Oppure:
lista = sorted([1, -2, 3, -7, -10])
Non sapevo esistesse anche un vettore tradizionale.
Lo andrò a studiare.
Funziona in maniera molto simile alle liste, per cui ti basta istanziarlo, e poi sei già operativo.
Vincenzo1968
05-02-2013, 09:30
Poi a me sta antipatico principalmente il fanatismo. Uno come Stallman si farebbe disprezzare lo stesso anche se fosse un fanatico sostenitore dell'Amiga.
Detto da uno che fino a poco tempo fa aveva l'avatar con su scritto "In Python we trust".
:rotfl:
cdimauro
05-02-2013, 09:40
Poi a me sta antipatico principalmente il fanatismo. Uno come Stallman si farebbe disprezzare lo stesso anche se fosse un fanatico sostenitore dell'Amiga.
Detto da uno che fino a poco tempo fa aveva l'avatar con su scritto "In Python we trust".
:rotfl:
Vabbé, era e rimane una battuta. E' il mio linguaggio preferito, per cui ne faccio una bandiera. :cool:
Ma mi sporco le mani anche col C, PHP, HTML, Javascript, Java, e negli ultimi anni ho avuto parecchio a che fare con assembly e linguaggio macchina (x86 e x64; da qualche mese sono tornato a interessarmi anche di 68000).
Mica sono chiuso in maniera preconcetta, come certi personaggi che considerano CRIMINALI i programmatori che lavorano a software closed... :rolleyes:
Vincenzo1968
05-02-2013, 12:07
"Non ti seccare, ma XP è un s.o. del 2001. Da Vista, se non ricordo male, non vengono rasi al suolo eventuali altri s.o. installati."
Ma quando mai. Windows 8 mantiene a malapena la cartella "Documenti" e i file personali di Windows 7. E sempreché si tratti della stessa versione 64 o 32 bit. Se hai Windows 7 a 32 bit installato e vuoi installare Windows 8 a 64 bit, ti puoi scordare pure quelli. Devi creare le partizioni a mano se vuoi mantenere entrambi i sistemi.
È la stessa cosa di Ubuntu che, se si accorge che hai un qualunque altro sistema già installato, ti chiede se vuoi creare una partizione sul disco in modo da mantenere entrambi i sistemi? E, per soprammercato, ti crea automaticamente il dual boot permettendoti di scegliere, all'avvio della macchina, quale sistema vuoi avviare.
Nientedimeno il dual boot te lo crea anche se hai due dischi, come nel mio caso. Il programma d'installazione, se vede che hai un secondo disco con un sistema operativo installato, si ferma e ti chiede:
"Giovanotto, mi sono accorto che hai un sistema operativo su un altro disco. Che faccio? Creo automaticamente il dual boot?".
Tu rispondi "si" (ma non a voce. Devi pigiare col ditino il carattere 's' o 'y' sulla tastiera) e lui fa tutto in automatico.
Uguale è, si si, proprio la stessa cosa. :cool:
cdimauro
05-02-2013, 18:10
No, ma la situazione non è nemmeno come quella di XP, che se ne fregava di qualunque cosa. Da Vista in poi tutto ciò è cambiato, come già detto. :read:
Riguardo all'installazione che non conserva i dati, è normale. Con Windows o installi da zero ignorando quello che c'era prima, oppure esegui l'aggiornamento, se è possibile (come ho fatto di recente coi miei due PC principali).
All'utente rimane la libertà di installare quello che vuole, e questo era il nocciolo della questione.
Vincenzo1968
05-02-2013, 18:15
Si ma come si usa la classe Counter per contare le parole?
http://www.hwupgrade.it/forum/showpost.php?p=38981124&postcount=578
:read:
Si ma come si usa la classe Counter per contare le parole?
http://www.hwupgrade.it/forum/showpost.php?p=38981124&postcount=578
:read:
evitiamo i crossposting, ammonizione
Vincenzo1968
07-02-2013, 15:10
No, ma la situazione non è nemmeno come quella di XP, che se ne fregava di qualunque cosa. Da Vista in poi tutto ciò è cambiato, come già detto. :read:
Riguardo all'installazione che non conserva i dati, è normale. Con Windows o installi da zero ignorando quello che c'era prima, oppure esegui l'aggiornamento, se è possibile (come ho fatto di recente coi miei due PC principali).
All'utente rimane la libertà di installare quello che vuole, e questo era il nocciolo della questione.
Si ma che c'entra XP? Non ho mica detto che installando XP questo m'ha cancellato ogni cosa. Ho detto che, installando Ubuntu, questo si accorge che uno ha un qualunque altro sistema già installato e ti chiede se vuoi creare il dual boot. Io avevo già installato, su un altro disco, XP. Combinazione ha voluto che fosse XP. Ma la stessa cosa sarebbe avvenuta con Vista, Win 7, Win 8, etc. E financo con altre distro Linux o con una precedente versione di Ubuntu.
Se questa non è libertà... Molto meglio della "libertà" offerta dai sistemi Windows. A parer mio, eh! ;)
Vincenzo1968
07-02-2013, 15:14
No, ma la situazione non è nemmeno come quella di XP, che se ne fregava di qualunque cosa. Da Vista in poi tutto ciò è cambiato, come già detto. :read:
Riguardo all'installazione che non conserva i dati, è normale. Con Windows o installi da zero ignorando quello che c'era prima, oppure esegui l'aggiornamento, se è possibile (come ho fatto di recente coi miei due PC principali).
All'utente rimane la libertà di installare quello che vuole, e questo era il nocciolo della questione.
Ubuntu invece ti chiede se vuoi creare una partizione(permettendoti di scegliere lo spazio da assegnare ad ogni sistema operativo) e conservare il sistema(qualunque sia) già installato.
Scusa se è poco... :D
mi sono preso la libertà di provare a farlo girare in C. "Stile Conico" :asd: :asd:
http://i.imgur.com/uSHpJWS.png
senza il ciclo di 100000 volte, ci mette 0.002s
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.