A linguagem Python possui diversos recursos interessantes. Nesse post, vamos falar de dois recursos: List comprehension e Generator Expressions.
List comprehension é uma construção sintática que permite a criação de lista muito parecida com a forma de definir conjuntos na teoria dos conjuntos. Na teoria de conjuntos, podemos definir um conjunto através de uma propriedade que caracteriza os seus elementos.
S = {2x | x Î N e 1<=x<=3}
Na linguagem Python, podemos definir uma lista com os mesmo elementos de S da seguinte maneira:
S = [2*x for x in range(1,4)]
Essa maneira de definir lista permite implementar algoritmos de maneira mais clara e direta. O algoritmo quicksort pode ser definido da seguinte maneira:
def qsort(list):
if list == []:
return []
else:
pivot = list[0]
lesser = qsort([x for x in list[1:] if x < pivot])
greater = qsort([x for x in list[1:] if x >= pivot])
return lesser + [pivot] + greater
A partir de uma lista, podemos criar duas listas: lesser e greater. A lista lesser é formada pelos elementos menores que o pivô e a lista greater formada pelos elementos maiores que o pivô. Essas duas listas são ordenadas recursivamente e depois são unidas novamente.
A versão com escolha do pivô dinâmica pode ser definida facilmente da seguinte maneira:
from random import randrange
def qsortrand(list):
if list == []:
return []
else:
pivot = list.pop(randrange(len(list)))
lesser = qsortrand([l for l in list if l < pivot])
greater = qsortrand([l for l in list if l >= pivot])
return lesser + [pivot] + greater
Código:
http://ideone.com/5mtoj
import random
import time
from quick import qsort, qsortrand
numElements = int( raw_input("Entre com o tamanho do vetor:") )
testlist = random.sample(xrange(999999999), numElements)
for q in qsort, qsortrand:
print "# elements: %d"%numElements
list = testlist[:]
start = time.clock()
result = q(list)
elapsed = (time.clock() - start)
print "%.3f secs"%(elapsed)
Resultados dos Testes:
|
100
|
1000
|
10000
|
qsort
|
0.001 secs
|
0.008 secs
|
0.043 secs
|
qsortrand
|
0.001 secs
|
0.011 secs
|
0.052 secs
|
Apesar de ser uma boa ideia, o uso de list comprehension têm alguns problemas relacionados com perfomance e uso eficiente de memória. Então, a linguagem Python introduziu o conceito de expressões geradoras e geradores.
Em alguns casos, a experiência mostra que não precisamos ter toda a lista criada na memória. Precisamos apenas saber iterar sobre os elementos um por um. Para realizar a soma de todos os números em um certo intervalo não precisamos de todo o intervalo criado a priori. Considere o seguinte exemplo:
import time
start = time.clock()
#usando list compreenhension
print sum( [x for x in range(1,1000000)] )
elapsed = (time.clock() - start)
print "%.3f secs"%(elapsed)
start = time.clock()
#usando generator expression
print sum( x for x in range(1,1000000) )
elapsed = (time.clock() - start)
print "%.3f secs"%(elapsed)
Resultado:
499999500000
0.340 secs
499999500000
0.224 secs
A semântica de uma generator expression é que os elementos são gerados quando necessários.
g = ( x for x in range(1,1000000) )
print g.next()
print g.next()
print g.next()
Saída
1
2
3
A semântica de uma expressão geradora é equivalente a uma função geradora anônima usando yield.
def __f(exp):
for x in exp:
yield x
g = __f(iter(range(1,1000000)))
print g.next()
print g.next()
print g.next()
Saída
1
2
3
Um exemplo interessante é utilizar a expressão geradora para fazer um programa que exibe os números de Fibonacci menores que 100.
def fib():
a,b = 0,1
while 1:
yield b
a,b = b,a+b
for x in fib():
if x < 100:
print x