AprendaComputaocomPython PDF
AprendaComputaocomPython PDF
Verso 1.1
12/08/2010
Contedo
Prefcio 1.1 Como e porque eu vim a usar Python . 1.2 Encontrando um livro-texto . . . . . . 1.3 Introduzindo programao com Python 1.4 Construindo uma comunidade . . . . . Apresentao
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
3 3 4 4 6 7 9 9 11 11 13 14 14 17 17 18 19 20 20 21 22 22 23 23 25 25 26 26 27 28
2 3
Captulo 1: O caminho do programa 3.1 1.1 A linguagem de programao Python . . 3.2 1.2 O que um programa? . . . . . . . . . . 3.3 1.3 O que depurao (debugging)? . . . . . 3.4 1.4 Linguagens naturais e linguagens formais 3.5 1.5 O primeiro programa . . . . . . . . . . . 3.6 1.6 Glossrio . . . . . . . . . . . . . . . . . Captulo 2: Variveis, expresses e comandos 4.1 2.1 Valores e tipos . . . . . . . . . . . . . . 4.2 2.2 Variveis . . . . . . . . . . . . . . . . . 4.3 2.3 Nomes de variveis e palavras reservadas 4.4 2.4 Comandos . . . . . . . . . . . . . . . . 4.5 2.5 Avaliando expresses . . . . . . . . . . . 4.6 2.6 Operadores e operandos . . . . . . . . . 4.7 2.7 Ordem dos operadores . . . . . . . . . . 4.8 2.8 Operaes com strings . . . . . . . . . . 4.9 2.9 Composio . . . . . . . . . . . . . . . 4.10 2.11 Glossrio . . . . . . . . . . . . . . . . Captulo 3: Funes 5.1 3.1 Chamadas de funes 5.2 3.2 Converso entre tipos 5.3 3.3 Coero entre tipos . . 5.4 3.4 Funes matemticas . 5.5 3.5 Composio . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
3.6 Adicionando novas funes . . . 3.7 Denies e uso . . . . . . . . . 3.8 Fluxo de execuo . . . . . . . . 3.9 Parmetros e argumentos . . . . . 3.10 Variveis e parmetros so locais 3.11 Diagramas da pilha . . . . . . . 3.12 Funes com resultados . . . . . 3.13 Glossrio . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
28 29 30 30 31 32 33 33 35 35 36 36 37 37 38 38 39 39 40 41 42 42 45 45 46 48 49 49 51 51 52 53 55 55 56 57 59 59 60 61 61 63 63 65 65 66 66 67 68 68 69
Captulo 4: Condicionais e recursividade 6.1 4.1 O operador mdulo . . . . . . . . . . . . . . 6.2 4.2 Expresses booleanas . . . . . . . . . . . . 6.3 4.3 Operadores lgicos . . . . . . . . . . . . . . 6.4 4.4 Execuo condicional . . . . . . . . . . . . 6.5 4.5 Execuo alternativa . . . . . . . . . . . . . 6.6 4.6 Condicionais encadeados . . . . . . . . . . . 6.7 4.7 Condicionais aninhados . . . . . . . . . . . 6.8 4.8 A instruo return . . . . . . . . . . . . . 6.9 4.9 Recursividade . . . . . . . . . . . . . . . . 6.10 4.10 Diagramas de pilha para funes recursivas 6.11 4.11 Recursividade innita . . . . . . . . . . . . 6.12 4.12 Entrada pelo teclado . . . . . . . . . . . . 6.13 4.13 Glossrio . . . . . . . . . . . . . . . . . . Captulo 5: Funes frutferas 7.1 5.1 Valores de retorno . . . . . . . . 7.2 5.2 Desenvolvimento de programas . 7.3 5.3 Composio . . . . . . . . . . . 7.4 5.4 Funes booleanas . . . . . . . . 7.5 5.5 Mais recursividade . . . . . . . . 7.6 5.6 Voto de conana (Leap of faith) 7.7 5.7 Mais um exemplo . . . . . . . . 7.8 5.8 Checagem de tipos . . . . . . . . 7.9 5.9 Glossrio . . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
Captulo 6: Iterao 8.1 6.1 Reatribuies . . . . . . . . . . . . . . . . . . . 8.2 6.2 O comando while . . . . . . . . . . . . . . . . 8.3 6.3 Tabelas . . . . . . . . . . . . . . . . . . . . . . 8.4 6.4 Tabelas de duas dimenses (ou bi-dimensionais) 8.5 6.5 Encapsulamento e generalizao . . . . . . . . . 8.6 6.6 Mais encapsulamento . . . . . . . . . . . . . . 8.7 6.7 Variveis locais . . . . . . . . . . . . . . . . . . 8.8 6.8 Mais generalizao . . . . . . . . . . . . . . . . 8.9 6.9 Funes . . . . . . . . . . . . . . . . . . . . . . 8.10 6.10 Glossrio . . . . . . . . . . . . . . . . . . . . Captulo 7: Strings 9.1 7.1 Um tipo de dado composto . . . 9.2 7.2 Comprimento . . . . . . . . . . 9.3 7.3 Travessia e o loop for . . . . . 9.4 7.4 Fatias de strings . . . . . . . . 9.5 7.5 Comparao de strings . . . . . 9.6 7.6 Strings so imutveis . . . . . . 9.7 7.7 Uma funo find (encontrar)
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
ii
7.8 Iterando e contando . . . . 7.9 O mdulo string . . . . . 7.10 Classicao de caracteres 7.11 Glossrio . . . . . . . . . 7.11 Glossrio2 . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
10 Captulo 8: Listas 10.1 8.1 Valores da lista . . . . . . . . . . . 10.2 8.2 Acessado elementos . . . . . . . . 10.3 8.3 Comprimento da lista . . . . . . . 10.4 8.4 Membros de uma lista . . . . . . . 10.5 8.5 Listas e laos for . . . . . . . . . 10.6 8.6 Operaes em listas . . . . . . . . 10.7 8.7 Fatiamento de listas . . . . . . . . 10.8 8.8 Listas so mutveis . . . . . . . . . 10.9 8.9 Remoo em lista . . . . . . . . . 10.10 8.10 Ojetos e valores . . . . . . . . . . 10.11 8.11 Apelidos . . . . . . . . . . . . . . 10.12 8.12 Clonando listas . . . . . . . . . . 10.13 8.13 Lista como parmetro . . . . . . . 10.14 8.14 Lista aninhadas . . . . . . . . . . 10.15 8.15 Matrizes . . . . . . . . . . . . . . 10.16 8.16 Strings e listas . . . . . . . . . . . 10.17 8.17 Glossrio . . . . . . . . . . . . . 10.18 Outros termos utilizados neste captulo 11 Captulo 9: Tuplas 11.1 9.1 Mutabilidade e tuplas . . . . . 11.2 9.2 Atribuies de tupla . . . . . . 11.3 9.3 Tuplas como valores de retorno 11.4 9.4 Nmeros aleatrios . . . . . . . 11.5 9.5 Lista de nmeros aleatrios . . 11.6 9.6 Contando . . . . . . . . . . . . 11.7 9.7 Vrios intervalos . . . . . . . . 11.8 9.8 Uma soluo em um s passo . 11.9 9.9 Glossrio . . . . . . . . . . . . 12 Captulo 10: Dicionrios 12.1 10.1 Operaes dos Dicionrios 12.2 10.2 Mtodos dos Dicionrios . 12.3 10.3 Aliasing (XXX) e Copiar . 12.4 10.4 Matrizes Esparsas . . . . . 12.5 10.5 Hint XXX . . . . . . . . . 12.6 10.6 Inteiros Longos . . . . . . 12.7 10.7 Contando Letras . . . . . . 12.8 10.8 Glossrio . . . . . . . . . 13 Captulo 11: Arquivos e excees 13.1 Arquivos e excees . . . . 13.2 11.1 Arquivos texto . . . . 13.3 11.2 Gravando variveis . . 13.4 11.3 Diretrios . . . . . . . 13.5 11.4 Pickling . . . . . . . . 13.6 11.5 Excees . . . . . . . 13.7 11.6 Glossrio . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
14 Captulo 12: Classes e objetos 14.1 12.1 Tipos compostos denidos pelo usurio 14.2 12.2 Atributos . . . . . . . . . . . . . . . . 14.3 12.3 Instncias como parmetros . . . . . . 14.4 12.4 O signicado de mesmo . . . . . . . 14.5 12.5 Retngulos . . . . . . . . . . . . . . . 14.6 12.6 Instancias como valores retornados . . . 14.7 12.7 Objetos so mutveis . . . . . . . . . . 14.8 12.8 Copiando . . . . . . . . . . . . . . . . 14.9 12.9 Glossrio . . . . . . . . . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
109 109 110 111 111 112 113 113 114 115 117 117 118 119 120 120 121 121 122 123 123 124 125 125 125 127 127 127 128 129 130 130 131 132 133 135 135 136 136 137 138 138 139 142 143 143 144 145 146 146
15 Captulo 13: Classes e funes 15.1 13.1 Horario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.2 13.2 Funes Puras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.3 13.3 Modicadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.4 13.4 O que melhor ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.5 13.5 Desenvolvimento Prototipado versus Desenvolvimento Planejamento . 15.6 13.6 Generalizao . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.7 13.7 Algoritmos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.8 13.8 Glossrio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Captulo 14: Classes e mtodos 16.1 14.1 Caractersticas da orientao a objetos 16.2 14.2 exibeHora (printTime) . . . . . . . . 16.3 14.3 Um outro exemplo . . . . . . . . . . 16.4 14.4 Um exemplo mais complicado . . . . 16.5 14.10 Glossrio . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
17 Captulo 15: Conjuntos de objetos 17.1 15.1 Composio . . . . . . . . . . . . . . . . 17.2 15.2 Objetos Carta . . . . . . . . . . . . . . 17.3 15.3 Atributos de classe e o mtodo __str__ 17.4 15.4 Comparando cartas . . . . . . . . . . . . 17.5 15.5 Baralhos . . . . . . . . . . . . . . . . . . 17.6 15.6 Imprimindo o baralho . . . . . . . . . . . 17.7 15.7 Embaralhando . . . . . . . . . . . . . . . 17.8 15.8 Removendo e distribuindo cartas . . . . . 17.9 15.9 Glossrio . . . . . . . . . . . . . . . . . 18 Capitulo 16: Herana 18.1 16.1 Herana . . . . . . . . . . 18.2 16.2 Uma mo de cartas . . . . 18.3 16.3 Dando as cartas . . . . . . 18.4 16.4 Exibindo a mao . . . . . . 18.5 16.5 A classe JogoDeCartas 18.6 16.6 Classe MaoDeMico . . . 18.7 16.7 Classe Mico . . . . . . . 18.8 16.8 Glossrio . . . . . . . . . 19 Captulo 17: Listas encadeadas 19.1 17.1 Referncias Embutidas 19.2 17.2 A classe No (Node) . . 19.3 17.3 Listas como Colees . 19.4 17.4 Listas e Recorrncia . 19.5 17.5 Listas Innitas . . . . iv
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
17.6 O Teorema da Ambigidade Fundamental 17.7 Modicando Listas . . . . . . . . . . . . 17.8 Envoltrios e Ajudadores . . . . . . . . . 17.9 A Classe ListaLigada . . . . . . . . 17.10 Invariantes . . . . . . . . . . . . . . . . 17.11 Glossrio . . . . . . . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
147 148 148 149 150 150 153 153 154 154 155 155 155 156 159 159 159 160 161 162 163 164 165 166 167 167 168 170 173 174 176
20 Captulo 18: Pilhas 20.1 18.1 Tipos abstratos de dados . . . . . . . . . . . . . . 20.2 18.2 O TAD Pilha . . . . . . . . . . . . . . . . . . . . 20.3 18.3 Implementando pilhas com listas de Python . . . . 20.4 18.4 Empilhando e desempilhando . . . . . . . . . . . . 20.5 18.5 Usando uma pilha para avaliar expresses ps-xas 20.6 18.6 Anlise sinttica . . . . . . . . . . . . . . . . . . . 20.7 18.7 Avaliando em ps-xo. . . . . . . . . . . . . . . . 21 Captulo 19: Filas 21.1 19.1 Um TDA Fila . . . . . . . . . 21.2 19.2 Fila encadeada . . . . . . . . 21.3 19.3 Caractersticas de performance 21.4 19.4 Fila encadeada aprimorada . . 21.5 19.5 Fila por prioridade . . . . . . 21.6 19.6 A classe Golfer . . . . . . . 21.7 19.7 Glossrio . . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
22 Captulo 20: rvores 22.1 20.1 Construindo rvores . . . . . . . . . . 22.2 20.2 Percorrendo rvores . . . . . . . . . . 22.3 20.3 rvores de expresses . . . . . . . . 22.4 20.4 Percurso de rvores . . . . . . . . . . 22.5 20.5 Construindo uma rvore de expresso 22.6 20.6 Manipulando erros . . . . . . . . . . 22.7 20.7 A rvore dos animais . . . . . . . . . 22.8 20.8 Glossrio . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
23 Apndice A: Depurao 177 23.1 A.1 Erros de sintaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 23.2 A.2 Erros de tempo de execuo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 23.3 A.3 Erros de semntica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 24 Apndice B: Criando um novo tipo de dado 24.1 B.1 Multiplicao de fraes . . . . . . . . . . . . . 24.2 B.2 Soma de fraes . . . . . . . . . . . . . . . . . 24.3 B.3 Simplicando fraes: O algoritmo de Euclides 24.4 B.4 Comparando fraes . . . . . . . . . . . . . . . 24.5 B.5 Indo mais alm... . . . . . . . . . . . . . . . . . 24.6 B.6 Glossrio . . . . . . . . . . . . . . . . . . . . . 185 186 187 187 189 189 190
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
25 Apndice C: Leituras recomendadas 191 25.1 C.1 Recomendaes para leitura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 25.2 C.2 Sites e livros sobre Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 25.3 C.3 Livros de cincia da computao recomendados . . . . . . . . . . . . . . . . . . . . . . . . . . 193 26 Apndice D: GNU Free Documentation License 195
197
vi
Contents:
Contedo
Contedo
CAPTULO 1
Prefcio
Por Jeff Elkner Este livro deve sua existncia colaborao possibilitada pela Internet e pelo movimento do software livre. Seus trs autores um professor universitrio, um professor secundarista e um programador prossional ainda no se encontraram pessoalmente, mas temos sido capazes de trabalhar em estreita colaborao e temos sido ajudados por muitos colegas maravilhosos que tm dedicado seu tempo e energia para ajudar a fazer deste um livro cada vez melhor. Achamos que este livro um testemunho dos benefcios e possibilidades futuras deste tipo de colaborao, cujo modelo tem sido posto em prtica por Richard Stallman e pela Free Software Foundation.
Pedi para um dos talentosos estudantes de Yorktown, Matt Ahrens, que experimentasse Python. Em dois meses ele no s aprendeu a linguagem como tambm escreveu uma aplicao chamada pyTicket que possibilitou nossa equipe reportar problemas de tecnologia pela Web. Eu sabia que Matt no poderia ter nalizado uma aplicao daquele porte em perodo to curto em C++, e esta realizao, combinada com a avaliao positiva de Python dada por Matt, sugeriam que Python era a soluo que eu estava procurando.
Captulo 1. Prefcio
#include <iostream.h> void main() { cout << "Al, mundo." << endl; }
Mesmo sendo um exemplo trivial, as vantagens do Python saltam aos olhos. O curso de Cincia da Computao I que ministro em Yorktown no tem pr-requisitos, assim, muitos dos alunos que veem esse exemplo esto olhando para o seu primeiro programa. Alguns deles esto indubitavelmente nervosos, por j terem ouvido falar que programao de computadores difcil de aprender. A verso C++ tem sempre me forado a escolher entre duas opes insatisfatrias: ou explicar os comandos #include, void main(), {, e } e arriscar confundir ou intimidar alguns dos alunos logo assim que iniciam, ou dizer a eles No se preocupem com todas estas coisas agora; falaremos sobre elas mais tarde, e correr o mesmo risco. O objetivo educacional neste ponto do curso introduzir os alunos idia de comando em programao e v-los escrever seu primeiro programa, deste modo introduzindo-os ao ambiente de programao. O programa em Python tem exatamente o que necessrio para conseguir isto, e nada mais. Comparar o texto explicativo do programa em cada verso do livro ilustra ainda mais o que signica para o aluno iniciante. Existem treze pargrafos de explicao do Al, mundo! na verso C++; na verso Python existem apenas dois. Mais importante, os onze pargrafos perdidos no se ocupam das idias chave da programao de computadores, mas com a mincia da sintaxe C++. Vejo a mesma coisa acontecendo atravs de todo o livro. Pargrafos inteiros simplesmente desaparecem da verso Python do texto porque a sintaxe muito mais clara de Python os torna desnecessrios. Utilizar uma linguagem de to alto nvel como Python, permite ao professor deixar para falar mais tarde sobre os nveis mais baixos, prximos mquina, quando os alunos j tero a experincia necessria para ver com mais sentido os detalhes. Desta maneira podemos pedagogicamente por em primeiro lugar as primeiras coisas. Um dos melhores exemplos disto a maneira com que Python lida com variveis. Em C++ uma varivel um nome para um lugar que guarda uma coisa. Variveis tm de ser declaradas com seu tipo pelo menos em parte por que o tamanho do lugar a que se referem precisa ser predeterminado. Assim, a idia de varivel ca amarrada ao hardware da mquina. O conceito poderoso e fundamental de varivel j bastante difcil para o aluno iniciante (tanto em cincia da computao quanto em lgebra). Bytes e endereos no ajudam neste caso. Em Python uma varivel um nome que se refere a uma coisa. Este um conceito muito mais intuitivo para alunos iniciantes e est muito mais prximo do signicado de varivel que eles aprenderam em seus cursos de matemtica. Eu tive muito menos diculdade em ensinar variveis este ano do que tive no passado, e gastei menos tempo ajudando aos alunos com problemas no uso delas. Um outro exemplo de como Python ajuda no ensino e aprendizagem de programao em sua sintaxe para funo. Meus alunos tm sempre tido grande diculdade na compreenso de funes. O problema principal gira em torno da diferena entre a denio de uma funo e a chamada de uma funo, e a distino relacionada entre um parmetro e um argumento. Python vem em auxlio com uma sintaxe no apenas curta quanto bela. As denies de funo comeam com def, ento eu simplesmente digo aos meus alunos Quando voc dene uma funo, comece com def, seguido do nome da funo que voc est denindo; quando voc chama uma funo, simplesmente chame-a digitando o nome dela. Parmetros cam nas denies; argumentos vo com as chamadas. No existem tipos de retorno, tipos de parmetro ou passagem de parmetros por valor ou por referncia no meio do caminho, permitindome ensinar funes em menos da metade do tempo que isto me tomava anteriormente, com uma melhor compreenso. A utilizao do Python tem melhorado a efetividade de nosso programa em cincia da computao para todos os estudantes. Eu vejo um nvel geral de sucesso muito mais alto e um nvel mais baixo de frustrao do que experimentei durante os dois anos em que ensinei C++. Eu avano mais rpido com melhores resultados. Mais alunos deixam o curso com a habilidade de criar programas signicativos e com uma atitude positiva em relao a experincia de programao que isso traz.
Captulo 1. Prefcio
CAPTULO 2
Apresentao
Por David Beazley Como educador, pesquisador e autor de livros, regozija-me ver completo este trabalho. Python uma linguagem de programao divertida e extremamente fcil de usar que tem ganho forte popularidade nestes ltimos poucos anos. Desenvolvida dez anos atrs por Guido van Rossun, a sintaxe simples do Python e seu sentido geral so grandemente derivados do ABC, uma linguagem didtica que foi desenvolvida nos anos 80. Entretanto, Python tambm foi criado para solucionar problemas reais e tomou emprestado uma grande quantidade de caractersticas de linguagens de programao como C++, Java, Modula-3 e Scheme. Por causa disso, uma das mais notveis caractersticas do Python o grande apelo que tem junto a desenvolvedores prossionais de software, cientistas, pesquisadores, artistas e educadores. A Despeito deste apelo do Python junto s mais variadas comunidades, voc pode ainda estar pensando ?por que Python?? ou ?por que ensinar programao com Python??. Responder estas perguntas no uma tarefa fcil ? especialmente se a opinio pblica est do lado de alternativas mais masoquistas como C++ e Java. Entretanto, eu acho que a resposta mais direta que programar com Python um bocado divertido e mais produtivo. Quando ministro cursos de cincias da computao, o que desejo cobrir conceitos importantes alm de tornar a matria interessante e os alunos participativos. Infelizmente, existe uma tendncia entre os cursos introdutrios de programao a focar ateno demais em abstraes matemticas, e de frustrao entre os alunos com problemas enfadonhos e inoportunos relacionados a detalhes de sintaxe em baixo nvel, compilao e a imposio de regras que aparentemente s um expert pode compreender (enforcement of seemingly arcane rules XXX). Embora alguma abstrao e formalismo sejam importantes para engenheiros prossionais de software e estudantes que planejam continuar seus estudos em cincias da computao, escolher tal abordagem em um curso introdutrio faz da cincia da computao algo entediante. Quando ministro um curso, no desejo uma sala cheia de alunos sem inspirao. Em vez disso, preferiria muito mais v-los tentando solucionar problemas interessantes explorando idias diferentes, trilhando caminhos no convencionais, quebrando regras, e aprendendo a partir de seus erros. Fazendo assim, no pretendo desperdiar metade de um semestre tentando explicar problemas obscuros de sintaxe, mensagens ininteligveis de compiladores ou as vrias centenas de maneiras pelas quais um programa pode gerar uma falha geral de proteo. Uma das razes pelas quais eu gosto de Python que ele oferece um equilbrio realmente bom entre o lado prtico e o lado conceitual. Sendo Python interpretado, os iniciantes podem pegar a linguagem e comear a fazer coisas legais quase imediatamente sem se perderem em problemas de compilao e ligao (linking XXX). Alm disso, Python vem com uma grande biblioteca de mdulos que podem ser utilizados para fazer todo tipo de tarefa, desde a programao para a web at grcos. Com tal enfoque prtico temos uma bela maneira de alcanar o engajamento dos alunos e permitir que eles nalizem projetos signicativos. Entretanto, Python tambm pode servir de excelente embasamento para a introduo de conceitos importantes em cincia da computao. J que Python suporta plenamente procedimentos (procedures) e classes, os alunos podem ser gradualmente introduzidos a tpicos como abstrao procedural, estruturas de dados, e programao orientada a objetos ? todos aplicveis em cursos posteriores de Java ou C++.
Python ainda toma emprestado certas caractersticas de linguagens de programao funcionais e pode ser usado para introduzir conceitos cujos detalhes poderiam ser aprofundados em cursos de Scheme e Lisp. Lendo o prefcio de Jeffrey, quei impressionado com seu comentrio de que Python o fez ver um ?maior nvel de sucesso e um menor nvel de frustrao? o que lhe permitiu ?progredir mais depressa com resultados melhores?. Embora estes comentrios reram-se aos seus cursos introdutrios, eu s vezes uso Python exatamente pelas mesmas razes em cursos avanados de ps-graduao (graduate = pos-graduacao XXX) em cincia da computao na Universidade de Chicago. Nestes cursos, enfrento constantemente a assustadora tarefa de cobrir muitos tpicos difceis em um rapidssimo trimestre (quarter XXX) de nove semanas. Embora me seja possvel iningir um bocado de dor e sofrimento pelo uso de uma linguagem como C++, tenho percebido muitas vezes que este enfoque contraproducente ? especialmente quando o curso sobre um tpico no relacionado apenas com ?programar?. Acho que usar Python me permite um melhor foco no tpico em questo, enquanto permite que os alunos completem projetos substanciais em classe. Embora Python seja ainda uma linguagem jovem e em evoluo, acredito que tem um futuro brilhante em educao. Este livro um passo importante nessa direo. David Beazley Universidade de Chicago Autor de Python Essencial Reference
Captulo 2. Apresentao
CAPTULO 3
Tpicos Captulo 1: O caminho do programa 1.1 A linguagem de programao Python 1.2 O que um programa? 1.3 O que depurao (debugging)? * 1.3.1 Erros de sintaxe * 1.3.2 Erros em tempo de execuo (runtime errors) * 1.3.3 Erros de semntica (ou de lgica) * 1.3.4 Depurao experimental (debugging) 1.4 Linguagens naturais e linguagens formais 1.5 O primeiro programa 1.6 Glossrio O objetivo deste livro ensinar o leitor a pensar como um cientista da computao. Essa maneira de pensar combina algumas das melhores caractersticas da matemtica, da engenharia e das cincias naturais. Como os matemticos, os cientistas da computao usam linguagens formais para representar ideias (especicamente, computaes). Como os engenheiros, eles projetam coisas, montando sistemas a partir de componentes e avaliando as vantagens e desvantagens de diferentes alternativas. Como os cientistas naturais, eles observam o comportamento de sistemas complexos, formulam hipteses e testam previses. A habilidade mais importante de um cientista da computao a soluo de problemas. Soluo de problemas a habilidade de formular questes, pensar criativamente sobre solues possveis e expressar uma soluo de forma clara e precisa. Ocorre que aprender a programar uma excelente oportunidade de praticar a habilidade da soluo de problemas. por isso que este captulo se chama O caminho do programa. Em certo nvel, voc estar aprendendo a programar, habilidade que til em si mesma. Em outro nvel, voc usar a programao como um meio para atingir um objetivo. medida que voc for avanando na leitura, esse objetivo car mais claro.
Como voc pode deduzir a partir da expresso linguagem de alto nvel, tambm existem as linguagens de baixo nvel, s vezes chamadas de linguagens de mquina ou linguagem assembly (linguagens de montagem). De forma simples, o computador s consegue executar programas escritos em linguagens de baixo nvel. Deste modo, programas escritos em linguagens de alto nvel precisam ser processados antes que possam rodar. Esse processamento extra toma algum tempo, o que uma pequena desvantagem em relao s linguagens de alto nvel. Mas as vantagens so enormes. Primeiro, muito mais fcil programar em uma linguagem de alto nvel. mais rpido escrever programas em uma linguagem de alto nvel; eles so mais curtos e mais fceis de ler, e h maior probabilidade de esterem corretos. Segundo, as linguagens de alto nvel so portveis, o que signica que podem rodar em diferentes tipos de computador, com pouca ou nenhuma modicao. Programas em baixo nvel s podem rodar em um nico tipo de computador e precisam ser re-escritos para rodar em outro tipo. Devido a essas vantagens, quase todos os programas so escritos em linguagens de alto nvel. As de baixo nvel so utilizadas somente para umas poucas aplicaes especializadas. Dois tipos de programas processam linguagens de alto nvel, traduzindo-as em linguagens de baixo nvel: interpretadores e compiladores. O interpretador l um programa escrito em linguagem de alto nvel e o executa, ou seja, faz o que o programa diz. Ele processa o programa um pouco de cada vez, alternadamente: hora lendo algumas linhas, hora executando essas linhas e realizando clculos.
O compilador l o programa e o traduz completamente antes que o programa comece a rodar. Neste caso, o programa escrito em linguagem de alto nvel chamado de cdigo fonte, e o programa traduzido chamado de cdigo objeto ou executvel. Uma vez que um programa compilado, voc pode execut-lo repetidamente, sem que precise de nova traduo.
Python considerada uma linguagem interpretada, pois os programas em Python so executados por um interpretador. Existem duas maneiras de usar o interpretador: no modo de linha de comando e no modo de script. No modo de linha de comando, voc digita programas em Python e o interpretador mostra o resultado:
$ python Python 2.5.2 (r252:60911, Jan 4 2009, 17:40:26) [GCC 4.3.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> print 1 + 1 2
A primeira linha deste exemplo o comando que inicia o interpretador Python. As trs linhas seguintes so mensagens do interpretador. A quarta linha comea com >>>, que o sinal usado pelo interpretador para indicar que ele est pronto. No exemplo anterior, digitamos print 1 + 1 e o interpretador respondeu 2. Voc tambm pode escrever um programa em um arquivo e usar o interpretador para executar o contedo desse arquivo.
10
Um arquivo como este chamado de script. Por exemplo, usamos um editor de texto para criar um arquivo chamado leticia.py com o seguinte contedo:
print 1 + 1
Por conveno, arquivos que contenham programas em Python tm nomes que terminam com .py. Para executar o programa, temos de dizer ao interpretador o nome do script:
$ python leticia.py 2
Em outros ambientes de desenvolvimento, os detalhes da execuo de programas podem ser diferentes. Alm disso, a maioria dos programas so mais interessantes do que esse. A maioria dos exemplos neste livro so executados a partir da linha de comando. Trabalhar com a linha de comando conveniente no desenvolvimento e testagem de programas, porque voc pode digitar os programas e execut-los imediatamente. Uma vez que voc tem um programa que funciona, deve guard-lo em um script, de forma a poder execut-lo ou modic-lo no futuro.
11
12
Por exemplo, o Linux um sistema operacional que contm milhares de linhas de cdigo, mas comeou como um programa simples, que Linus Torvalds usou para explorar o chip Intel 80386. De acordo com Larry Greeneld, Um dos primeiros projetos de Linus Torvalds foi um programa que deveria alternar entre imprimir AAAA e BBBB. Isso depois evoluiu at o Linux. (The Linux Users Guide Verso Beta 1) Captulos posteriores faro mais sugestes sobre depurao e outras prticas de programao.
13
literalidade: As linguagens naturais esto cheias de expresses idiomticas e metforas. Se eu digo Caiu a cha, possvel que no exista cha nenhuma, nem nada que tenha cado. Nas linguagens formais, no h sentido ambguo. Pessoas que crescem falando uma linguagem natural, ou seja, todo mundo, muitas vezes tm diculdade de se acostumar com uma linguagem formal. De certa maneira, a diferena entre linguagens formais e naturais como a diferena entre poesia e prosa, porm mais acentuada: poesia: As palavras so usadas pela sua sonoridade, alm de seus sentidos, e o poema como um todo cria um efeito ou uma reao emocional. A ambiguidade no apenas frequente, mas na maioria das vezes, proposital. prosa: O sentido literal das palavras mais importante, e a estrutura contribui mais para o signicado. A prosa mais fcil de analisar do que a poesia, mas ainda , muitas vezes, ambgua. programas: O signicado de um programa de computador exato e literal, e pode ser inteiramente entendido pela anlise de seus smbolos e de sua estrutura. Aqui vo algumas sugestes para a leitura de programas (e de outras linguagens formais). Primeiro, lembre-se de que linguagens formais so muito mais densas do que linguagens naturais, por isso, mais demorado l-las. A estrutura tambm muito importante, logo, geralmente no uma boa ideia ler de cima para baixo, da esquerda para a direita. Em vez disso, aprenda a analisar o programa na sua cabea, identicando os smbolos e interpretando a estrutura. Finalmente, os detalhes so importantes. Pequenas coisas, como erros ortogrcos e m pontuao, com as quais voc pode se safar nas linguagens naturais, podem fazer uma grande diferena em uma linguagem formal.
Isso um exemplo de um comando print, que, na realidade, no imprime nada em papel. Ele apresenta o valor na tela. Neste caso, o resultado so as palavras:
Al, Mundo!
As aspas no programa marcam o comeo e o m do valor, elas no aparecem no resultado nal. Algumas pessoas julgam a qualidade de uma linguagem de programao pela simplicidade do programa Al, Mundo!. Por esse padro, Python se sai to bem quanto possvel.
14
depurao (debugging) O processo de encontrar e remover qualquer um dos trs tipos de erros de programao. erro de semntica ou lgica (semantic error) Erro em um programa, que o leva a fazer algo diferente do que pretendia o programador. erro de sintaxe (syntax error) Erro em um programa, que torna impossvel a anlise sinttica (logo, tambm impossvel a interpretao). erro em tempo de execuo (runtime error) Erro que no ocorre at que o programa seja executado, mas que impede que o programa continue. exceo (exception) Um outro nome para um erro em tempo de execuo ou erro de runtime. executvel (executable) Um outro nome para cdigo objeto que est pronto para ser executado. interpretar (interpret) Executar um programa escrito em uma linguagem de alto nvel, traduzindo-o uma linha de cada vez. linguagem de alto nvel (high-level language) Uma linguagem de programao como Python: projetada para ser fcil para os seres humanos utilizarem. linguagem de baixo nvel (low-level language) Uma linguagem de programao que concebida para ser fcil para um computador, tal como a linguagem de mquina ou a linguagem montagem (assembly language) linguagem formal (formal language) Qualquer linguagem desenvolvida pelas pessoas para propsitos especcos, tais como, a representao de ideias matemticas ou programas de computadores; todas as linguagens de programao so linguagens formais. linguagem natural (natural language) Qualquer lngua falada pelos seres humanos que tenha evoludo naturalmente. portabilidade (portability) Propriedade que um programa tem de rodar em mais de um tipo de computador. programa (program) Conjunto de instrues que especica uma computao. script Um programa guardado em um arquivo (normalmente um que ser interpretado). semntica (semantics) O signicado de um programa. smbolo (token) Um elemento bsico da estrutura sinttica de um programa, anlogo a uma palavra em uma linguagem natural. sintaxe (syntax) A estrutura de um programa. soluo de problemas (problem solving) O processo de formular um problema, encontrar uma soluo e expressar esta soluo.
15
16
CAPTULO 4
Tpicos Captulo 2: Variveis, expresses e comandos 2.1 Valores e tipos 2.2 Variveis 2.3 Nomes de variveis e palavras reservadas 2.4 Comandos 2.5 Avaliando expresses 2.6 Operadores e operandos 2.7 Ordem dos operadores 2.8 Operaes com strings 2.9 Composio 2.11 Glossrio
Se voc estiver em dvida sobre qual o tipo de um determinado valor, o interpretador pode revelar:
>>> type("Al, Mundo!") <type string>
17
Nenhuma surpresa: strings pertencem ao tipo string e inteiros pertencem ao tipo int. Menos obviamente, nmeros com um ponto decimal pertencem a um tipo chamado float, porque estes nmeros so representados em um formato chamado ponto utuante 1 :
>>> type(3.2) <type float>
O que dizer de valores como "17" e "3.2"? Eles parecem nmeros, mas esto entre aspas, como strings:
>>> type("17") <type string> >>> type("3.2") <type string>
Eles so strings. Ao digitar um nmero grande, tentador usar pontos entre grupos de trs dgitos, assim: 1.000.000. Isso no funciona por que Python usa o ponto como separador decimal. Usar a vrgula, como se faz em ingls, resulta numa expresso vlida, mas no no nmero que queramos representar:
>>> print 1,000,000 1 0 0
No nada do que se esperava! Python interpreta 1,000,000 como uma tupla, algo que veremos no Captulo 9. Por hora, lembre-se apenas de no colocar vrgulas nos nmeros.
Este exemplo faz trs atribuies. A primeira atribui a string "E a, Doutor?" a uma nova varivel chamada mensagem. A segunda d o valor inteiro 17 a n, e a terceira atribui o nmero de ponto utuante 3.14159 varivel chamada pi. Uma maneira comum de representar variveis no papel escrever o nome delas com uma seta apontando para o valor da varivel. Esse tipo de gura chamado de diagrama de estado porque mostra em que estado cada varivel est (pense nisso como o estado de esprito da varivel). O diagrama a seguir mostra o resultado das instrues de atribuio:
1
N.T.: Observe o uso de ponto no lugar da vrgula para separar a parte inteira da parte fracionria.
18
Em cada um dos casos, o resultado o valor da varivel. Variveis tambm tm tipo. Novamente, podemos perguntar ao interpretador quais so eles:
>>> type(mensagem) <type string> >>> type(n) <type int> >>> type(pi) <type float>
76trombones invlida por no comear com uma letra. muito$ invlida por conter um caractere ilegal, o cifro. Mas o que est errado com class?
19
Ocorre que class uma das palavras reservadas em Python. Palavras reservadas denem as regras e a estrutura da linguagem e no podem ser usadas como nomes de variveis. Python tem 29 palavras reservadas:
and assert break class continue def del elif else except exec finally for from global if import in is lambda not or pass print raise return try while yield
Pode ser til ter essa lista mo 2 . Se o interpretador acusar erro sobre um de seus nomes de varivel e voc no souber o porqu, veja se o nome est na lista.
produz a sada:
1 2
Embora expresses contenham valores, variveis e operadores, nem toda expresso contm todos estes elementos. Um valor por si s considerado uma expresso, do mesmo modo que uma varivel:
>>> 17 17 >>> x 2
2 N.T.: esta lista pode ser obtida atravs do prprio interpretador Python, com apenas dois comandos: import keyword; print keyword.kwlist
20
Quando Python exibe o valor de uma expresso, usa o mesmo formato que voc usaria para entrar com o valor. No caso de strings, isso signica que as aspas so includas 3 . Mas o comando print imprime o valor da expresso, que, neste caso, o contedo da string. Num script, uma expresso sozinha um comando vlido, porm sem efeito. O script:
17 3.2 "Al, Mundo!" 1 + 1
no produz qualquer sada. Como voc mudaria o script para exibir os valores destas quatro expresses?
Em Python, os smbolos +, -, / e o uso de parnteses para agrupamento tm o mesmo signicado que em matemtica. O asterisco (*) o smbolo para multiplicao e ** o smbolo para potenciao. Quando um nome de varivel aparece no lugar de um operando, ele substitudo pelo valor da varivel, antes da operao ser executada. Adio, subtrao, multiplicao e potenciao fazem o que se espera, mas voc pode car surpreso com a diviso. A operao seguinte tem um resultado inesperado:
>>> minuto = 59 >>> minuto/60 0
O valor de minuto 59 e, em aritmtica convencional, 59 dividido por 60 0,98333, no 0. A razo para a discrepncia que Python est realizando uma diviso inteira. Quando ambos os operandos so inteiros, o resultado tem de ser tambm um inteiro e, por conveno, a diviso inteira sempre arredonda para baixo, mesmo em casos como este, em que o inteiro seguinte est muito prximo:
>>> minuto*100/60 98
De novo, o resultado arredondado para baixo, mas agora pelo menos a resposta aproximadamente correta. A alternativa usar a diviso em ponto utuante, o que veremos no captulo 3.
3
21
Interessante o operador +, que funciona com strings, embora ele no faa exatamente o que voc poderia esperar. Para strings, o operador + representa concatenao, que signica juntar os dois operandos ligando-os pelos extremos. Por exemplo:
fruta = "banana" assada = " com canela" print fruta + assada
A sada deste programa banana com canela. O espao antes da palavra com parte da string e necessrio para produzir o espao entre as strings concatenadas. O operador * tambm funciona com strings; ele realiza repetio. Por exemplo, "Legal"*3 "LegalLegaLegal". Um dos operadores tem que ser uma string; o outro tem que ser um inteiro. Por um lado, esta interpretao de + e * faz sentido pela analogia entre adio e multiplicao. Assim como 4*3 equivale a 4+4+4, no de estranhar que "Legal"*3 seja o mesmo que "Legal"+"Legal"+"Legal". Por outro lado, uma diferena signicativa separa concatenao e repetio de adio e multiplicao. Voc saberia mencionar uma propriedade da adio e da multiplicao que no ocorre na concatenao e na repetio?
22
Na realidade, a soma tem que acontecer antes da impresso, assim, as aes no esto na realidade acontecendo ao mesmo tempo. O ponto que qualquer expresso envolvendo nmeros, strings, e variveis pode ser usada dentro de um comando print. Voc j tinha visto um exemplo disto:
print "Nmero de minutos desde a meia-noite: ", hora*60+minuto
Esta possibilidade pode no parecer muito impressionante agora, mas voc ver outros exemplos em que a composio torna possvel expressar clculos e tarefas complexas de modo limpo e conciso. Ateno: Existem limites quanto ao lugar onde voc pode usar certos tipos de expresso. Por exemplo, o lado esquerdo de um comando de atribuio tem que ser um nome de varivel, e no uma expresso. Assim, o seguinte no vlido: minuto+1 = hora.
23
regras de precedncia (rules of precedence) O conjunto de regras que governa a ordem em que expresses envolvendo mltiplos operadores e operandos so avaliadas. tipo (type) Um conjunto de valores. O tipo de um valor determina como ele pode ser usado em expresses. At agora, os tipos vistos so: inteiros (tipo int), nmeros em ponto-utuante (tipo float) e strings (tipo string). valor (value) Um nmero ou string (ou outra coisa que ainda vamos conhecer) que pode ser atribuda a uma varivel ou computada em uma expresso. varivel (variable) Nome que se refere a um valor.
24
CAPTULO 5
Captulo 3: Funes
Tpicos Captulo 3: Funes 3.1 Chamadas de funes 3.2 Converso entre tipos 3.3 Coero entre tipos 3.4 Funes matemticas 3.5 Composio 3.6 Adicionando novas funes 3.7 Denies e uso 3.8 Fluxo de execuo 3.9 Parmetros e argumentos 3.10 Variveis e parmetros so locais 3.11 Diagramas da pilha 3.12 Funes com resultados 3.13 Glossrio
O nome da funo type e ela exibe o tipo de um valor ou varivel. O valor ou varivel, que chamado de argumento da funo, tem que vir entre parnteses. comum se dizer que uma funo recebe um valor ou mais valores e retorna um resultado. O resultado chamado de valor de retorno. Em vez de imprimir um valor de retorno, podemos atribui-lo a uma varivel:
>>> bia = type(32) >>> print bia <type str>
25
Como outro exemplo, a funo id recebe um valor ou uma varivel e retorna um inteiro, que atua como um identicador nico para aquele valor:
>>> id(3) 134882108 >>> bia = 3 >>> bia(beth) 134882108
Todo valor tem um id, que um nmero nico relacionado ao local onde ele est guardado na memria do computador. O id de uma varivel o id do valor a qual ela se refere.
int tambm pode converter valores em ponto utuante para inteiro, mas lembre que isso trunca a parte fracionria:
>>> int(3.99999) 3 >>> int(-2.3) -2
Pode parecer curioso que Python faa distino entre o valor inteiro 1 e o valor em ponto utuante 1.0. Eles podem representar o mesmo nmero, mas pertencem a tipos diferentes. A razo que eles so representados de modo diferente dentro do computador.
26
Opcionalmente, podemos tirar vantagem das regras de converso automtica entre tipos, chamada de coero de tipos. Para os operadores matemticos, se qualquer operando for um float, o outro automaticamente convertido para float:
>>> minuto = 59 >>> minuto / 60.0 0.983333333333
Para chamar uma das funes, temos que especicar o nome do mdulo e o nome da funo, separados por um ponto. Esse formato chamado de notao de ponto:
>>> decibel = math.log10(17.0) >>> angulo = 1.5 >>> altura = math.sin(angulo)
A primeira instruo atribui a decibel o logaritmo de 17 na base 10. Existe tambm uma funo chamada log, usada para calcular o logaritmo em outra base ou o logaritmo natural de um nmero (base e). A terceira instruo encontra o seno do valor da varivel angulo. sin e as outras funes trigonomtricas (cos, tan, etc.) recebem argumentos em radianos. Para converter de graus em radianos, divida por 360 e multiplique por 2*pi. Por exemplo, para encontrar o seno de 45 graus, primeiro calcule o ngulo em radianos e depois ache o seno:
>>> graus = 45 >>> angulo = graus * 2 * math.pi / 360.0 >>> math.sin(angulo) 0.707106781187
A constante pi tambm parte do mdulo math. Se voc sabe geometria, pode checar o resultado anterior comparando-o com a raiz quadrada de dois dividido por dois:
>>> math.sqrt(2) / 2.0 0.707106781187
27
Esta instruo toma o valor de pi, divide-o por 2, e soma o resultado ao valor de angulo. A soma ento passada como um argumento para a funo cos. Voc tambm pode pegar o resultado de uma funo e pass-lo como um argumento para outra:
>>> x = math.exp(math.log(10.0))
Esta instruo encontra o logaritmo natural (base e) de 10 e ento eleva e quela potncia. O resultado atribudo a x.
Voc pode usar o nome que quiser para as funes que criar, exceto as palavras reservadas do Python. A lista de parmetros especica que informao, se houver alguma, voc tem que fornecer para poder usar a nova funo. Uma funo pode ter quantos comandos forem necessrios, mas eles precisam ser endentados a partir da margem esquerda. Nos exemplos deste livro, usaremos uma endentao de dois espaos. As primeiras funes que vamos mostrar no tero parmetros, ento, a sintaxe ter esta aparncia:
def novaLinha(): print
Esta funo chamada de novaLinha. Os parnteses vazios indicam que ela no tem parmetros. Contm apenas um nico comando, que gera como sada um caractere de nova linha (isso o que acontece quando voc usa um comando print sem qualquer argumento). A sintaxe para a chamada desta nova funo a mesma sintaxe para as funes nativas:
print Primeira Linha. novaLinha() print Segunda Linha.
28
Observe o espao extra entre as duas linhas. E se quisssemos mais espao entre as linhas? Poderamos chamar a mesma funo repetidamente:
print Primeira Linha. novaLinha() novaLinha() novaLinha() print Segunda Linha.
Ou poderamos escrever uma nova funo chamada tresLinhas, que produzisse trs novas linhas:
def tresLinhas() : novaLinha() novaLinha() novaLinha() print Primeira Linha. tresLinhas() print Segunda Linha.
Esta funo contm trs comandos, todos com recuo de dois espaos a partir da margem esquerda. J que o prximo comando no est endentado, Python reconhece que ele no faz parte da funo. Algumas coisas que devem ser observadas sobre este programa: 1. Voc pode chamar o mesmo procedimento repetidamente. Isso muito comum, alm de til. 2. Voc pode ter uma funo chamando outra funo; neste caso tresLinhas chama novaLinha. Pode no estar claro, at agora, de que vale o esforo de criar novas funes - existem vrias razes, mas este exemplo demonstra duas delas: Criar uma nova funo permite que voc coloque nome em um grupo de comandos. As funes podem simplicar um programa ao ocultar a execuo de uma tarefa complexa por trs de um simples comando cujo nome pode ser uma palavra em portugus, em vez de algum cdigo misterioso. Criar uma nova funo pode tornar o programa menor, por eliminar cdigo repetido. Por exemplo, um atalho para imprimir nove novas linhas consecutivas chamar tresLinhas trs vezes. Como exerccio, escreva uma funo chamada noveLinhas que use tresLinhas para imprimir nove linhas em branco. Como voc poderia imprimir vinte e sete novas linhas?
29
Esse programa contm duas denies de funes: novaLinha e tresLinhas. Denies de funes so executadas como quaisquer outros comandos, mas o efeito criar a nova funo. Os comandos dentro da denio da funo no so executados at que a funo seja chamada, logo, a denio da funo no gera nenhuma sada. Como voc j deve ter imaginado, preciso criar uma funo antes de poder execut-la. Em outras palavras, a denio da funo tem que ser executada antes que ela seja chamada pela primeira vez. Como exerccio, mova as ltimas trs linhas deste programa para o topo, de modo que a chamada da funo aparea antes das denies. Rode o programa e veja que mensagem de erro voc ter. Tambm a ttulo de exerccio, comece com a verso que funciona do programa e mova a denio de novaLinha para depois da denio de tresLinhas. O que acontece quando voc roda este programa?
30
Esta funo recebe um nico argumento e o atribui a um parmetro chamado bruno. O valor do parmetro (a essa altura, no sabemos qual ser) impresso duas vezes, seguido de uma nova linha. Estamos usando bruno para mostrar que o nome do parmetro deciso sua, mas claro que melhor escolher um nome que seja mais ilustrativo. A funo imprimeDobrado funciona para qualquer tipo que possa ser impresso:
>>> imprimeDobrado(Spam) Spam Spam >>> imprimeDobrado(5) 5 5 >>> imprimeDobrado(3.14159) 3.14159 3.14159
Na primeira chamada da funo, o argumento uma string. Na segunda, um inteiro. Na terceira um float. As mesmas regras de composio que se aplicam a funes nativas tambm se aplicam s funes denidas pelo usurio, assim, podemos usar qualquer tipo de expresso como um argumento para imprimeDobrado:
>>> imprimeDobrado(Spam*4) SpamSpamSpamSpam SpamSpamSpamSpam >>> imprimeDobrado(math.cos(math.pi)) -1.0 -1.0
Como acontece normalmente, a expresso avaliada antes da execuo da funo, assim imprimeDobrado imprime SpamSpamSpamSpam SpamSpamSpamSpam em vez de Spam*4 Spam*4. Como exerccio, escreva um chamada a imprimeDobrado que imprima Spam*4 Spam*4. Dica: strings podem ser colocadas tanto entre aspas simples quanto duplas e o tipo de aspas que no for usado para envolver a string pode ser usado dentro da string, como parte dela. Tambm podemos usar uma varivel como argumento:
>>> miguel = Eric, the half a bee. >>> imprimeDobrado(miguel) Eric, the half a bee. Eric, the half a bee.
N.T.: Eric, the half a bee uma msica do grupo humorstico britnico Monty Python. A linguagem Python foi batizada em homenagem ao grupo e, por isso, os programadores gostam de citar piadas deles em seus exemplos. Repare numa coisa importante: o nome da varivel que passamos como um argumento (miguel) no tem nada a ver com o nome do parmetro (bruno). No importa de que modo o valor foi chamado de onde veio (do chamador); aqui, em imprimeDobrado, chamamos a todo mundo de bruno.
Esta funo recebe dois argumentos, concatena-os, e ento imprime o resultado duas vezes. Podemos chamar a funo com duas strings:
>>> >>> >>> Pie canto1 = Pie Jesu domine, canto2 = dona eis requiem. concatDupla(canto1, canto2) Jesu domine, Dona eis requiem. Pie Jesu domine, Dona eis requiem.
31
Quando a funo concatDupla termina, a varivel concat destruda. Se tentarmos imprimi-la, teremos um erro:
>>> print concat NameError: concat
Parmetros so sempre locais. Por exemplo, fora da funo imprimeDobrado, no existe nada que se chama bruno. Se voc tentar utiliz-la, o Python vai reclamar.
A ordem da pilha mostra o uxo de execuo. imprimeDobrado foi chamado por concatDupla, e concatDupla foi chamado por __main__ (principal), que um nome especial para a funo mais no topo. Quando voc cria uma varivel fora de qualquer funo, ela pertence __main__. Cada parmetro se refere ao mesmo valor que o seu argumento correspondente. Assim, parte1 tem o mesmo valor de canto1, parte2 tem o mesmo valor de canto2 e bruno tem o mesmo valor de concat. Se um erro acontece durante uma chamada de funo, Python imprime o nome da funo, e o nome da funo que a chamou, e o nome da funo que chamou a que chamou, percorrendo todo o caminho de volta a __main__. Por exemplo, se tentssemos acessar concat de dentro de imprimeDobrado, teramos um NameError:
Traceback (innermost last): File "teste.py", line 13, in __main__ concatDupla(canto1, canto2) File "teste.py", line 5, in concatDupla imprimeDobrado(concat) File "teste.py", line 9, in imprimeDobrado
32
Esta lista de funes chamada de traceback. Ela mostra em qual arquivo de programa o erro ocorreu, em que linha, e quais funes estavam sendo executadas naquele momento. Mostra tambm a linha de cdigo que causou o erro. Note a similaridade entre o traceback e o diagrama da pilha. No coincidncia.
33
notao de ponto (dot notation) A sintaxe para chamar uma funo que est em outro mdulo, especicando o nome do mdulo, seguido por um ponto (.) e o nome da funo. parmetro (parameter) Nome usado numa funo para referir-se a um valor passado como argumento. traceback Lista de funes que esto em execuo, impressa quando um erro de execuo ocorre. valor de retorno (return value) O resultado da funo. Se uma chamada de funo usada como expresso, o valor de retorno o valor da expresso. varivel local (local variable) Varivel denida dentro da funo. Uma varivel local s pode ser usada dentro da funo onde foi denida.
34
CAPTULO 6
Tpicos Captulo 4: Condicionais e recursividade 4.1 O operador mdulo 4.2 Expresses booleanas 4.3 Operadores lgicos 4.4 Execuo condicional 4.5 Execuo alternativa 4.6 Condicionais encadeados 4.7 Condicionais aninhados 4.8 A instruo return 4.9 Recursividade 4.10 Diagramas de pilha para funes recursivas 4.11 Recursividade innita 4.12 Entrada pelo teclado 4.13 Glossrio
35
O operador mdulo se revela surpreendentemente til. Por exemplo, voc pode checar se um nmero divisvel por outro - se x % y d zero, ento x divisvel por y. Voc tambm pode extrair o algarismo ou algarismos mais direita de um nmero. Por exemplo, x % 10 resulta o algarismo mais direita de x (na base 10). Similarmente, x % 100 resulta nos dois dgitos mais direita.
No primeiro comando, os dois operadores so iguais, ento a expresso avalia como True (verdadeiro); no segundo comando, 5 no igual a 6, ento temos False (falso). O operador == um dos operadores de comparao; os outros so:
x x x x x != y > y < y >= y <= y # # # # # x x x x x diferente de y maior que y menor que y maior ou igual a y menor ou igual a y
Embora esses operadores provavelmente sejam familiares a voc, os smbolos em Python so diferentes dos smbolos da matemtica. Um erro comum usar um sinal de igual sozinho (=) em vez de um duplo (==). Lembre-se de que = um operador de atribuio e == um operador de comparao. Tambm no existem coisas como =< ou =>.
Em geral, esse tipo de coisa no considerado de bom estilo. Se voc precisa comparar um valor com zero, deve faz-lo explicitamente. 36 Captulo 6. Captulo 4: Condicionais e recursividade
A expresso booleana depois da instruo if chamada de condio. Se ela verdadeira (true), ento a instruo endentada executada. Se no, nada acontece. Assim como outras instrues compostas, a instruo if constituda de um cabealho e de um bloco de instrues:
CABECALHO: PRIMEIRO COMANDO ... ULTIMO COMANDO
O cabealho comea com uma nova linha e termina com dois pontos (:). Os comandos ou instrues endentados que seguem so chamados de bloco. A primeira instruo no endentada marca o m do bloco. Um bloco de comandos dentro de um comando composto ou instruo composta chamado de corpo do comando. No existe limite para o nmero de instrues que podem aparecer no corpo de uma instruo if, mas tem que haver pelo menos uma. Ocasionalmente, til ter um corpo sem nenhuma instruo (usualmente, como um delimitador de espao para cdigo que voc ainda no escreveu). Nesse caso, voc pode usar o comando pass, que indica ao Python: passe por aqui sem fazer nada.
Se o resto da diviso de x por 2 for 0, ento sabemos que x par, e o programa exibe a mensagem para esta condio. Se a condio falsa, o segundo grupo de instrues executado. Desde que a condio deva ser verdadeira (True) ou falsa (False), precisamente uma das alternativas vai ser executada. As alternativas so chamadas ramos (branches), porque existem ramicaes no uxo de execuo. Por nal, se voc precisa checar a paridade de nmeros com frequncia, pode colocar este cdigo dentro de uma funo:
def imprimeParidade(x): if x % 2 == 0: print x, " par" else: print x, " impar"
Para qualquer valor de x, imprimeParidade exibe uma mensagem apropriada. Quando voc a chama, pode fornecer uma expresso de resultado inteiro como um argumento:
>>> imprimeParidade(17) >>> imprimeParidade(y+1)
37
elif uma abreviao de else if (seno se). De novo, precisamente um ramo ser executado. No existe limite para o nmero de instrues elif, mas se existir uma instruo else ela tem que vir por ltimo:
if escolha == A: funcaoA() elif escolha == B: funcaoB() elif escolha == C: funcaoC() else: print "Escolha invlida."
Cada condio checada na ordem. Se a primeira falsa, a prxima checada, e assim por diante. Se uma delas verdadeira, o ramo correspondente executado, e a instruo termina. Mesmo que mais de uma condio seja verdadeira, apenas o primeiro ramo verdadeiro executa. Como exerccio, coloque os exemplos acima em funes chamadas comparar(x, y) e executar(escolha).
O condicional mais externo tem dois ramos. O primeiro ramo contm uma nica instruo de sada. O segundo ramo contm outra instruo if, que por sua vez tem dois ramos. Os dois ramos so ambos instrues de sada, embora pudessem conter instrues condicionais tambm. Embora a endentao das instrues torne a estrutura aparente, condicionais aninhados tornam-se difceis de ler rapidamente. Em geral, uma boa ideia evitar o aninhamento quando for possvel. Operadores lgicos frequentemente fornecem uma maneira de simplicar instrues condicionais aninhadas. Por exemplo, podemos reescrever o cdigo a seguir usando uma nica condicional:
if 0 < x: if x < 10: print "x um nmero positivo de um s algarismo."
38
A instruo print executada somente se a zermos passar por ambos os condicionais, ento, podemos usar um operador and:
if 0 < x and x < 10: print "x um nmero positivo de um s algarismo."
Esses tipos de condies so comuns, assim, Python prov uma sintaxe alternativa que similar notao matemtica:
if 0 < x < 10: print "x um nmero positivo de um s algarismo."
A funo imprimeLogaritmo recebe um parmetro de nome x. A primeira coisa que ela faz checar se x menor ou igual a 0, neste caso ela exibe uma mensagem de erro e ento usa return para sair da funo. O uxo de execuo imediatamente retorna ao ponto chamador, quer dizer, de onde a funo foi chamada, e as linhas restantes da funo no so executadas. Lembre-se que para usar uma funo do mdulo de matemtica, math, voc tem de import-lo.
contagemRegressiva espera que o parmetro, n, seja um inteiro positivo. Se n for 0, ela produz como sada a palavra Fogo!. De outro modo, ela produz como sada n e ento chama uma funo de nome contagemRegressiva ela mesma passando n-1 como argumento. O que acontece se chamarmos essa funo da seguinte maneira:
>>> contagemRegressiva(3)
39
A execuo de contagemRegressiva comea com n=3, e desde que n no 0, produz como sada o valor 3, e ento chama a si mesma... A execuo de contagemRegressiva comea com n=2, e desde que n no 0, produz como sada o valor 2, e ento chama a si mesma... A execuo de contagemRegressiva comea com n=1, e desde que n no 0, produz como sada o valor 1, e ento chama a si mesma... A execuo de contagemRegressiva comea com n=0, e desde que n 0, produz como sada a palavra Fogo! e ento retorna. A contagemRegressiva que tem n=1 retorna. A contagemRegressiva que tem n=2 retorna. A contagemRegressiva que tem n=3 retorna. E ento estamos de volta em __main__ (que viagem!). Assim, a sada completa se parece com:
3 2 1 Fogo!
Como um segundo exemplo, d uma olhada novamente nas funes novaLinha e tresLinhas:
def novaLinha(): print def tresLinhas(): novaLinha() novaLinha() novaLinha()
Muito embora isso funcione, no seria muito til se precisssemos gerar como sada 2 novas linhas, ou 106. Uma alternativa melhor seria esta:
def nLinhas(n): if n > 0: print nLinhas(n-1)
Esse programa similar a contagemRegressiva. Sempre que n for maior que 0, ele gera como sada uma nova linha e ento chama a si mesmo para gerar como sada n-1 linhas adicionais. Deste modo, o nmero total de novas linhas 1 + (n-1) que, se voc estudou lgebra direitinho, vem a ser o prprio n. O processo de uma funo chamando a si mesma chamado de recursividade, e tais funes so ditas recursivas.
40
Como de costume, no topo da pilha est o quadro para __main__. Ele est vazio porque nem criamos qualquer varivel em __main__ nem passamos qualquer valor para ele. Os quatro quadros contagemRegressiva tm valores diferentes para o parmetro n. A parte mais em baixo na pilha, onde n=0, chamada de caso base. Ele no faz uma chamada recursiva, ento no h mais quadros. Como exerccio, desenhe um diagrama de pilha para nLinhas chamada com n=4.
Na maioria dos ambientes de programao, um programa com recursividade innita na verdade no roda para sempre. Python reporta uma mensagem de erro quando a profundidade mxima de recursividade alcanada:
File "<stdin>", line 2, in recursiva (98 repetitions omitted) File "<stdin>", line 2, in recursiva RuntimeError: Maximum recursion depth exceeded
Este traceback um pouco maior do que aquele que vimos no captulo anterior. Quando o erro ocorre, existem 100 quadros recursiva na pilha! Como exerccio, escreva uma funo com recursividade innita e rode-a no interpretador Python.
41
Antes de chamar raw_input, uma boa ideia exibir uma mensagem dizendo ao usurio o que ele deve entrar. Esta mensagem uma como se fosse uma pergunta (prompt). Esta pergunta pode ser enviada como um argumento para raw_input:
>>> nome = raw_input("Qual... o seu nome? ") Qual... o seu nome? Arthur, Rei dos Bretes! >>> print nome Arthur, Rei dos Bretes!
Se o usurio digita uma string de nmeros, ela convertida para um inteiro e atribuda a velocidade. Infelizmente, se o usurio digitar um caractere que no seja um nmero, o programa trava:
>>> velocidade = input(pergunta) Qual... a velocidade de vo de uma andorinha? De qual voc fala, uma andorinha Africana ou uma Europeia? SyntaxError: invalid syntax
Para evitar esse tipo de erro, geralmente bom usar raw_input para pegar uma string e, ento, usar funes de converso para converter para outros tipos.
42
expresso booleana (boolean expression) Uma expresso que verdadeira ou falsa. operador de comparao (comparison operator) Um dos operadores que compara dois valores: ==, !=, >, <, >=, e <=. operador lgico (logical operator) Um dos operadores que combina expresses booleanas: and, or, e not. operador mdulo (modulus operator) Operador denotado por um smbolo de porcentagem (%), que trabalha com inteiros e retorna o resto da diviso de um nmero por outro. prompt Indicao visual que diz ao usurio que o programa est esperando uma entrada de dados. recursividade (recursion) O processo de chamar a prpria funo que est sendo executada. recursividade innita (innite recursion) Funo que chama a si mesma recursivamente sem nunca chegar ao caso base. Aps algum tempo, uma recursividade innita causa um erro de execuo.
43
44
CAPTULO 7
Tpicos Captulo 5: Funes frutferas 5.1 Valores de retorno 5.2 Desenvolvimento de programas 5.3 Composio 5.4 Funes booleanas 5.5 Mais recursividade 5.6 Voto de conana (Leap of faith) 5.7 Mais um exemplo 5.8 Checagem de tipos 5.9 Glossrio
Mas at agora, nenhuma das funes que ns escrevemos retornou um valor. Neste captulo, iremos escrever funes que retornam valores, as quais chamaremos de funes frutferas, ou funes que do frutos, na falta de um nome melhor. O primeiro exemplo area, que retorna a rea de um crculo dado o seu raio:
import math def area(raio): temp = math.pi * raio**2 return temp
45
J vimos a instruo return antes, mas em uma funo frutfera a instruo return inclui um valor de retorno. Esta instruo signica: Retorne imediatamente desta funo e use a expresso em seguida como um valor de retorno. A expresso fornecida pode ser arbitrariamente complicada, de modo que poderamos ter escrito esta funo de maneira mais concisa:
def area(raio): return math.pi * raio**2
Por outro lado, variveis temporrias como temp muitas vezes tornam a depurao mais fcil. s vezes til ter mltiplos comandos return, um em cada ramo de uma condicional:
def valorAbsoluto(x): if x < 0: return -x else: return x
J que estes comandos return esto em ramos alternativos da condicional, apenas um ser executado. To logo um seja executado, a funo termina sem executar qualquer instruo ou comando subsequente. O cdigo que aparece depois de uma instruo return, ou em qualquer outro lugar que o uxo de execuo jamais alcance, chamado cdigo morto (dead code). Em uma funo frutfera, uma boa ideia assegurar que todo caminho possvel dentro do programa encontre uma instruo return. Por exemplo:
def valorAbsoluto(x): if x < 0: return -x elif x > 0: return x
Este programa no est correto porque se x for 0, nenhuma das condies ser verdadeira, e a funo terminar sem encontrar um comando return. Neste caso, o valor de retorno ser um valor especial chamado None:
>>> print valorAbsoluto(0) None
Como exerccio, escreva uma funo compare que retorne 1 se x > y, 0 se x == y e -1 se x < y.
XXX: falta o sinal de raiz e elevar os expoentes desta frmula O primeiro passo considerar como deveria ser uma funo distancia em Python. Em outras palavras, quais so as entradas (parmetros) e qual a sada (valor de retorno)? 46 Captulo 7. Captulo 5: Funes frutferas
Neste caso, os dois pontos so as entradas, os quais podemos representar usando quatro parmetros. O valor de retorno a distncia, que um valor em ponto utuante. J podemos escrever um esboo da funo:
def distancia(x1, y1, x2, y2): return 0.0
Obviamente, esta verso da funo no calcula distncias; ela sempre retorna zero. Mas ela est sintaticamente correta, e vai rodar, o que signica que podemos test-la antes de torn-la mais complicada. Para testar a nova funo, vamos cham-la com valores hipotticos:
>>> distancia(1, 2, 4, 6) 0.0
Escolhemos estes valores de modo que a distncia horizontal seja igual a 3 e a distncia vertical seja igual a 4; deste modo, o resultado 5 (a hipotenusa de um tringulo 3-4-5). Quando testamos uma funo, til sabermos qual o resultado correto. Neste ponto, j conrmamos que a funo est sintaticamente correta, e podemos comear a adicionar linhas de cdigo. Depois de cada mudana adicionada, testamos a funo de novo. Se um erro ocorre em qualquer ponto, sabemos aonde ele deve estar: nas linhas adicionadas mais recentemente. Um primeiro passo lgico nesta operao encontrar as diferenas x2 - x1 e y2 - y1. Ns iremos guardar estes valores em variveis temporrias chamadas dx e dy e imprimi-las:
def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 print "dx vale", dx print "dy vale", dy return 0.0
Se a funo estiver funcionando, as sadas devero ser 3 e 4. Se assim, sabemos que a funo est recebendo os parmetros corretos e realizando o primeiro clculo corretamente. Se no, existem poucas linhas para checar. Em seguida, calcularemos a soma dos quadrados de dx e dy:
def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dquadrado = dx**2 + dy**2 print "dquadrado vale: ", dquadrado return 0.0
Note que removemos os comandos print que havamos escrito no passo anterior. Cdigo como este ajuda a escrever o programa, mas no parte do produto nal (em ingls usado o termo scaffolding). De novo, ns vamos rodar o programa neste estgio e checar a sada (que deveria ser 25). Finalmente, se ns tnhamos importado o mdulo matemtico math, podemos usar a funo sqrt para computar e retornar o resultado:
def distancia(x1, x2, y1, y2): dx = x2 - x1 dy = y2 - y1 dquadrado = dx**2 + dy**2 resultado = math.sqrt(dquadrado) return resultado
Se isto funcionar corretamente, voc conseguiu. Caso contrrio, talvez fosse preciso imprimir (exibir) o valor de resultado antes da instruo return. 7.2. 5.2 Desenvolvimento de programas 47
Enquanto for iniciante, voc deve acrescentar apenas uma ou duas linhas de cdigo de cada vez. Conforme ganhar mais experincia, voc se ver escrevendo e depurando pedaos maiores. De qualquer modo, o processo de desenvolvimento incremental pode poupar um bocado de tempo de depurao. Os aspectos chave do processo so: 1. Comece com um programa que funciona e faa pequenas mudanas incrementais. Em qualquer ponto do processo, se houver um erro, voc saber exatamente onde ele est. 2. Use variveis temporrias para manter valores intermedirios de modo que voc possa exibi-los e chec-los. 3. Uma vez que o programa funcione, voc pode querer remover algum cdigo muleta, ou algum scaffolding ou consolidar mltiplos comandos dentro de expresses compostas, mas somente se isto no tornar o programa difcil de ler. Como um exerccio, use o desenvolvimento incremental para escrever uma funo chamada hipotenusa que retorna a medida da hipotenusa de um tringulo retngulo dadas as medidas dos dois catetos como parmetros. Registre cada estgio do desenvolvimento incremental conforme voc avance.
Chamamos esta funo de area2 para distinguir da funo area, denida anteriormente. S pode existir uma nica funo com um determinado nome em um determinado mdulo. As variveis temporrias raio e resultado so teis para o desenvolvimento e para depurao (debugging), mas uma vez que o programa esteja funcionando, podemos torn-lo mais conciso atravs da composio das chamadas de funo:
def area2(xc, yc, xp, yp): return area(distancia(xc, yc, xp, yp))
Como exerccio, escreva uma funo coeficienteAngular(x1, y1, x2, y2) que retorne a coeciente angular de uma linha dados os pontos (x1, y1) e (x2, y2). Depois use esta funo em uma funo chamada cortaY(x1, y1, x2, y2) que retorne a interseo da linha com o eixo y, dados os pontos (x1, y1) e (x2, y2). 48 Captulo 7. Captulo 5: Funes frutferas
O nome desta funo ehDivisivel ( divisvel). comum dar a uma funo booleana nomes que soem como perguntas sim/no. ehDivisivel retorna ou True ou False para indicar se x ou no divisvel por y. Podemos tornar a funo mais concisa se tirarmos vantagem do fato de a condio da instruo if ser ela mesma uma expresso booleana. Podemos retorn-la diretamente, evitando totalmente o if:
def ehDivisivel(x, y): return x % y == 0
Mas a comparao extra desnecessria. Como exerccio, escreva uma funo estaEntre(x, y, z) que retorne True se y < x < z ou False, se no.
49
Se voc visse esta denio em um dicionrio, caria confuso. Por outro lado, se voc procurasse pela denio da funo matemtica fatorial, voc encontraria algo assim:
0! = 1 n! = n.(n-1)!
Esta denio diz que o fatorial de 0 1, e que o fatorial de qualquer outro valor, n, n multiplicado pelo fatorial de n-1. Assim, 3! (l-se 3 fatorial ou fatorial de 3) 3 vezes 2!, o qual 2 vezes 1!, o qual 1 vezes 0!. Colocando tudo isso junto, 3! igual 3 vezes 2 vezes 1 vezes 1, o que 6. Se voc pode escrever uma denio recursiva de alguma coisa, voc geralmente pode escrever um programa em Python para execut-la. O primeiro passo decidir quais so os parmetros para esta funo. Com pouco esforo, voc dever concluir que fatorial recebe um nico parmetro:
def fatorial(n):
Por outro lado, e esta a parte interessante, temos que fazer uma chamada recursiva para encontrar o fatorial de n-1 e ento multiplic-lo por n:
def fatorial(n): if n == 0: return 1 else: recursivo = fatorial(n-1) resultado = n * recursivo return resultado
O uxo de execuo para este programa similar ao uxo de contagemRegressiva na Seo 4.9. Se chamarmos fatorial com o valor 3: J que 3 no 0, tomamos o segundo ramo e calculamos o fatorial de n-1 ... J que 2 no 0, tomamos o segundo ramo e calculamos o fatorial de n-1 ... J que 1 no 0, tomamos o segundo ramo e calculamos o fatorial de n-1 ... J que 0 0, tomamos o primeiro ramo e retornamos 1 sem fazer mais qualquer chamada recursiva. O valor retornado (1) multiplicado por n, que 1, e o resultado retornado. O valor retornado (1) multiplicado por n, que 2, e o resultado retornado. O valor retornado (2) multiplicado por n, que 3, e o resultado, 6, se torna o valor de retorno da chamada de funo que iniciou todo o processo. Eis o diagrama de pilha para esta sequncia de chamadas de funo:
50
Os valores de retorno so mostrados sendo passados de volta para cima da pilha. Em cada quadro, o valor de retorno o valor de resultado, o qual o produto de n por recursivo.
51
De agora em diante, tenderemos a utilizar um formato mais conciso, mas recomendamos que voc use a verso mais explcita enquanto estiver desenvolvendo cdigo. Quando ele estiver funcionando, voc pode enxug-lo se estiver se sentindo inspirado. Depois de fatorial, o exemplo mais comum de uma funo matemtica denida recursivamente fibonacci, a qual tem a seguinte denio:
fibonacci(0) = 1 fibonacci(1) = 1 fibonacci(n) = fibonacci(n-1) + fibonacci(n-2);
Se voc tentar seguir o uxo de execuo aqui, mesmo para valores bem pequenos de n, sua cabea explodir. Mas, de acordo com o voto de conana, se voc assume que as duas chamadas recursivas funcionam corretamente, ento claro que voc ter o resultado correto ao junt-las.
Parece um caso de recursividade innita. Mas o que ser que de fato? Existe um caso base quando n == 0. O problema que o valor de n nunca encontra o caso base. Na primeira chamada recursiva, o valor de n 0.5. Na prxima, ele igual a -0.5. Da em diante, ele se torna cada vez menor, mas jamais ser 0. Temos ento duas alternativas. Podemos tentar generalizar a funo fatorial para que funcione com nmeros em ponto utuante, ou fazemos fatorial realizar a checagem de tipo de seus parmetros. A primeira chamada funo gamma e est um pouco alm do escopo deste livro. Sendo assim, caremos com a segunda. Podemos usar type para comparar o tipo do parmetro com o tipo de um valor inteiro conhecido (como 1). Ao mesmo tempo em que fazemos isto, podemos nos certicar tambm de que o parmetro seja positivo:
def fatorial (n): if type(n) != type(1): print "Fatorial somente definido para inteiros." return -1 elif n < 0: print "Fatorial somente definido para inteiros positivos." return -1 elif n ==0: return 1 else: return n * fatorial(n-1)
52
Agora temos trs casos base. O primeiro pega os no-inteiros. O segundo pega os inteiros negativos. Em ambos os casos, o programa exibe uma mensagem de erro e retorna um valor especial, -1, para indicar que alguma coisa saiu errada:
>>> fatorial ("Fred") Fatorial somente definido para inteiros. -1 >>> fatorial (-2) Fatorial somente definido para inteiros positivos. -1
Se passarmos pelas duas checagens, ento saberemos que n um inteiro positivo, e poderemos provar que a recursividade encontra seu trmino. Este programa demonstra um padro (pattern) chamado s vezes de guardio. As duas primeiras condicionais atuam como guardis, protegendo o cdigo que vem em seguida de valores que poderiam causar um erro. Os guardies tornam possvel garantir a correo do cdigo.
53
54
CAPTULO 8
Captulo 6: Iterao
Tpicos Captulo 6: Iterao 6.1 Reatribuies 6.2 O comando while 6.3 Tabelas 6.4 Tabelas de duas dimenses (ou bi-dimensionais) 6.5 Encapsulamento e generalizao 6.6 Mais encapsulamento 6.7 Variveis locais 6.8 Mais generalizao 6.9 Funes 6.10 Glossrio
A sada deste programa 5 7, porque na primeira vez que bruno impresso, seu valor 5 e na segunda vez, seu valor 7. A vrgula no nal do primeiro comando print suprime a nova linha no nal da sada, que o motivo pelo qual as duas sadas aparecem na mesma linha. Veja uma reatribuio em um diagrama de estado:
55
Com a reatribuio torna-se ainda mais importante distinguir entre uma operao de atribuio e um comando de igualdade. Como Python usa o sinal de igual ( = ) para atribuio, existe a tendncia de lermos um comando como a = b como um comando de igualdade. Mas no ! Em primeiro lugar, igualdade comutativa e atribuio no . Por exemplo, em matemtica, se a = 7 ento 7 = a. Mas em Python, o comando a = 7 permitido e 7 = a no . Alm disso, em matemtica, uma expresso de igualdade sempre verdadeira. Se a = b agora, ento, a ser sempre igual a b. Em Python, um comando de atribuio pode tornar duas variveis iguais, mas elas no tm que permanecer assim:
a = 5 b = a # a e b agora so iguais b = 3 # a e b no so mais iguais
A terceira linha muda o valor de a mas no muda o valor de b, ento, elas no so mais iguais. (Em algumas linguagens de programao, um smbolo diferente usado para atribuio, como <- ou :=, para evitar confuso.) Embora a reatribuio seja freqentemente til, voc deve us-la com cautela. Se o valor das variveis muda freqentemente, isto pode fazer o cdigo difcil de ler e de depurar.
Desde que removemos a chamada recursiva, esta funo no recursiva. Voc quase pode ler o comando while como se fosse Ingls. Ele signica, Enquanto (while) n for maior do que 0, siga exibindo o valor de n e diminuindo 1 do valor de n. Quando chegar a 0, exiba a palavra Fogo!. Mais formalmente, aqui est o uxo de execuo para um comando while: 1. Teste a condio, resultando 0 ou 1. 2. Se a condio for falsa (0), saia do comando while e continue a execuo a partir do prximo comando. 3. Se a condio for verdadeira (1), execute cada um dos comandos dentro do corpo e volte ao passo 1.
56
O corpo consiste de todos os comandos abaixo do cabealho, com a mesma endentao. Este tipo de uxo chamado de um loop (ou lao) porque o terceiro passo cria um loop ou um lao de volta ao topo. Note que se a condio for falsa na primeira vez que entrarmos no loop, os comandos dentro do loop jamais sero executados. O corpo do loop poderia alterar o valor de uma ou mais variveis de modo que eventualmente a condio se torne falsa e o loop termine. Se no for assim, o loop se repetir para sempre, o que chamado de um loop innito. Uma fonte de diverso sem m para os cientistas da computao a observao de que as instrues da embalagem de shampoo, Lave, enxge, repita um loop innito. No caso de contagemRegressiva, podemos provar que o loop terminar porque sabemos que o valor de n nito, e podemos ver que o valor de n diminui dentro de cada repetio (iterao) do loop, ento, eventualmente chegaremos ao 0. Em outros casos, isto no to simples de armar:
def sequencia(n): while n != 1: print n, if n%2 == 0: n = n/2 else: n = n*3+1
# n par # n impar
A condio para este loop n != 1, ento o loop vai continuar at que n seja 1, o que tornar a condio falsa. Dentro de cada repetio (iterao) do loop, o programa gera o valor de n e ento checa se ele par ou impar. Se ele for par, o valor de n dividido por 2. Se ele for impar, o valor substitudo por n*3+1. Por exemplo, se o valor inicial (o argumento passado para seqncia) for 3, a seqncia resultante ser 3, 10, 5, 16, 8, 4, 2, 1. J que n s vezes aumenta e s vezes diminui, no existe uma prova bvia de que n jamais venha a alcanar 1, ou de que o programa termine. Para alguns valores particulares de n, podemos provar o trmino. Por exemplo, se o valor inicial for uma potncia de dois, ento o valor de n ser par dentro de cada repetio (iterao) do loop at que alcance 1. O exemplo anterior termina com uma dessas seqncias comeando em 16. Valores especcos parte, A questo interessante se h como provarmos que este programa termina para todos os valores de n. At hoje, ningum foi capaz de provar que sim ou que no! Como um exerccio, reescreva a funo nLinhas da seo 4.9 usando iterao em vez de recurso.
57
A string \t representa um caracter de tabulao. Conforme caracteres e strings vo sendo mostrados na tela, um ponteiro invisvel chamado cursor marca aonde aparecer o prximo caractere. Depois de um comando print, o cursor normalmente vai para o incio de uma nova linha. O caractere de tabulao desloca o cursor para a direita at que ele encontre uma das marcas de tabulao. Tabulao til para fazer colunas de texto line up, como na sada do programa anterior:
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 0.0 0.69314718056 1.09861228867 1.38629436112 1.60943791243 1.79175946923 1.94591014906 2.07944154168 2.19722457734
Se estes valores parecem odd, lembre-se que a funo log usa a base e. J que potncias de dois so to importantes em cincia da computao, ns freqentemente temos que achar logaritmos referentes base 2. Para fazermos isso, podemos usar a seguinte frmula: (XXX diagramar frmula matemtica) log2 x = loge x (6.1) loge 2 Alterando o comando de sada para:
print x, \t, math.log(x)/math.log(2.0)
Podemos ver que 1, 2, 4 e 8 so potncias de dois porque seus logaritmos na base 2 so nmeros redondos. Se precisssemos encontrar os logaritmos de outras potncias de dois, poderamos modicar o programa deste modo:
x = 1.0 while x < 100.0: print x, \t, math.log(x)/math.log(2.0) x = x * 2.0
Agora, em vez de somar algo a x a cada iterao do loop, o que resulta numa seqncia aritmtica, ns multiplicamos x por algo, resultando numa seqncia geomtrica. O resultado :
1.0 2.0 0.0 1.0
58
Por causa do caractere de tabulao entre as colunas, a posio da segunda coluna no depende do nmero de dgitos na primeira coluna. Tabelas de logaritmos podem no ser mais teis, mas para cientistas da computao, conhecer as potncias de dois ! Como um exerccio, modique este programa de modo que ele produza as potncias de dois acima de 65.535 (ou seja, 216). Imprima e memorize-as. O caractere de barra invertida em \t indica o incio de uma seqncia de escape. Seqncias de escape so usadas para representar caracteres invisveis como de tabulao e de nova linha. A seqncia \n representa uma nova linha. Uma seqncia de escape pode aparecer em qualquer lugar em uma string; no exemplo, a seqncia de escape de tabulao a nica coisa dentro da string. Como voc acha que se representa uma barra invertida em uma string? Como um exerccio, escreva um nica string que produza esta sada.
A primeira linha inicializa a varivel chamada i, a qual age como um contador ou varivel de controle do loop. Conforme o loop executado, o valor de i incrementado de 1 a 6. Quando i for 7, o loop termina. A cada repetio (iterao) do loop, mostrado o valor de 2*i, seguido de trs espaos. De novo, a vrgula no comando print suprime a nova linha. Depois que o loop se completa, o segundo comando print inicia uma nova linha. A sada do programa :
2 4 6 8 10 12
59
na seo 4.5; e eDivisivel na seo 5.4 Generalizao signica tomar algo que especco, tal como imprimir os mltiplos de 2, e torn-lo mais geral, tal como imprimir os mltiplos de qualquer inteiro. Esta funo encapsula o loop anterior e generaliza-o para imprimir mltiplos de n:
def imprimeMultiplos(n): i = 1 while i <= 6: print n*i, \t , i = i + 1 print
Para encapsular, tudo o que tivemos que fazer foi adicionar a primeira linha, que declara o nome de uma funo e sua lista de parmetros. Para generalizar, tudo o que tivemos que fazer foi substituir o valor 2 pelo parmetro n. Se chamarmos esta funo com o argumento 2, teremos a mesma sada que antes. Com o argumento 3, a sada :
3 6 9 12 15 18
Agora voc provavelmente pode adivinhar como imprimir uma tabela de multiplicao - chamando imprimeMultiplos repetidamente com argumentos diferentes. De fato, podemos usar um outro loop:
i = 1 while i <= 6: imprimeMultiplos(i) i = i + 1
Note o quanto este loop parecido com aquele dentro de imprimeMultiplos. Tudo o que z foi substituir o comando print pela chamada funo. A sada deste programa uma tabela de multiplicao:
1 2 3 4 5 6 2 4 6 8 10 12 3 6 9 12 15 18 4 8 12 16 20 24 5 10 15 20 25 30 6 12 18 24 30 36
Este processo um plano de desenvolvimento comum. Ns desenvolvemos cdigo escrevendo linhas de cdigo fora de qualquer funo, ou digitando-as no interpretador. Quando temos o cdigo funcionando, extramos ele e o embalamos em uma funo.
60
Este plano de desenvolvimento particularmente til se voc no sabe, quando voc comea a escrever, como dividir o programa em funes. Esta tcnica permite a voc projetar enquanto desenvolve.
O valor de i em imprimeTabMult vai de 1 a 6. No diagrama, i agora 3. Na prxima iterao do loop i ser 4. A cada iterao do loop, imprimeTabMult chama imprimeMultiplos com o valor corrente de i como argumento. O valor atribudo ao parmetro n. Dentro de imprimeMultiplos, o valor de i vai de 1 a 6. No diagrama, i agora 2. Mudar esta varivel no tem efeito sobre o valor de i em imprimeTabMult. comum e perfeitamente legal ter variveis locais diferentes com o mesmo nome. Em particular, nomes como i e j so muito usados para variveis de controle de loop. Se voc evitar utiliz-los em uma funo s porque voc j os usou em outro lugar, voc provavelmente tornar seu programa mais difcil de ler.
61
Ns substitumos o valor 6 pelo parmetro altura. Se chamarmos imprimeTabMult com o argumento 7, ela mostra:
1 2 3 4 5 6 7 2 4 6 8 10 12 14 3 6 9 12 15 18 21 4 8 12 16 20 24 28 5 10 15 20 25 30 35 6 12 18 24 30 36 42
Isto bom, exceto que ns provavelmente quereramos que a tabela fosse quadrada - com o mesmo nmero de linhas e colunas. Para fazer isso, adicionamos outro parmetro a imprimeMultiplos para especicar quantas colunas a tabela deveria ter. S para confundir, chamamos este novo parmetro de altura, demonstrando que diferentes funes podem ter parmetros com o mesmo nome (como acontece com as variveis locais). Aqui est o programa completo:
def imprimeMultiplos(n, altura): i = 1 while i <= altura: print n*i, t, i = i + 1 print def imprimeTabMult(altura): i = 1 while i <= altura: imprimeMultiplos(i, altura) i = i + 1
Note que quando adicionamos um novo parmetro, temos que mudar a primeira linha da funo (o cabealho da funo), e ns tambm temos que mudar o lugar de onde a funo chamada em imprimeTabMult. Como esperado, este programa gera uma tabela quadrada de sete por sete:
1 2 3 4 5 6 7 2 4 6 8 10 12 14 3 6 9 12 15 18 21 4 8 12 16 20 24 28 5 10 15 20 25 30 35 6 12 18 24 30 36 42 7 14 21 28 35 42 49
Quando voc generaliza uma funo apropriadamente, voc muitas vezes tem um programa com capacidades que voc no planejou. Por exemplo, voc pode ter notado que, porque ab = ba, todas as entradas na tabela aparecem duas vezes. Voc poderia economizar tinta imprimindo somente a metade da tabela. Para fazer isso, voc tem que mudar apenas uma linha em imprimeTabMult. Mude:
imprimeTabMult(i, altura)
para:
imprimeTabMult(i, i)
1 2 3 4 5 6 7
4 6 8 10 12 14
9 12 15 18 21
16 20 24 28
25 30 35
36 42
49
Como um exerccio, trace a execuo desta verso de imprimeTabMult e explique como ela funciona.
63
plano de desenvolvimento (development plan) um processo denido para desenvolvimento de um programa. Neste captulo, ns demonstramos um estilo de desenvolvimento baseado em escrever cdigo para executar tarefas simples e especcas, usando encapsulamento e generalizao.
64
CAPTULO 9
Captulo 7: Strings
Tpicos Captulo 7: Strings 7.1 Um tipo de dado composto 7.2 Comprimento 7.3 Travessia e o loop for 7.4 Fatias de strings 7.5 Comparao de strings 7.6 Strings so imutveis 7.7 Uma funo find (encontrar) 7.8 Iterando e contando 7.9 O mdulo string 7.10 Classicao de caracteres 7.11 Glossrio 7.11 Glossrio2
A expresso fruta[1] seleciona o caractere nmero 1 de fruta. A varivel letra referencia ou refere-se ao resultado da expresso. Quando exibimos letra, temos uma surpresa:
65
A primeira letra de "banana" no a. A menos que voc seja um cientista da computao. Neste caso, voc deve entender a expresso dentro dos colchetes como um deslocamento (offset,) a partir do comeo da string, e o deslocamento da primeira letra zero. Assim, b a 0 (zero-sima) letra de "banana", a a 1 (um-sima, diferente de primeira), e n a 2 (dois-sima, diferente de segunda) letra. Para pegar a primeira letra de uma string, voc simplesmente pe 0, ou qualquer expresso que resulte o valor 0, dentro dos colchetes:
>>> letra = fruta[0] >>> print letra b
A expresso entre colchetes chamada de ndice. Um ndice especica um membro de um conjunto ordenado, neste caso o conjunto de caracteres da string. O ndice indica aquele membro que voc quer, da seu nome. Ele pode ser qualquer expresso inteira.
Para pegar a ltima letra de uma string, voc pode car tentado a fazer alguma coisa assim:
comprimento = len(fruta) ultima = fruta[comprimento] # ERRO!
No vai funcionar. Isto vai causar o seguinte erro em tempo de execuo (runtime error): IndexError: string index out of range. (ErroDeIndice: ndice da string fora do intervalo). A razo que no existe 6 letra em "banana". J que comeamos a contar do zero, as seis letras so numeradas de 0 a 5. Para pegar o ltimo caractere, temos que subtrair 1 de comprimento:
comprimento = len(fruta) ultima = fruta[comprimento-1]
Como alternativa, podemos usar ndices negativos, os quais contam de trs pra frente os elementos da string. A expresso fruta[-1] resulta a ltima letra, fruta[-2] resulta a penltima (a segunda de trs para frente), e assim por diante.
66
Este loop percorre a string e exibe cada letra em sua prpria linha. A condio do loop indice < len(fruta), assim, quando ndice igual ao comprimento da string, a condio se torna falsa, e o corpo do loop no executado. O ltimo caractere acessado aquele com o ndice len(fruta)-1, que vem a ser o ltimo caractere da string. Como um exerccio, escreva uma funo que tome uma string como argumento e devolva suas letras de trs para frente, uma por linha. Usar um ndice para percorrer um conjunto de valores to comum que Python oferece uma sintaxe alternativa simplicada - o loop for:
for char in fruta: print char
A cada vez atravs do loop, o prximo caractere da string atribudo varivel char. O loop continua at que no reste mais caracteres. O exemplo seguinte mostra como usar concatenao e um loop for para gerar uma srie abecedrio. Abecedrio se refere a uma srie ou lista na qual os elementos aparecem em ordem alfabtica. Por exemplo, no livro de Robert McCloskeys Make Way for Ducklings, os nomes dos ducklings so Jack, Kack, Lack, Mack, Nack, Ouack, Pack e Quack. O loop seguinte, produz como sada aqueles nomes, em ordem:
prefixos = "JKLMNOPQ" sufixo = "ack" for letra in prefixos: print letra + sufixo
Naturalmente, esta sada no est cem por cento certa porque Ouack e Quack esto escritos de maneira errada. Como um exerccio, modique o programa para corrigir este erro.
O operador [n:m] retorna a parte da string do n-simo caractere ao m-simo caractere, incluindo o primeiro mas excluindo o ltimo. Este comportamento no intuitivo; ele faz mais sentido se voc imaginar os ndices apontando para os intervalos entre os caracteres, como no seguinte diagrama:
67
Se voc omitir o primeiro ndice (antes dos dois pontos :), a fatia comea do incio da string. Se voc omitir o segundo ndice, a fatia vai at o nal da string. Assim:
>>> fruta = "banana" >>> fruta[:3] ban >>> fruta[3:] ana
Entretanto, voc deve atentar para o fato de que Pyhton no manipula letras maisculas e minsculas da mesma maneira que as pessoas o fazem. Todas as letras maisculas vm antes das minsculas. Como resultado:
Sua palavra, Zebra, vem antes de banana.
Uma maneira comum de resolver este problema converter as strings para um formato padro, seja todas minsculas, ou todas maisculas, antes de realizar a comparao. Um problema mais difcil fazer o programa perceber que zebras no so frutas.
68
Em vez de produzir a sada El, Mundo!, este cdigo produz o erro em tempo de execuo (runtime error): TypeError: object doesnt support item assignment (ErroDeTipo: objeto no d suporte atribuio de item.) Strings so imutveis, o que signica que voc no pode mudar uma string que j existe. O melhor que voc pode fazer criar uma nova string que seja uma variao da original:
saudacao = "Al, mundo!" novaSaudacao = E + saudacao[1:] print novaSaudacao
A soluo aqui concatenar uma nova primeira letra com uma fatia de saudacao. Esta operao no tem nenhum efeito sobre a string original.
Num certo sentido, find (encontrar) o oposto do operador []. Em vez de pegar um ndice e extrair o caractere correspondente, ela pega um caractere e encontra (nds) em qual ndice aquele caractere aparece. Se o caractere no encontrado, a funo retorna -1. Este o primeiro exemplo que vemos de uma instruo return dentro de um loop. Se str[indice] == ch, a funo retorna imediatamente, abandonando o loop prematuramente. Se o caractere no aparece na string, ento o programa sai do loop normalmente e retorna -1. Este padro de computao s vezes chamado de travessia eureka, porque to logo ele encontra (nd) o que est procurando, ele pode gritar Eureka! e parar de procurar. Como um exerccio, modique a funo nd (encontrar) de modo que ela receba um terceiro parmetro, o ndice da string por onde ela deve comear sua procura.
Este programa demonstra um outro padro de computao chamado de contador. A varivel contador inicializada em 0 e ento incrementada cada vez que um a encontrado. (Incrementar o mesmo que aumentar em um; o oposto de decrementar, e no tem relao com excremento, que um substantivo.) Quando se sai do loop, contador guarda o resultado - o nmero total de as.
69
Como um exerccio, encapsule este cdigo em uma funo chamada contaLetras, e generalize-a de modo que possa aceitar uma string e uma letra como parmetros. Como um segundo exerccio, reescreva esta funo de modo que em vez de percorrer a string, ela use a verso com trs parmetros de find (encontrar) da seo anterior.
O mdulo string inclui uma funo chamada find (encontrar) que faz a mesma coisa que a funo que escrevemos. Para cham-la, temos que especicar o nome do mdulo e o nome da funo usando a notao de ponto.:
>>> fruta = "banana" >>> indice = string.find(fruta, "a") >>> print indice 1
Este exemplo demonstra um dos benefcios dos mdulos - eles ajudam a evitar colises entre nomes de funes nativas e nomes de funes denidas pelo usurio. Usando a notao de ponto podemos especicar que verso de find (encontrar) ns queremos. De fato, string.find mais generalizada que a nossa verso. Primeiramente, ela pode encontrar substrings, no apenas caracteres:
>>> string.find("banana", "na") 2
Alm disso, ela recebe um argumento adicional que especica o ndice pelo qual ela deve comear sua procura:
>>> string.find("banana", "na", 3) 4
Ou ela pode receber dois argumentos adicionais que especicam o intervalo de ndices:
>>> string.find("bob", "b", 1, 2) -1
Neste exemplo, a busca falha porque a letra b no aparece no intervalo entre 1 e 2 (no incluindo o 2) do ndice.
Ns podemos usar essas constantes e find (encontrar) para classicar caracteres. find(lowercase, ch) retorna um valor outro que no -1, ento ch deve ser minsculo:
Por exemplo, se
70
Como uma alternativa, podemos tirar vantagem do operador in, que determina se um caractere aparece em uma string:
def eMinusculo(ch): return ch in string.lowercase
Se ch estiver entre a e z, ele deve ser uma letra minscula. Como um exerccio, discuta que verso de eMinusculo voc acha que ser a mais rpida. Voc pode pensar em outras razes alm da velocidade para preferir uma em vez de outra? Outra constante denida no mdulo string pode te surpreender quando voc executar um print sobre ela:
>>> print string.whitespace
Caracteres de espaamento (ou espaos em branco) movem o cursor sem imprimir qualquer coisa. Eles criam os espaos em branco entre os caracteres visveis (pelo menos numa folha de papel branco). A string constante string.whitespace contm todos os caracteres de espaamento, incluindo espao, tabulao (\t) e nova linha (\n). Existem outras funes teis no mdulo string, mas este livro no pretende ser um manual de referncia. Por outro lado, Python Library Reference exatamente isto. Em meio a uma abundante documentao, ele est disponvel no site da web do Python, www.python.org.
71
72
CAPTULO 10
Captulo 8: Listas
Tpicos Captulo 8: Listas 8.1 Valores da lista 8.2 Acessado elementos 8.3 Comprimento da lista 8.4 Membros de uma lista 8.5 Listas e laos for 8.6 Operaes em listas 8.7 Fatiamento de listas 8.8 Listas so mutveis 8.9 Remoo em lista 8.10 Ojetos e valores 8.11 Apelidos 8.12 Clonando listas 8.13 Lista como parmetro 8.14 Lista aninhadas 8.15 Matrizes 8.16 Strings e listas 8.17 Glossrio Outros termos utilizados neste captulo Uma lista um conjunto ordenado de valores, onde cada valor identicado por um ndice. Os valores que compem uma lista so chamados elementos. Listas so similares a strings, que so conjuntos ordenados de caracteres, com a diferena que os elementos de uma lista podem possuir qualquer tipo. Listas e strings XXX e outras coisas que se comportam como conjuntos ordenados XXX so chamados seqncias.
73
O primeiro exemplo uma lista de quatro inteiros. O segundo uma lista de trs strings. Os elementos de uma lista no necessitam ser do mesmo tipo. A lista a seguir contm uma string, um valor oat, um valor inteiro, e mirabile dictu uma outra lista:
>>> [alo, 2.0, 5, [10,20]]
Uma lista dentro de outra lista dita estar aninhada. Listas que contm inteiros consecutivos so comuns, ento Python fornece uma maneira simples de cri-los:
>>> range(1,5) [1, 2, 3, 4]
A funo range pega dois argumentos e devolve uma lista que contm todos os inteiros do primeiro at o segundo, incluindo o primeiro mas no incluindo o segundo! Existem outras formas de range. Com um argumento simples, ela cria uma lista que inicia em 0:
>>> range(10) [0,1, 2, 3, 4, 5, 6, 7, 8, 9]
Se existe um terceiro argumento, ele especica o espao entre os valores sucessivos, que chamado de tamanho do passo. Este exemplo conta de 1 at 10 em passos de 2:
>>> range(1, 10, 2) [1, 3, 5, 7, 9]
Finalmente, existe uma lista especial que no contm elementos. Ela chamada lista vazia, e sua notao []. Com todas estas formas de criar listas, seria decepcionante se no pudssemos atribuir valores de listas a variveis ou passar listas como parmetros a funes. Felizmente, podemos.
>>> vocabulario = [melhorar, castigar, defenestrar] >>> numeros = [17, 123] >>> vazio = [] >>> print vocabulario, numeros, vazio [melhorar, castigar, defenestrar] [17, 123] []
O operador colchete pode aparecer em qualquer lugar em uma expresso. Quando ele aparece no lado esquerdo de uma atribuio, ele modica um dos elementos em uma lista, de forma que o um-simo elemento de numeros, que era 123, agora 5. Qualquer expresso inteira pode ser utilizada como um ndice:
>>> numeros[3-2] 5 >>> numeros[1.0] TypeError: sequence index must be integer
Se voc tentar ler ou escrever um elemento que no existe, voc recebe um erro de tempo de execuo (runtime error):
74
Se um ndice possui um valor negativo, ele conta ao contrrio a partir do nal da lista:
>>> numeros[-1] 5 >>> numeros[-2] 17 >>> numeros[-3] IndexError: list index out of range
numeros[-1] o ltimo elemento da lista, numeros[-2] o penltimo e numeros[-3] no existe. comum utilizar uma varivel de lao como um ndice da lista:
>>> cavaleiros = [guerra, fome, peste, morte] i = 0 while i < 4: print cavaleiros[i] i = i + 1
Este lao while conta de 0 at 4. Quando a varivel do lao i 4, a condio falha e o lao se encerra. Desta forma o corpo do lao executado somente quando i 0, 1, 2 e 3. Em cada vez dentro do lao, a varivel i utilizada como um ndice para a lista, exibindo o i-simo elemento. Este padro de computao chamado de percurso na lista.
A ltima vez que o corpo do lao executado, i len(cavaleiros) - 1, que o ndice do ltimo elemento. Quando i igual a len(cavaleiros), a condio falha e o corpo no executado, o que uma boa coisa, porque len(cavaleiros) no um ndice legal. Embora uma lista possa conter uma outra lista, a lista aninhada ainda conta como um elemento simples. O comprimento desta lista quatro:
>>> [spam!, 1, [Brie, Roquefort, Pol l Veq], [1, 2 3]]
Como um exerccio, escreva um lao que percorra a lista anterior e exiba o comprimento de cada elemento. O que acontece se voc manda um inteiro para len?
>>> cavaleiros = [guerra, fome, peste, morte] >>> peste in cavaleiros True >>> depravao in cavaleiros False
Uma vez que peste um membro da lista cavaleiros, o operador in devolve verdadeiro. Uma vez que depravao no est na lista, in devolve falso. Podemos utilizar tambm o not em combinao com o in para testar se um elemento no um membro de uma lista:
>>> depravao not in cavaleiros True
O lao for mais conciso porque podemos eliminar a varivel do lao, i. Aqui est o lao anterior escrito com umlao for:
>>> for cavaleiro in cavaleiros: print cavaleiro
Quase se l como Portugus: For (para cada) cavaleiro in (na lista de) cavaleiros, print (imprima o nome do) cavaleiro. Qualquer expresso de lista pode ser utilizada num lao for:
>>> for numero in range(20): if numero % 2 == 0: print numero >>> for fruta in ["banana", "abacaxi", "laranja"]: print "Eu gosto de comer " + fruta + "s!"
O primeiro exemplo exibe todos os nmeros pares entre zero e dezenove. O segundo exemplo expressa o entusiasmo por vrias frutas.
76
O primeiro exemplo repete [0] quatro vezes. O segundo exemplo repete a lista [1, 2, 3] trs vezes.
Tambm podemos remover elementos de uma lista atribuindo a lista vazia a eles:
>>> lista = [a, b, c, d, e, f] >>> lista[1:3] = [] >>> print lista [a, d, e, f]
E podemos adicionar elementos a uma lista enando-os numa fatia vazia na posio desejada:
77
>>> lista = [a, d, f] >>> lista[1:1] = [b, c] >>> print lista [a, b, c, d, f] >>> lista[4:4] = [e] >>> print lista [a, b, c, d, e, f]
Como voc deveria esperar, del trata valores negativos e causa erros de tempo de execuo se o ndice estiver fora da faixa. Voc tambm pode utilizar uma faixa como um ndice para del:
>>> lista = [a, b, c, d, e, f] >>> del lista[1:5] >>> print lista [a, f]
Como de costume, fatias selecionam todos os elementos at, mas no incluindo, o segundo ndice.
sabemos que a e b se referem a uma string com as letras banana. Mas no podemos dizer se elas apontam para a mesma string. Existem dois possveis estados:
Em um caso, a e b se referem a duas coisas diferentes que possuem o mesmo valor. No segundo caso, elas se referem mesma coisa. Estas coisas possume nomes - elas so chamadas objetos. Um objeto algo ao qual uma varivel pode se referenciar. 78 Captulo 10. Captulo 8: Listas
Todo objeto possui um identicador nico, que podemos obter com a funo id. Exibindo o identicador de a e b, podemos dizer se elas se referem ao mesmo objeto.
>>> id(a) 135044008 >>> id(b) 135044008
De fato, obtivemos o mesmo identicador duas vezes, o que signica que Python criou apenas uma string, e tanto a quanto b se referem a ela. Interessantemente, listas se comportam de forma diferente. Quando criamos duas listas, obtemos dois objetos:
>>> a = [1, 2, 3] >>> b = [1, 2, 3] >>> id(a) 135045528 >>> id(b) 135041704
Uma vez que a lista possui dois nomes diferentes, a e b, dizemos que ela est apelidada (aliased). Mudanas feitas em um apelido afetam o outro nome:
>>> b[0] = 5 >>> print a [5, 2, 3]
79
Embora este comportamento possa ser til, ele s vezes inesperado e indesejado. Em geral, mais seguro evitar os apelidos quando voc est trabalhando com objetos mutveis. claro, para objetos imutveis, no h problema. por isto que Python livre para apelidar cadeias de caracteres quando v uma oportunidade de economizar.
Pegar qualquer fatia de a cria uma nova lista. Neste caso acontece da fatia consistir da lista inteira. Agora estamos livres para fazer alteraes a b sem nos preocuparmos coma:
>>> b[0] = 5 >>> print a [1, 2, 3]
Como exerccio, desenhe um diagrama de estado paraa e b antes e depois desta mudana.
O parmetro lista e a varivel numeros so apelidos para o mesmo objeto. O diagrama de estado se parece com isto:
Uma vez que o objeto compartilhado pelos dois quadros, o desenhamos entre eles.
80
Se a funo modica um parmetro da lista, a funo chamadora v a mudana. Por exemplo, removeCabeca remove o primeiro elemento da lista:
>>> def removecabeca(lista): del lista[0]
Se uma funo devolve uma lista, ela devolve uma referncia lista. Por exemplo, cauda devolve uma lista que contm todos menos o primeiro elemento de uma determinada lista:
>>> def cauda(lista): return lista[1:]
Uma vez que o valor de retorno foi criado com o operador de fatia, ele uma nova lista. A criao de resto, e qualquer alterao subseqente a resto, no tem efeito sobre numeros.
Se exibimos lista[3], obtemos [10, 20]. Para extrairmos um elemento de uma lista aninhada, podemos agir em duas etapas:
>>> elem = lista[3] >>> elem[0] 10
Ou podemos combin-las:
>>> lista[3][1] 20
Os operadores colchete avaliam da esquerda para a direita, ento a expresso pega o terceiro elemento de lista e extrai o primeiro elemento dela.
81
matriz uma lista com trs elementos, onde cada elemento uma linha da matriz. Podemos selecionar uma linha inteira da matriz da maneira habitual:
>>> matriz[1] [4, 5, 6]
O primeiro ndice seleciona a linha, e o segundo ndice seleciona a coluna. Embora esta maneira de representar matrizes seja comum, ela no a nica possibilidade. Uma pequena variao utilizar uma lista de colunas ao invs de uma lista de linhas. Mais adiante veremos uma alternativa mais radical utilizando um dicionrio.
Um argumento opcional chamado um delimitador pode ser utilizado para especicar qual caracter utilizar como limites da palavra. O exemplo a seguir utiliza a string va:
>>> string.split(poesia, va) [O or, lho no car, lho...]
Perceba que o delimitador no aparece na lista. A funo join (juntar) o inverso de split. Ela pega uma lista de strings e concatena os elementos com um espao entre cada par:
>>> lista = [O, orvalho, no, carvalho...] >>> string.join(lista) O orvalho no carvalho...
82
Como um execcio, descreva o relacionamento entre string.join(string.split(poesia)) e poesia. Eles so o mesmo para qualquer string? Quando eles seriam diferentes?
83
XXX handle tratar XXX proceed agir XXX By default por padro XXX notice perceber (observar?) XXX mirabile dictu Algum tem idia do que signica isto? Meu latim no chegou l. :) XXX traduzir os exemplos? considero melhor fazer a traduzir os exemplos sempre que possvel. S no gostaria de tirar o esprito que levou o autor a utilizar tais exemplos. Podem haver trocadilhos, homenagens e outros sentimentos no autor que no devemos retirar. Desta forma, estou traduzindo todos os termos que consigo entender e encontrar palavras que exprimam a idia. Nos demais, estou mantendo os termos originais para uma discusso futura.
84
CAPTULO 11
Captulo 9: Tuplas
Tpicos Captulo 9: Tuplas 9.1 Mutabilidade e tuplas 9.2 Atribuies de tupla 9.3 Tuplas como valores de retorno 9.4 Nmeros aleatrios 9.5 Lista de nmeros aleatrios 9.6 Contando 9.7 Vrios intervalos 9.8 Uma soluo em um s passo 9.9 Glossrio
Para criar uma tupla com um nico elemento, temos que incluir uma vrgula nal:
>>> t1 = (a,) >>> type(t1) <type tuple>
Sem a vrgula, Python entende (a) como uma string entre parnteses:
85
Questes de sintaxe de lado, as operaes em tuplas so as mesmas operaes das listas. O operador ndice seleciona um elemento da tupla.
>>> tupla = (a, b, c, d, e) >>> tupla[0] a
Naturalmente, mesmo que no possamos modicar os elementos de uma tupla, podemos substitu-la por uma tupla diferente:
>>> tupla = (A,) + tupla[1:] >>> tupla (A, b, c, d, e)
Se voc tiver que fazer isso com frequncia, esta abordagem se torna incmoda. Python fornece uma forma de atribuio de tupla que resolve esse problema elegantemente:
>>> a, b = b, a
O lado esquedo uma tupla de variveis; o lado direito uma tupla de valores. Cada valor atribudo sua respectiva varivel. Todas as expresses do lado direito so avaliadas antes de qualquer das atribuies. Esta caracterstica torna as atribuies de tupla bastante versteis. Naturalmente, o nmero de variveis na esquerda e o nmero de valores na direita deve ser igual:
>>> a, b, c, d = 1, 2, 3 ValueError: unpack tuple of wrong size
86
Ento ns poderamos atribuir o valor de retorno para uma tupla com duas variveis:
a, b = troca(a, b)
Neste caso, no existe uma grande vantagem em fazer de troca (swap) uma funo. De fato, existe um perigo em tentar encapsular troca, o qual a tentao de cometer o seguinte erro:
def troca(x, y): x, y = y, x # versao incorreta
ento a e x so apelidos para um mesmo valor. Mudar x dentro da funo troca, faz com que x se referencie a um valor diferente, mas sem efeito sobre a dentro de __main__. Do mesmo modo, a mudana em y no tem efeito sobre b. Esta funo roda sem produzir uma mensagem de erro, mas ela no faz o que pretendemos. Este um exemplo de um erro semntico. Como exerccio, desenhe um diagrama de estado pra esta funo de modo que voc possa ver porque ela no funciona.
Para gerar um nmero aleatrio ente 0.0 e um limite superior, digamos superior, multiplique x por superior. Como exerccio, gere um nmero aleatrio entre inferior e superior. Como exerccio adicional, gere um nmero inteiro aleatrio entre inferior e superior, inclusive os dois extremos.
87
Vamos realizar um teste desta funo com uma lista de oito elementos. Para efeitos de depurao, uma boa idia comear com uma lista pequena.
>>> listaAleatoria(8) 0.15156642489 0.498048560109 0.810894847068 0.360371157682 0.275119183077 0.328578797631 0.759199803101 0.800367163582
Os nmeros gerados por random so supostamente uniformemente distribudos, o que signica que cada valor tem uma probabilidade igual de acontecer. Se ns dividirmos a faixa de valores possveis em intervalos do mesmo tamanho, e contarmos o nmero de vezes que um determinado valor aleatrio caiu em seu respectivo intervalo, ns devemos obter o mesmo nmero aproximado de valores em cada um dos intervalos. Ns podemos testar esta teoria escrevendo um programa que divida a faixa de valores em intervalos e conte o nmero de valores de cada intervalo.
O primeiro passo substituir fruta por lista e letra por numero. Isso no muda o programa, apenas o ajusta para que ele se torne mais fcil de ler e entender. O segundo passo mudar o teste. Ns no estamos interessados em procurar letras. Ns queremos ver se numero est entre inferior e superior.: 88 Captulo 11. Captulo 9: Tuplas
contador = 0 for numero in lista if inferior < numero < superior: contador = contador + 1 print contador
O ltimo passo encapsular este cdigo em uma funo chamada noIntervalo. Os parmetros so a lista e os valores inferior e superior:
def noIntervalo(lista, inferior, superior): contador = 0 for numero in lista: if inferior < numero < superior: contador = contador + 1 return contador
Atravs da cpia e da modicao de um programa existente, estamos aptos a escrever esta funo rapidamente e economizar um bocado de tempo de depurao. Este plano de desenvolvimento chamado de casamento de padres. Se voc se encontrar trabalhando em um problema que voc j solucionou antes, reuse a soluo.
Existem aqui dois problemas. Um que temos que criar novos nomes de varivel para cada resultado. O outro que temos que calcular os limites de cada intervalo. Vamos resolver o segundo problema primeiro. Se o nmero de intervalos numeroDeIntervalos, ento a largura de cada intervalo 1.0 / numeroDeIntervalos. Vamos usar um lao (loop) para calcular a faixa, ou largura, de cada intervalo. A varivel do loop, i, conta de 0 at numeroDeIntervalos-1:
larguraDoIntervalo = 1.0 / numeroDeIntervalos for i in range(numeroDeIntervalos): inferior = i * larguraDoIntervalo superior = inferior + larguraDoIntervalo print "do" inferior, "ao", superior
Para calcular o limite inferior (inferior) de cada intervalo, ns multiplicamos a varivel do loop (i) pela largura do intervalo (larguraDoIntervalo). O limite superior (superior) est exatamente uma largura de intervalo acima. Com numeroDeIntervalos = 8, o resultado :
0.0 to 0.125 0.125 to 0.25 0.25 to 0.375
89
0.375 to 0.5 0.5 to 0.625 0.625 to 0.75 0.75 to 0.875 0.875 to 1.0
Voc pode conrmar que cada intervalo tem a mesma largura, que eles no se sobrepe, e que eles cobrem toda a faixa de valores de 0.0 a 1.0. Agora, de volta ao primeiro problema. Ns precisamos de uma maneira de guardar oito inteiros, usando a vriavel do loop para indicar cada um destes inteiros. Voc deve estar pensando, Lista! Ns temos que criar a lista de intervalos fora do loop, porque queremos fazer isto apenas uma vez. Dentro do loop, ns vamos chamar noIntervalo repetidamente e atualizar o i-simo elemento da lista:
numeroDeIntervalos = 8 intervalos = [0] * numeroDeIntervalos larguraDoIntervalo = 1.0 / numeroDeIntervalos for i in range(numeroDeIntervalos): inferior = i * larguraDoIntervalo superior = inferior + larguraDoIntervalo intervalos[i] = noIntervalo(lista, inferior, superior) print intervalos
Com uma lista de 1000 valores, este cdigo vai produzir esta lista de quantidades de valores em cada intervalo:
[138, 124, 128, 118, 130, 117, 114, 131]
Esses nmeros esto razoavelmente pximos de 125, o que era o que espervamos. Pelo menos eles esto prximos o bastante para nos fazer acreditar que o gerador de nmero aleatrios est funcionando. Como exerccio, teste esta funo com algumas listas longas, e veja se o nmero de valores em cada um dos intervalos tendem a uma distribuio nivelada.
90
Usamos a funo int para converter um nmero em ponto utuante (oat) para um inteiro. Existe a possibilidade deste clculo produzir um ndice que esteja fora dos limites (seja negativo ou maior que len(intervalos)-1)? Uma lista como intervalos que contm uma contagem do nmero de valores em cada intervalo chamada de histograma. Como exerccio, escreva uma funo chamada histograma que receba uma lista e um nmero de intervalos como argumentos e retorne um histograma com o nmero de intervalos solicitado.
91
92
CAPTULO 12
Tpicos Captulo 10: Dicionrios 10.1 Operaes dos Dicionrios 10.2 Mtodos dos Dicionrios 10.3 Aliasing (XXX) e Copiar 10.4 Matrizes Esparsas 10.5 Hint XXX 10.6 Inteiros Longos 10.7 Contando Letras 10.8 Glossrio Os tipos compostos que voce aprendeu - strings, listas e tuplas - utilizam inteiros como indices. Se voce tentar utilizar qualquer outro tipo como indice, voce receber um erro. Dicionrios sao similiares a outros tipos compostos exceto por eles poderem user qualquer tipo imutavel de dados como indice. Como exemplo, nos criaremos um dicionrio para traduzir palavras em Ingls para Espanhol. Para esse dicionrio, os indices sero strings. Uma maneira de criar um dicionario comecando com um dicionrio vazio e depois adiconando elementos. Um dicionrio vazio denotado assim {}:
>>> ing2esp = {} >>> ing2esp[one] = uno >>> ing2esp[two] = dos
A primeira atribuio cria um dicionario chamado ing2esp; as outras atribuies adicionam novos elementos para o dicionrio. Nos podemos imprimir o valor corrente de um dicionario da maneira usual:
>>> print ing2esp {one: uno, two: dos}
Os elementos de um dicionrio aparecem em uma lista separada por vrgulas. Cada entrada contm um indice e um valor separado por dois-pontos. Em um dicionrio, os ndices sao chamados de chaves, entao os elementos so chamados de pares chave-valor. Outra maneira de criar dicionrios fornecendo uma lista de pares chaves-valor utilizando a mesma sintaxe da ltima sada.
93
Os pares chave-valor no esto em ordem! Felizmente, no a motivos para se preocupar com a ordem, desde que os elementos do dicionrio nunca sejam indexados com indices inteiros. Podemos usar as chaves para buscar os valores correspondentes:
>>> print ing2esp[two] dos
A chave two retornou o valor dos mesmo pensando que retornaria o terceiro par chave-valor.
Ou se ns esperamos por mais peras em breve, nos podemos simplesmente trocar o valor associoado as peras:
>>> inventario[peras] = 0 >>> print inventario {laranjas: 525, abacaxis: 430, peras: 0, bananas: 312}
A funo len tambm funciona com dicionrios; retornando o nmero de pares chave-valor:
>>> len(inventario) 4
Dessa forma o ponto especica o nome da funo, keys, e o nome do objeto em que deve ser aplicada a funo, ing2esp. Os parenteses indicam que esse mtodo no possui parameteros. Ao invs de chamarmos um mtodo, dizemos que ele invocado, nesse caso, ns podemos dizer que ns estamos invocando keys do objeto ing2esp. 94 Captulo 12. Captulo 10: Dicionrios
O mtodo items retorna os dois, na forma de uma lista de tuplas - cada tupla com um par chave-valor:
>>> ing2esp.items() [(one,uno), (three,tres), (two,dos)]
A sintaxe fornece uma informao util. Os colchetes indicam que isso uma lista. Os parentses indicam que os elementos da lista so tuplas. Se o mtodo recebe de algum parmetro, se utiliza a mesma sintaxe das funes. Por exemplo, o mtodo has_key recebe uma chave e retorna verdadeiro (1) se a chave existe no dicionrio:
>>> ing2esp.has_key(one) True >>> ing2esp.has_key(deux) False
Se voce tentar chamar um mtodo sem especicar em qual objeto, voce obter um erro. Nesse caso, a mensagem de erro no muito til:
>>> has_key(one) NameError: has_key
alias e opposites se referem ao mesmo objeto; copy se refere a um novo objeto igual ao dicionrio opposites. Se voc modicar o alias, opposites tambm ser alterado.
>>> alias[right] = left >>> opossites[right] left
95
Uma alternativa usarmos um dicionrio. Para as chaves, ns podemos usar tuplas que contm os nmeros da linha e a coluna. Abaixo uma representao em um dicinario da mesma matriz:
>>> matriz = {(0,3): 1, (2, 1): 2, (4, 3): 3}
Ns precisamos apenas de trs pares chave-valor, cada um sendo um elemento diferente de zero da matriz. Cada chave uma tupla, e cada valor um nmero inteiro. Para acessarmos um elemento da matriz, nos utilizamos o operador []:
>>> matriz[0,3] 1
Note que a sintaxe da representao de um dicionrio no a mesma que a sintaxe usada pela representao pelas listas. Em vez de usarmos dois ndices inteiros, ns usamos apenas um ndice, que uma tupla de inteiros. Mas existe um problema. Se tentarmos buscar um elemento zero, obteremos um erro, pois no existe uma entrada no dicionrio para a chave especicada:
>>> matriz[1,3] KeyError: (1,3)
O primeiro parmetro a chave; o segundo o valor que get retornar caso no existe a chave no dicionrio:
>>> matriz.get((1,3), 0) 0
Em uma das nossas mquinas, bonacci(20) executa instantaneamente, bonacci(30) demora cerca de um segundo, e bonacci(40) demora uma eternidade. Para entender o porque, considere o grco de chamadas para bonacci com n=4:
O grco mostra a estrutura da funo, com linhas conectando cada execuo com a execuo que a chamou. No topo do grco, bonacci tem n=4, que chama bonacci com n=3 e n=2. Em seguida, bonacci com n=3 chama bonacci com n=2 e n=1. E assim por diante. Conte quantas vezes bonacci(0) e bonacci(1) so chamadas. Essa uma soluo ineciente para o problema, e torna-se pior quando o parmetro recebido um nmero maior. Uma boa soluo guardar os valores que j foram calculados armazenando-os em um dicionrio. Um valor previamente calculado que guardado para ser utilizado mais tarde chamado de hint. Abaixo uma implementao de bonacci usando hints:
>>> previous = {0:1, 1:1} >>> def fibonacci(n): if previous.has_key(n): return previous[n] else: newValue = fibonacci(n-1) + fibonacci(n-2) previous[n] = newValue return newValue
O dicionrio chamado previous guarda os nmeros de Fibonacci que ns ja conhecemos. Ele comea com apenas dois pares: 0 possui 1; e 1 possui 1. 12.5. 10.5 Hint XXX 97
Sempre que bonacci chamada, ela verica o dicionrio para determinar se ele j possui o resultado. Se o resultado estiver ali, a funo pode retornar imediatamente sempre precisar fazer mais chamadas recursivas. Se o resultado no estiver ali, ele calculado no newValue. O valor de newValue adicionado no dicionrio antes da funo retornar. Usando essa verso de bonacci, nossa mquina consegue calcular bonacci(40) em um piscar de olhos. Mas quando tentamos calcular bonacci(50), ns veremos um problema diferente:
>>> fibonacci(50) OverflowError: integer addition
A resposta, que voc ver em um minuto, 20.365.011.074. O problema que esse nmero muito grande para guardarmos como um inteiro do Python 1 . Isso overow. Felizmente, esse problema tem uma soluo simples.
A outra maneira usarmos a funo long que converte um valor para um long int. long pode receber qualquer valor nmerico e at mesmo uma string de digitos:
>>> long(1) 1L >>> long(3.9) 3L >>> long(57) 57L
Todas as operaes matemticas funcionam com long int s, ento no precisamos modicar muito para adaptar bonacci:
>>> previous = {0: 1L, 1:1L} >>> fibonacci(50) 20365011074L
Somente trocando os valores iniciais de previous, conseguimos mudar o comportamento da bonacci. Os dois primeiros numeros da sequncia so long ints, ento todos os nmeros subsequentes da sequncia tambm sero. Como exerccio, converta fatorial para produzir um inteiro longo como resultado.
98
>>> letterCounts = {} >>> for letter in "Mississippi": ... letterCounts[letter] = letterCounts.get(letter,0) + 1 ... >>> letterCounts {M: 1, s: 4, p: 2, i: 4}
Comeamos com um dicionrio vazio. Para cada letra da string, achamos o contador (possivelmente zero) e o incrementamos. No nal, o dicionrio contem pares de letras e as suas frequncias. mais atraente mostrarmos o histograma na ordem alfabtica. Podemos fazer isso com os mtodos items e sort:
>>> letterItems = letterCounts.items() >>> letterItems.sort() >>> print letterItems [(M, 1), (i, 4), (p, 2), (s, 4)]
Voc ja tinha visto o mtodo items antes, mas sort o primeiro mtodo que voc se depara para aplicar em listas. Existem muitos outros mtodos de listas, incluindo append, extend, e reverse. Consulte a documentao do Python para maiores detalhes.
99
100
CAPTULO 13
Tpicos Captulo 11: Arquivos e excees Arquivos e excees 11.1 Arquivos texto 11.2 Gravando variveis 11.3 Diretrios 11.4 Pickling 11.5 Excees 11.6 Glossrio
101
>>> f = open("teste.dat", "w") >>> print f <open file "teste.dat", mode "w" at fe820>
A funo open recebe dois argumentos. O primeiro o nome do arquivo, e o segundo o modo. Modo ?w? signica que estamos abrindo o arquivo para gravao (?*write*?, escrever). Se no existir nenhum arquivo de nome teste.dat, ele ser criado. Se j existir um, ele ser substitudo pelo arquivo que estamos gravando (ou escrevendo). Quando executamos um comando print sobre o objeto arquivo, visualizamos o nome do arquivo, o modo e a localizao do objeto na memria. Para colocar dados dentro do arquivo, invocamos o mtodo write do objeto arquivo:
>>> f.write("Agora hora") >>> f.write("de fechar o arquivo")
Fechar o arquivo diz ao sistema que terminamos de escrever (gravar) e que o arquivo est livre para ser lido:
>>> f.close()
Agora podemos abrir o arquivo de novo, desta vez para leitura, e ler o seu contedo para uma string. Desta vez, o argumento modo ?r? para leitura (?reading?):
>>> f = open("teste.dat", "r")
Sem nenhuma surpresa, o mtodo read l dados do arquivo. Sem argumentos, ele l todo o contedo do arquivo:
>>> texto = f.read() >>> print texto Agora horade fechar o arquivo
No existe espao entre ?hora? e ?de? porque ns no gravamos um espao entre as strings. read tambm pode receber um argumento que indica quantos caracteres ler:
>>> f = open("teste.dat", "r") >>> print f.read(9) Agora h
Se no houver caracteres sucientes no arquivo, read retorna os caracteres restantes. Quando chegamos ao nal do arquivo, read retorna a string vazia:
>>> print f.read(1000006) orade fechar o arquivo >>> print f.read() >>>
A funo seguinte, copia um arquivo, lendo e gravando at cinqenta caracteres de uma vez. O primeiro argumento o nome do arquivo original; o segundo o nome do novo arquivo:
def copiaArquivo(velhoArquivo, novoArquivo): f1 = open(velhoArquivo, "r") f2 = open(novoArquivo, "w") while 1:
102
A comando break novo. O que ele faz saltar a execuo para fora do loop; o uxo de execuo passa para o primeiro comando depois do loop. Neste exemplo, o loop while innito porque o valor 1 sempre verdadeiro. O nico modo de sair do loop executando o break, o que ocorre quando texto a string vazia, o que ocorre quando alcanamos o m do arquivo.
O mtodo readline l todos os caracteres at, e incluindo, o prximo caractere de nova linha:
>>> f = open("teste.dat", "r") >>> print f.readline() linha um >>>
Neste caso, a sada est em formado de lista, o que signica que as strings aparecem entre aspas e o caractere de nova linha aparece como a seqncia de escape 012. No m do arquivo, readline retorna a string vazia e readlines retorna a lista vazia:
>>> print f.readline() >>> print f.readlines() []
A seguir temos um exemplo de um programa de processamento de linhas. filtraArquivo faz uma cpia de velhoArquivo, omitindo quaisquer linhas que comecem por #:
def filtraArquivo(velhoArquivo, novoArquivo): f1 = open(velhoArquivo, "r") f2 = open(novoArquivo, "w") while 1: texto = f1.readline() if texto == "":
103
O comando continue termina a iterao corrente do loop, mas continua iterando o loop. O uxo de execuo passa para o topo do loop, checa a condio e prossegue conforme o caso. Assim, se texto for a string vazia, o loop termina. Se o primeiro caractere de texto for o jogo da velha (? # ?), o uxo de execuo passa para o topo do loop. Somente se ambas as condies falharem que texto ser copiado para dentro do novo arquivo.
Uma alternativa usar o operador de formatao %. Quando aplicado a inteiros, % o operador mdulo. Mas quando o primeiro operador uma string, % o operador de formatao. O primeiro operando a string de formatao, e o segundo operando uma tupla de expresses. O resultado uma string que contm os valores das expresses, formatadas de acordo com a string de formatao. Num exemplo simples, a seqncia de formatao ??%d?? signica que a primeira expresso na tupla deve ser formatada como um inteiro. Aqui a letra d representa ?decimal?.
>>> carros = 52 >>> "%d " % carros 52
O resultado a string ?52?, que no deve ser confundida com o valor inteiro 52. Uma seqncia de formatao pode aparecer em qualquer lugar na string de formatao, assim, podemos embutir um valor em uma seqncia:
>>> carros = 52 >>> "Em julho vendemos %d carros." % carros Em julho vendemos 52 carros.
A seqncia de formatao %f formata o prximo item da tupla como um nmero em ponto utuante, e %s formata o prximo como uma string:
>>> "Em %d dias fizemos %f milhes %s." % (34,6.1,reais) Em 34 dias fizemos 6.100000 milhes de reais.
Por padro, o formato de ponto utuante exibe seis casas decimais. O nmero de expresses na tupla tem que ser igual ao nmero de seqncias de formatao na string. Alm disso, os tipos das expresses tm que iguais aos da seqncia de formatao:
>>> "%d %d %d " % (1,2) TypeError: not enough arguments for format string
104
>>> "%d " % reais TypeError: illegal argument type for built-in operation
No primeiro exemplo, no existem expresses sucientes; no segundo, a expresso do tipo errado. Para um controle maior na formatao de nmeros, podemos especicar o nmero de dgitos como parte da seqncia de formatao:
>>> "%6d " % 62 62 >>> "%12f " % 6.1 6,100000
O nmero depois do sinal de porcentagem o nmero mnimo de espaos que o valor ocupar. Se o valor fornecido tiver um nmero menor de dgitos, espaos em branco sero adicionados antes para preencher o restante. Se o nmero de espaos for negativo, os espaos sero adicionados depois:
>>> "%-6d " % 62 62
Para nmeros em ponto-utuante, tambm podemos especicar o nmero de dgitos depois da vrgula:
>>> "%12.2f " % 6.1 6.10
Neste exemplo, o resultado reserva 12 espaos e inclui dois dgitos depois da vrgula. Esta formatao til para exibir valores monetrios com os centavos alinhados. Por exemplo, imagine um dicionrio que contm nomes de estudantes como chaves e salrios-hora como valores. Aqui est uma funo que imprime o contedo do dicionrio como um relatrio formatado:
def relatorio(salarios): estudantes = salarios.keys() estudantes.sort() for estudante in estudantes: print "%-20s %12.02f " % (estudante, salarios[estudante])
Controlando a largura de cada valor, podemos garantir que as colunas caro alinhadas, desde que os nomes contenham menos que vinte e um caracteres e os salrios sejam menores do que um bilho de reais por hora.
105
Este exemplo abre um arquivo chamado words que reside em um diretrio de nome dict, o qual reside em share, o qual reside em usr, o qual reside no diretrio de mais alto nvel do sistema, chamado /. Voc no pode usar / como parte do nome de um arquivo; ela um caractere reservado como um delimitador entre nomes de diretrios e nomes de arquivos. O arquivo /usr/share/dict/words contm uma lista de palavras em ordem alfabtica, na qual a primeira palavra o nome de uma universidade Dinamarquesa.
O problema que quando voc l de volta o valor, voc tem uma string. O Tipo original da informao foi perdido. De fato, voc no pode sequer dizer onde comea um valor e termina outro:
>>> f.readline() ?12.3[1, 2, 3]?
A soluo o pickling, assim chamado porque ?preserva? estruturas de dados. O mdulo pickel contm os comandos necessrios. Para us-lo, importe pickle e ento abra o arquivo da maneira usual:
>>> import pickle >>> f = open(?test.pck?, ?w?)
Para armazenar uma estrutura de dados, use o mtodo dump e ento feche o arquivo do modo usual:
>>> pickle.dump(12.3, f) >>> pickle.dump([1,2,3], f) >>> f.close()
Ento, podemos abrir o arquivo para leitura e carregar as estruturas de dados que foram descarregadas (dumped):
>>> f = open(?test.pck?, ?r?) >>> x = pickle.load(f) >>> x 12,3 >>> type(x) <type ?float?> >>> y = pickle.load(f) >>> y [1, 2, 3] >>> type(y) <type ?list?>
Cada vez que invocamos load, obtemos um nico valor do arquivo, completo com seu tipo original.
106
Em cada caso, a mensagem de erro tem duas partes: o tipo do erro antes dos dois pontos, e especicidades do erro depois dos dois pontos. Normalmente Python tambm exibe um ?*traceback*? de onde estava a execuo do programa, mas ns temos omitido esta parte nos exemplos. s vezes queremos executar uma operao que pode causar uma exceo, mas no queremos que o programa pare. Ns podemos tratar a exceo usando as instrues try e except. Por exemplo, podemos pedir ao usurio um nome de arquivo e ento tentar abr-lo. Se o arquivo no existe, no queremos que o programa trave; queremos tratar a exceo:
nomedoarquivo = raw_input(?Entre com o nome do arquivo: ?) try: f = open (nomedoarquivo, ?r?) except: print ?No existe arquivo chamado?, nomedoarquivo
A instruo try executa os comandos do primeiro bloco. Se no ocorrerem excees, ele ignora a instruo except. Se qualquer exceo acontece, ele executa os comandos do ramo except e continua. Podemos encapsular esta habilidade numa funo: existe toma um nome de arquivo e retorna verdadeiro se o arquivo existe e falso se no existe:
def existe(nomedoarquivo) try: f = open(nomedoarquivo) f.close() return 1 except: return 0
Voc pode usar mltiplos blocos except para tratar diferentes tipos de excees. O Manual de Referncia de Python (Python Reference Manual) tem os detalhes. Se o seu programa detecta uma condio de erro, voc pode faz-lo lanar uma exceo. Aqui est um exemplo que toma uma entrada do usurio e testa se o valor 17. Supondo que 17 no seja uma entrada vlida por uma razo qualquer, ns lanamos uma exceo.
107
def entraNumero(): x = input (?Escolha um nmero: ?) if x == 17: raise ?ErroNumeroRuim?, ?17 um nmero ruim? return x
O comando raise toma dois argumentos: o tipo da exceo e informaes especcas sobre o erro. ErroNumeroRuim um novo tipo de exceo que ns inventamos para esta aplicao. Se a funo que chamou entraNumero trata o erro, ento o programa pode continuar; de outro modo, Pyhton exibe uma mensagem de erro e sai:
>>> entraNumero() Escolha um nmero: 17 ErroNumeroRuim: 17 um nmero ruim
A mensagem de erro inclui o tipo da exceo e a informao adicional que voc forneceu. Como um exerccio, escreva uma funo que use entraNumero para pegar um nmero do teclado e que trate a exceo ErroNumeroRuim.
108
CAPTULO 14
Tpicos Captulo 12: Classes e objetos 12.1 Tipos compostos denidos pelo usurio 12.2 Atributos 12.3 Instncias como parmetros 12.4 O signicado de mesmo 12.5 Retngulos 12.6 Instancias como valores retornados 12.7 Objetos so mutveis 12.8 Copiando 12.9 Glossrio
109
Denies de classes podem aparecer em qualquer parte de um programa, mas elas costuma car prximas do comeo do programa (aps os comandos import). As regras de sintaxe para a denio de classes so as mesmas de outros comandos compostos (veja Seo 4.4). A denio acima cria uma nova classe chamada Ponto. O comando pass no tem nenhum efeito; aqui ele necessrio porque um comando composto precisa ter algo no seu corpo. Quando criamos a classe Ponto, criamos um novo tipo de dado, tambm chamado Ponto. Os membros deste novo tipo so chamados instncias deste tipo ou objetos. Criar uma nova instncia instanciar. Para instanciar o objeto Ponto, invocamos a funo (adivinhou?) Ponto:
final = Ponto()
A varivel final agora contm uma referncia a um novo objeto da classe Ponto. Uma funo como Ponto, que cria novos objetos, chamada construtor.
Esta sintaxe similar sintaxe para acessar uma varivel de um mdulo, como math.pi ou string.uppercase. Neste caso, porm, estamos acessando um item de dado de uma instncia. Estes itens so chamados atributos. O seguinte diagrama de estado mostra o resultado destas atribuies:
A varivel final refere a um objeto Ponto, que contm dois atributos. Cada atributo faz referncia a um nmero em ponto utuante. Podemos ler o valor de um atributo usando a mesma sintaxe:
>>> print final.y 4.0 >>> x = final.x >>> print x 3.0
A expresso final.x signica, V ao objeto final e pegue o valor de x. Neste caso, atribumos este valor a uma varivel cujo nome x. No h conito entre a varivel x e o atributo x. O propsito da notao objeto.atributo identicar a qual varivel voc est fazendo referncia de forma que no ambguo. Voc pode usar a notao objeto.atributo como parte de qualquer expresso; assim os seguintes comandos so vlidos:
110
A primeira linha imprime (3.0, 4.0); a segunda linha calcula o valor 25.0. tentador imprimir o valor do prprio objeto final:
>>> print final <__main__.Ponto instance at 80f8e70>
O resultado indica que final uma instncia da classe Ponto e foi denida no prgrama principal: __main__. 80f8e70 o identicador nico deste objeto, escrito em hexadecimal (base 16). Esta no provavelmente a forma mais informativa para mostrar um objeto Ponto. Logo voc ir ver como mudar isso. Como exerccio, crie e imprima um objeto Ponto, e ento use id para imprimir o identicador nico do objeto. Traduza a forma hexadecimal para a forma decimal e conrme se so compatveis.
A funo mostrarPonto pega o ponto (p) como um argumento e mostra-o no formato padro. Se voc chamar mostrarPonto(final), a sada ser (3.0, 4.0). Como um exerccio, re-escreva a funo distncia da Seo 5.2 para receber dois pontos como parmetros, ao invs de quatro nmeros.
2 Nem todos os idiomas tm este problema. Por exemplo, em alemo h palavras diferentes para diferentes sentidos de mesmo. Mesmo carro nesse contexto seria gleiche Auto, e mesma me seria selbe Mutter. 3 XXX LR: Eu no diria que devemos usar == para vericar se dois objetos so o mesmo. Isto uma falha do livro que talvez se origine no original que falava de Java. Em Python o operador is faz o mesmo que o == de Java: compara referncias, e portanto serve para determinar se duas variveis apontam para o mesmo objeto. No entanto, a o cdigo acima est correto porque em Python a implemetao default de == (mtodo __eq__) comparar o id das instncias, porm as classes list e dict, por exemplo, implementam __eq__ comparando os valores contidos (ex.: isto retorna True: l1 = [1,2,3]; l2 = [1,2,3]; l1 == l2).
111
Mesmo que p1 e p2 contenham as mesmas coordenadas, os dois no representam o mesmo objeto. Se atribuirmos p1 a p2, ento as duas variveis so pseudnimos do mesmo objeto.
>>> p2 = p1 >>> p1 == p2 True
Este tipo de igualdade chamado de igualdade rasa porque ela compara somente as referncias e no o contedo dos objetos. Para comparar o contedo dos objetos igualdade profunda podemos escrever uma funo chamada mesmoPonto:
def mesmoPonto(p1, p2) : return (p1.x == p2.x) and (p1.y == p2.y)
Agora se criarmos dois diferentes objetos que contm os mesmos dados, podemos usar mesmoPonto para vericar se eles representam o mesmo ponto.
>>> p1 = Ponto() >>> p1.x = 3 >>> p1.y = 4 >>> p2 = Ponto() >>> p2.x = 3 >>> p2.y = 4 >>> mesmoPonto(p1, p2) True
claro, se as duas variveis referirem ao mesmo objeto, elas tm igualdade rasa e igualdade profunda.
E instanci-la:
box = Rectangle() box.width = 100.0 box.height = 200.0
Este cdigo cria um novo objeto Retngulo com dois atributos ponto-utuante. Para especicar o canto superior esquerdo, podemos embutir um objeto dentro de um objeto!
112
A expresso box.corner.x signica, v ao objeto referenciado por box e selecione o atributo corner; ento v ao objeto corner e deste, selecione o atributo de nome x. A gura mostra o estado deste objeto:
Para chamar esta funo, passe box como um argumento e coloque o resultado em uma varivel.
>>> center = findCenter(box) >>> print mostrarPonto(center) (50.0, 100.0)
Poderamos encapsular este cdigo em um mtodo e generaliza-lo para aumentar o tamanho deste retngulo em qualquer medida:
def growRect(box, dwidth, dheight) : box.width = box.width + dwidth box.height = box.height + dheight
113
As variveis dwidth e dheight indicam em quanto vamos aumentar o tamanho do retngulo em cada direo. Chamando este mtodo, teramos o mesmo efeito. Por exemplo, poderamos criar um novo Retngulo com o nome de bob e passar este nome para o mtodo growRect:
>>> >>> >>> >>> >>> >>> bob = Rectangle() bob.width = 100.00 bob.height = 200.00 bob.corner.x = 0.0; bob.corner.y = 0.0; growRect(bob, 50, 100)
Enquanto growRect est sendo executado, o parmetro box um alias (apelido) para bob. Qualquer mudana feita em box, tambm ir afetar bob. Como exerccio, escreva uma function (mtodo) com o nome de moveRect que pega um Rectangle e dois parmetros com o nome de dx e dy. Esta funo dever mudar a localizao do retngulo atravs da adio de dx coordenada x e da adio de dy coordenada y.
Uma vez que importamos o modulo copy, podemos usar o mtodo copy para criar um outro Ponto. p1 e p2 no representam o mesmo ponto, mas eles contem os mesmo dados. Para copiar um simples objeto como um Ponto, que no contem nenhum objeto embutido, copy suciente. Isto eh chamado shallow copia. Mas para um objeto como um Rectangle, que contem uma referencia para um Ponto, o mtodo copy no ir executar corretamente a copia. Ele ir copiar a referencia para o objeto Ponto, portanto o que acontece aqui que os dois Rectangle (o novo e o antigo) iro fazer referencia a um simples Ponto. Em outras palavras, se criarmos um box, c1, utilizando a forma usual, e depois fazer uma copia, c2, usando o mtodo copy, o diagrama de estado resultante car assim:
114
o resultado no ser o que esperamos. Neste caso, invocando growRect em um dos retngulos (c1), isto no ir afetar o outro retngulo (c2, neste exemplo). Mas se usarmos o mtodo moveRect em qualquer um deles, isto ir inevitavelmente afetar o outro. Este comportamento confuso e propenso a erros! Mas felizmente o modulo copy contem um mtodo chamado deepcopy que copia no somente o objeto, mas tambm copia todo e qualquer objeto embutido neste objeto. Por isto, voc no car surpreso porque este mtodo chama-se deepcopy (copia profunda) no ? Veja como funciona:
>>> c2 = copy.deepcopy(c1)
Agora, c1 e c2 so objetos completamente separados. Podemos usar deepcopy para re-escrever growRect sendo que ao invs de modicar um Rectangle existente, ele cria um novo que tem a mesma localizao do outro, mas com novas dimenses:
def growRect(box, dwidth, dheight): import copy newBox = copy.deepcopy(box) newBox.width = newBox.width + dwidth newBox.height = newBox.height + dheight return newBox
Como exerccio, re-escreva o mtodo moveRect para ele criar e retornar um novo Rectangle ao invs de apenas modicar o antigo.
115
cpia rasa (shallow copy) Ato de copiar o contedo de um objeto, incluindo as referncias a objetos embutidos (XXX embedded); implementada pela funo copy do mdulo copy. cpia profunda (deep copy) Ato de copiar o contedo de um objeto, bem como dos objetos embutidos (XXX embedded), e dos objetos embutidos nestes, e assim por diante; implementada pela funo deepcopy do mdulo copy.
116
CAPTULO 15
Tpicos Captulo 13: Classes e funes 13.1 Horario 13.2 Funes Puras 13.3 Modicadores 13.4 O que melhor ? 13.5 Desenvolvimento Prototipado versus Desenvolvimento Planejamento 13.6 Generalizao 13.7 Algoritmos 13.8 Glossrio
Podemos criar uma nova instncia de Horario e determinar atributos para horas, minutos e segundos:
horario = Horario() horario.horas = 11 horario.minutos = 59 horario.segundos = 30
117
Como exerccio, escreva uma funo imprimirHorario que tenha como argumento um objeto Horario e imprima-o na forma horas:minutos:segundos. Como um segundo exerccio, escreva uma funo booleana que tenha como argumento dois objetos Horario, h1 e h2, e retorne verdadeiro (1) se h1 vem depois de h2 cronologicamente, do contrrio, retorne falso (0).
A funo cria um novo objeto Horario, inicializa os seus atributos, e retorna uma referncia para o novo objeto. Isso chamado de funo pura pois ela no modica nenhum dos objetos que so passados como parmetros e no tem nenhum efeito colateral, como imprimir um valor ou pegar entrada do usurio. Aqui est um exemplo de como usar esta funo. Ns vamos criar dois objetos Horario: horarioAtual, que contm o horrio atual; e horarioDoPao, que contm a quantidade de tempo que a mquina de fazer po gasta para fazer po. Ento vamos usar somaHorario para tentar saber quando o po estar pronto. Se voc no tiver terminado de escrever imprimirHorario ainda, de uma olhada na seo 14.2 antes de voc continuar isso:
>>> >>> >>> >>> >>> >>> >>> >>> horarioAtual = Horario() horarioAtual.horas = 9 horarioAtual.minutos = 14 horarioAtual.segundos = 30 horarioDoPao = Horario() horarioDoPao.horas = 3 horarioDoPao.minutos = 35 horarioDoPao.segundos = 0
A sada deste programa 12:49:30, o que correto. Por outro lado, existem casos onde o resultado no correto. Voc pode pensar em algum ?
118
O problema que esta funo no lida com casos onde o nmero de segundos ou minutos acrescentado em mais de sessenta. Quando isso acontece, temos de transportar os segundos extras para a coluna dos minutos ou os minutos extras na coluna das horas. Aqui est a segunda verso corrigida da funo:
def somaHorario(t1, t2): soma = Horario() soma.horas = t1.horas + t2.horas soma.minutos = t1.minutos + t2.minutos soma.segundos = t1.segundos + t2.segundos if soma.segundos >= 60: soma.segundos = soma.segundos - 60 soma.minutos = soma.minutos + 1 if soma.minutos >= 60: soma.minutos = soma.minutos - 60 soma.horas = soma.horas + 1 return soma
Apesar desta funo estar correta, ela est comeando a car grande. Depois vamos sugerir uma aproximao alternativa que rende um cdigo menor. Clique aqui para feedback
A primeira linha executa a operao bsica; o resto lida com os caso especiais que vimos antes. Esta funo esta correta ? O que aconteceria se o parametro segundos for muito maior que sessenta ? Nesse caso, no suciente transportar apenas uma vez; teramos de continuar fazendo isso at que segundos seja menor que sessenta. Uma soluo seria substituir os comando if por comandos while:
def incrementar(horario, segundos): horario.segundos = horario.segundos + segundos while horario.segundos >= 60: horario.segundos = horario.segundos - 60 horario.minutos = horario.minutos + 1
119
Esta funo agora esta correta, mas no a soluo mais eciente. Como um exerccio, reescreva esta funo de maneira que ela no contenha nenhum loop. Como um segundo exerccio, reescreva incrementar como uma funo pura, e escreva chamadas de funes para as duas funes. Clique aqui para feedback
Agora, tudo que precisamos uma maneira de converter de um inteiro para um objeto Horario:
def criarHorario(segundos): horario = Time() horario.horas = segundos/3600 segundos = segundos - horario.horas * 3600
120
Voc deve ter que pensar um pouco para se convencer que esta tcnica de converter de uma base para outra correta. Assumindo que voc est convencido, voc pode usar essas funes para reescrever somaHorario:
def somaHorario(t1, t2): segundos = converterParaSegundos(t1) + converterParaSegundos(t2) return criarHorario(segundos)
Esta verso muito mais curta que a original, e muito mais fcil para demonstrar que est correta (assumindo, como sempre, que as funes que so chamadas esto corretas). Como um exerccio, reescreva incrementar da mesma forma. Clique aqui para feedback
121
Por outro lado, o processo de projetar algoritmos interessante, intelectualmente desaante, e uma parte central daquilo que chamamos programao. Algumas das coisas que as pessoas fazem naturalmente, sem diculdade ou conscincia, so as mais difceis de se expressar atravs de algoritmos. Entender a linguagem natural um bom exemplo. Todos ns fazemos isso, mas at hoje ningum conseguiu explicar como fazemos isso, pelo menos no na forma de algoritmo. Clique aqui para feedback.
122
CAPTULO 16
Tpicos Captulo 14: Classes e mtodos 14.1 Caractersticas da orientao a objetos 14.2 exibeHora (printTime) 14.3 Um outro exemplo 14.4 Um exemplo mais complicado 14.10 Glossrio ATENO As referncias cruzadas a nomes em cdigos de outros captulos (especialmente 13) ainda no foram unicadas...
Esta observao a motivao por trs dos mtodos. J temos visto alguns mtodos, tais como keys (chaves) e values (valores), os quais foram invocados em dicionrios. Cada mtodo associado com uma classe e intended para ser invocado em instncias daquela classe. Mtodos so simplesmente como funes, com duas diferenas: Mtodos so denidos dentro da denio de uma classe para tornar explcita a relao entre a classe e o mtodo. A sintaxe para a chamada do mtodo diferente da sintaxe para a chamada de uma funo. Nas prximas sees, vamos pegar as funes dos dois captulos anteriores e transform-las em mtodos. Esta transformao puramente mecnica: voc pode consegu-la simplesmente seguindo uma seqncia de passos. Se voc se sentir confortvel convertendo de uma forma para a outra, voc estar apto para escolher a melhor forma para seja o l o que for que voc estiver fazendo.
Para fazer de exibeHora um mtodo, tudo o que temos a fazer mover a denio da funo para dentro da denio da classe. Note a mudana na endentao:
class Horario: def exibeHora(time): print str(time.horas) + ?:? + \ str(time.minutos) + ?:? + \ str(time.segundos)
Como usual, o objeto no qual o mtodo invocado aparece antes do ponto e o nome do mtodo aparece depois do ponto. O objeto no qual o mtodo invocado atribudo ao primeiro parmetro, ento, neste caso, horaCorrente atribudo ao parmetro time. Por conveno, o primeiro parmetro de um mtodo chamado self. A razo para isto um pouco convoluted, mas baseada numa metfora til. A sintaxe para uma chamada de funo, exibeHora(horaCorrente), sugere que a funo um agente ativo. Diz algo como, ?Ei, exibeHora! Aqui est um objeto para voc exibir.? 124 Captulo 16. Captulo 14: Classes e mtodos
Na programao orientada a objetos, os objetos so agentes ativos. Uma chamado do tipo horaCorrente.exibeHora() diz ?Ei, horaCorrente! Por favor exiba-se a si mesmo!? Esta mudana de perspectiva pode ser mais polida, mas no ca bvio que seja til. Nos exemplos que temos visto at aqui, pode ser que no seja. Mas s vezes, deslocar a responsabilidade das funes para cima dos objetos torna possvel escrever funes mais versteis, e torna mais fcil manter e reutilizar o cdigo.
A transformao puramente mecnica ? movemos a denio do mtodo para dentro da denio da classe e mudamos o nome do primeiro parmetro. Agora podemos chamar incremento como um mtodo:
horaCorrente.incremento(500)
De novo, o objeto no qual o mtodo chamado gets atribui ao primeiro parmetro, self. O segundo parmetro, segundo toma(gets) o valor 500. Como um exerccio, converta ?converteParaSegundos? (da Seo 13.5) para um mtodo na classe ?Time?.
125
override (sem traducao; termo consagrado) Substituir uma denio j pronta. Exemplos incluem substituir um parmetro padro por um argumento particular e substituir um mtodo padro, fornecendo um novo mtodo com o mesmo nome. mtodo de inicializao (tambem chamado de construtor) Um mtodo especial que invocado automaticamente quando um novo objeto criado e que inicializa os atributos deste objeto. sobrecarga de operador Estender a funcionalidade dos operadores nativos (+, -, *, >, <, etc.) de forma que eles funcionem tambm com tipos denidos pelo usurio. produto escalar Operao denida na lgebra linear que multiplica dois pontos (com coordenadas (x,y,z)) e retorna um valor numrico. multiplicao por escalar Operao denida na lgebra linear que multiplica cada uma das coordenadas de um ponto por um valor numrico. polimrca Uma funo que pode operar com mais de um tipo. Se todas as operaes de uma funo pode ser aplicadas a um certo tipo, ento a funo pode ser aplicada a este tipo.
126
CAPTULO 17
Tpicos Captulo 15: Conjuntos de objetos 15.1 Composio 15.2 Objetos Carta 15.3 Atributos de classe e o mtodo __str__ 15.4 Comparando cartas 15.5 Baralhos 15.6 Imprimindo o baralho 15.7 Embaralhando 15.8 Removendo e distribuindo cartas 15.9 Glossrio
127
Se quisermos denir um novo objeto para representar uma carta, bvio que os atributos devem ser posicao e naipe. No to bvio so os tipos aos quais devem pertencer os atributos. Uma possibilidade usar strings contendo palavras como Espada para naipes e Rainha para posies. Um problema com esta implementao que no seria fcil comparar cartas para ver qual possui o maior naipe ou posio. Uma alternativa usar inteiros para codicar as posies e naipes. Codicar, neste caso, no signica o mesmo que as pessoas normalmente pensam, que criptografar ou traduzir para um cdigo secreto. O que um cientista da computao quer dizer com codicar denir um mapeamento entre uma seqncia de nmeros e os itens que eu quero representar. Por exemplo: Espadas -> 3 Copas -> 2 Ouros -> 1 Paus -> 0 Uma caracterstica bvia deste mapeamento que os naipes so mapeados para inteiros na ordem, de modo que ns podemos comparar naipes pela comparao de inteiros. O mapeamento de posies bastante bvio. Cada uma das posies numricas mapeia para o inteiro correspondente e, as cartas com gura so mapeadas conforme abaixo: Valete -> 11 Rainha -> 12 Rei -> 13 O motivo pelo qual ns estamos usando notao matemtica para estes mapeamentos que eles no so parte do programa Python. Eles so parte do projeto do programa, mas eles nunca aparecem explicitamente no cdigo. A denio de classe para o tipo Carta ca parecida com esta:
class Carta: def __init__(self, naipe=0, posicao=0): self.naipe = naipe self.posicao = posicao
Como sempre, ns fornecemos um mtodo de inicializao que recebe um parmetro opcional para cada atributo. Para criar um objeto que representa o 3 de Paus, usa-se este comando:
tresDePaus = Carta(0, 3)
128
Um atributo de classe denido fora de qualquer mtodo, e ele pode ser acessado por quaisquer mtodos da classe. Dentro de __str__, ns podemos usar listaDeNaipes e listaDePosicoes para mapear os valores numricos de naipe e posicao para strings. Por exemplo, a expresso self.listaDeNaipes[self.naipe] signica use o atributo naipe do objeto self como um ndice para o atributo de classe chamado listaDeNaipes, e selecione a string apropriada. O motivo para o narf no primeiro elemento em listaDePosicoes preencher o lugar do 0-simo elemento da lista, que nunca ser usado. As nicas posies vlidas so de 1 a 13. Este item desperdiado no inteiramente necessrio. Ns poderamos ter iniciado com 0, como normal. Porm, menos confuso codicar 2 como 2, 3 como 3, e assim por diante. Com os mtodos que ns temos at agora, ns podemos criar e imprimir cartas:
>>> carta1 = Carta(1, 11) >>> print carta1 Valete de Ouros
Atributos de classe como listaDeNaipes so compartilhados por todos os objetos Carta. A vantagem disso que ns podemos usar qualquer objeto Carta para acessar os atributos de classe:
>>> carta2 = Carta(1, 3) >>> print carta2 3 de Ouros >>> print carta2.listaDeNaipes[1] Ouros
A desvantagem que se ns modicarmos um atributo de classe, isso afetar cada instncia da classe. Por exemplo, se ns decidirmos que Valete de Ouros deveria realmente se chamar Valete de Baleias Rodopiantes, ns poderamos fazer isso:
>>> carta1.listaDeNaipes[1] = "Baleias Rodopiantes" >>> print carta1 Valete de Baleias Rodopiantes
129
O conjunto de cartas de jogo parcialmente ordenado, o que signica que s vezes voc pode comparar cartas, e s vezes no. Por exemplo, voc sabe que o 3 de Paus maior do que o 2 de Paus, e que o 3 de Ouros maior do que o 3 de Paus. Mas qual o melhor, o 3 de Paus ou o 2 de Ouros? Um tem uma posio maior, mas o outro tem um naipe maior. Para tornar as cartas comparveis, voc tem que decidir o que mais importante: posio ou naipe. Para ser honesto, a escolha arbitrria. Por questo de escolha, ns iremos dizer que naipe mais importante, porque um baralho de cartas novo vem ordenado com todas as cartas de Paus juntas, seguidas pelas de Ouros, e assim por diante. Com essa deciso, ns podemos escrever __cmp__:
def __cmp__(self, other): # verificar os naipes if self.naipe > other.naipe: return 1 if self.naipe < other.naipe: return -1 # as cartas tm o mesmo naipe... verificar as posies if self.posicao > other.posicao: return 1 if self.posicao < other.posicao> return -1 # as posies so iguais... um empate return 0
Nesta ordenao, Ases so menores do que 2. Como um exerccio, modique __cmp__, de modo que os Ases sejam maiores do que os Reis.
A maneira mais fcil de popular o baralho com um lao aninhado. O lao externo enumera os naipes de 0 at 3. O lao interno enumera as posies de 1 at 13. Como o lao externo repete quatro vezes e o lao interno 13 vezes, o nmero total de vezes que o corpo executado 52 (13 vezes quatro). Cada iterao cria uma nova instncia de Carta com o naipe e posio atuais e a inclui na lista cartas. O mtodo append trabalha sobre listas mas no, obviamente, sobre tuplas.
130
Aqui, e a partir daqui, as reticncias (...) indicam que ns omitimos os outros mtodos da classe. Como uma alternativa a imprimirBaralho, ns poderamos escrever um mtodo __str__ para a classe Baralho. A vantagem de __str__ que ela mais exvel. Em vez de apenas imprimir o contedo de um objeto, ela gera uma representao em string que outras partes do programa podem manipular antes de imprimir ou armazenar para uso posterior. Abaixo, uma verso de __str__ que devolve uma representao em string de um Baralho. Para adicionar um pouco de estilo, ela distribui as cartas em uma cascata, na qual cada carta indentada um espao a mais do que a carta anterior:
class Baralho: ... def __str__(self): s = "" for i in range(len(self.cartas)): s = s + " "*i + str(self.cartas[i]) + "\n" return s
Este exemplo demonstra diversas caractersticas. Primeiro, em vez de percorrer self.cartas e atribuir cada carta a uma varivel, ns estamos usando i como uma varivel de lao e um ndice para a lista de cartas. Segundo, ns estamos usando o operador de multiplicao de strings para indentar cada carta com um espao adicional com relao anterior. A expresso " "*i produz um nmero de espaos igual ao valor atual de i. Terceiro, em vez de usar o comando print para imprimir as cartas, ns usamos a funo str. Passar um objeto como um argumento para str equivale a invocar o mtodo __str__ sobre o objeto. Finalmente, ns estamos usando a varivel s como um acumulador. Inicialmente, s a string vazia. A cada repetio do lao, uma nova string gerada e concatenada com o valor antigo de s para obter um novo valor. Quando o lao termina, s contm a representao em string completa do Baralho, que se parece com:
>>> baralho = Baralho() >>> print Baralho s de Paus 2 de Paus 3 de Paus 4 de Paus 5 de Paus 6 de Paus 7 de Paus 8 de Paus 9 de Paus 10 de Paus Valete de Paus Rainha de Paus Rei de Paus s de Ouros
E assim por diante. Mesmo que o resultado aparea em 52 linhas, uma string longa que contm newlines.
Para embaralhar as cartas, ns usaremos a funo randrange do mdulo random. Com dois argumentos inteiros, a e b, randrange escolhe um inteiro aleatrio no intervalo a <= x < b. Como o limite superior estritamente menor que b, ns podemos usar o comprimento de uma lista como o segundo parmetro, e ns garantimos que o ndice sempre ser vlido. Por exemplo, esta expresso escolhe o ndice de uma carta aleatria em um baralho:
random.randrange(0, len(self.cartas))
Uma maneira fcil de embaralhar as cartas percorrer a lista e trocar cada carta por outra escolhida aleatoriamente. possvel que a carta seja trocada por ela mesma, mas isso no problema. Na verdade, se ns exclussemos essa possibilidade, a ordem das cartas no seria totalmente aleatria:
class Baralho: ... def embaralhar(self): import random nCartas = len(self.cartas) for i in range(nCartas): j = random.randrange(i, nCartas) self.cartas[i], self.cartas[j] = self.cartas[j], self.cartas[i]
Em vez de assumir que existem 52 cartas no baralho, ns obtivemos o comprimento real da lista e o guardamos na varivel nCartas. Para cada carta no baralho, ns escolhemos uma carta aleatria dentre as cartas que ainda no foram embaralhadas. Ento, ns trocamos a carta atual (i) pela carta selecionada (j). Para trocar as cartas, ns usamos uma atribuio de tupla, como visto na Seo 9.2:
self.cartas[i], self.cartas[j] = self.cartas[j], self.cartas[i]
Como exerccio, reescreva esta linha de cdigo sem usar uma atribuio de seqncia.
O operador in retorna verdadeiro se o primeiro operando estiver contido no segundo, que deve ser uma lista ou uma tupla. Se o primeiro operando for um objeto, Python usa o mtodo __cmp__ do objeto para determinar igualdade com os itens da lista. Como o mtodo __cmp__ da classe Carta verica por igualdade profunda, o mtodo removerCarta tambm testa por igualdade profunda. Para distribuir as cartas, ns iremos remover e devolver a carta do topo. O mtodo de lista pop fornece uma maneira conveniente de fazer isso:
class Baralho: ... def distribuirCarta(self): return self.cards.pop()
132
Na verdade, pop remove a ltima carta da lista. Portanto, ns estamos realmente distribuindo as cartas do m para o incio do baralho. Uma ltima operao que ns poderamos querer a funo booleana estahVazio, que retorna verdadeiro se o baralho no contm cartas:
class Baralho: ... def estahVazio(self): return (len(self.cartas) == 0)
133
134
CAPTULO 18
Tpicos Capitulo 16: Herana 16.1 Herana 16.2 Uma mo de cartas 16.3 Dando as cartas 16.4 Exibindo a mao 16.5 A classe JogoDeCartas 16.6 Classe MaoDeMico 16.7 Classe Mico 16.8 Glossrio
135
Esse comando indica que a nova classe Mao herda da classe existente Baralho. O construtor de Mao inicializa os atributos da mo, que so nome e cartas. A string nome identica essa mo, provavelmente pelo nome do jogador que est segurando as cartas. O nome um parmetro opcional com a string vazia como valor default. cartas a lista de cartas da mo, inicializada com uma lista vazia
class Mao(Baralho): def __init__(self, nome=""): self.cartas = [] self.nome = nome
Em praticamente todos os jogos de cartas, necessario adicionar e remover cartas do baralho. Remover cartas j est resolvido, uma vez que Mao herda removerCarta de Baralho. Mas precisamos escrever adicionarCarta:
class Mao(Baralho): #... def adicionarCarta(self,carta): self.cartas.append(carta)
De novo, a elipse indica que omitimos outros mtodos. O mtodo de listas append adiciona a nova carta no nal da lista de cartas.
136
if self.estahVazia(): break # interromper se acabaram as cartas carta = self.pegarCarta() # pegar a carta do topo mao = maos[i % nMaos] # quem deve receber agora? mao.adicionarCarta(carta) # adicionar a carta mao
O segundo parmetro, nCartas, opcional; o default um nmero grande, o que na prtica signica que todas as cartas do baralho sero dadas se este parmetro for omitido. A varivel do lao i vai de 0 a nCartas-1. A cada volta do lao, uma carta removida do baralho, usando o mtodo de lista pop, que remove e retorna o ltimo item na lista. O operador mdulo (%) permite dar cartas em ao redor da mesa (uma carta de cada vez para cada mo). Quando i igual ao numero de mos na lista, a expresso i % nMaos volta para o comeo da lista (ndice 0).
Nao l uma grande mo, mas tem potencial para um straight ush. Embora seja conveniente herdar os mtodos existentes, h outras informacoes num objeto Mao que podemos querer incluir quando ao exib-lo. Para fazer isso, podemos fornecer um mtodo __str__ para a classe Mao que sobrescreva o da classe Baralho:
class Mao(Baralho) #... def __str__(self): s = "Mao " + self.nome if self.estahVazia(): return s + " est vazia\n" else: return s + " contm\n" + Baralho.__str__(self)
Inicialmente, s uma string que identica a mo. Se a mo estiver vazia, o programa acrescenta as palavras est vazia e retorna o resultado. Se no, o programa acrescenta a palavra contm e a representao de string do Baralho, computada pela invocao do mtodo __str__ na classe Baralho em self. Pode parecer estranho enviar self, que se refere Mao corrente, para um mtodo Baralho, mas isso s at voce se lembrar que um Mao um tipo de Baralho. Objetos Mao podem fazer tudo que os objetos Baralho fazem, entao, permitido passar uma instncia de Mao para um mtodo Baralho. Em geral, sempre permitido usar uma instncia de uma subclasse no lugar de uma instncia de uma classe me.
137
Este o primeiro dos casos que vimos at agora em que o mtodo de inicializao realiza uma computao signicativa, para alm de inicializar atributos. Para implementar jogos especcos, podemos herdar de JogoDeCartas e adicionar caracteristicas para o novo jogo. Como exemplo, vamos escrever uma simulao de Mico. O objetivo do jogo livrar-se das cartas que estiverem na mo. Para fazer isso, preciso combinar cartas formando pares ou casais que tenham a mesma cor e o mesmo nmero ou gura. Por exemplo, o 4 de paus casa com o 4 de espadas porque os dois naipes so pretos. O Valete de copas combina com o Valete de ouros porque ambos so vermelhos. Antes de mais nada, a Dama de paus removida do baralho, para que a Dama de espadas que sem par. A Dama de espadas ento faz o papel do mico. As 51 cartas que sobram so distribuidas aos jogadores em ao redor da mesa (uma carta de cada vez para cada mo). Depois que as cartas foram dadas, os jogadores devem fazer todos os casais possveis que tiverem na mo, e em seguida descart-los na mesa. Quando ningum mais tiver nenhum par para descartar, o jogo comea. Na sua vez de jogar, o jogador pega uma carta (sem olhar) do vizinho mais proximo esquerda, que ainda tiver cartas. Se a carta escolhida casar com uma carta que ele tem na mo, ele descarta esse par. Quando todos os casais possveis tiverem sido feitos, o jogador que tiver sobrado com a Dama de espadas na mo perde o jogo. Em nossa simulao computacional do jogo, o computador joga todas as mos. Infelizmente, algumas nuances do jogo presencial se perdem. Num jogo presencial, o jogador que est com o mico na mo pode usar uns truques para induzir o vizinho a pegar a carta, por exemplo, segurando-a mais alto que as outras, ou mais baixo, ou se esforando para que ela no que em destaque. J o computador simplesmente pega a carta do vizinho aleatoriamente...
Comeamos fazendo uma cpia da lista de cartas, para poder percorrer a cpia enquanto removemos cartas do original. Uma vez que self.cartas modicada no lao, no queremos us-la para controlar o percurso. Python pode car bem confuso se estiver percorrendo uma lista que est mudando! 138 Captulo 18. Capitulo 16: Herana
Para cada carta na mo, vericamos qual a carta que faz par com ela e vamos procur-la. O par da carta tem o mesmo valor (nmero ou gura) e naipe da mesma cor. A expresso 3 - carta.naipe transforma um paus (naipe 0) numa espadas (naipe 3) e um ouros (naipe 1) numa copas (naipe 2). Voc deve analisar a frmula at se convencer de que as operaes opostas tambm funcionam. Se o par da carta tambem estiver na mo, ambas as cartas so removidas. O exemplo a seguir demonstra como usar descartarCasais:
>>> jogo = JogoDeCartas() >>> mao = MaoDeMico("fabio") >>> jogo.baralho.distribuir([mao], 13) >>> print mao mo fabio contm s de espadas 2 de ouros 7 de espadas 8 de paus 6 de copas 8 de espadas 7 de paus Rainha de paus 7 de ouros 5 de paus Valete de ouros 10 de ouros 10 de copas >>> mao.descartarCasais() Mo fabio: 7 de espadas faz par com 7 de paus Mo fabio: 8 de espadas faz par com 8 de paus Mo fabio: 10 de ouros faz par com 10 de copas >>> print mao Mo fabio contm s de espadas 2 de ouros 6 de copas Rainha de paus 7 de ouros 5 de paus Valete de ouros
Observe que no existe um mtodo __init__ para a classe MaoDeMico. Ele herdado de Mao.
139
for nome in nomes : self.maos.append(MaoDeMico(nome)) # distribuir as cartas self.baralho.distribuir(self.maos) print "---------- As cartas foram dadas" self.exibirMaos() # remover casais iniciais casais = self.removerTodosOsCasais() print "---------- Os pares foram descartados, o jogo comea" self.exibirMaos() # jogar at que 25 casais se formem vez = 0 numMaos = len(self.maos) while casais < 25: casais = casais + self.jogarVez(vez) vez = (vez + 1) % numMaos print "---------- Fim do jogo" self.exibirMaos()
Algumas etapas do jogo foram separadas em mtodos. removerTodosOsCasais percorre a lista de mos e invoca descartarCasais em cada uma:
class Mico(JogoDeCartas): #... def removerTodosOsCasais(self): conta = 0 for mao in self.maos: conta = conta + mao.descartarCasais() return conta Como exerccio, escreva exibirMaos que percorre self.maos e exibe cada mo.
conta uma acumulador que soma o nmero de pares em cada mo e retorna o total. Quando o nmero total de pares alcana 25, 50 cartas foram removidas das mos, o que signica que sobrou s uma carta e o jogo chegou ao m. A varivel vez mantm controle sobre de quem a vez de jogar. Comea em 0 e incrementa de um em um; quando atinge numMaos, o operador mdulo faz ela retornar para 0. O mtodo jogarVez recebe um argumento que indica de quem a vez de jogar. O valor de retorno o nmero de pares feitos durante essa rodada:
class Mico(JogoDeCartas): #... def jogarVez(self, i): if self.maos[i].estahVazia(): return 0 vizinho = self.buscarVizinho(i) novaCarta = self.maos[vizinho].pegarCarta() self.maos[i].adicionarCarta(novaCarta) print "Mao", self.maos[i].nome, "pegou", novaCarta conta = self.maos[i].descartarCasais() self.maos[i].embaralhar() return conta
140
Se a mo de um jogador estiver vazia, ele est fora do jogo, ento, ele no faz nada e retorna 0. Do contrrio, uma jogada consiste em achar o primeiro jogador esquerda que tenha cartas, pegar uma carta dele, e tentar fazer pares. Antes de retornar, as cartas na mo so embaralhadas, para que a escolha do prximo jogador seja aleatria. O mtodo buscarVizinho comea com o jogador imediatamente esquerda e continua ao redor da mesa at encontrar um jogador que ainda tenha cartas:
class Mico(JogoDeCartas): #... def buscarVizinho(self, i): numMaos = len(self.maos) for next in range(1,numMaos): vizinho = (i + next) % numMaos if not self.maos[vizinho].estahVazia(): return vizinho
Se buscarVizinho alguma vez circulasse pela mesa sem encontrar cartas, retornaria None e causaria um erro em outra parte do programa. Felizmente, podemos provar que isso nunca vai acontecer (desde que o m do jogo seja detectado corretamente). No mencionamos o mtodo exibirBaralhos. Esse voc mesmo pode escrever. A sada a seguir produto de uma forma reduzida do jogo, onde apenas as 15 cartas mais altas do baralho (do 10 para cima) foram dadas, para trs jogadores. Com esse baralho reduzido, a jogada pra depois que 7 combinaes foram feitas, ao invs de 25:
>>> import cartas >>> jogo = cartas.Mico() >>> jogo.jogar(["Alice","Jair","Clara"]) ---------- As cartas foram dadas Mo Alice contm Rei de copas Valete de paus Rainha de espadas Rei de espadas 10 de ouros Mo Jair contm Rainha de copas Valete de espadas Valete de copas Rei de ouros Rainha de ouros Mo Clara contm Valete of ouros Rei de paus 10 de espadas 10 de copas 10 de paus Mo Jair: Dama de copas faz par com Dama de ouros Mo Clara: 10 de espadas faz par com 10 de paus ---------- Os pares foram descartados, o jogo comea Mo Alice contm Rei de copas Valete de paus Rainha de espadas
141
Rei de espadas 10 de ouros Mo Jair contm Valete de espadas Valete de copas Rei de ouros Mo Clara contm Valete de ouros Rei de paus 10 de copas Mo Alice pegou o Rei de ouros Mo Alice: Rei de copas faz par com Rei de ouros Mo Jair pegou 10 de copas Mo Clara pegou Valete de paus Mo Alice pegou Valete de copas Mo Jair pegou Valete de ouros Mo Clara pegou Dama de espadas Mo Alice pegou Valete de ouros Mo Alice: Valete de copas faz par com Valete de ouros Mo Jair pegou Rei de paus Mo Clara pegou Rei de espadas Mo Alice pegou 10 de copas Mo Alice: 10 de ouros faz par com 10 de copas Mo Jair pegou Dama de espadas Mo Clara pegou Valete de espadas Mo Clara: Valete de paus faz par com Valete de espadas Mo Jair pegou Rei de espadas Mo Jeff: Rei de paus faz par com Rei de espadas ---------- Fim do jogo Mo Alice est vazia Mo Jair contm Rainha de espadas Mo Clara est vazia
142
CAPTULO 19
Tpicos Captulo 17: Listas encadeadas 17.1 Referncias Embutidas 17.2 A classe No (Node) 17.3 Listas como Colees 17.4 Listas e Recorrncia 17.5 Listas Innitas 17.6 O Teorema da Ambigidade Fundamental 17.7 Modicando Listas 17.8 Envoltrios e Ajudadores 17.9 A Classe ListaLigada 17.10 Invariantes 17.11 Glossrio
143
Como de costume, os parmetros para o mtodo de inicializao so opcionais. Por omisso (default), ambos, a carga e a ligao, proximo, so denidas como None. A representao string de um n simplesmente a representao string da carga. Como qualquer valor pode ser passado para a funo str, ns podemos armazenar qualquer valor em uma lista. Para testar a implementao at agora, ns criamos um No e o imprimimos:
>>> no = No("teste") >>> print no teste
Este cdigo cria trs ns, mas ns ainda no temos uma lista ainda porque os ns no esto ligados. O diagrama de estado parecido com este:
Para ligar os ns, temos que fazer o primeiro n da lista referir ao segundo e o segundo n referir ao terceiro:
>>> no1.proximo = no2 >>> no2.proximo = no3
A referncia do terceiro n None, que indica que ele o nal da lista. Agora o diagrama de estado se parece com:
144
Agora voc sabe como criar ns e lig-los em uma lista. O que pode estar menos claro neste ponto por qu.
Dentro de imprimeLista ns temos uma referncia para o primeiro n da lista, mas no h variveis que reram aos outros ns. Ns temos que usar o valor proximo de cada n para alcanar o prximo n. Para percorrer uma lista ligada, comum usar uma varivel lao como no para referir a cada um dos ns sucessivamente. Este diagrama mostra o valor de lista e os valores que no assume:
Por conveno, listas so freqentemente impressas em braquetes com vrgulas entre os elementos, como em [1, 2, 3]. Como um exerccio, modique imprimeLista para que ela gere uma sada neste formato.
145
A primeira linha trata o caso base fazendo nada. As prximas duas linhas dividem a lista em cabeca e rabo. As duas ltimas linhas imprimem a lista. A vrgula no nal da ltima linha impede o Python de imprimir uma nova linha aps cada n. Ns invocamos este mtodo como invocamos o imprimeLista:
>>> imprimeDeTrasParaFrente(no1) 3 2 1
O resultado a lista de trs para frente. Voc pode se perguntar por qu imprimeLista e imprimeDeTrasParaFrente so funes e no mtodos da classe No. A razo que ns queremos usar None para representa a lista vazia e no legal invocar um mtodo sobre None. Esta limitao torna complicado escrever cdigo de manipulao de lista em estilo orientado a objeto limpo. Podemos provar que imprimeDeTrasParaFrente sempre termina? Em outras palavras, ir ela sempre atingir o caso base? De fato, a resposta no. Algumas listas faro este mtodo falhar.
146
Se ns invocarmos imprimeLista nesta lista, ele car em lao para sempre. Se ns invocarmos imprimeDeTrasParaFrente, ele recorrer innitamente. Este tipo de comportamento torna as listas innitas difceis de se lidar. A despeito disto, elas ocasionalmente so teis. Por exemplo, podemos representar um nmero como uma lista de dgitos e usar uma lista innita para representar uma frao repetente. Mesmo assim, problemtico que no possamos provar que imprimeLista e imprimeDeTrasParaFrente terminem. O melhor que podemos fazer a armao hipottica, Se a lista no contm laos, ento este mtodo terminar. Este tipo de hiptese chamado uma pr-condio. Ele impe uma limitao sobre um dos parmetros e descreve o comportamento do mtodo se a limitao satisfeita. Voc ver mais exemplos em breve.
Aps a primeira atribuio, cabeca e lista tm o mesmo tipo e o mesmo valor. Ento por que ns criamos uma nova varivel? A razo que as duas variveis tm diferentes papis. Quando pensamos em cabeca, pensamos como uma referncia a um nico n, e quando pensamos em lista o fazemos como uma referncia ao primeiro n da lista. Estes papis no so parte do programa; eles esto na mente do programador. Em geral no podemos dizer olhando para o programa qual o papel que uma varivel tem. Esta ambigidade pode ser til, mas tambm pode tornar os programas difceis de serem lidos. Usamos freqentemente nomes de variveis como no e lista para documentar como pretendemos usar uma varivel e algumas vezes criamos variveis adicionais para remover a ambigidade. Poderamos ter escrito imprimeDeTrasParaFrente sem cabeca e rabo, que a tornaria mais concisa mas possivelmente menos clara:
def imprimeDeTrasParaFrente(lista): if lista == None : return imprimeDeTrasParaFrente(lista.proximo) print lista,
Olhando para as duas chamadas de funo, temos que lembrar que imprimeDeTrasParaFrente trata seu argumento como uma coleo e print trata seu argumento como um objeto nico. O teorema da ambigidade fundamental descreve a ambigidade que inerente referncia a um n:
147
Uma varivel que refere a um n pode tratar o n como um objeto nico ou como o primeiro em uma lista de ns.
Novamente, estamos usando variveis temporrias para tornar o cdigo mais fcil de ser lido. Aqui est como usar este mtodo:
>>> 1 2 >>> >>> 2 >>> 1 3 imprimeLista(no1) 3 removido = removeSegundo(no1) imprimeLista(removido) imprimeLista(no1)
O que acontece se voc invocar este mtodo e passar uma lista com somente um elemento (um singleton)? O que acontece se voc passar a lista vazia como um argumento? Existe uma pr-condio para este mtodo? Se houver, corrija o mtodo para tratar uma violao da pr-condio de modo razovel.
148
imprimir 3, 2, mas queremos um metodo separado para imprimir os braquetes e o primeiro n. Vamos cham-lo de imprimeDeTrasParaFrenteLegal:
def imprimeDeTrasParaFrenteLegal(lista): print "[", if lista != None : cabeca = lista rabo = lista.proximo imprimeDeTrasParaFrente(rabo) print cabeca, print "]",
Novamente, uma boa idia vericar mtodos como este para ver se eles funcionam com casos especiais como uma lista vazia ou um singleton. Quando usamos este mtodo em algum lugar no programa, invocamos imprimeDeTrasParaFrenteLegal diretamente, e ele invoca imprimeDeTrasParaFrente por ns. Neste sentido, imprimeDeTrasParaFrenteLegal atua como um envoltrio, e usa imprimeDeTrasParaFrente como um ajudador.
Uma coisa legal acerca da classe ListaLigada que ela prov um lugar natural para se colocar funes envoltrias como imprimeDeTrasParaFrenteLegal, que podemos transformar em um mtodo da classe ListaLigada:
class ListaLigada: ... def imprimeDeTrasParaFrente(self): print "[", if self.cabeca != None : self.cabeca.imprimeDeTrasParaFrente() print "]",
class No: ... def imprimeDeTrasParaFrente(self): if self.proximo != None: rabo = self.proximo rabo.imprimeDeTrasParaFrente() print self.carga,
Apenas para tornar as coisas confusas, mudamos o nome de imprimeDeTrasParaFrenteLegal. Agora existem dois mtodos chamados imprimeDeTrasParaFrente: um na classe No (o ajudador); e um na classe ListaLigada(o envoltrio). Quano o envoltrio invoca
149
self.cabeca.imprimeDeTrasParaFrente, ele est invocando o ajudador, porque self.cabeca um objeto No. Outro benefcio da classe ListaLigada que ela torna mais fcil adicionar e remover o primeiro elemento de uma lista. Por exemplo, adicionaPrimeiro um mtodo para ListaLigada; ele toma um item de carga como argumento e o coloca no incio da lista:
class ListaLigada: ... def adicionaPrimeiro(self, carga): no = No(carga) no.proximo = self.cabeca self.cabeca = no self.comprimento = self.comprimento + 1
Como de costume, voc deve conferir cdigos como este para ver se eles tratam os casos especiais. Por exemplo, o que acontece se a lista est inicialmente vazia?
150
envoltrio (wrapper) Um mtodo que atua como um intermedirio (middleman) entre um chamador e um mtodo ajudador (helper), freqentemente tornando a invocao do mtodo mais fcil ou menos propensa a erros. ajudador (helper) Um mtodo que no invocado diretamente pelo chamador (caller) mas usado por outro mtodo para realizar parte de uma operao. invariante (invariant) Uma assero que deveria ser verdadeira sempre para um objeto (exceto talvez enquanto o objeto estiver sendo modicado).
151
152
CAPTULO 20
Tpicos Captulo 18: Pilhas 18.1 Tipos abstratos de dados 18.2 O TAD Pilha 18.3 Implementando pilhas com listas de Python 18.4 Empilhando e desempilhando 18.5 Usando uma pilha para avaliar expresses ps-xas 18.6 Anlise sinttica 18.7 Avaliando em ps-xo. * 18.9 Glossrio
Um objeto Stack contm um atributo chamado items que uma lista de tens na pilha. O mtodo de inicializao dene items como uma lista vazia. Para adicionar um novo tem na pilha, push o coloca em items. Para remover um tem da pilha, pop usa o mtodo de lista homnimo para remover e retornar um ltimo tem da lista. Finalmente, para vericar se a pilha est vazia, isEmpty comprara items a uma lista vazia. Uma implementao como esta, na qual os mtodos consistem de simples invocaes de mtodos existentes, chamado revestimento. Na vida real, revestimento uma na camada de madeira de boa qualidade usado em XXX*furniture-making* para esconder madeira de menor qualidade embaixo. Cientistas da Computao usam esta metfora para descrever um pequeno trecho de cdigo que esconde os detalhes de uma implementao e fornece uma interface mais simples, ou mais padronizada.
154
Podemos usar isEmpty e pop para remover e imprimir todos os tens da pilha: while not s.isEmpty() : priint s.pop() A sada + 45 54. Em outras palavras, usamos a pilha para imprimir os tens ao contrrio! Sabidamente, este no o formato padro de imprimir uma lista, mas, usando uma pilha, foi notavelmente fcil de fazer. Voc deve comparar este trecho de cdigo com a implementao de printBackward na seo 17.4. Existe um paralelo natura entre a verso recursiva de printBackward e o algoritmo de pilha aqui. A diferen que printBackward usa a pilha de execuo para XXXmanter a trilha(keep track) dos ns enquanto percorre a lista, e ento imprime-a no retorno da recurso. o algoritmo de pilha faz a mesma coisa, exceto que usa o objeto Stack ao invs da pilha de execuo.
155
>>> import string >>> string.split ("Now is the time", " ") [Now, is, the, time]
Neste caso, o delimitador o caracter de espao, ento a string dividida a cada espao. A funo re.split mais poderosa, permitindo-nos fornecer uma expreso regular ao invs de um delimitador. Uma expresso regular uma maneira de especicar um conjunto de strings. Por exemplo, [A-z] o conjunto de todas as letras e [0-9] o conjunto de todos os dgitos. O operador ^nega um conunto, ento [^0-9] o conjunto de tudo o que no nmero, que exatamente o que queremos para dividir expresses ps-xas.
>>> import re >>> re.split ("[^0-9]", "123+456*/") [123, +, 456, *, , /, ]
Note que a ordem dos argumentos diferente de string.split, o delimitador vem antes da string. A lista resultante inclui os operandos 123 e 456, e os operadores * e /. Tambm inclui duas strings vazias que so inseridas depois dos operadores.
A primeira condio cuida de espaos e strings vazias. As duas prximas condies manipulam os operadores. Ns assumimos, agora que qualquer coisa um operador. claro, seria melhor chegar por entrada errnea e enviar uma mensagem de erro, mas faremos isto depois. Vamos test-lo avaliando a forma ps-xa de (56 + 47) * 2
>>> print evalPostfix("56 47 + 2 *") 206
XXXthats close enough 18.8 Clientes de fornecedores. Um dos objetivos de um TAD separar os interesses do fornecedor, quem escreve o cdigo que implementa o TAD, e o cliente, que usa o TAD. O fornecedor tem que se preocupar apenas se a implementao est correta - de acordo com a especicao do TAD - e no como ele ser usado.
156
Inversamente, o cliente assume que a implementao do TAD est correta e no se preocupa com os detalhes. Quando voc est usando um tipo nativo do Python, tem o luxo de pensar exclusivamente como um cliente. claro, quanto voc implementa um TAD, voc tambm tem que escrever cdigo cliente para test-lo. Neste caso, voc faz os dois papis, o que pode ser confuso. Voc deve fazer algum esfor para manter a trilha do papel que est fazendo a cada momento.
157
158
CAPTULO 21
Este captulo apresenta dois TDAs: Fila e Fila por Prioridade. Na nossa vida diria, la um alinhamento de consumidores aguardando algum tipo de servio. Na maioria dos casos, o primeiro da la o primeiro a ser atendido. Mas h excees. No aeroporto, passageiros cujo vo vai decolar logo, s vezes so chamados primeiro ao balco do check-in, mesmo que estejam no meio da la. No supermercado, comum na la do caixa algum deixar passar na frente uma pessoa que chega la s com um ou dois produtos na mo. A regra que determina quem o prximo da la chama-se poltica de enleiramento. A poltica de enleiramento mais simples chama-se FIFO, sigla de rst-in-rst-out: primeiro a entrar, primeiro a sair. A poltica de enleiramento mais geral o enleiramento por prioridade, em que se atribui uma prioridade a cada pessoa da la e a que tiver maior prioridade vai primeiro, independente da sua ordem de chegada. Dizemos que essa a poltica mais geral de todas, porque a prioridade pode ser baseada em qualquer coisa: hora de partida do vo; quantos produtos a pessoa vai passar pelo caixa; o grau de prestgio da pessoa. claro que nem todas as polticas de enleiramento so justas, mas o que justo depende do ponto de vista. O TDA Fila e o TDA Fila por Prioridade tm o mesmo conjunto de operaes. A diferena est na semntica das operaes: a la usa a poltica FIFO; e a la por prioridade (como o prprio nome sugere) usa a poltica de enleiramento por prioridade.
159
class Queue: def __init__(self): self.length = 0 self.head = None def isEmpty(self): return (self.length == 0) def insert(self, cargo): node = Node(cargo) node.next = None if self.head == None: # if list is empty the new node goes first self.head = node else: # find the last node in the list last = self.head while last.next: last = last.next # append the new node last.next = node self.length = self.length + 1 def remove(self): cargo = self.head.cargo self.head = self.head.next self.length = self.length - 1 return cargo
Os mtodos isEmpty e remove so idnticos aos mtodos isEmpty e removeFirst de LinkedList. O mtodo insert novo e um pouco mais complicado. Queremos inserir novos itens no m da lista. Se a la estiver vazia, basta fazer head apontar ao novo n. Se no, percorremos a lista at o ltimo n e l penduramos o novo n. possvel identicar o ltimo n porque o seu atributo next None. Existem duas invariantes para um objeto Fila bem formado: o atributo length deve ser o nmero de ns na la, e o ltimo n deve ter seu atributo next igual a None. Estude o mtodo at car convencido de que ele preserva ambas invariantes.
160
At agora, a nica mudana o atributo last. Ele usado nos mtodos insert e remove:
class ImprovedQueue: # ... def insert(self, cargo): node = Node(cargo) node.next = None if self.length == 0: # if list is empty, the new node is head and last self.head = self.last = node else: # find the last node last = self.last # append the new node last.next = node self.last = node self.length = self.length + 1
Uma vez que last no perde de vista o ultimo n, no necessrio busc-lo. Como resultado, esse mtodo tem tempo constante. Mas essa rapidez tem preo. preciso adicionar um caso especial a remove, para congurar last para None quando o ultimo n removido:
class ImprovedQueue: #... def remove(self): cargo = self.head.cargo self.head = self.head.next self.length = self.length - 1 if self.length == 0:
161
Essa implementao mais complicada que a primeira, e mais difcil de se demonstrar que est correta. A vantagem que o objetivo foi atingido tanto insert quanto remove so operaes de tempo constante. Como exerccio, escreva uma implementao do TDA Fila usando uma lista nativa do Python. Compare a performance dessa implementao com a de ImprovedQueue, para las de diversos comprimentos.
O mtodo de inicializao, isEmpty, e insert so apenas uma fachada para operaes bsicas de lista. O nico mtodo interessante remove:
class PriorityQueue: # ... def remove(self): maxi = 0 for i in range(1,len(self.items)): if self.items[i] > self.items[maxi]: maxi = i item = self.items[maxi] self.items[maxi:maxi+1] = [] return item
No incio de cada iterao, maxi armazena o ndice do maior item (a prioridade mais alta de todas) que vimos at agora. A cada volta do lao, o programa compara o i-simo item ao campeo. Se o novo item for maior, maxi recebe o valor de i. 162 Captulo 21. Captulo 19: Filas
Quando o comando for se completa, maxi o ndice do maior item. Esse item removido da lista e retornado. Vamos testar a implementao:
>>> >>> >>> >>> >>> >>> 14 13 12 11 q = PriorityQueue() q.insert(11) q.insert(12) q.insert(14) q.insert(13) while not q.isEmpty(): print q.remove()
Se a la contm nmeros ou strings simples, eles so removidas em ordem numrica decrescente ou alfabtica invertida (de Z at A). Pyhton consegue achar o maior inteiro ou string porque consegue compar-los usando os operadores de comparao nativos. Se a la contm objetos de outro tipo, os objetos tm que prover um mtodo __cmp__. Quando remove usa o operador > para comparar dois itens, o mtodo __cmp__ de um dos itens invocado, recebendo o segundo item como argumento. Desde que o mtodo __cmp__ funcione de forma consistente, a Fila por Prioridade vai funcionar.
O mtodo __str__ usa o operador de formato para colocar nomes e pontuaes em colunas arrumadas. Em seguida, denimos uma verso de __cmp__, ma qual a pontuao mais baixa ca com prioridade mxima. Como sempre, __cmp__ retorna 1 se self maior que other, -1 se self menor que other, e 0 se eles so iguais.
class Golfer: #... def __cmp__(self, other): if self.score < other.score: return 1 if self.score > other.score: return -1 return 0
# less is more
Agora estamos prontos para testar a la por prioridade com a classe Golfer:
>>> >>> >>> >>> >>> >>> >>> tiger = Golfer("Tiger Woods", 61) phil = Golfer("Phil Mickelson", 72) hal = Golfer("Hal Sutton", 69) pq = PriorityQueue() pq.insert(tiger) pq.insert(phil)
163
>>> pq.insert(hal) >>> while not pq.isEmpty(): print pq.remove() Tiger Woods : 61 Hal Sutton : 69 Phil Mickelson : 72
Como exerccio, escreva uma implementao do TDA Fila por Prioridade usando uma lista encadeada. Mantenha a lista em ordem para que a remoo seja uma operao de tempo constante. Compare a performance dessa implementao com a implementao usando uma lista nativa do Python.
164
CAPTULO 22
Tpicos Captulo 20: rvores 20.1 Construindo rvores 20.2 Percorrendo rvores 20.3 rvores de expresses 20.4 Percurso de rvores 20.5 Construindo uma rvore de expresso 20.6 Manipulando erros 20.7 A rvore dos animais 20.8 Glossrio Nota: Veja a discusso sobre o vocabulrio usado no m da pgina.
Como listas ligadas, rvores so constitudas de clulas. Uma espcie comum de rvores a rvore binria, em que cada clula contm referncias a duas outras clulas (possivelmente nulas). Tais referncias so chamadas de subrvore esquerda e direita. Como as clulas de listas ligadas, as clulas de rvores tambm contm uma carga. Um diagrama de estados para uma rvore pode aparecer assim:
165
Figura 1
Para evitar a sobrecarga da gura, ns frequentemente omitimos os Nones. O topo da rvore (a clula qual o apontador tree se refere) chamada de raiz. Seguindo a metfora das rvores, as outras clulas so chamadas de galhos e as clulas nas pontas contendo as referncias vazia so chamadas de folhas. Pode parecer estranho que desenhamos a gura com a raiz em cima e as folhas em baixo, mas isto nem ser a coisa mais estranha. Para piorar as coisas, cientistas da computao misturam outra metfora alm da metfora biolgica - a rvore genealgica. Uma clula superior pode ser chamada de pai e as clulas a que ela se refere so chamadas de seus lhos. Clulas com o mesmo pai so chamadas de irmos. Finalmente, existe tambm o vocabulrio geomtrico para falar de rvores. J mencionamos esquerda e direita, mas existem tambm as direes para cima (na direo da raiz) e para baixo (na direo dos lhos/folhas). Ainda nesta terminologia, todas as clulas situadas mesma distncia da raiz constituem um nvel da rvore. Provavelmente no precisamos de trs metforas para falar de rvores, mas a elas esto. Como listas ligadas, rvores so estruturas de dados recursivas j que elas so denidas recursivamente: Uma rvore a rvore vazia, representada por None, ou uma clula que contm uma referncia a um objeto (a carga da clula) e duas referncias a rvores.
166
class Tree : def __init__(self, cargo, left=None, right=None) : self.cargo = cargo self.left = left self.right = right def __str__(self) : return str(self.cargo)
A carga pode ser de qualquer tipo, mas os parmetros left e right devem ser clulas. left e right so opcionais; o valor default None. Para imprimir uma clula, imprimimos apenas a sua carga. Uma forma de construir uma rvore de baixo para cima. Aloque os lhos primeiro:
left = Tree(2) right = Tree(3)
O caso base a rvore vazia, que no contm nenhuma carga, logo a soma das cargas 0. O passo recursivo faz duas chamadas recursivas para achar a soma das cargas das subrvores dos lhos. Ao nalizar a chamada recursiva, adicionamos a carga do pai e devolvemos o valor total.
167
Figura 2
As clulas de uma rvore de expresso podem ser operandos como 1 e 2 ou operaes como + e *. As clulas contendo operandos so folhas; aquelas contendo operaes devem ter referncias aos seus operandos. (Todos os nossos operandos so binrios, signicando que eles tem exatamente dois operandos.) Podemos construir rvores assim:
>>> tree = Tree(+, Tree(1), Tree(*, Tree(2), Tree(3)))
Examinando a gura, no h dvida quanto ordem das operaes; a multiplicao feita primeiro para calcular o segundo operando da adio. rvores de expresso tem muitos usos. O exemplo neste captulo usa rvores para traduzir expresses para as notaes psxa, prexa e inxa. rvores similares so usadas em compiladores para analisar sintaticamente, otimizar e traduzir programas.
168
Em outras palavras, para imprimir uma rvore, imprima primeiro o contedo da raiz, em seguida imprima toda a subrvore esquerda e nalmente imprima toda a subrvore direita. Esta forma de percorrer uma rvore chamada de prordem, porque o contedo da raiz aparece antes dos contedos dos lhos. Para o exemplo anterior, a sada :
>>> tree = Tree(+, Tree(1), Tree(*, Tree(2), Tree(3))) >>> printTree(tree) + 1 * 2 3
Esta notao diferente tanto da notao psxa quanto da inxa; uma notao chamada de prexa, em que os operadores aparecem antes dos seus operandos. Voc pode suspeitar que se Voc percorre a rvore numa ordem diferente, Voc produzir expresses numa notao diferente. Por exemplo, se Voc imprime subrvores primeiro e depois a raiz, Voc ter:
def printTreePostorder(tree) : if tree == None : return printTreePostorder(tree.left) printTreePostorder(tree.right) print tree.cargo,
O resultado, 1 2 3 * +, est na notao psxa! Esta ordem de percurso chamada de psordem. Finalmente, para percorrer uma rvore em inordem, Voc imprime a subrvore esquerda, depois a raiz e depois a subrvore direita:
def printTreeInorder(tree) : if tree == None : return printTreeInorder(tree.left) print tree.cargo, printTreeInorder(tree.right)
O resultado 1 + 2 * 3, que a expresso na notao inxa. Para sermos justos, devemos lembrar que acabamos de omitir uma complicao importante. algumas vezes quando escrevemos expresses na notao inxa devemos usar parntesis para prescrever a ordem das operaes. Ou seja, um percurso em inordem no suciente para gerar a expresso inxa. Ainda assim, com alguns aperfeioamentos, a rvore de expresso e os trs modos recursivos de percurso resultam em algoritmos para transformar expresses de uma notao para outra. Como um exerccio, modique printTreeInorder de modo que ele coloque parntesis em volta de cada operador e par de operandos. A sada correta e no ambgua? Os parntesis so sempre necessrios? Se percorrermos uma rvore em inordem e acompanharmos em qual nvel na rvore estamos, podemos gerar uma representao grca da rvore:
def printTreeIndented(tree, level=0) : if tree == None : return printTreeIndented(tree.right, level+1) print *level + str(tree.cargo) printTreeIndented(tree.left, level+1)
O parmetro level registra aonde estamos na rvore. Por default, o nvel inicialmente zero. A cada chamada recursiva repassamos level+1 porque o nvel do lho sempre um a mais do que o nvel do pai. Cada item indentado dois espaos por nvel. Para o nosso exemplo obtemos:
>>> printTreeIndented(tree) 3 * 2
169
+ 1
Se Voc deitar a sada acima Voc enxerga uma verso simplicada da gura original.
Note que simplicamos o diagrama omitindo os nomes dos campos. O analisador que escreveremos aceitar expresses que incluam nmeros, parntesis e as operaes + e *. Vamos supor que a cadeia de entrada j foi tokenizada numa lista do Python. A lista de tokens para a expresso (3+7)*9 : [(, 3, +, 7, ), *, 9, end] O token nal end prtico para prevenir que o analisador tente buscar mais dados aps o trmino da lista. A ttulo de um exerccio, escreva uma funo que recebe uma expresso na forma de uma cadeia e devolve a lista de tokens. A primeira funo que escreveremos getToken que recebe como parmetros uma lista de tokens e um token esperado. Ela compara o token esperado com o o primeiro token da lista: se eles batem a funo remove o token da lista e devolve um valor verdadeiro, caso contrrio a funo devolve um valor falso:
def getToken(tokenList, expected) : if tokenList[0] == expected : tokenList[0:1] = [] # remove the token return 1 else : return 0
170
J que tokenList refere a um objeto mutvel, as alteraes feitas aqui so visveis para qualquer outra varivel que se rera ao mesmo objeto. A prxima funo, getNumber, trata de operandos. Se o primeiro token na tokenList for um nmero ento getNumber o remove da lista e devolve uma clula folha contendo o nmero; caso contrrio ele devolve None.
def getNumber(tokenList) : x = tokenList[0] if type(x) != type(0) : return None del tokenList[0] return Tree(x, None, None)
Antes de continuar, convm testar getNumber isoladamente. Atribumos uma lista de nmeros a tokenList, extramos o primeiro, imprimimos o resultado e imprimimos o que resta na lista de tokens:
>>> tokenList = [9, 11, end] >>> x = getNumber(tokenList) >>> printTreePostorder(x) 9 >>> print tokenList [11, end]
Em seguida precisaremos da funo getProduct, que constri uma rvore de expresso para produtos. Os dois operandos de um produto simples so nmeros, como em 3 * 7. Segue uma verso de getProduct que trata de produtos simples.
def getProduct(tokenList) : a = getNumber(tokenList) if getToken(tokenList, *) : b = getNumber(tokenList) return Tree(*, a, b) else : return a
Supondo que a chamada de getNumber seja bem sucedida e devolva uma rvore de uma s clula atribumos o primeiro operando a . Se o prximo caractere for *, vamos buscar o segundo nmero e construir a rvore com a, b e o operador. Se o caractere seguinte for qualquer outra coisa, ento simplesmente devolvemos uma clula folha com a. Seguem dois exemplos:
>>> tokenList = [9, *, 11, end] >>> tree = getProduct(tokenList) >>> printTreePostorder(tree) 9 11 *
>>> tokenList = [9, +, 11, end] >>> tree = getProduct(tokenList) >>> printTreePostorder(tree) 9
O segundo exemplo sugere que ns consideramos um operando unitrio como uma espcie de produto. Esta denio de produto talvez no seja intuitiva, mas ela ser til. Agora tratamos produtos compostos, como 3 * 5 * 13. Encaramos esta expresso como um produto de produtos, mais precisamente como 3 * (5 * 13). A rvore resultante :
171
Figura 4
Com uma pequena alterao em getProduct, podemos acomodar produtos arbitrariamente longos:
def getProduct(tokenList) : a = getNumber(tokenList) if getToken(tokenList, *) : b = getProduct(tokenList) return Tree(*, a, b) else : return a
Em outras palavras, um produto pode ser um singleton ou uma rvore com * na raiz, que tem um nmero como lho esquerdo e um produto como lho direito. Este tipo de denio recursiva devia comear a car familiar. Testemos a nova verso com um produto composto:
>>> >>> >>> 2 3 tokenList = [2, *, 3, *, 5 , *, 7, end] tree = getProduct(tokenList) printTreePostorder(tree) 5 7 * * *
A seguir adicionamos o tratamento de somas. De novo, usamos uma denio de soma que ligeiramente no intuitiva. Para ns, uma soma pode ser uma rvore com + na raiz, que tem um produto como lho esquerdo e uma soma como lho direito. Ou, uma soma pode ser simplesmente um produto. Se Voc est disposto a brincar com esta denio, ela tem uma propriedade interessante: podemos representar qualquer expresso (sem parntesis) como uma soma de produtos. Esta propriedade a base do nosso algoritmo de anlise sinttica. getSum tenta construir a rvore com um produto esquerda e uma soma direita. Mas, se ele no encontra uma +, ele simplesmente constri um produto.
def getSum(tokenList) : a = getProduct(tokenList) if getToken(tokenList, +) : b = getSum(tokenList) return Tree(+, a, b)
172
else : return a
Quase terminamos, mas ainda temos que tratar dos parntesis. Em qualquer lugar numa expresso onde podemos ter um nmero, podemos tambm ter uma soma inteira envolvida entre parntesis. Precisamos, apenas, modicar getNumber para que ela possa tratar de subexpresses:
def getNumber(tokenList) : if getToken(tokenList, () : x = getSum(tokenList) # get subexpression getToken(tokenList, )) # eat the closing parenthesis return x else : x = tokenList[0] if type(x) != type(0) : return None tokenList[0:1] = [] # remove the token return Tree(x, None, None) # return a leaf with the number
O analisador tratou os parntesis corretamente; a adio feita antes da multiplicao. Na verso nal do programa, seria uma boa idia dar a getNumber um nome mais descritivo do seu novo papel.
O comando raise cria uma exceo; neste caso criamos um novo tipo de exceo, chamada de BadExpressionError. Se a funo que chamou getNumber, ou uma das outras funes no traceback, manipular a exceo, ento o programa pode continuar. caso contrrio Python vai imprimir uma mensagem de erro e terminar o processamento em seguida. A ttulo de exerccio, encontre outros locais nas funes criadas onde erros possam ocorrer e adiciona comandos raise apropriados. Teste seu cdigo com expresses mal formadas. 22.6. 20.6 Manipulando erros 173
No comeo de cada rodada, o programa parte do topo da rvore e faz a primeira pergunta. Dependendo da resposta, ele segue pelo lho esquerdo ou direito e continua at chegar numa folha. Neste ponto ele arrisca um palpite. Se o palpite no for correto, ele pergunta ao usurio o nome de um novo animal e uma pergunta que distingue o palpite errado do novo animal. A seguir, adiciona uma clula rvore contendo a nova pergunta e o novo animal.
174
A funo yes um auxiliar; ele imprime um prompt e em seguida solicita do usurio uma entrada. Se a resposta comear com y ou Y, a funo devolve um valor verdadeiro:
def yes(ques) : from string import lower ans = lower(raw_input(ques)) return (ans[0:1] == y)
A condio do lao externo 1, que signica que ele continuar at a execuo de um comando break, caso o usurio no pense num animal. O lao while interno caminha na rvore de cima para baixo, guiado pelas respostas do usurio. Quando uma nova clula adicionada rvore, a nova pergunta substitui a carga e os dois lhos so o novo animal e a carga original. Uma falha do programa que ao sair ele esquece tudo que lhe foi cuidadosamente ensinado!
175
A ttulo de exerccio, pense de vrias jeitos para salvar a rvore do conhecimento acumulado num arquivo. Implemente aquele que Voc pensa ser o mais fcil.
176
CAPTULO 23
Apndice A: Depurao
Tpicos Apndice A: Depurao A.1 Erros de sintaxe * A.1.1 Eu no consigo fazer meu programa executar no importa o que eu faa A.2 Erros de tempo de execuo * A.2.1 Meu programa no faz absolutamente nada * A.2.2 Meu programa trava Lao Innito Recurso Innita Fluxo de Execuo A.2.3 Quando eu executo o programa, recebo uma exceo * A.2.4 Eu adicionei tantas declaraes print que a sada do programa cou bagunada * A.3 Erros de semntica * A.3.1 Meu programa no funciona * A.3.2 Eu tenho uma grande expresso cabeluda e ela no faz o que eu espero * A.3.3 Eu tenho uma funo ou mtodo que no retorna o que eu espero * A.3.4 Eu estou empacado mesmo e eu preciso de ajuda * A.3.5 No, eu preciso mesmo de ajuda Diferentes tipos de erros podem acontecer em um programa, e til distinguir entre eles para os localizar mais rapidamente: Erros de sintaxe so produzidos por Python quando o interpretador est traduzindo o cdigo fonte em bytecode. Estes erros geralmente indicam que existe algo errado com a sintaxe do programa. Exemplo: Omitir o sinal de dois pontos (:) no nal de uma declarao def produz a mensagem um tanto redundante SintaxError: invalid sintax (Erro de sintaxe: sintaxe invlida). Erros de tempo de execuo so produzidos se algo de errado acontece enquanto o programa est em execuo. A maioria das mensagens de tempo de execuo incluem informao sobre onde o erro ocorreu e que funo estava em execuo. Exemplo: Uma recurso innita eventualmente causa um erro em tempo de execuo identicado como maximum recursion depth exceeded (Excedida a mxima profundidade de recurso). Erros de semntica, chamado por alguns autores de erros de lgica, so problemas com um programa que compila e executa mas no tem o resultado esperado. Exemplo: Uma expresso pode no ser avaliada da forma que voc espera, produzindo um resultado inesperado.
177
O primeiro passo na depurao descobrir com que tipo de erro voc est lidando. Embora as sees a seguir estejam organizadas por tipo de erro, algumas tcnicas so aplicveis em mais de uma situao.
23.1.1 A.1.1 Eu no consigo fazer meu programa executar no importa o que eu faa
Se o compilador diz que existe um erro e voc no o v, isto pode ser por que voc e o compilador no esto olhando para o mesmo cdigo. Verique seu ambiente para se assegurar que o programa que voc est editando o mesmo que o Python est tentando executar. Se voc no est certo, tente colocar um erro de sintaxe bvio e deliberado no incio do programa. Agora execute (ou importe) o programa novamente. Se o compilador no encontrar o novo erro, provavelmente existe algo de errado com a maneira como o ambiente est congurado.
178
Se isto acontecer, uma abordagem iniciar novamente com um novo programa como Hello World!, e se assegurar de que voc consegue colocar um programa conhecido para executar. Ento gradualmente adicione as partes do novo programa ao programa que est funcionando.
179
Agora, quando voc executar o programa, sero exibidas trs linhas de sada para cada execuo do lao. Na ltima execuo do lao, a condio deveria ser False (falso). Se o lao continuar, voc ter condies de ver os valores de x e y, podendo, assim, descobrir o porqu das variveis no serem atualizadas corretamente. Recurso Innita Na maioria das vezes, uma recurso innita far com que um programa execute por um determinado tempo e ento produza um erro de Maximum recursion depth exceeded (Excedida a profundidade mxima de recurso). Se voc suspeita que uma funo ou mtodo est causando uma recurso innita, comece vericando para se assegurar que exista um caso bsico. Em outras palavras, deve existir alguma condio que faa com que a funo ou mtodo nalize sem fazer uma chamada recursiva. Se no existe tal condio, voc precisa repensar o algoritmo e identicar um caso base. Se existe um caso base mas o programa parece no estar alcanando-o, adicione uma declarao print no incio da funo ou mtodo que imprime o(s) parmetro(s). Agora, quando voc executar o programa, voc ver umas poucas linhas de sada todas as vezes que a funo ou mtodo for executado, e voc ver o(s) parmetro(s). Se o(s) parmetro(s) no est(o) se movendo em direo ao caso base, voc ter ideias de por que no, e poder ento corrigir o problema. Fluxo de Execuo Se voc no est certo de como o seu programa est uindo, adicione declaraes print ao incio de cada funo com uma mensagem semelhante a entrando na funo foo, onde foo o nome da funo. Agora quando voc executar o programa, ser exibido um rastro de cada funo medida em que ela invocada.
180
KeyError (Erro de Chave): Voc est tentando acessar um elemento de um dicionrio utilizando um valor de chave que o dicionrio no contm. AttributeError (Erro de Atributo): Voc est tentando acessar um atributo ou mtodo que no existe em um objeto. IndexError (Erro de ndice): O ndice que voc est usando para acessar uma lista, string ou tupla no existe no objeto, ou seja, maior que seu comprimento menos um. Imediatamente antes do ponto do erro, adicione uma delarao print para mostrar o valor do ndice e o comprimento do objeto. O objeto do tamanho correto? O ndice est com o valor adequado?
23.2.4 A.2.4 Eu adicionei tantas declaraes print que a sada do programa cou bagunada
Um dos problemas com o uso de declaraes print para a depurao que a sada pode car confusa, dicultando a depurao, ao invs de facilitar. H duas coisas que podem ser feitas: simplicar a sada ou simplicar o programa. Para simplicar a sada, voc pode remover ou comentar as declaraes print que no esto ajudando, ou combin-las, ou, ainda, formatar a sada para facilitar o entendimento. Para simplicar o programa, existem vrias coisas que voc pode fazer: Primeiro, reduza o problema no qual o programa est trabalhando. Por exemplo, se voc est ordenando um array, ordene um * array* pequeno. Se o programa recebe entrada do usurio, d a ele a entrada mais simples capaz de causar o problema. Segundo, limpe o programa. Remova cdigo intil e reorganize o programa para torn-lo to fcil de ler quanto possvel. Por exemplo, se voc suspeita que o problema est numa parte profundamente aninhada do programa, tente reescrever aquela parte com uma estrutura mais simples. Se voc suspeita de uma funo longa, tente dividi-la em funes menores e test-las separadamente. Muitas vezes o processo de encontrar o caso de teste mnimo leva voc ao erro. Se voc descobrir que o programa funciona em uma situao, mas no em outra, voc j tem uma boa dica a respeito do que est acontecendo. De forma semelhante, reescrever pedaos de cdigo pode ajuda a encontrar erros sutis. Se voc faz uma alterao que voc pensa que no afeta o programa, e ela afeta, voc tambm tem uma dica.
181
No est acontecendo alguma coisa que no deveria acontecer? Encontre o cdigo no seu programa que executa a funo e veja se ele est executando no momento errado ou de forma errada. Uma parte do cdigo est produzindo o efeito esperado? Cetique-se que voc entende o cdigo em questo, especialmente se ele envolve chamadas de funes ou mtodos em outros mdulos da linguagem. Leia a documentao das funes e mdulos que voc est utilizando. Teste as funes escrevendo casos simples de teste e vericando os resultados. Para programar, voc precisa ter um modelo mental de como seus programas trabalham. Se voc escreve um programa que no faz aquilo que voc espera, muito comum que o problema no esteja no programa, mas sim no seu modelo mental. A melhor maneira de corrigir seu modelo mental quebrar o programa em seus componentes (geralmente as funes e mtodos) e testar cada componente de forma independente. Uma vez que voc tenha encontrado a diferena entre seu modelo e a realidade, voc pode resolver o problema. Obviamente, componentes devem ser desenvolvidos e testado medida que o seu programa vai ganhando vida. Se voc encontra um problema, haver uma pequena quantidade de novo cdigo com funcionamento incerto.
23.3.2 A.3.2 Eu tenho uma grande expresso cabeluda e ela no faz o que eu espero
Escrever expresses complexas legal se elas forem legveis, mas pode ser difcil de depurar. Geralmente uma boa ideia quebrar uma expresso complexa em uma srie de atribuies para variveis temporrias. Por exemplo:
>>> self.mao[i].adicionaCarta(self.mao[self.encontraVizinho(i)].popCarta())
A verso explcita mais fcil de ler, pois os nomes das variveis fornecem documentao adicional, e mais fcil depurar, j que voc pode vericar os tipos das variveis intermedirias e mostrar seus valores. Um outro problema que pode ocorrer com expresses longas que a ordem de avaliao pode no ser o que voc espera. Por exemplo, se voc est traduzido a expresso x / 2pi (XXX fazer a equao matemtica) para Python, voc poderia escrever:
>>> y = x / 2 * math.pi
Isto est correto por que a multiplicao e a diviso possuem a mesma precedncia e so avalidadas da esquerda pra direita. Ento a expresso calcula *x*pi/2 (XXX fazer a equao matemtica). Uma boa maneira de depurar expresses adicionar parnteses para tornar a ordem de avaliao explcita:
>>> y = x / (2 * math.pi)
Sempre que voc no estiver seguro sobe a ordem de avaliao, utilize parnteses. No apenas o programa estar correto(no sentido de fazer aquilo que voc tinha a inteno), ele tambm ser mais legvel por outras pessoas que no tenham memorizado as regras de precedncia.
182
23.3.3 A.3.3 Eu tenho uma funo ou mtodo que no retorna o que eu espero
Se voc possui uma declarao return com uma expresso complexa, voc no tem a chance de exibir o valor de return antes que ele seja devolvido. Novamente, voc pode utilizar uma variavel temporria. Por exemplo, ao invs de:
>>> def pega_encontrados(self): return self.mao[i].devolveEncontrados()
183
Lembre-se, o objetivo no apenas fazer o programa funcionar. O objetivo aprender como fazer o programa funcionar.
184
CAPTULO 24
Tpicos Apndice B: Criando um novo tipo de dado B.1 Multiplicao de fraes B.2 Soma de fraes B.3 Simplicando fraes: O algoritmo de Euclides B.4 Comparando fraes B.5 Indo mais alm... * B.5.1 Exerccio B.6 Glossrio Em linguagens com suporte orientao a objetos, programadores podem criar novos tipos de dados que se comportam de forma semelhante aos tipos de dados built-in. Vamos explorar esse recurso criando uma classe chamada Fracao. Esta classe ter comportamento muito semelhante aos tipos numricos da linguagem: int, long e float. Fraes, tambm conhecidas como nmeros racionais, so valores que podem ser expressos como uma razo de dois nmeros inteiros, por exemplo, 5/6. No exemplo fornecido, o 5 representa o numerador, o nmero que ca em cima, que dividido, e o 6 representa o denominador, o nmero que ca embaixo, pelo qual a diviso feita. A frao 5/6 pode ser lida cinco dividido por seis. O primeiro passo denir a classe Fracao com o mtodo __init__ que recebe como parmetros o numerador e o denominador, sendo ambos do tipo int:
class Fracao: def __init__(self, numerador, denominador=1): self.numerador = numerador self.denominador = denominador
A passagem do denominador opcional. Uma Fracao com apenas um parmetro representa um nmero inteiro. Sendo o numerador n, a frao construda ser n/1. O prximo passo escrever o mtodo __str__ que exibe as fraes corretamente: a forma numerador/denominador.
class Fracao: ...
185
Para testar o que foi feito at aqui, salve a classe Fracao em um arquivo chamado Fracao.py e importe-a no interpretador interativo. Criaremos uma instncia da classe e imprimiremos ele na tela:
>>> from Fracao import Fracao >>> numero = Fracao(5, 6) >>> print "A frao ", numero A frao 5/6
O mtodo funciona, mas pode ser aprimorado! Podemos melhorar o mtodo visando possibilitar a multiplicao de uma frao por um inteiro. Usaremos a funo isinstance para vericar se o objeto other um inteiro, para em seguida convert-lo em uma frao.
class Fracao: ... def __mul__(self, other): if isinstance(other, int): other = Fracao(other) return Fracao(self.numerador * other.numerador, self.denominador * ohter.denominador)
Agora conseguimos multiplicar funes por inteiros, mas s se a frao estiver esquerda do operador *. Vejamos um exemplo em que nossa multiplicao funciona e outro no qual ela no funciona:
>>> print Fracao(5, 6) * 4 20/6 >>> print 4 * Fracao(5, 6) TypeError: __mul__ nor __rmul__ defined for these operands
O erro nos da uma dica: no mexemos em nenhum __rmul__. Para realizar a multiplicao, busca no elemento esquerda do operador * o mtodo __mul__ compatvel com a multiplicao realizada (no nosso caso, que receba um inteiro e uma frao, nesta ordem). Se o mtodo no for encon186 Captulo 24. Apndice B: Criando um novo tipo de dado
trado, o Python buscar no elemento direita do operador * o mtodo __rmul__ compatvel com a multiplicao realizada (que ento deve ser lida da direita para a esquerda). Como a multiplicao lida da direita para a esquerda, temos que o nosso mtodo __rmul__ deve ser igual ao mtodo __mul__ implementado anteriormente.:
class Fracao: ... __rmul__ = __mul__
Fazendo assim, dizemos que o mtodo __rmul__ funciona da mesma forma que o mtodo __mul__. Agora, quando multiplicarmos 4 * Fracao(5, 6), o interpretador Python chamar o mtodo __rmul__ do objeto Fracao, passando o 4 como parmetro.
>>> print 4 * Fracao(5, 6) 20/6
Como o mtodo __rmul__ tambm o mtodo __mul__, e o mtodo __mul__ consegue trabalhar com parmetro do tipo inteiro, nossa multiplicao est completa.
Os dois primeiros exemplos chamam o mtodo __add__, enquanto o ltimo exemplo chama o mtodo __radd__.
187
De forma geral, sempre que um objeto do tipo Fracao for criado, a frao deve ser simplicada, atravs da diviso do numerador e do denominador pelo seu MDC. Quando a frao j est em sua forma mais simples, o MDC vale 1. Euclides de Alexandria (aprox. 325 a. C. 365 a. C.) desenvolveu um algoritmo para encontrar o MDC de dois inteiros m e n: Se n mltiplo de m, ento n o MDC. Caso contrrio, o MDC o MDC de n e o resto da diviso de m por n. Esta denio recursiva pode ser representada de forma concisa pela funo:
def mdc(m, n): if m % n == 0: return n else: return mdc(n, m % n)
Na primeira linha da funo, utilizamos o operador de mdulo para checar se m divisvel por n. Na ltima linha, usamos o mesmo operador para obter o resto da diviso de m por n. J que todas as operaes que escrevemos criam um novo objeto do tipo Fracao, podemos utilizar a funo mdc no mtodo __init__ de nossa classe:
class Fracao: def __init__(self, numerador, denominador=1): m = mdc(numerador, denominador) self.numerador = numerador / m self.denominador = denominador / m
Sempre que o denominador negativo, o sinal negativo deve passar para o numerador. O interessante que, ao usarmos o Algoritmo de Euclides, esta operao ocorre de forma transparente. Antes de seguirmos para o prximo passo, vamos ver como est nossa classe Fracao completa?
class Fracao: def __init__(self, numerador, denominador=1): m = mdc(numerador, denominador) self.numerador = numerador / m self.denominador = denominador / m def __str__(self): return "%d /%d " %(self.numerador, self.denominador) def __mul__(self, other): if isinstance(other, int): other = Fracao(other) return Fracao(self.numerador * other.numerador, self.denominador * ohter.denominador) __rmul__ = __mul__ def __add__(self, other): if isinstance(other, int): other = Fracao(other) return Fracao(self.numerador * other.denominador + self.denominador * other.numerador,
188
Se self for maior que other, a diferenca ser positiva. Se other for maior, a diferenca ser negativa. Se os dois forem iguais, a diferenca ser zero.
189
190
CAPTULO 25
Tpicos Apndice C: Leituras recomendadas C.1 Recomendaes para leitura C.2 Sites e livros sobre Python C.3 Livros de cincia da computao recomendados
191
Banco de dados so como super arquivos, que armazenam dados em esquemas predenidos, e mantm relaes entre itens de dados que lhe permitem acessar os dados de vrias formas. Python tem vrios mdulos que possibilitam ao usurio conectar seus programas a diversos sistemas gerenciadores de banco de dados, tanto sistemas livres (Open source) quanto sistemas comerciais. A programao multitarefa (multithread) permite que voc execute vrias tarefas (threads) dentro de um nico programa. Se voc j teve a experincia de usar um navegador web para se deslocar por uma pgina web enquanto o navegador ainda est carregando ela, ento voc j tem uma ideia do que da pra fazer usando a programao multitarefa. Quando o desempenho primordial, voc pode escrever extenses para o Python em linguagens compiladas, como C e C++. Este abordagem vastamente utilizada na biblioteca padro do Python, formando a sua base. O mecanismo de ligao de dados e funes um pouco complexo. Existe uma ferramenta, chamada SWIG (Simplied Wrapper and Interface Generator), que faz este processo de ligao ser mais simples.
192
193
194
CAPTULO 26
Coligi aqui os links para tradues da Licensa Pblica GNU fornecidos pelo professor Imre Simon: http://creativecommons.org/licenses/GPL/2.0/legalcode.pt http://www.magnux.org/doc/GPL-pt_BR.txt http://www.ead.unicamp.br/minicurso/bw/texto/fdl.pt.html (esta no consegui abrir...) Encontrei tambm uma reproduo neste texto em pdf, alis sobre o incentivo do governo ao uso do Software Livre: http://www.inf.ufpr.br/info/techrep/RT_DINF004_2002.pdf.gz Parecem haver pequenas discrepncias entre as vrias tradues, mas acho que deveramos escolher uma para publicar aqui como referncia.
195
196
CAPTULO 27
197