segunda-feira, 23 de janeiro de 2017

Voltemos aos ciclos: as listas de compreensão

Já vimos antes o que são ciclos e o que são listas, e até podemos usar ciclos para gerar listas como se ilustra no programa seguinte:
lista = []
for i in 'palavra':
    lista += [i]
Bem, aproveito para apresentar um novo conjunto de atribuições: as atribuições aumentadas. Estas operações não atribuem um valor a uma variável; em vez disso, alteram o valor dessa variável com o que estiver à direita da atribuição. Uma maneira muito mais simples de explicar isto é a seguinte:
Atribuições aumentadas: 
a += ba = a + b
a -= ba = a - b
a *= ba = a * b
a /= ba = a / b
a //= ba = a // b
a %= ba = a % b
Assim, o que está à esquerda é equivalente ao que está à direita. Isto explica a última linha do programa. Vamos então ver o que são afinal essas listas de compreensão. Mais uma vez, um exemplo ilustra melhor o que quero dizer:
lista = [i for i in 'palavra']
Muito bem, o que está aqui faz o mesmo que o programa mais acima! Vamos por partes: o facto de envolver  o i for i in 'palavra' com [] quer dizer que vamos obter uma lista. Se tivéssemos {} ou () poderíamos obter conjuntos ou tuplos, respetivamente. De seguida, as listas de compreensão devem ter uma instrução for, para iterar os vários elementos que irão compor a lista (no caso acima, vai iterar as letras da cadeia de caracteres e colocar cada uma como elemento da lista final). E finalmente, o i antes do for indica o que se vai colocar em cada elemento.
Vamos supor agora que queríamos a lista dos quadrados dos primeiros 10 inteiros:
>>> quad = [i*i for i in range(1,11)]
>>> quad
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Repare agora o que ficou no início, i*i em vez de i.
Mas isto pode tornar-se mais complicado:
>>> mult = [i*j for i in [1,2,3] for j in [2,4,6]]
>>> mult
[2, 4, 6, 4, 8, 12, 6, 12, 18]
Repare que aqui temos dois ciclos e o que vai acontecer é que para cada iteração de i, vai multipicar-se por cada um dos j e preencher um elemento, resultando num total de 9 elementos, para este exemplo.
Um exemplo bastante ilustrativo que vem diretamente da documentação do Python:
>>> matriz = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]
>>> [[linha[i] for linha in matriz] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
O que são aqueles ... à esquerda? É quando o IDLE está a aceitar mais do que uma linha porque deteta que ainda não acabou tudo na primeira linha (o parêntesis reto não fechou). E repare que a matriz é uma lista com 3 listas no seu interior. O que o comando na segunda entrada faz é transpor a matriz: cria 4 listas, e cada lista vai ter os itens i das linhas da matriz. Muito bem pensado, não é?
Vamos complicar ainda mais um bocadinho:
>>> x = [i for i in range(10) if i%3 != 0]
>>> x
[1, 2, 4, 5, 7, 8]
O que aconteceu aqui? Agora adicionámos uma condição com if, e só se esta se verificar é que o elemento é incluído na lista. No exemplo acima, o número só é introduzido se não for um múltiplo de 3.
E vamos terminar com uma função curiosa:
>>> zip([1,2,3],[4,5,6])
<zip object at 0x0423B2D8>
>>> list(zip([1,2,3],[4,5,6]))
[(1, 4), (2, 5), (3, 6)]
>>> list(zip([1,2,3],[4,5,6],[7,8,9]))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Repare como faz uma espécie de transposição...  Na primeira linha serve para demonstrar como o comando zip() cria um objeto; para vermos o seu conteúdo temos de o converter em lista. E o que faz é pegar nos primeiros elementos das listas de argumento e juntá-los em tuplos. Voltaremos a este comando mais tarde!

segunda-feira, 2 de janeiro de 2017

Mais uma estrutura: os conjuntos

Pois é, ainda há mais estruturas em Python para conhecer. Hoje vamos ver o que são os conjuntos e como usá-los. Estes são uma espécie de listas mas, ao contrário destas, não são ordenados (e, consequentemente, não são indexáveis) e não admitem elementos repetidos. Vamos ver como criá-los:
>>> a = set([4, 'batata', 5, 5.0])
>>> a
{'batata', 4, 5}
>>> b = set((5,6,7))
>>> b
{5, 6, 7}
Muito bem, como se percebe, podemos criar um conjunto com o comando set(), em que o argumento deve ser um iterável (pode ser uma lista, um tuplo, uma sequência criada por range()). Se quisermos criar um conjunto vazio, tem de se usar set(), e não {}, pois isto cria um dicionário vazio.
Antes de avançarmos, reparou que o conjunto a só tem um 5? Pois é, 5 e 5.0 são o mesmo número e os conjuntos não aceitam elementos repetidos... E repare que a ordem dos elementos se alterou. Não podemos aceder a elementos através de um índice, como nas listas, nos tuplos ou nos dicionários, pois os conjuntos não são ordenados. Também não podemos fazer slicing com conjuntos pelo mesmo motivo.
Mais uma curiosidade dos conjuntos:
>>> set('abracadabra')
{'r', 'c', 'a', 'b', 'd'}
O que se passou aqui? Antes de mais, quando se usa set() para criar um conjunto,  as cadeias de caracteres são divididas em caracteres individuais. Além disso, como os conjuntos não admitem elementos repetidos, apenas ficamos com as letras que constituem a palavra na cadeia de caracteres. Vamos agora ver operações com conjuntos:
>>> {2,3,5,6,7} | {2,6,7,8,9}
{2, 3, 5, 6, 7, 8, 9}
>>> {2,3,5,6,7} & {2,6,7,8,9}
{2, 6, 7}
>>> {2,3,5,6,7} - {2,6,7,8,9}
{3, 5}
>>> {2,3,5,6,7} ^ {2,6,7,8,9}
{3, 5, 8, 9}
Vejamos... A primeira operação (|), não é mais do que a união de conjuntos, em que o conjunto final é a junção dos elementos de cada conjunto, eliminando as repetições. A segunda (&) corresponde à interseção de conjuntos, em que apenas sobram os elementos que existiam em ambos os conjuntos iniciais. Depois temos a diferença de conjuntos (-), em que retiramos ao primeiro conjunto os elementos que constam no segundo. Finalmente, a última operação é a diferença simétrica (^), que corresponde à diferença entre união e a interseção dos conjuntos, ou por outras palavras, os elementos que apenas pertencem a cada um dos conjuntos mas não a ambos.
Bem, mas como se adicionam elementos a conjuntos? Uma resposta é usar a união de conjuntos:
>>> a = {5,7,8}
>>> a = a | {9}
>>> a
{8, 9, 5, 7}
E continuando o exemplo acima, também podemos retirar elementos a conjuntos usando a diferença:
>>> a = a - {5}
>>> a
{8, 9, 7}
Também podemos ver se um dado elemento pertence a um conjunto usando o operador in, e saber o número de elementos do conjunto usando len():
>>> 9 in a
True
>>> 6 in a
False
>>> len(a)
3
E, se o conjunto contiver apenas números, também podemos usar as funções max(), min() e sum():
>>> max(a)
9
>>> min(a)
7
>>> sum(a)
24
E agora, imagine que queria ver todos os elementos do conjunto um a um. Vamos ver um programa:
a = {1, 4, 5, 6, 3}
for i in a:
    print(i)
Quando executar este programa, vai obter o seguinte resultado:
1
3
4
5
6
Sim, podemos iterar os elementos de um conjunto usando um ciclo for. Já nem deve ser surpreendente, pois não?
Antes de acabarmos, existe um outro comando que cria conjuntos, o comando frozenset(). A diferença entre este e o set() é que no frozenset() os conjuntos criados já não se podem alterar. Mais tarde, quando falarmos em métodos e propriedades de objetos, isto fará mais sentido!
Ok, basta por hoje!