Guia completo de python

  • Published on
    18-Dec-2014

  • View
    551

  • Download
    2

DESCRIPTION

 

Transcript

  • 1. Aprenda Computao com Python Documentation Verso 1.1 Allen Downey, Jeff Elkner and Chris Meyers 12/08/2010
  • 2. Contedo 1 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 4 4 6 2 Apresentao 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 9 11 11 13 14 14 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 17 18 19 20 20 21 22 22 23 23 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 25 26 26 27 28 4 5 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
  • 3. 5.6 5.7 5.8 5.9 5.10 5.11 5.12 5.13 6 7 8 9 ii 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 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 35 36 36 37 37 38 38 39 39 40 41 42 42 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 45 46 48 49 49 51 51 52 53 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 55 56 57 59 59 60 61 61 63 63 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) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 65 66 66 67 68 68 69 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • 4. 9.8 9.9 9.10 9.11 9.12 7.8 Iterando e contando . . . . 7.9 O mdulo string . . . . . 7.10 Classicao de caracteres 7.11 Glossrio . . . . . . . . . 7.11 Glossrio2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 70 70 71 72 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 73 74 75 75 76 76 77 77 78 78 79 80 80 81 81 82 83 83 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 85 86 86 87 88 88 89 90 91 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 94 94 95 95 96 98 98 99 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 101 103 104 105 106 107 108 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
  • 5. 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 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 117 118 119 120 120 121 121 122 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 123 124 125 125 125 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 127 127 128 129 130 130 131 132 133 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 135 136 136 137 138 138 139 142 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 143 144 145 146 146 iv . . . . . . . . . . . . . . .
  • 6. 19.6 19.7 19.8 19.9 19.10 19.11 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 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. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 153 154 154 155 155 155 156 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 159 159 160 161 162 163 164 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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 166 167 167 168 170 173 174 176 . . . . . . . . . . . . . . . . . . . . . 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 v
  • 7. 27 Indices and tables vi 197
  • 8. Aprenda Computao com Python Documentation, Verso 1.1 Contents: Contedo 1
  • 9. Aprenda Computao com Python Documentation, Verso 1.1 2 Contedo
  • 10. 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. 1.1 Como e porque eu vim a usar Python Em 1999, o Exame de Colocao Avanada em Cincia da Computao da Comisso de Faculdades (College Boards Advanced Placement (AP) Computer Science XXX) foi aplicado em C++ pela primeira vez. Como em muitas escolas secundrias atravs do pas, a deciso de mudar linguagens teve um impacto direto no currculo de cincia da computao na Yorktown High School em Arlington, Virginia, onde leciono. At ento, Pascal era a linguagem didtica para nossos cursos de primeiro ano e avanado. Mantendo a prtica corrente de dar aos estudantes dois anos de exposio mesma linguagem, tomamos a deciso de mudar para C++ no curso de primeiro ano para o ano letivo de 1997-98 de modo que estaramos em sincronismo com a mudana da Comisso de Faculdades (College Boards XXX) em relao ao curso avanado (AP XXX) para o ano seguinte. Dois anos depois, eu estava convencido que C++ foi uma escolha infeliz para introduzir os alunos em cincia da computao. Ao mesmo tempo em que certamente uma linguagem de programao muito poderosa, tambm uma linguagem extremamente difcil de aprender e de ensinar. Eu me encontrava constantemente lutando com a sintaxe difcil de C++ e as mltiplas maneiras de fazer a mesma coisa, e estava, como resultado, perdendo muitos alunos desnecessariamente. Convencido de que deveria existir uma linguagem melhor para a nossa classe de primeiro ano, fui procurar por uma alternativa a C++. Eu precisava de uma linguagem que pudesse rodar nas mquinas em nosso laboratrio Linux bem como nas plataformas Windows e Macintosh que a maioria dos alunos tinha em casa. Eu precisava que ela fosse gratuita e disponvel eletronicamente, assim os alunos poderiam utiliz-la em casa independentemente de suas rendas. Eu queria uma linguagem que fosse utilizada por programadores prossionais, e que tivesse uma comunidade de desenvolvimento ativa em torno dela. Ela teria que suportar ambas, programao procedural e orientada a objetos. E, mais importante, deveria ser fcil de aprender e de ensinar. Quando considerei as alternativas tendo em mente aquelas metas, Python sobressaiu-se como a melhor candidata para a tarefa. 3
  • 11. Aprenda Computao com Python Documentation, Verso 1.1 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. 1.2 Encontrando um livro-texto Tendo decidido usar Python em minhas aulas introdutrias de cincia da computao do ano seguinte, o problema mais urgente era a falta de um livro-texto disponvel. O contedo livre veio em socorro. Anteriormente naquele ano, Richard Stallman tinha me apresentado a Allen Downey. Ambos havamos escrito a Richard expressando interesse em desenvolver contedo educacional livre. Allen j tinha escrito um livro-texto para o primeiro ano de cincia da computao, How to Think Like a Computer Scientist. Quando li este livro, soube imediatamente que queria utiliz-lo nas minhas aulas. Era o mais claro e proveitoso texto em cincia da computao que eu tinha visto. Ele enfatizava o processo de reexo envolvido em programao em vez de caractersticas de uma linguagem em particular. L-lo fez de mim imediatamente um professor melhor. O How to Think Like a Computer Scientist era no s um excelente livro, como tambm fora lanado sob uma licena pblica GNU, o que signicava que ele poderia ser usado livremente e modicado para atender as necessidades de seu usurio. Uma vez que eu havia decidido usar Python, me ocorreu que eu poderia traduzir a verso original do livro de Allen do Java para a nova linguagem. Apesar de no estar capacitado para escrever eu mesmo um livro-texto, tendo o livro de Allen para trabalhar, tornou possvel para mim faz-lo, ao mesmo tempo demonstrando que o modelo de desenvolvimento cooperativo to bem utilizado em software poderia tambm funcionar para contedo educacional. Trabalhar neste livro pelos ltimos dois anos tem sido recompensador para mim e meus alunos, e eles tiveram um grande papel neste processo. A partir do momento em que eu podia fazer mudanas instantneas assim que algum encontrasse um erro ortogrco ou um trecho difcil, eu os encorajei a procurar por erros no livro, dando a eles pontos de bonicao cada vez que zessem uma sugesto que resultasse em uma mudana no texto. Isto teve o duplo benefcio de encoraj-los a ler o texto mais cuidadosamente e de ter o texto totalmente revisado por seus crticos mais importantes: alunos utilizando-o para aprender cincia da computao. Para a segunda metade do livro, sobre programao orientada a objetos, eu sabia que seria preciso algum com uma maior experincia do que a minha em programao real para faz-lo corretamente. O livro esteve em estado inacabado por quase um ano at que a comunidade de software livre providenciasse mais uma vez os meios necessrios para sua concluso. Eu recebi um e-mail de Chris Meyers mostrando interesse no livro. Chris um programador prossional que comeou a dar um curso de programao no ano anterior usando Python no Lane Community College em Eugene, Oregon. A perspectiva de dar aquele curso ligou Chris ao livro, e ele comeou a ajudar o trabalho imediatamente. At o nal do ano letivo ele tinha criado um projeto colaborativo em nosso Website em http://www.ibiblio.org/obp chamado Python for Fun e estava trabalhando com alguns dos meus alunos mais avanados como um guru (master teacher XXX), guiando-os alm de onde eu poderia lev-los. 1.3 Introduzindo programao com Python O processo de traduzir e utilizar How to Think Like a Computer Scientist pelos ltimos dois anos tem conrmado a convenincia de Python no ensino de alunos iniciantes. Python simplica tremendamente os programas exemplo e torna idias importantes de programao mais fceis de ensinar. O primeiro exemplo do texto ilustra este ponto. o tradicional programa Al mundo, o qual na verso C++ do livro se parece com isto: 4 Captulo 1. Prefcio
  • 12. Aprenda Computao com Python Documentation, Verso 1.1 #include void main() { cout > 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 Captulo 3. Captulo 1: O caminho do programa
  • 18. Aprenda Computao com Python Documentation, Verso 1.1 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. 3.2 1.2 O que um programa? Um programa uma sequncia de instrues que especicam como executar um clculo ou determinada tarefa. Tal tarefa pode matemtica, como solucionar um sistema de equaes ou encontrar as razes de um polinmio, mas tambm pode ser simblica, como buscar e substituir uma palavra em um documento ou (estranhamente) compilar um programa. Os detalhes so diferentes em diferentes linguagens, mas algumas instrues bsicas aparecem em praticamente todas as linguagens: entrar: Pegar dados do teclado, de um arquivo ou de algum outro dispositivo de entrada. sair: Mostrar dados na tela ou enviar dados para um arquivo ou outro dispositivo de sada. calcular: Executar operaes matemticas bsicas, como adio e multiplicao. executar condicionalmente: Checar certas condies e executar a sequncia apropriada de instrues. repetir: Executar alguma ao repetidamente, normalmente com alguma variao. Acredite se quiser: isso praticamente tudo. Todos os programas que voc j usou, no importa quo complicados, so feitos de instrues mais ou menos parecidas com essas. Assim, poderamos denir programao como o processo de dividir uma tarefa grande e complexa em subtarefas cada vez menores, at que as subtarefas sejam simples o suciente para serem executadas com uma dessas instrues bsicas. Isso pode parecer um pouco vago, mas vamos voltar a esse tpico mais adiante, quando falarmos sobre algoritmos. 3.3 1.3 O que depurao (debugging)? Programar um processo complicado e, como feito por seres humanos, frequentemente conduz a erros. Por mero capricho, erros em programas so chamados de bugs e o processo de encontr-los e corrigi-los chamado de depurao (debugging). Trs tipos de erro podem acontecer em um programa: erros de sintaxe, erros em tempo de execuo (runtime errors) e erros de semntica (tambm chamados de erros de lgica). Distinguir os trs tipos ajuda a localiz-los mais rpido: 3.2. 1.2 O que um programa? 11
  • 19. Aprenda Computao com Python Documentation, Verso 1.1 3.3.1 1.3.1 Erros de sintaxe O interpretador do Python s executa um programa se ele estiver sintaticamente correto; caso contrrio, o processo falha e retorna uma mensagem de erro. Sintaxe se refere estrutura de um programa e s regras sobre esta estrutura. Por exemplo, em portugus, uma frase deve comear com uma letra maiscula e terminar com um ponto. esta frase contm um erro de sintaxe. Assim como esta Para a maioria dos leitores, uns errinhos de sintaxe no chegam a ser um problema signicativo e por isso que conseguimos ler a poesia moderna de e. e. cummings sem cuspir mensagens de erro. Python no to indulgente. Se o seu programa tiver um nico erro de sintaxe em algum lugar, o interpretador Python vai exibir uma mensagem de erro e vai terminar - e o programa no vai rodar. Durante as primeiras semanas da sua carreira como programador, voc provavelmente perder um bocado de tempo procurando erros de sintaxe. Conforme for ganhando experincia, entretanto, cometer menos erros e os localizar mais rpido. 3.3.2 1.3.2 Erros em tempo de execuo (runtime errors) O segundo tipo de erro o erro de runtime, ou erro em tempo de execuo, assim chamado porque s aparece quando voc roda o programa. Esses erros so tambm conhecidos como excees, porque normalmente indicam que alguma coisa excepcional (e ruim) aconteceu. Erros de runtime so raros nos programas simples que voc vai ver nos primeiros captulos - ento, vai demorar um pouco at voc se deparar com um erro desse tipo. 3.3.3 1.3.3 Erros de semntica (ou de lgica) O terceiro tipo de erro o erro de semntica (mais comumente chamado erro de lgica). Mesmo que o seu programa tenha um erro de semntica, ele vai rodar com sucesso, no sentido de que o computador no vai gerar nenhuma mensagem de erro. S que o programa no vai fazer a coisa certa, vai fazer alguma outra coisa. Especicamente, aquilo que voc tiver dito para ele fazer (o computador trabalha assim: seguindo ordens). O problema que o programa que voc escreveu no aquele que voc queria escrever. O signicado do programa (sua semntica ou lgica) est errado. Identicar erros semnticos pode ser complicado, porque requer que voc trabalhe de trs para frente, olhando a sada do programa e tentando imaginar o que ele est fazendo. 3.3.4 1.3.4 Depurao experimental (debugging) Uma das habilidades mais importantes que voc vai adquirir a de depurar. Embora possa ser frustrante, depurar uma das partes intelectualmente mais ricas, desaadoras e interessantes da programao. De certa maneira, a depurao como um trabalho de detetive. Voc se depara com pistas, e tem que deduzir os processos e eventos que levaram aos resultados que aparecem. Depurar tambm como uma cincia experimental. Uma vez que voc tem uma ideia do que est errado, voc modica o seu programa e tenta de novo. Se a sua hiptese estava correta, ento voc consegue prever o resultado da modicao e ca um passo mais perto de um programa que funciona. Se a sua hiptese estava errada, voc tem que tentar uma nova. Como Sherlock Holmes mostrou: Quando voc tiver eliminado o impossvel, aquilo que restou, ainda que improvvel, deve ser a verdade. (Arthur Conan Doyle, O signo dos quatro). Para algumas pessoas, programao e depurao so a mesma coisa. Ou seja, programar o processo de gradualmente depurar um programa, at que ele faa o que voc quer. A ideia comear com um programa que faa alguma coisa e ir fazendo pequenas modicaes, depurando-as conforme avana, de modo que voc tenha sempre um programa que funciona. 12 Captulo 3. Captulo 1: O caminho do programa
  • 20. Aprenda Computao com Python Documentation, Verso 1.1 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. 3.4 1.4 Linguagens naturais e linguagens formais Linguagens naturais so as linguagens que as pessoas falam, como o portugus, o ingls e o espanhol. Elas no foram projetadas pelas pessoas (muito embora as pessoas tentem colocar alguma ordem nelas); elas evoluram naturalmente. Linguagens formais so linguagens que foram projetadas por pessoas, para aplicaes especcas. Por exemplo, a notao que os matemticos usam uma linguagem formal, que particularmente boa em denotar relaes entre nmeros e smbolos. Os qumicos usam uma linguagem formal para representar a estrutura qumica das molculas. E, mais importante: Linguagens de programao so linguagens formais que foram desenvolvidas para expressar computaes. As linguagens formais tendem a ter regras estritas quanto sintaxe. Por exemplo, 3 + 3 = 6 uma expresso matemtica sintaticamente correta, mas 3=+6$ no . H2O um nome qumico sintaticamente correto, mas 2Zz no . As regras de sintaxe so de dois tipos, um relacionado aos smbolos, outro estrutura. Os smbolos so os elementos bsicos da linguagem, como as palavras, nmeros, e elementos qumicos. Um dos problemas com 3=+6$ que $ no um smbolo vlido em linguagem matemtica (pelo menos at onde sabemos). Do mesmo modo, 2Zz invlida porque no existe nenhum elemento cuja abreviatura seja Zz. O segundo tipo de erro de sintaxe est relacionado estrutura de uma expresso, quer dizer, ao modo como os smbolos esto arrumados. A expresso 3=+6$ estruturalmente invlida, porque voc no pode colocar um sinal de mais imediatamente aps um sinal de igual. Do mesmo modo, frmulas moleculares devem ter ndices subscritos colocados depois do nome do elemento, no antes. Faa este exerccio: crie o que parea ser uma frase bem estruturada em portugus com smbolos irreconhecveis dentro dela. Depois escreva outra frase com todos os smbolos vlidos, mas com uma estrutura invlida. Quando voc l uma frase em portugus ou uma expresso em uma linguagem formal, voc tem de imaginar como a estrutura da frase (embora, em uma linguagem natural, voc faa isso inconscientemente). Este processo, na computao, chamado parsing (anlise sinttica). Por exemplo, quando voc ouve a frase, Caiu a cha, entende que a cha o sujeito e caiu o verbo. Uma vez que voc analisou a frase, consegue entender o seu signicado, ou a semntica da frase. Assumindo que voc saiba o que uma cha e o que signica cair, voc entender o sentido geral dessa frase. Muito embora as linguagens formais e as naturais tenham muitas caractersticas em comum (smbolos, estrutura, sintaxe e semntica), existem muitas diferenas: ambiguidade: As linguagens naturais esto cheias de ambiguidades, que as pessoas contornam usando pistas contextuais e outras informaes. J as linguagens formais so desenvolvidas para serem quase ou totalmente desprovidas de ambiguidade, o que signica que qualquer expresso tem precisamente s um sentido, independentemente do contexto. redundncia: Para compensar a ambiguidade e reduzir maus entendidos, emprega-se muita redundncia nas linguagens naturais, o que frequentemente as torna prolixas. As linguagens formais so menos redundantes e mais concisas. 3.4. 1.4 Linguagens naturais e linguagens formais 13
  • 21. Aprenda Computao com Python Documentation, Verso 1.1 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. 3.5 1.5 O primeiro programa Tradicionalmente, o primeiro programa escrito em uma nova linguagem de programao chamado de Al, Mundo! porque tudo que ele faz apresentar as palavras Al, Mundo!. Em Python, ele assim: print "Al, Mundo!" 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. 3.6 1.6 Glossrio algoritmo (algorithm) Processo geral para soluo de uma certa categoria de problema. anlise sinttica (parse) Examinar um programa e analisar sua estrutura sinttica. bug Erro em um programa. cdigo fonte (source code) Um programa em uma linguagem de alto nvel, antes de ter sido compilado. cdigo objeto (object code) A sada do compilador, depois que ele traduziu o programa. comando print (print statement) Instruo que leva o interpretador Python a apresentar um valor na tela. compilar (compile) Traduzir todo um programa escrito em uma linguagem de alto nvel para uma de baixo nvel de uma s vez, em preparao para uma execuo posterior. 14 Captulo 3. Captulo 1: O caminho do programa
  • 22. Aprenda Computao com Python Documentation, Verso 1.1 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. 3.6. 1.6 Glossrio 15
  • 23. Aprenda Computao com Python Documentation, Verso 1.1 16 Captulo 3. Captulo 1: O caminho do programa
  • 24. CAPTULO 4 Captulo 2: Variveis, expresses e comandos 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 4.1 2.1 Valores e tipos O valor (por exemplo, letras e nmeros) uma das coisas fundamentais que um programa manipula. Os valores que j vimos at agora foram o 2 (como resultado, quando adicionamos 1 + 1) e "Al, Mundo!". Esses valores pertencem a tipos diferentes: 2 um inteiro, e "Al, Mundo!" uma string, assim chamada porque string, em ingls, quer dizer sequncia, srie, cadeia (de caracteres), ou neste caso, srie de letras. Voc (e o interpretador) consegue identicar strings porque elas aparecem entre aspas. O comando print tambm funciona com inteiros: >>> print 4 4 Se voc estiver em dvida sobre qual o tipo de um determinado valor, o interpretador pode revelar: >>> type("Al, Mundo!") 17
  • 25. Aprenda Computao com Python Documentation, Verso 1.1 >>> type(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) O que dizer de valores como "17" e "3.2"? Eles parecem nmeros, mas esto entre aspas, como strings: >>> type("17") >>> type("3.2") 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. 4.2 2.2 Variveis Uma das caractersticas mais poderosas de uma linguagem de programao a habilidade de manipular variveis. Uma varivel um nome que se refere a um valor. O comando de atribuio cria novas variveis e d a elas valores: >>> mensagem = "E a, Doutor?" >>> n = 17 >>> pi = 3.14159 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 18 N.T.: Observe o uso de ponto no lugar da vrgula para separar a parte inteira da parte fracionria. Captulo 4. Captulo 2: Variveis, expresses e comandos
  • 26. Aprenda Computao com Python Documentation, Verso 1.1 O comando print tambm funciona com variveis: >>> print mensagem E a, Doutor? >>> print n 17 >>> print pi 3.14159 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(n) >>> type(pi) O tipo de uma varivel o tipo do valor ao qual ela se refere. 4.3 2.3 Nomes de variveis e palavras reservadas Os programadores geralmente escolhem nomes signicativos para suas variveis, pois os nomes documentam para o que a varivel usada. Nomes de variveis podem ser arbitrariamente longos. Eles podem conter tanto letras quanto nmeros, mas tm de comear com uma letra. Embora seja vlida a utilizao de letras maisculas, por conveno, no usamos. Se voc o zer, lembre-se de que maisculas e minsculas so diferentes. Bruno e bruno so variveis diferentes. O caractere para sublinhado ( _ ) pode aparecer em um nome. Ele muito utilizado em nomes com mltiplas palavras, tal como em meu_nome ou preco_do_cha_na_china. Se voc der a uma varivel um nome invlido, causar um erro de sintaxe: >>> 76trombones = "grande parada" SyntaxError: invalid syntax >>> muito$ = 1000000 SyntaxError: invalid syntax >>> class = "Ciencias da Computacao 101" SyntaxError: invalid syntax 76trombones invlida por no comear com uma letra. muito$ invlida por conter um caractere ilegal, o cifro. Mas o que est errado com class? 4.3. 2.3 Nomes de variveis e palavras reservadas 19
  • 27. Aprenda Computao com Python Documentation, Verso 1.1 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. 4.4 2.4 Comandos Um comando uma instruo que o interpretador Python pode executar. Vimos at agora dois tipos de comandos: de exibio (print) e de atribuio. Quando voc digita um comando na linha de comando, o Python o executa e mostra o resultado, se houver um. O resultado de um comando print a exibio de um valor. Comandos de atribuio no produzem um resultado visvel. Um script normalmente contm uma sequncia de comandos. Se houver mais de um comando, os resultados aparecero um de cada vez, conforme cada comando seja executado. Por exemplo, o script: print 1 x = 2 print 2 produz a sada: 1 2 Lembrando que o comando de atribuio no produz sada. 4.5 2.5 Avaliando expresses Uma expresso uma combinao de valores, variveis e operadores. Se voc digitar uma expresso na linha de comando, o interpretador avalia e exibe o resultado: >>> 1 + 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 Captulo 4. Captulo 2: Variveis, expresses e comandos
  • 28. Aprenda Computao com Python Documentation, Verso 1.1 Avaliar uma expresso no exatamente a mesma coisa que imprimir um valor: >>> mensagem = "E a, Doutor?" >>> mensagem E a, Doutor? >>> print mensagem E a, Doutor? 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? 4.6 2.6 Operadores e operandos Operadores so smbolos especiais que representam computaes como adio e multiplicao. Os valores que o operador usa so chamados operandos. Todas as expresses seguintes so vlidas em Python e seus signicados so mais ou menos claros: 20+32 hora-1 hora*60+minuto minuto/60 5**2 (5+9)*(15-7) 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 N.T.: Python aceita aspas simples ou duplas para delimitar strings. 4.6. 2.6 Operadores e operandos 21
  • 29. Aprenda Computao com Python Documentation, Verso 1.1 4.7 2.7 Ordem dos operadores Quando mais de um operador aparece em uma expresso, a ordem de avaliao depende das regras de precedncia. Python segue as mesmas regras de precedncia para seus operadores matemticos que a matemtica. O acrnimo PEMDAS uma maneira prtica de lembrar a ordem das operaes: P: Parnteses tm a mais alta precedncia e podem ser usados para forar uma expresso a ser avaliada na ordem que voc quiser. J que expresses entre parnteses so avaliadas primeiro, 2 * (3-1) 4, e (1+1)**(5-2) 8. Voc tambm pode usar parnteses para tornar uma expresso mais fcil de ler, como em (minuto * 100) / 60, ainda que isso no altere o resultado. E: Exponenciao ou potenciao tem a prxima precedncia mais alta, assim 2**1+1 3 e no 4, e 3*1**3 3 e no 27. MDAS: Multiplicao e Diviso tm a mesma precedncia, que mais alta do que a da Adio e da Subtrao, que tambm tm a mesma precedncia. Assim 2*3-1 d 5 em vez de 4, e 2/3-1 -1, no 1 (lembre-se de que na diviso inteira, 2/3=0). Operadores com a mesma precedncia so avaliados da esquerda para a direita. Assim, na expresso minuto*100/60, a multiplicao acontece primeiro, resultando em 5900/60, o que se transforma produzindo 98. Se as operaes tivessem sido avaliadas da direita para a esquerda, o resultado poderia ter sido 59*1, que 59, que est errado. 4.8 2.8 Operaes com strings De maneira geral, voc no pode executar operaes matemticas em strings, ainda que as strings se paream com nmeros. O que segue invlido (assumindo que mensagem do tipo string): mensagem-1 "Al"/123 mensagem*"Al" "15"+2 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 Captulo 4. Captulo 2: Variveis, expresses e comandos
  • 30. Aprenda Computao com Python Documentation, Verso 1.1 4.9 2.9 Composio At agora, vimos os elementos de um programa (variveis, expresses, e instrues ou comandos) isoladamente, sem mencionar como combin-los. Uma das caractersticas mais prticas das linguagens de programao a possibilidade de pegar pequenos blocos e combin-los numa composio. Por exemplo, ns sabemos como somar nmeros e sabemos como exibi-los; acontece que podemos fazer as duas coisas ao mesmo tempo: >>> print 17 + 3 20 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. 4.10 2.11 Glossrio atribuio (assignment) Comando que atribui um valor a uma varivel. avaliar (evaluate) Simplicar uma expresso atravs da realizao de operaes, para produzir um valor nico. comando (statement) Trecho de cdigo que representa uma instruo ou ao. At agora, os comandos vistos foram de atribuio e exibio. comentrio (comment) Informao em um programa dirigida a outros programadores (ou qualquer pessoa que esteja lendo o cdigo fonte) e que no tem efeito na execuo do programa. composio (composition) Habilidade de combinar expresses e comandos simples em expresses e comandos compostos, de forma a representar operaes complexas de forma concisa. concatenar (concatenate) Juntar dois operandos lado a lado. diagrama de estado (state diagram) Representao grca de um conjunto de variveis e os valores aos quais elas se referem. diviso inteira (integer division) Operao que divide um inteiro por outro e resulta em um inteiro. A diviso inteira resulta no nmero de vezes que o numerador divisvel pelo denominador e descarta qualquer resto. expresso (expression) Combinao de variveis, operadores e valores, que representa um resultado nico. operando (operand) Um dos valores sobre o qual o operador opera. operador (operator) Smbolo especial que representa uma computao simples, como adio, multiplicao ou concatenao de strings. palavra-chave (keyword) Palavra reservada usada pelo compilador/interpretador para analisar o programa; voc no pode usar palavras-chave como if, def, e while como nomes de variveis. ponto-utuante (oating-point) Formato para representar nmeros que possuem partes fracionrias. 4.9. 2.9 Composio 23
  • 31. Aprenda Computao com Python Documentation, Verso 1.1 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 4. Captulo 2: Variveis, expresses e comandos
  • 32. 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 5.1 3.1 Chamadas de funes Voc j viu um exemplo de uma chamada de funo: >>> type(32) 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 25
  • 33. Aprenda Computao com Python Documentation, Verso 1.1 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. 5.2 3.2 Converso entre tipos Python prov uma coleo de funes nativas que convertem valores de um tipo em outro. A funo int recebe um valor e o converte para inteiro, se possvel, ou, se no, reclama: >>> int(32) 32 >>> int(Al) ValueError: invalid literal for int() : Al 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 A funo float converte inteiros e strings em nmeros em ponto utuante: >>> float(32) 32.0 >>> float(3.14159) 3.14159 Finalmente, a funo str converte para o tipo string: >>> str(32) 32 >>> str(3.14149) 3.14149 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. 5.3 3.3 Coero entre tipos Agora que podemos converter entre tipos, temos outra maneira de lidar com a diviso inteira. Voltando ao exemplo do captulo anterior, suponha que queiramos calcular a frao de hora que j passou. A expresso mais bvia, minuto / 60, faz aritmtica inteira, assim, o resultado sempre 0, mesmo aos 59 minutos passados da hora. Uma soluo converter minuto para ponto utuante e fazer a diviso em ponto utuante: 26 Captulo 5. Captulo 3: Funes
  • 34. Aprenda Computao com Python Documentation, Verso 1.1 >>> minuto = 59 >>> float(minuto) / 60 0.983333333333 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 Fazendo o denominador um float, foramos o Python a fazer a diviso em ponto utuante. 5.4 3.4 Funes matemticas Em matemtica, voc provavelmente j viu funes como seno (sen, sin em ingls) e logaritmo (log), e aprendeu a resolver expresses como sen(pi/2) e log(1/x). Primeiro voc resolve e expresso entre parnteses (o argumento). Por exemplo, pi/2 aproximadamente 1,571, e 1/x 0.1 (se x for 10,0). A voc avalia a funo propriamente dita, seja procurando numa tabela ou realizando vrios clculos. O sen de 1,571 1 e o log de 0,1 -1 (assumindo que log indica o logaritmo na base 10). Este processo pode ser aplicado repetidamente para avaliar expresses mais complicadas, como log(1/sen(pi/2)). Primeiro voc avalia o argumento na funo mais interna, depois avalia a funo e assim por diante. Python tem um mdulo matemtico que prov a maioria das funes matemticas mais familiares. Um mdulo um arquivo que contm uma coleo de funes relacionadas agrupadas juntas. Antes de podermos usar as funes contidas em um mdulo, temos de import-lo: >>> import math 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 5.4. 3.4 Funes matemticas 27
  • 35. Aprenda Computao com Python Documentation, Verso 1.1 5.5 3.5 Composio Do mesmo modo como nas funes matemticas, as funes do Python podem ser compostas, o que signica que voc pode usar uma expresso como parte de outra. Por exemplo, voc pode usar qualquer expresso como um argumento para uma funo: >>> x = math.cos(angulo + pi/2) 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. 5.6 3.6 Adicionando novas funes At aqui, temos utilizado somente as funes que vm com Python, mas tambm possvel adicionar novas funes. Criar novas funes para resolver seus prprios problemas uma das coisas mais teis de uma linguagem de programao de propsito geral. No contexto de programao, funo uma sequncia nomeada de instrues ou comandos, que realizam uma operao desejada. Esta operao especicada numa denio de funo. At agora, as funes que usamos neste livro so pr-denidas e suas denies no foram apresentadas. Isso demonstra que podemos usar funes sem ter que nos preocupar com os detalhes de suas denies. A sintaxe para uma denio de funo : def NOME_DA_FUNCAO( LISTA DE PARAMETROS ) : COMANDOS 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. A sada deste programa : 28 Captulo 5. Captulo 3: Funes
  • 36. Aprenda Computao com Python Documentation, Verso 1.1 Primeira Linha. Segunda Linha. 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? 5.7 3.7 Denies e uso Reunindo os fragmentos de cdigo da Seo 3.6, o programa completo ca assim: def novaLinha() : print def tresLinhas() : novaLinha() novaLinha() novaLinha() print Primeira Linha. 5.7. 3.7 Denies e uso 29
  • 37. Aprenda Computao com Python Documentation, Verso 1.1 tresLinhas() print Segunda Linha. 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? 5.8 3.8 Fluxo de execuo Para assegurar que uma funo esteja denida antes do seu primeiro uso, preciso saber em que ordem os comandos so executados, ou seja, descobrir qual o uxo de execuo do programa. A execuo sempre comea com o primeiro comando do programa. Os comandos so executados um de cada vez, pela ordem, de cima para baixo. As denies de funo no alteram o uxo de execuo do programa, mas lembre-se que comandos dentro da funo no so executados at a funo ser chamada. Embora no seja comum, voc pode denir uma funo dentro de outra. Neste caso, a denio mais interna no executada at que a funo mais externa seja chamada. Chamadas de funo so como um desvio no uxo de execuo. Em vez de ir para o prximo comando, o uxo salta para a primeira linha da funo chamada, executa todos os comandos l e ento volta atrs para retomar de onde havia deixado. Parece muito simples, at a hora em que voc lembra que uma funo pode chamar outra. Enquanto estiver no meio de uma funo, o programa poderia ter de executar os comandos em uma outra funo. Mas enquanto estivesse executando esta nova funo, o programa poderia ter de executar ainda outra funo! Felizmente, Python adepto de monitorar a posio onde est, assim, cada vez que uma funo se completa, o programa retoma de onde tinha parado na funo que a chamou. Quando chega ao m do programa, ele termina. Qual a moral dessa histria srdida? Quando voc for ler um programa, no o leia de cima para baixo. Em vez disso, siga o uxo de execuo. 5.9 3.9 Parmetros e argumentos Algumas das funes nativas que voc j usou requerem argumentos, aqueles valores que controlam como a funo faz seu trabalho. Por exemplo, se voc quer achar o seno de um nmero, voc tem que indicar qual nmero . Deste modo, sin recebe um valor numrico como um argumento. Algumas funes recebem mais de um argumento. Por exemplo, pow recebe dois argumentos, a base e o expoente. Dentro da funo, os valores que lhe so passados so atribudos a variveis chamadas parmetros. Veja um exemplo de uma funo denida pelo usurio, que recebe um parmetro: def imprimeDobrado(bruno): print bruno, bruno 30 Captulo 5. Captulo 3: Funes
  • 38. Aprenda Computao com Python Documentation, Verso 1.1 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. 5.10 3.10 Variveis e parmetros so locais Quando voc cria uma varivel local dentro de uma funo, ela s existe dentro da funo e voc no pode us-la fora de l. Por exemplo: def concatDupla(parte1, parte2): concat = parte1 + parte2 imprimeDobrado(concat) 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. 5.10. 3.10 Variveis e parmetros so locais 31
  • 39. Aprenda Computao com Python Documentation, Verso 1.1 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. 5.11 3.11 Diagramas da pilha Para entender que variveis podem ser usadas aonde, s vezes til desenhar um diagrama da pilha. Como os diagramas de estado, diagramas da pilha mostram o valor de cada varivel, mas tambm a funo qual cada varivel pertence. Cada funo representada por um quadro. Um quadro uma caixa com o nome de uma funo ao lado dela e os parmetros e variveis da funo dentro dela. O diagrama de pilha para o exemplo anterior tem a seguinte aparncia: 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 Captulo 5. Captulo 3: Funes
  • 40. Aprenda Computao com Python Documentation, Verso 1.1 print concat NameError: concat 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. 5.12 3.12 Funes com resultados A essa altura, voc deve ter percebido que algumas das funes que estamos usando, tais como as funes matemticas, produzem resultados. Outras funes, como novaLinha, executam uma ao, mas no retornam um valor. O que levanta algumas questes: 1. O que acontece se voc chama uma funo e no faz nada com o resultado (por exemplo, no atribui o resultado a uma varivel ou o usa como parte de uma expresso maior)? 2. O que acontece se voc usa uma funo que no produz resultado em uma expresso tal como novaLinha() + 7? 3. Voc pode escrever funes que produzem resultados, ou est preso a funes como novaLinha e imprimeDobrado? A resposta para a terceira questo armativa e ns vamos fazer isso no Captulo 5. A ttulo de exerccio, responda as outras duas questes testando-as. Se tiver dvida sobre o que vlido ou invlido em Python, tente buscar a resposta perguntando ao interpretador. 5.13 3.13 Glossrio argumento (argument) Valor fornecido a uma funo quando ela chamada. Este valor atribudo ao parmetro correspondente na funo. chamada de funo (function call) Comando que executa uma funo. Consiste do nome da funo seguido de uma lista de argumentos entre parnteses. coero de tipo (type coercion) Uma coero de tipo que ocorre automaticamente, de acordo com as regras de coercividade do Python. converso de tipo (type conversion) Comando explcito que pega um valor de um tipo e devolve o valor correspondente em outro tipo. denio de funo (function denition) Comando que cria uma nova funo, especicando seu nome, parmetros e comandos que ela executa. diagrama da pilha (stack diagram) Representao grca da pilha de funes, suas variveis e os valores aos quais elas se referem. uxo de execuo (ow of execution) A ordem na qual os comandos so executados durante a execuo do programa. frame Retngulo no diagrama da pilha que representa uma chamada de funo. Contm as variveis locais e os parmetros da funo. funo (function) Sequncia de comandos nomeada, que realiza alguma tarefa til. As funes podem ou no receber parmetros e podem ou no retornar valores. mdulo (module) Arquivo que contm uma coleo de funes e classes relacionadas entre si. 5.12. 3.12 Funes com resultados 33
  • 41. Aprenda Computao com Python Documentation, Verso 1.1 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 5. Captulo 3: Funes
  • 42. CAPTULO 6 Captulo 4: Condicionais e recursividade 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 6.1 4.1 O operador mdulo O operador mdulo trabalha com inteiros (e expresses que tm inteiros como resultado) e produz o resto da diviso do primeiro pelo segundo. Em Python, o operador mdulo um smbolo de porcentagem (%). A sintaxe a mesma que a de outros operadores: >>> >>> 2 >>> >>> 1 quociente = 7 / 3 print quociente resto = 7 % 3 print resto Ento, 7 dividido por 3 2 e o resto 1. 35
  • 43. Aprenda Computao com Python Documentation, Verso 1.1 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. 6.2 4.2 Expresses booleanas Uma expresso booleana uma expresso que verdadeira (True) ou falsa (False). Em Python, uma expresso que verdadeira tem o valor 1, e uma expresso que falsa tem o valor 0. O operador == compara dois valores e produz uma expresso booleana: >>> 5 == 5 True >>> 5 == 6 False 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 . 6.3 4.3 Operadores lgicos Existem trs operadores lgicos: and, or, not (e, ou, no). A semntica (signicado) destes operadores similar aos seus signicados em ingls (ou portugus). Por exemplo, x > 0 and x < 10 verdadeiro somente se x for maior que 0 e menor que 10. n%2 == 0 or n%3 == 0 verdadeiro se qualquer das condies for verdadeira, quer dizer, se o nmero n for divisvel por 2 ou por 3. Finalmente, o operador lgico not nega uma expresso booleana, assim, not(x > y) verdadeiro se (x > y) for falso, quer dizer, se x for menor ou igual a y. A rigor, os operandos de operadores lgicos deveriam ser expresses booleanas, mas Python no muito rigoroso. Qualquer nmero diferente de zero interpretado como verdadeiro (True): >>> >>> 1 >>> >>> 0 x = 5 x and 1 y = 0 y and 1 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
  • 44. Aprenda Computao com Python Documentation, Verso 1.1 6.4 4.4 Execuo condicional Para poder escrever programas teis, quase sempre precisamos da habilidade de checar condies e mudar o comportamento do programa de acordo com elas. As instrues condicionais nos do essa habilidade. A forma mais simples a instruo if (se): if x > 0 print "x positivo" 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. 6.5 4.5 Execuo alternativa Um segundo formato da instruo if a execuo alternativa, na qual existem duas possibilidades e a condio determina qual delas ser executada. A sintaxe se parece com: if x % 2 == 0: print x, " par" else: print x, " impar" 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) 6.4. 4.4 Execuo condicional 37
  • 45. Aprenda Computao com Python Documentation, Verso 1.1 6.6 4.6 Condicionais encadeados s vezes existem mais de duas possibilidades e precisamos de mais que dois ramos. Uma condicional encadeada uma maneira de expressar uma operao dessas: if x < y: print x, " menor que", y elif x > y: print x, " maior que", y else: print x, "e", y, "so iguais" 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). 6.7 4.7 Condicionais aninhados Um condicional tambm pode ser aninhado dentro de outra. Poderamos ter escrito o exemplo tricotmico (dividido em trs) como segue: if x == y: print x, "e", y, "so iguais" else: if x < y: print x, " menor que", y else: print x, " maior que", y 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 Captulo 6. Captulo 4: Condicionais e recursividade
  • 46. Aprenda Computao com Python Documentation, Verso 1.1 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." 6.8 4.8 A instruo return O comando return permite terminar a execuo de uma funo antes que ela alcance seu m. Uma razo para us-lo se voc detectar uma condio de erro: import math def imprimeLogaritmo(x): if x >> contagemRegressiva(3) 6.8. 4.8 A instruo return 39
  • 47. Aprenda Computao com Python Documentation, Verso 1.1 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. 6.10 4.10 Diagramas de pilha para funes recursivas Na Seo 3.11, usamos um diagrama de pilha para representar o estado de um programa durante uma chamada de funo. O mesmo tipo de diagrama pode ajudar a interpretar uma funo recursiva. Toda vez que uma funo chamada, Python cria um novo quadro (frame) para a funo, que contm as variveis locais e parmetros da funo. Para uma funo recursiva, ter que existir mais de um quadro na pilha ao mesmo tempo. Esta gura mostra um diagrama de pilha para contagemRegressiva, chamada com n = 3: 40 Captulo 6. Captulo 4: Condicionais e recursividade
  • 48. Aprenda Computao com Python Documentation, Verso 1.1 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. 6.11 4.11 Recursividade innita Se uma recursividade nunca chega ao caso base, ela prossegue fazendo chamadas recursivas para sempre, e o programa nunca termina. Isto conhecido como recursividade innita, e geralmente no considerada uma boa ideia. Aqui est um programa mnimo com uma recursividade innita: def recursiva(): recursiva() 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 "", line 2, in recursiva (98 repetitions omitted) File "", 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. 6.11. 4.11 Recursividade innita 41
  • 49. Aprenda Computao com Python Documentation, Verso 1.1 6.12 4.12 Entrada pelo teclado Os programas que temos escrito at agora so um pouco crus, no sentido de no aceitarem dados entrados pelo usurio. Eles simplesmente fazem a mesma coisa todas as vezes. Python fornece funes nativas que pegam entradas pelo teclado. A mais simples chamada raw_input. Quando esta funo chamada, o programa pra e espera que o usurio digite alguma coisa. Quando o usurio aperta a tecla Enter ou Return, o programa prossegue e a funo raw_input retorna o que o usurio digitou como uma string: >>> entrada = raw_input() O que voc est esperando? >>> print entrada O que voc est esperando? 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 esperamos que a entrada seja um inteiro, podemos usar a funo input: pergunta = "Qual... a velocidade de vo de uma andorinha?n" velocidade = input(pergunta) 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. 6.13 4.13 Glossrio aninhamento (nesting) Estrutura de programa dentro da outra, como um comando condicional dentro de um bloco de outro comando condicional. bloco (block) Grupo de comandos consecutivos com a mesma endentao. caso base (base case) Bloco de comando condicional numa funo recursiva que no resulta em uma chamada recursiva. comando composto (compound statement) Comando que consiste de um cabealho e um corpo. O cabealho termina com um dois-pontos (:). O corpo endentado em relao ao cabealho. comando condicional (conditional statement) Comando que controla o uxo de execuo dependendo de alguma condio. condio (condition) A expresso booleana que determina qual bloco ser executado num comando condicional. corpo (body) O bloco que se segue ao cabealho em um comando composto. 42 Captulo 6. Captulo 4: Condicionais e recursividade
  • 50. Aprenda Computao com Python Documentation, Verso 1.1 expresso booleana (boolean expression) Uma expresso que verdadeira ou falsa. operador de comparao (comparison operator) Um dos operadores que compara dois valores: ==, !=, >, =, e 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. 7.2 5.2 Desenvolvimento de programas Neste ponto, voc deve estar apto a olhar para funes completas e dizer o que elas fazem. Tambm, se voc vem fazendo os exerccios, voc escreveu algumas pequenas funes. Conforme escrever funes maiores, voc pode comear a ter mais diculdade, especialmente com erros em tempo de execuo (erros de runtime) ou erros semnticos. Para lidar com programas de crescente complexidade, vamos sugerir uma tcnica chamada desenvolvimento incremental. A meta do desenvolvimento incremental evitar sees de depurao (debugging) muito longas pela adio e teste de somente uma pequena quantidade de cdigo de cada vez. Como exemplo, suponha que voc queira encontrar a distncia entre dois pontos, dados pelas coordenadas (x1,y1) e (x2,y2). Pelo teorema de Pitgoras, a distncia : distancia = V (x2 - x1)2 + (y2 - y1)2 (5.1) 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
  • 54. Aprenda Computao com Python Documentation, Verso 1.1 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
  • 55. Aprenda Computao com Python Documentation, Verso 1.1 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. 7.3 5.3 Composio Conforme voc poderia esperar agora, voc pode chamar uma funo de dentro de outra. Esta habilidade chamada de composio. Como um exemplo, vamos escrever uma funo que recebe dois pontos, o centro de um crculo e um ponto em seu permetro, e calcula a rea do crculo. Assuma que o ponto do centro est guardado nas variveis xc e yc, e que o ponto do permetro est nas variveis xp e yp. O primeiro passo encontrar o raio do crculo, o qual a entre os dois pontos. Felizmente, temos uma funo, distancia, que faz isto: Raio = distancia(xc, yc, xp, yp) O segundo passo encontrar a rea de um crculo com o raio dado e retorn-la: resultado = area(raio) return resultado Juntando tudo numa funo, temos: def area2(xc, yc, xp, yp): raio = distancia(xc, yc, xp, yp) resultado = area(raio) return resultado 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
  • 56. Aprenda Computao com Python Documentation, Verso 1.1 7.4 5.4 Funes booleanas Funes podem retornar valores booleanos, o que muitas vezes conveniente por ocultar testes complicados dentro de funes. Por exemplo: def ehDivisivel(x, y): If x % y == 0: return True # verdadeiro (True), divisvel else: return False # falso (False), no divisvel 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 Esta sesso mostra a nova funo em ao: >>> ehDivisivel(6, 4) False >>> ehDivisivel(6, 3) True Funes booleanas so frequentemente usadas em comandos condicionais: if ehDivisivel(x, y): print "x divisvel por y" else: print "x no divisvel por y" 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. 7.5 5.5 Mais recursividade At aqui, voc aprendeu apenas um pequeno subconjunto da linguagem Python, mas pode ser que te interesse saber que este pequeno subconjunto uma linguagem de programao completa, o que signica que qualquer coisa que possa ser traduzida em operao computacional pode ser expressa nesta linguagem. Qualquer programa j escrito pode ser reescrito usando somente os aspectos da linguagem que voc aprendeu at agora (usualmente, voc precisaria de uns poucos comandos para controlar dispositivos como o teclado, mouse, discos, etc., mas isto tudo). Provar esta armao um exerccio nada trivial, que foi alcanado pela primeira vez por Alan Turing, um dos primeiros cientistas da computao (algum poderia dizer que ele foi um matemtico, mas muitos dos primeiros cientistas da computao comearam como matemticos). Por isso, cou conhecido como Tese de Turing. Se voc zer um curso em Teoria da Computao, voc ter chance de ver a prova. Para te dar uma ideia do que voc pode fazer com as ferramentas que aprendeu a usar at agora, vamos avaliar algumas funes matemticas recursivamente denidas. Uma denio recursiva similar uma denio circular, no sentido de que a denio faz referncia coisa que est sendo denida. Uma verdadeira denio circular no muito til: vorpal: adjetivo usado para descrever algo que vorpal. 7.4. 5.4 Funes booleanas 49
  • 57. Aprenda Computao com Python Documentation, Verso 1.1 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): Se acontece de o argumento ser 0, tudo o que temos de fazer retornar 1: def fatorial(n): if n == 0: return 1 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 Captulo 7. Captulo 5: Funes frutferas
  • 58. Aprenda Computao com Python Documentation, Verso 1.1 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. 7.6 5.6 Voto de conana (Leap of faith) Seguir o uxo de execuo uma maneira de ler programas, mas que pode rapidamente se transformar em um labirinto. Uma alternativa o que chamamos de voto de conana. Quando voc tem uma chamada de funo, em vez de seguir o uxo de execuo, voc assume que a funo funciona corretamente e retorna o valor apropriado. De fato, voc est agora mesmo praticando este voto de conana ao usar as funes nativas. Quando voc chama math.cos ou math.exp, voc no examina a implementao destas funes. Voc apenas assume que elas funcionam porque as pessoas que escreveram as bibliotecas nativas eram bons programadores. O mesmo tambm verdade quando voc chama uma de suas prprias funes. Por exemplo, na Seo 5.4, escrevemos a funo chamada ehDivisivel que determina se um nmero divisvel por outro. Uma vez que nos convencemos que esta funo est correta ao testar e examinar o cdigo podemos usar a funo sem examinar o cdigo novamente. O mesmo tambm verdadeiro para programas recursivos. Quando voc tem uma chamada recursiva, em vez de seguir o uxo de execuo, voc poderia assumir que a chamada recursiva funciona (produz o resultado correto) e ento perguntar-se, Assumindo que eu possa encontrar o fatorial de n-1, posso calcular o fatorial de n? Neste caso, claro que voc pode, multiplicando por n. Naturalmente, um pouco estranho que uma funo funcione corretamente se voc ainda nem terminou de escrev-la, mas por isso que se chama voto de conana! 7.7 5.7 Mais um exemplo No exemplo anterior, usamos variveis temporrias para deixar claros os passos e tornar o cdigo mais fcil de depurar, mas poderamos ter economizado algumas linhas: def fatorial(n): if n == 0: return 1 7.6. 5.6 Voto de conana (Leap of faith) 51
  • 59. Aprenda Computao com Python Documentation, Verso 1.1 else: return n * fatorial(n-1) 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); Traduzido em Python, parecer assim: def fibonacci(n): if n == 0 or n == 1: return 1 else: return 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. 7.8 5.8 Checagem de tipos O que acontece se chamamos fatorial e damos a ela 1.5 como argumento?: >>> fatorial (1.5) RuntimeError: Maximum recursion depth exceeded 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 Captulo 7. Captulo 5: Funes frutferas
  • 60. Aprenda Computao com Python Documentation, Verso 1.1 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. 7.9 5.9 Glossrio cdigo morto (dead code) Parte de um programa que nunca pode ser executada, muitas vezes por que ela aparece depois de uma instruo return. desenvolvimento incremental (incremental development) Uma estratgia de desenvolvimento de programas que evita a depurao ao adicionar e testar somente uma pequena quantidade de cdigo de cada vez. funo frutfera (fruitful function) Uma funo que produz um valor de retorno. guardio (guardian) Uma condio que checa e manipula circunstncias que poderiam causar um erro. None Um valor especial em Python, retornado por funes que no possuem uma instruo return ou tm uma instruo return sem argumento. scaffolding Cdigo usado durante o desenvolvimento do programa, mas que no faz parte do produto nal. varivel temporria (temporary variable) Uma varivel usada para guardar um valor intermedirio em um clculo complexo. valor de retorno (return value) O valor entregue como resultado de uma chamada de funo. 7.9. 5.9 Glossrio 53
  • 61. Aprenda Computao com Python Documentation, Verso 1.1 54 Captulo 7. Captulo 5: Funes frutferas
  • 62. 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 8.1 6.1 Reatribuies Como voc talvez j tenha descoberto, permitido fazer mais de uma atribuio mesma varivel. Uma nova atribuio faz uma varivel existente referir-se a um novo valor (sem se referir mais ao antigo).: bruno print bruno print = 5 bruno, = 7 bruno 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
  • 63. Aprenda Computao com Python Documentation, Verso 1.1 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 0: print n n = n-1 print "Fogo!" 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 Captulo 8. Captulo 6: Iterao
  • 64. Aprenda Computao com Python Documentation, Verso 1.1 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. 8.3 6.3 Tabelas Uma das coisas para qual os loops so bons para gerar dados tabulares. Antes que os computadores estivessem readily disponveis, as pessoas tinham que calcular logaritmos, senos, cossenos e outras funes matemticas mo. Para tornar isto mais fcil, os livros de matemtica continham longas tabelas listando os valores destas funes. Criar as tabelas era demorado e entediante, e elas tendiam a ser cheias de erros. Quando os computadores entraram em cena, uma das reaes iniciais foi Isto timo! Podemos usar computadores para geras as tabelas, assim no haver erros. Isto veio a se tornar verdade (na maioria das vezes) mas shortsighted. Rapidamente, porm, computadores e calculadoras tornaram-se to pervasivos que as tabelas caram obsoletas. Bem, quase. Para algumas operaes, os computadores usam tabelas de valores para conseguir uma resposta aproximada e ento realizar clculos para melhorar a aproximao. Em alguns casos, tm havido erros nas tabelas underlying, o caso mais famoso sendo o da tabela usada pelo processador Pentium da Intel para executar a diviso em ponto-utuante. Embora uma tabela de logaritmos no seja mais to til quanto j foi um dia, ela ainda d um bom exemplo de iterao. O seguinte programa gera uma seqncia de valores na coluna da esquerda e seus respectivos logaritmos na coluna da direita: 8.3. 6.3 Tabelas 57
  • 65. Aprenda Computao com Python Documentation, Verso 1.1 x = 1.0 while x < 10.0: print x, t, math.log(x) x = x + 1.0 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) o que resultar em: 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 0.0 1.0 1.58496250072 2.0 2.32192809489 2.58496250072 2.80735492206 3.0 3.16992500144 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 58 0.0 1.0 Captulo 8. Captulo 6: Iterao
  • 66. Aprenda Computao com Python Documentation, Verso 1.1 4.0 8.0 16.0 32.0 64.0 2.0 3.0 4.0 5.0 6.0 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. 8.4 6.4 Tabelas de duas dimenses (ou bi-dimensionais) Uma tabela de duas dimenses uma tabela em que voc l o valor na interseo entre uma linha e uma coluna. Uma tabela de multiplicao um bom exemplo. Digamos que voc queira imprimir uma tabela de multiplicao de 1 a 6. Uma boa maneira de comear escrever um loop que imprima os mltiplos de 2, todos em uma linha: i = 1 while i >> print letra 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
  • 73. Aprenda Computao com Python Documentation, Verso 1.1 a 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. 9.2 7.2 Comprimento A funo len retorna o nmero de caracteres de uma string: >>> fruta = "banana" >>> len(fruta) 6 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. 9.3 7.3 Travessia e o loop for Vrias computaes envolvem o processamento de uma string um caractere de cada vez. Muitas vezes elas comeam com o primeiro, selecionam um de cada vez, fazem alguma coisa com ele, e continuam at o m. Este padro de processamento chamado uma travessia (traversal, com a idia de percorrimento). Uma maneira de codicar uma travessia com um comando while: indice = 0 while indice < len(fruta): letra = fruta[indice] print letra indice = indice + 1 66 Captulo 9. Captulo 7: Strings
  • 74. Aprenda Computao com Python Documentation, Verso 1.1 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 A sada deste programa : Jack Kack Lack Mack Nack Oack Pack Qack 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. 9.4 7.4 Fatias de strings Um segmento de uma string chamado de uma fatia. Selecionar uma fatia similar a selecionar um caractere: >>> s = "Pedro, Paulo e Maria" >>> print s[0:5] Pedro >>> print s[7:12] Paulo >>> print s[16:21] Maria 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: 9.4. 7.4 Fatias de strings 67
  • 75. Aprenda Computao com Python Documentation, Verso 1.1 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 O que voc acha de s[:] signica? 9.5 7.5 Comparao de strings O operador de comparao funciona com strings. Para ver se duas strings so iguais: if palavra == "banana": print "Sim, ns no temos bananas!" Outras operaes de comparao so teis para colocar palavras em ordem alfabtica: if palavra < "banana": print "Sua palavra," + palavra + ", vem antes de banana." elif palavra > "banana": print "Sua palavra," + palavra + ", vem depois de banana." else: print "Sim, ns no temos bananas!" 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. 9.6 7.6 Strings so imutveis tentador usar o operador [] no lado esquerdo de uma expresso de atribuio, com a inteno de alterar um caractere em uma string. Por exemplo: saudacao = "Al, mundo!" saudacao[0] = E print saudacao 68 # ERRO! Captulo 9. Captulo 7: Strings
  • 76. Aprenda Computao com Python Documentation, Verso 1.1 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. 9.7 7.7 Uma funo find (encontrar) O que faz a seguinte funo?: def find(str, ch): indice = 0 while indice < len(str): if str[indice] == ch: return indice indice = indice + 1 return -1 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. 9.8 7.8 Iterando e contando O programa seguinte conta o nmero e vezes que a letra a aparece em uma string: fruta = "banana" contador = 0 for letra in fruta: if letra == a: contador = contador + 1 print contador 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. 9.7. 7.7 Uma funo find (encontrar) 69
  • 77. Aprenda Computao com Python Documentation, Verso 1.1 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. 9.9 7.9 O mdulo string O mdulo string contm funes teis que manipulam strings. Conforme usual, ns temos que importar o mdulo antes que possamos utiliz-lo: >>> import string 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. 9.10 7.10 Classicao de caracteres Muitas vezes til examinar um caractere e testar se ele maisculo ou minsculo, ou se ele um caractere ou um dgito. O mdulo string oferece vrias constantes que so teis para esses propsitos. A string string.lowercase contm todas as letras que o sistema considera como sendo minsculas. Similarmente, string.uppercase contm todas as letras maisculas. Tente o seguinte e veja o que voc obtm: >>> print string.lowercase >>> print string.uppercase >>> print string.digits 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: 70 Por exemplo, se Captulo 9. Captulo 7: Strings
  • 78. Aprenda Computao com Python Documentation, Verso 1.1 def eMinusculo(ch): return string.find(string.lowercase, ch) != -1 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 Ainda, como uma outra alternativa, podemos usar o operador de comparao: def eMinusculo(ch): return a > 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. 9.11 7.11 Glossrio tipo de dado composto (compound data type) Um tipo de dado no qual o valor consiste de componentes, ou elementos, que so eles mesmos valores. travessia (traverse) Iterar atravs dos elementos de um conjunto, realizando uma operao similar em cada um deles. ndice (index) Uma varivel ou valor usados para selecionar um membro de um conjunto ordenado, como um caractere em uma string. fatia (slice) Uma parte de uma string especicada por um intervalo de ndices. mutvel (mutable) Um tipo de dado composto a cujos elementos podem ser atribudos novos valores. contador (counter) Uma varivel utilizada para contar alguma coisa, usualmente inicializada em zero e ento incrementada. incrementar (increment) aumentar o valor de uma varivel em 1. decrementar (decrement) diminuir o valor de uma varivel em 1. espaamento (whitespace) Qualquer um dos caracteres que move o cursor sem imprimir caracteres visveis. A constante string.whitespace contm todos os caracteres de espaamento. 9.11. 7.11 Glossrio 71
  • 79. Aprenda Computao com Python Documentation, Verso 1.1 9.12 7.11 Glossrio2 tipo de dado composto (compound data type) Um tipo de dado em que os valores so compostos de componentes, ou elementos, que podem ser tratados como valores separados. atravessar (traverse) Iterar atravs dos elementos denidos, executando uma operao similar em cada. ndice (index) Uma varivel ou valor usado para selecionar um membro de uma denio ordenada, como um caractere de uma string. fatia (slice) Uma parte da string especicada por um intervalo de ndice. mutvel (mutable) Um tipo de dado composto do qual elementos podem atribuir novos valores. contador (counter) Uma varivel usada para contar alguma coisa, geralmente iniciada em zero e incrementada. incremento (increment) Para aumentar o valor da varivel. decremento (decrement) Para dimiuir o valor da varivel. espao em branco (whitespace) Qualquer caractere que move o cursor sem imprimir caracteres visveis. A constante string.whitespace contm todos os caracteres de espao em branco. 72 Captulo 9. Captulo 7: Strings
  • 80. 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. 10.1 8.1 Valores da lista Existem vrias maneiras de criar uma nova lista; a mais simples envolver os elementos em colchetes ([ e ]): >>> [10, 20, 30, 40] >>> [spam, bungee, swallow] 73
  • 81. Aprenda Computao com Python Documentation, Verso 1.1 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] [] 10.2 8.2 Acessado elementos A sintaxe para acessar os elementos de uma lista a mesma que a sintaxe para acessar os caracteres de uma string XXX o operator colchete ([]). A expresso dentro dos colchetes especica o ndice. Lembre-se que os ndices iniciam em 0: >>> print numeros[0] >>> numeros[1]= 5 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 Captulo 10. Captulo 8: Listas
  • 82. Aprenda Computao com Python Documentation, Verso 1.1 >>> numeros[2]=5 IndexError: list assignment index out of range 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. 10.3 8.3 Comprimento da lista A funo len devolve o comprimento de uma lista. uma boa idia utilizar este valor como o limite superior de um lao ao invs de uma constante. Desta forma, se o tamanho da lista mudar, voc no precisar ir atravs de todo o programa modicando todos os laos; eles funcionaro corretamente para qualquer tamanho de lista: >>> cavaleiros = [guerra, fome, peste, morte] i = 0 while i < len(cavaleiros): print cavaleiros[i] i = i + 1 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? 10.4 8.4 Membros de uma lista in um operador lgico que testa se um elemento membro de uma seqncia. Ns o utilizamos na Seo 7.10 com strings, mas ele tambm funciona com listas e outras seqncias: 10.3. 8.3 Comprimento da lista 75
  • 83. Aprenda Computao com Python Documentation, Verso 1.1 >>> 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 10.5 8.5 Listas e laos for O lao for que vimos na Seo 7.3 tambm funciona com listas. A sintaxe generalizada de um lao for : for VARIAVEL in LISTA: CORPO Esta declarao equivalente a: >>> i = 0 >>> while >>> >>> >>> i = i < len(LISTA): VARIAVEL = LISTA[i] XXX BODY i + 1 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. 10.6 8.6 Operaes em listas O operador + concatena listas: 76 Captulo 10. Captulo 8: Listas
  • 84. Aprenda Computao com Python Documentation, Verso 1.1 >>> >>> >>> >>> [1, a = [1, 2, 3] b = [4, 5, 6] c = a + b print c 2, 3, 4, 5, 6] Similarmente, o operador * repete uma lista um nmero dado de vezes: >>> [0, >>> [1, [0] * 4 0, 0, 0] [1, 2, 3] * 3 2, 3, 1, 2, 3, 1, 2, 3] O primeiro exemplo repete [0] quatro vezes. O segundo exemplo repete a lista [1, 2, 3] trs vezes. 10.7 8.7 Fatiamento de listas A operao de fatiamento que vimos na Seo 7.4 tambm funciona sobre listas: >>> lista = [a, b, c, d, e, f] >>> lista[1:3] [b, c] >>> lista[:4] [a, b, c, d] >>> lista[3:] [d, e, f] >>> lista[:] [a, b, c, d, e, f] 10.8 8.8 Listas so mutveis Diferente das strings, as listas so mutveis, o que signica que podemos modicar seus elementos. Utilizando o operador colchete no lado esquerdo de uma atribuio, podemos atualizar um de seus elementos: >>> fruta = ["banana", "abacaxi", "laranja"] >>> fruta[0] = "abacate" >>> fruta[-1] = "tangerina" >>> print fruta [abacate, abacaxi, tangerina] Com o operador de fatiamento podemos atualizar vrios elementos de uma vez: >>> lista = [a, b, c, d, e, f] >>> lista[1:3] = [x, y] >>> print lista [a, x, y, d, e, f] 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: 10.7. 8.7 Fatiamento de listas 77
  • 85. Aprenda Computao com Python Documentation, Verso 1.1 >>> 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] 10.9 8.9 Remoo em lista Utilizando fatias para remover elementos pode ser complicado, e desta forma propenso a erro. Python fornece uma alternativa que mais legvel. del remove um elemento de uma lista: >>> a = [um, dois, tres] >>> del a[1] >>> a [um, tres] 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. 10.10 8.10 Ojetos e valores Se executamos estas declaraes de atribuio: >>> a = "banana" >>> b = "banana" 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
  • 86. Aprenda Computao com Python Documentation, Verso 1.1 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 Ento o diagrama de estado ca assim: a e b possuem o mesmo valor mas no se referem ao mesmo objeto. 10.11 8.11 Apelidos Uma vez que variveis se referem a objetos, se atribuimos uma varivel a uma outra, ambas as variveis se referem ao mesmo objeto: >>> a = [1, 2, 3] >>> b = a Neste caso, o diagrama de estado se parece com isto: 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] 10.11. 8.11 Apelidos 79
  • 87. Aprenda Computao com Python Documentation, Verso 1.1 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. 10.12 8.12 Clonando listas Se queremos modicar uma lista e tambm manter uma cpia da original, preciamos ter condies de fazer uma cpia da prpria lista, no apenas uma referncia. Este processo algumas vezes chamado clonagem, para evitar a ambigidade da palavra cpia. A maneira mas fcil de clonar uma lista utilizar o operador de fatia: >>> >>> >>> [1, a = [1, 2, 3] b = a[:] print b 2, 3] 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. 10.13 8.13 Lista como parmetro Passar uma lista como um argumento passa realmente uma referncia lista, no uma cpia da lista. Por exemplo, a funo cabeca pega uma lista como parmetro e devolve a cabea da lista, ou seja, seu primeiro elemento: >>> def cabeca(lista): return lista[0] Eis como ela utilizada: >>> numeros = [1, 2, 3] >>> cabeca(numeros) 1 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 Captulo 10. Captulo 8: Listas
  • 88. Aprenda Computao com Python Documentation, Verso 1.1 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] Aqui est a maneira como ela utilizada: >>> >>> >>> [2, numeros = [1, 2, 3] removeCabeca(numeros) print numeros 3] 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:] Aqui est a maneira como ela utilizada: >>> >>> >>> [2, numeros = [1, 2, 3] resto = cauda(numeros) print resto 3] 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. 10.14 8.14 Lista aninhadas Uma lista aninhada uma lista que aparece como um elemento de uma outra lista. Nesta lista, o terceiro elemento uma lista aninhada: >>> lista = ["alo", 2.0, 5, [10, 20]] 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. 10.15 8.15 Matrizes Listas aninhadas so freqentemente utilizadas para representar matrizes. Por exemplo, a matriz: 10.14. 8.14 Lista aninhadas 81
  • 89. Aprenda Computao com Python Documentation, Verso 1.1 poderia ser representada como: >>> matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 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] Ou podemos extrair um nico elemento da matriz utilinado a forma de duplo ndice: >>> matriz[1][1] 5 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. 10.16 8.16 Strings e listas Duas das mais teis funes no mdulo string envolvem listas de strings. A funo split (separar) quebra uma string em uma lista de palavras. Por padro, qualquer nmero de caracteres espao em branco considerado um limite de uma palavra: >>> import string >>> poesia = "O orvalho no carvalho..." >>> string.split(poesia) [O, orvalho, no, carvalho...] 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... Como split, join recebe um delimitador que inserido entre os elementos: >>> string.join(lista, _) O_orvalho_no_carvalho... 82 Captulo 10. Captulo 8: Listas
  • 90. Aprenda Computao com Python Documentation, Verso 1.1 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? 10.17 8.17 Glossrio lista (list) Uma coleo denominada de objetos, onde cada objeto identicado por um ndice. ndice (index) Uma varivel inteira ou valor que indica um elemento de uma lista. elemento (element) Um dos valores em uma lista(ou outra seqncia). O operador colchete seleciona elementos de uma lista. seqncia (sequence) Qualquer um dos tipos de dados que consiste de um conjunto ordenado de elementos, com cada elemento identicado por um ndice. lista aninhada (nested list) Uma lista que um elemento de uma outra lista. percurso na lista (list traversal) O acesso seqencial de cada elemento em uma lista. objeto (object) Um coisa a qual uma varivel pode se referir. apelidos (aliases) Mltiplas variveis que contm referncias ao mesmo objeto. clonar (clone) Criar um novo objeto que possui o mesmo valor de um objeto existente. Copiar a referncia a um objeto cria um apelido (alias) mas no clona o objeto. delimitador (delimiter) Um caracter uma string utilizados para indicar onde uma string deveria ser dividida(split). 10.18 Outros termos utilizados neste captulo (XXX esta lista deve ser retirada na verso nal) XXX has, have possuir (ter?) XXX there is, there are existir (haver?) XXX use utilizar (usar?) XXX string Utilizei string em itlico, por ser tratar de um termo que no em portugus. XXX enclose envolver??? XXX provide fornecer XXX return devolve XXX denoted denotada XXX XXX disappointing decepcionante (desapontador?) XXX assign atribuir XXX change modicar XXX length comprimento (tamanho?) XXX print exibir (imprimir?) XXX membership No creio que exista uma palavra que traduza este termo. Pelo menos em ingls no encontrei nenhum sinnimo. Vou tentar traduzir explicando o termo dependendo do contexto. XXX boolean lgico (booleano?) 10.17. 8.17 Glossrio 83
  • 91. Aprenda Computao com Python Documentation, Verso 1.1 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 10. Captulo 8: Listas
  • 92. 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 11.1 9.1 Mutabilidade e tuplas At agora, voc tem visto dois tipos compostos: strings, que so compostos de caracteres; e listas, que so compostas de elementos de qualquer tipo. Uma das diferenas que notamos que os elementos de uma lista podem ser modicados, mas os caracteres em uma string no. Em outras palavras, strings so imutveis e listas so mutveis. H um outro tipo em Python chamado tupla (tuple) que similar a uma lista exceto por ele ser imutvel. Sintaticamente, uma tupla uma lista de valores separados por vrgulas: >>> tupla = a, b, c, d, e Embora no seja necessrio, convencional colocar tuplas entre parnteses: >>> tupla = (a, b, c, d, e) Para criar uma tupla com um nico elemento, temos que incluir uma vrgula nal: >>> t1 = (a,) >>> type(t1) Sem a vrgula, Python entende (a) como uma string entre parnteses: 85
  • 93. Aprenda Computao com Python Documentation, Verso 1.1 >>> t2 = (a) >>> type(t2) 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 E o operador slice (fatia) seleciona uma faixa (range) de elementos. >>> tupla[1:3] (b, c) Mas se tentarmos modicar um dos elementos de uma tupla, teremos um erro: >>> tupla[0] = A TypeError: object doesnt support item assignment 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) 11.2 9.2 Atribuies de tupla De vez em quando, necessrio trocar entre si os valores de duas variveis. Com operaes de atribuio convencionais, temos que utilizar uma varivel temporria. Por exemplo, para fazer a troca entre a e b: >>> temp = a >>> a = b >>> b = temp 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 11.3 9.3 Tuplas como valores de retorno Funes podem retornar tuplas como valor de retorno. Por Exemplo, ns poderamos escrever uma funo que troca dois parmetros entre si: 86 Captulo 11. Captulo 9: Tuplas
  • 94. Aprenda Computao com Python Documentation, Verso 1.1 def troca(x, y): return y, x 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 Se ns chamarmos esta funo desta forma: troca(a, b) 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. 11.4 9.4 Nmeros aleatrios A maioria dos programas de computador fazem a mesma coisa sempre que so executados, ento, podemos dizer que eles so determinsticos. Determinismo em geral uma coisa boa, se ns esperamos que um clculo d sempre o mesmo resultado. Entretanto, para algumas aplicaes queremos que o computador se torne imprevisvel. Jogos so um exemplo bvio, mas existem outros. Fazer um programa realmente no-determinstico se mostra no ser to fcil, mas existem maneiras de faz-lo ao menos parecer no-determinstico. Uma dessas maneiras gerar nmeros aleatrios e us-los para determinar o resultado de um programa. Python tem uma funo nativa que gera nmeros pseudo aleatrios, os quais no so verdadeiramente aleatrios no sentido matemtico, mas para os nossos propsitos eles so. O mdulo random contm uma funo chamada random que retorna um nmero em ponto utuante (oating-point number) entre 0.0 e 1.0. Cada vez que voc chama random, voc recebe o prximo nmero de uma longa srie. Para ver uma amostra, execute este loop: import random for i in range(10): x = random.random() print x 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. 11.4. 9.4 Nmeros aleatrios 87
  • 95. Aprenda Computao com Python Documentation, Verso 1.1 11.5 9.5 Lista de nmeros aleatrios O primeiro passo gerar uma lista aleatria de valores. listaAleatoria pega um parmetro inteiro e retorna uma lista de nmeros aleatrios com o comprimento dado. Inicia-se com uma lista de n zeros. A cada iterao do loop, ele substitui um dos elementos por um nmero aleatrio. O valor retornado uma referncia para a lista completa: def listaAleatoria(n): s = [0] * n for i in range(n): s[i] = random.random() return s 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. 11.6 9.6 Contando Uma boa maneira de abordar problemas como esse dividir o problema em subproblemas, e encontrar um subproblema que se enquadre em um padro de soluo computacional que voc j tenha visto antes. Neste caso, queremos percorrer uma lista de nmeros e contar o nmero de vezes que valor se encaixa em um determinado intervalo. Isso soa familiar. Na Seo 7.8, ns escrevemos um programa que percorria uma string e contava o nmero de vezes que uma determinada letra aparecia. Assim, podemos prosseguir copiando o programa original e adaptando-o para o problema atual. O programa original era: contador = 0 for letra in fruta: if letra == a: contador = contador + 1 print contador 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
  • 96. Aprenda Computao com Python Documentation, Verso 1.1 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. 11.7 9.7 Vrios intervalos Conforme o nmero de intervalos aumenta, noIntervalo torna-se intragvel. Com dois intervalos, no to ruim: inferior = noIntervalo(a, 0.0, 0.5) superior = noIntervalo(a, 0.5, 1) Mas com quatro intervalos, comea a car desconfortvel.: intervalo1 intervalo2 intervalo3 intervalo4 = = = = noIntervalo(a, noIntervalo(a, noIntervalo(a, noIntervalo(a, 0.0, 0.25) 0.25, 0.5) 0.5, 0.75) 0.75, 1.0) 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 11.7. 9.7 Vrios intervalos 89
  • 97. Aprenda Computao com Python Documentation, Verso 1.1 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. 11.8 9.8 Uma soluo em um s passo Embora este programa funcione, ele no to eciente quanto poderia ser. Toda vez que ele chama noIntervalo, ele percorre a lista inteira. Conforme o nmero de intervalos aumenta, a lista ser percorrida um bocado de vezes. Seria melhor fazer uma nica passagem pela lista e calcular para cada valor o ndice do intervalo ao qual o valor pertena. Ento podemos incrementar o contador apropriado. Na seo anterior, pegamos um ndice, i, e o multiplicamos pela larguraDoIntervalo para encontrar o limite inferior daquele intervalo. Agora queremos pegar um valor entre 0.0 e 1.0 e encontrar o ndice do intervalo ao qual ele se encaixa. J que este problema o inverso do problema anterior, podemos imaginar que deveramos dividir por larguraDoIntervalo em vez de multiplicar. Esta suposio est correta. J que larguraDoIntervalo = 1.0 / numeroDeIntervalos, dividir por larguraDoIntervalo o mesmo que multiplicar por numeroDeIntervalos. Se multiplicarmos um nmero na faixa entre 0.0 e 1.0 por numeroDeIntervalos, obtemos um nmero na faixa entre 0.0 e numeroDeIntervalos. Se arredondarmos este nmero para baixo, ou seja, para o menor inteiro mais prximo, obtemos exatamente o que estamos procurando o ndice do intervalo: numeroDeIntervalos = 8 intervalos = [0] * numeroDeIntervalos for i in lista: 90 Captulo 11. Captulo 9: Tuplas
  • 98. Aprenda Computao com Python Documentation, Verso 1.1 indice = int(i * numeroDeIntervalos) intervalos[indice] = intervalos[indice] + 1 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. 11.9 9.9 Glossrio tipo imutvel (immutable type) Um tipo de elemento que no pode ser modicado. Atribuies a um elemento ou fatiamento (slices) XXX aos tipos imutveis causaro erro. tipo mutvel (mutable type) Tipo de dados onde os elementos podem ser modicados. Todos os tipos mutveis, so tipos compostos. Listas e dicionrios so exemplos de tipos de dados mutveis. String e tuplas no so. tupla (tuple) Tipo sequencial similar as listas com exceo de que ele imutvel. Podem ser usadas Tuplas sempre que um tipo imutvel for necessrio, por exemplo uma chave (key) em um dicionrio Atribuio a tupla (tuple assignment) Atribuio a todos os elementos de uma tupla feita num nico comando de atribuo. A atribuio aos elementos ocorre em paralelo, e no em sequncia, tornando esta operao til para swap, ou troca recproca de valores entre variveis (ex: a,b=b,a). determinstico (deterministic) Um programa que realiza a mesma coisa sempre que executado. pseudo aleatrio (pseudorandom) Uma sequncia de nmeros que parecem ser aleatrios mas so na verdade o resultado de uma computao determinstica histograma (histogram) Uma lista de inteiros na qual cada elemento conta o nmero de vezes que algo acontece. casamento de padro XXX (pattern matching) Um programa desenvolvido que envolve identicar um teste padro computacional familiar e copiar a soluo para um problema similar. 11.9. 9.9 Glossrio 91
  • 99. Aprenda Computao com Python Documentation, Verso 1.1 92 Captulo 11. Captulo 9: Tuplas
  • 100. CAPTULO 12 Captulo 10: Dicionrios 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
  • 101. Aprenda Computao com Python Documentation, Verso 1.1 >>> ing2esp = {one: uno, two: dos, three: tres} Se nos imprimirmos o valor de ing2esp novamente, nos teremos uma surpresa: >>> print ing2esp {one: uno, three: tres, two: dos} 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. 12.1 10.1 Operaes dos Dicionrios O comando del remove um par chave-valor de um dicionrio. Por exemplo, o dicionrio abaixo contem os nomes de varias frutas e o numero de cada fruta em no estoque: >>> inventario = {abacaxis: 430, bananas: 312, laranjas: 525, peras: 217} >>> print inventario {laranjas: 525, abacaxis: 430, peras: 217, bananas: 312} Se alguem comprar todas as peras, podemos remover a entrada do dicionrio: >>> del inventario[peras] >>> print inventario {laranjas: 525, abacaxis: 430, bananas: 312} 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 12.2 10.2 Mtodos dos Dicionrios Um mtodo parecido com uma funo - possui parametros e retorna valores - mas a sintaxe diferente. Por exemplo, o metodo keys recebe um dicionrio e retorna uma lista com as chaves, mas em vez de usarmos a sintaxe de funo keys(ing2esp), nos usamos a sintaxe de mtodo ing2esp.keys(): >>> ing2esp.keys() [one, three, two] 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
  • 102. Aprenda Computao com Python Documentation, Verso 1.1 O mtodo values parecido; retorna a lista de valores de um dicionrio: >>> ing2esp.values() [uno, tres, dos] 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 12.3 10.3 Aliasing (XXX) e Copiar Uma vez que os dicionrios so mutveis, voce precisa saber sobre Aliasing. Sempre que duas variveis referenciarem o mesmo objeto, quando uma alterada, afeta a outra. Se voc quer modicar um dicionrio e continuar com uma copia original, utilize o mtodo copy. Por exemplo, opposites um dicionrio que contm pares de antnimos: >>> opposites = {up: down, right: wrong, true: false} >>> alias = opposities >>> copy = opposities.copy() 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 Se modicarmos copy, opposites no ser modicado: >>> copy[right] = privilege >>> opposites[right] left 12.4 10.4 Matrizes Esparsas Na seo 8.14, ns usamos uma lista de listas para representar uma matriz. Essa uma boa escolha se a matriz for principalmente de valores diferentes de zero, mas considerando uma matriz esparsa como essa: 12.3. 10.3 Aliasing (XXX) e Copiar 95
  • 103. Aprenda Computao com Python Documentation, Verso 1.1 Uma representao usando uma lista contem muitos zeros: >>> matriz = [ [0,0,0,1,0], [0,0,0,0,0], [0,2,0,0,0], [0,0,0,0,0], [0,0,0,3,0] ] 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 mtodo get resolve esse problema: >>> matriz.get((0,3), 0) 1 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 get denitivamente melhora a semntica e a sintaxe do acesso a matrizes esparsas. 12.5 10.5 Hint XXX Se voc brincou com a funo bonacci da seo 5.7, provvel que voc notou que quanto maior o nmero passado para a funo, mais tempo a funo demora para executar. Alm disso, o tempo da execuo aumenta rapidamente. 96 Captulo 12. Captulo 10: Dicionrios
  • 104. Aprenda Computao com Python Documentation, Verso 1.1 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
  • 105. Aprenda Computao com Python Documentation, Verso 1.1 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. 12.6 10.6 Inteiros Longos Python possui um tipo chamado long int que permite trabalharmos com qualquer tamanho de inteiros. Existem duas maneiras de criarmos um valor long int. A primeira escrever um inteiro seguido de um L no nal: >>> type(1L) 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. 12.7 10.7 Contando Letras No captulo 7, escrevemos uma funo que contava o nmero de ocorrncias de uma letra em uma string. A verso mais comum desse problema fazer um histograma das letras da string, ou seja, quantas vezes cada letra aparece na string. Um histograma pode ser util para comprimir um arquivo de texto. Pois diferentes letras aparecem com diferentes frequncias, podemos comprimir um arquivo usando pequenos cdigos para letras comuns e longos cdigos para letras que aparecem em menor frequncia. Dicionrios fornecem uma maneira elegante de gerar um histograma: 1 N.T. A partir do Python 2. XXX este erro no ocorre mais, pois em caso de sobrecarga o valor inteiro automaticamente promovido para o tipo long. 98 Captulo 12. Captulo 10: Dicionrios
  • 106. Aprenda Computao com Python Documentation, Verso 1.1 >>> 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. 12.8 10.8 Glossrio dicionrio (dictionary) Uma coleo de pares de chaves-valores que so mapeados pelas chaves, para se obter os valores. As chaves podem ser qualquer tipo de dados imutavel, e os valores podem ser de qualquer tipo. chave (key) Um valor que usado para buscar uma entrada em um dicionrio. par chave-valor (key-value pair) Um dos itens de um dicionrio. mtodo (method) Um tipo de funo que chamada com uma sintaxe diferente e invocada no contexto de um objeto. invocar (invoke) Chamar um mtodo. hint O armazenamento temporrio de um valor pr-computado para evitar a computao redundante. overow Um resultado numrico que muito grande para ser representado no formato numrico. 12.8. 10.8 Glossrio 99
  • 107. Aprenda Computao com Python Documentation, Verso 1.1 100 Captulo 12. Captulo 10: Dicionrios
  • 108. CAPTULO 13 Captulo 11: Arquivos e excees 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 13.1 Arquivos e excees Durante a execuo de um programa, seus dados cam na memria. Quando o programa termina, ou o computador desligado, os dados na memria desaparecem. Para armazenar os dados permanentemente, voc tem que coloc-los em um arquivo. Arquivos usualmente so guardados em um disco rgido (HD), num disquete ou em um CD-ROM. Quando existe um nmero muito grande de arquivos, eles muitas vezes so organizados dentro de diretrios (tambm chamados de ?pastas? ou ainda ?*folders*?). Cada arquivo identicado por um nome nico, ou uma combinao de um nome de arquivo com um nome de diretrio. Lendo e escrevendo em arquivos, os programas podem trocar informaes uns com os outros e gerar formatos imprimveis como PDF. Trabalhar com arquivos muito parecido com trabalhar com livros. Para utilizar um livro, voc tem que abr-lo. Quando voc termina, voc tem que fech-lo. Enquanto o livro estiver aberto, voc pode tanto l-lo quanto escrever nele. Em qualquer caso, voc sabe onde voc est situado no livro. Na maioria das vezes, voc l o livro inteiro em sua ordem natural, mas voc tambm pode saltar atravs de alguns trechos (skip around). Tudo isso se aplica do mesmo modo a arquivos. Para abrir um arquivo, voc especica o nome dele e indica o que voc quer, seja ler ou escrever (gravar). Abrir um arquivo cria um objeto arquivo. Neste exemplo, a varivel f se referencia ao novo objeto arquivo. 101
  • 109. Aprenda Computao com Python Documentation, Verso 1.1 >>> f = open("teste.dat", "w") >>> print f 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") Se tentarmos abrir um arquivo que no existe, temos um erro: >>> f = open("teste.cat", "r") IOError: [Errno 2] No such file or directory: teste.cat 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 Captulo 13. Captulo 11: Arquivos e excees
  • 110. Aprenda Computao com Python Documentation, Verso 1.1 texto = f1.read(50) if texto == "": break f2.write(texto) f1.close() f2.close() return 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. 13.2 11.1 Arquivos texto Um arquivo texto um arquivo que contm caracteres imprimveis e espaos, organizados dentro de linhas separadas por caracteres de nova linha. J que Pyhton especialmente projetado para processar arquivos texto, ele oferece mtodos que tornam esta tarefa mais fcil. Para demonstrar, vamos criar um arquivo texto com trs linhas de texto separadas por caracteres de nova linha: >>> f = open("teste.dat", "w") >>> f.write("linha umnlinha doisnlinha trsn") >>> f.close() 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 >>> readlines retorna todas as linhas restantes como uma lista de strings: >>> print f.readlines() [linha dois012, linha trs012] 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 == "": 13.2. 11.1 Arquivos texto 103
  • 111. Aprenda Computao com Python Documentation, Verso 1.1 break if texto[0] == #: continue f2.write(texto) f1.close() f2.close() return 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. 13.3 11.2 Gravando variveis O argumento de write tem que ser uma string, assim se quisermos colocar outros valores em um arquivo, temos de convert-los para strings primeiro. A maneira mais fcil de fazer isso com a funo str: >>> x = 52 >>> f.write(str(x)) 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 Captulo 13. Captulo 11: Arquivos e excees
  • 112. Aprenda Computao com Python Documentation, Verso 1.1 >>> "%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]) Para testar esta funo, criaremos um pequeno dicionrio e imprimiremos o contedo: >>> salarios = {maria: 6.23, joo: 5.45, josu: 4.25} >>> relatorio(salarios) joo 5.45 josu 4.25 maria 6.23 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. 13.4 11.3 Diretrios Quando voc cria um novo arquivo abrindo-o e escrevendo nele, o novo arquivo ca no diretrio corrente (seja l onde for que voc esteja quando rodar o programa). Do mesmo modo, quando voc abre um arquivo para leitura, Python procura por ele no diretrio corrente. Se voc quiser abrir um arquivo que esteja em algum outro lugar, voc tem que especicar o caminho (path) para o arquivo, o qual o nome do diretrio (ou folder) onde o arquivo est localizado: 13.4. 11.3 Diretrios 105
  • 113. Aprenda Computao com Python Documentation, Verso 1.1 >>> f = open("/usr/share/dict/words", "r") >>> print f.readline() Aarhus 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. 13.5 11.4 Pickling Para colocar valores em um arquivo, voc tem que convert-los para strings. Voc j viu como fazer isto com str: >>> f.write (str(12.3)) >>> f.write (str([1,2,3])) 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) >>> y = pickle.load(f) >>> y [1, 2, 3] >>> type(y) Cada vez que invocamos load, obtemos um nico valor do arquivo, completo com seu tipo original. 106 Captulo 13. Captulo 11: Arquivos e excees
  • 114. Aprenda Computao com Python Documentation, Verso 1.1 13.6 11.5 Excees Whenever que um erro em tempo de execuo acontece, ele gera uma exceo. Usualmente, o programa pra e Python exibe uma mensagem de erro. Por exemplo, dividir por zero gera uma exceo: >>> print 55/0 ZeroDivisionError: integer division or modulo Do mesmo modo, acessar um item de lista inexistente: >>> a = [] >>> print a[5] IndexError: list index out of range Ou acessar uma chave que no est em um dicionrio: >>> b = {} >>> print b[?what?] KeyError: what 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. 13.6. 11.5 Excees 107
  • 115. Aprenda Computao com Python Documentation, Verso 1.1 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. 13.7 11.6 Glossrio arquivo (le) Uma entidade nomeada, usualmente armazenada em um disco rgido (HD), disquete ou CD-ROM, que contm uma seqncia de caracteres. diretrio (directory) Uma coleo nomeada de arquivos, tambm chamado de pasta ou folder. caminho (path) Uma seqncia de nomes de diretrios que especica a exata localizao de um arquivo. arquivo texto (text le) Um arquivo que contm caracteres organizados em linhas separadas por caracteres de nova linha. comando break (break statement) Um comando que fora a atual iterao de um loop a terminar. O uxo de execuo vai para o topo do loop, testa a condio e prossegue conforme o caso. 108 Captulo 13. Captulo 11: Arquivos e excees
  • 116. CAPTULO 14 Captulo 12: Classes e objetos 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 14.1 12.1 Tipos compostos denidos pelo usurio Depois de usarmos alguns tipos nativos do Python, estamos prontos para criar um tipo de dados: o Ponto. Considere o conceito matemtico de um ponto. Em duas dimenses, um ponto um par de nmeros (coordenadas) que so tratadas coletivamente como um objeto simples. Na notao matemtica, pontos so freqentemente escritos entre parnteses com vrgula separando as coordenadas. Por exemplo, (0, 0) representa a origem, e (x, y) representa o ponto x unidades direita, e y unidades acima da origem. Uma maneira natural para representar um ponto em Python, com dois valores numricos em ponto utuante. A questo, ento, como agrupar estes dois valores em um objeto composto. A maneira rpida e rasteira usar uma lista ou uma tupla, e para algumas aplicaes, esso pode ser a melhor escolha 1 . Uma alternativa denir um novo tipo composto, tambm chamado uma classe. Esta abordagem envolve um pouco mais de esforo, mas ela tem vantagens que logo caro evidentes. Eis a denio de uma classe: 1 N.T.: A linguagem Python tambm incorpora um tipo nativo complex que representa nmeros complexos. Uma instncia de complex, como a=3+5j possui dois valores de ponto utuante em seus atributos a.real e a.imag, e pode ser utilizada para armazenar pontos em um espao bi-dimensional. 109
  • 117. Aprenda Computao com Python Documentation, Verso 1.1 class Ponto: pass 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. 14.2 12.2 Atributos Podemos adicionar novos dados em uma instncia usando a notao de ponto (dot notation): >>> final.x = 3.0 >>> final.y = 4.0 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 Captulo 14. Captulo 12: Classes e objetos
  • 118. Aprenda Computao com Python Documentation, Verso 1.1 print ( + str(final.x) + , + str(final.y) + ) distAoQuadrado = final.x * final.x + final.y * final.y 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 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. 14.3 12.3 Instncias como parmetros Voc pode passar uma instncia como um parmetro da forma usual. Por exemplo: def mostrarPonto(p): print ( + str(p.x) + , + str(p.y) + ) 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. 14.4 12.4 O signicado de mesmo O signicado da palavra mesmo parece perfeitamente claro at que voc pense a respeito, e ento voc percebe que h mais nesta palavra do que voc esperava. Por exemplo, se voc diz Cris e eu temos o mesmo carro, voc est dizendo que o carro de Cris e o seu so do mesmo fabricante e modelo, mas so dois carros diferentes. Se voc disser Cris e eu temos a mesma me, voc est dizendo que a me de Cris e a sua, so a mesma pessoa 2 . Portanto a idia de semelhana diferente dependendo do contexto. Quando falamos de objetos, h uma ambigidade similar. Por exemplo, se dois Pontos forem os mesmos, isto quer dizer que eles contm os mesmos dados (coordenadas) ou que so realmente o mesmo objeto? Para vericar se duas referncias se referem ao mesmo objeto, use o operador == 3 . Por exemplo: >>> >>> >>> >>> p1 = p1.x p1.y p2 = Ponto() = 3 = 4 Ponto() 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). 14.3. 12.3 Instncias como parmetros 111
  • 119. Aprenda Computao com Python Documentation, Verso 1.1 >>> p2.x = 3 >>> p2.y = 4 >>> p1 == p2 False 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. 14.5 12.5 Retngulos Digamos que desejemos uma classe para representar um retngulo. A questo , qual informao temos de prover para especicar um retngulo? Para manter as coisas simples, assuma que o retngulo orientado verticalmente ou horizontalmente, nunca em um ngulo. H algumas possibilidades: poderamos especicar o centro do retngulo (duas coordenadas) e seu tamanho (largura e altura); ou poderamos especicar um dos lados e o tamanho; ou poderamos especicar dois lados opostos. A escolha convencional especicar o canto superior esquerdo do retngulo e o tamanho. Novamente, vamos denir uma nova classe: class Rectangle: pass 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 Captulo 14. Captulo 12: Classes e objetos
  • 120. Aprenda Computao com Python Documentation, Verso 1.1 box.corner = Ponto() box.corner.x = 0.0; box.corner.y = 0.0; 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: 14.6 12.6 Instancias como valores retornados Funes podem retornar instncias. Por exemplo, ndCenter pega um Retngulo como um argumento e retorna um Ponto que contem as coordenadas do centro do retngulo: def findCenter(box): p = Ponto() p.x = box.corner.x + box.width/2.0 p.y = box.corner.y + box.height/2.0 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) 14.7 12.7 Objetos so mutveis Podemos mudar o estado de um objeto fazendo uma atribuio a um dos seus atributos. Por exemplo, para mudar o tamanho de um retngulo sem mudar sua posio, podemos modicar os valores de sua largura e altura. Veja: box.width = box.width + 50 box.height = box.height + 100 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 14.6. 12.6 Instancias como valores retornados 113
  • 121. Aprenda Computao com Python Documentation, Verso 1.1 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. 14.8 12.8 Copiando Ao usar alias - como zemos na seo anterior - podemos tornar o programa um pouco difcil de ler ou entender, pois as mudanas feitas em um local, podem afetar inesperadamente um outro objeto. E pode se tornar difcil de encontrar todas as variveis que podem afetar um dado objeto. Copiar um objeto freqentemente uma alternativa ao alias. O modulo copy contm uma funo chamada copy que duplica um qualquer objeto. Veja: >>> >>> >>> >>> >>> >>> 0 >>> 1 import copy p1 = Ponto() p1.x = 3 p1.y = 4 p2 = copy.copy(p1) p1 == p2 mesmoPonto(p1, p2) 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 Captulo 14. Captulo 12: Classes e objetos
  • 122. Aprenda Computao com Python Documentation, Verso 1.1 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. 14.9 12.9 Glossrio classe (class) Um tipo composto (XXX compound type) denido pelo usurio. Uma classe tambm pode ser visualizada como um molde que dene a forma dos objetos que sero suas instncias. instanciar (instantiate) Criar uma instncia de uma classe. instncia (instance) Um objeto que pertence a uma classe. objeto (object) Um tipo de dado composto comumente utilizado para representar uma coisa ou um conceito do mundo real. construtor (constructor) Um mtodo utilizado para criar novos objetos. atributo (attribute) Um dos itens de dados nomeados que compem uma instncia. igualdade rasa (shallow equality) Igualdade de referncias; ocorre quando duas referncias apontam para o mesmo objeto. igualdade profunda (deep equality) Igualdade de valores; ocorre quando duas referncias apontam para objetos que tm o mesmo valor. 14.9. 12.9 Glossrio 115
  • 123. Aprenda Computao com Python Documentation, Verso 1.1 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 14. Captulo 12: Classes e objetos
  • 124. CAPTULO 15 Captulo 13: Classes e funes 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 15.1 13.1 Horario Como exemplo de outro tipo denido pelo usurio, vamos denir uma classe chamada Horario que grava os registros de horrio do dia. Eis a denio da classe: class Horario: pass 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 O diagrama de estado para o objeto Horario parece com isso: 117
  • 125. Aprenda Computao com Python Documentation, Verso 1.1 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). 15.2 13.2 Funes Puras Nas prximas sesses, vamos escrever duas verses de uma funo chamada adicionaHorario, que calcula a soma de dois horrios. Elas vo demonstrar 2 tipos de funes: funes puras e funes modicadoras. Segue uma verso tosca de somaHorario: def somaHorario(h1, h2): soma = Horario() soma.horas = h1.horas + h2.horas soma.minutos = h1.minutos + h2.minutos soma.segundos = h1.segundos + h2.segundos return soma 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 >>> horarioTermino = somaHorario(horarioAtual, horarioDoPao) >>> imprimirHorario(horarioTermino) 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 Captulo 15. Captulo 13: Classes e funes
  • 126. Aprenda Computao com Python Documentation, Verso 1.1 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 15.3 13.3 Modicadores Existem momentos quando til para uma funo modicar um ou mais dos objetos que ela recebe como parmetro. Usualmente, quem est chamando a funo mantm uma referncia para os objetos que ele passa, de forma que quaisquer mudanas que a funo faz so visveis para quem est chamando. Funes que trabalham desta forma so chamadas modicadores. incrementar, que adiciona um nmero dado de segundos para um objeto Horario, que poderia ser escrito quase naturalmente como um modicador. Um rascunho tosco da funo seria algo parecido com isso: def incrementar(horario, segundos): horario.segundos = horario.segundos + segundos if horario.segundos >= 60: horario.segundos = horario.segundos - 60 horario.minutos = horario.minutos + 1 if horario.minutos >= 60: horario.minutos = horario.minutos - 60 horario.horas = horario.horas + 1 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 15.3. 13.3 Modicadores 119
  • 127. Aprenda Computao com Python Documentation, Verso 1.1 while horario.minutos >= 60: horario.minutos = horario.minutos - 60 horario.horas = horario.horas + 1 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 15.4 13.4 O que melhor ? Qualquer coisa que pode ser feita com modicadores tambm podem ser feitas com funes puras. De fato, algumas linguagens de programao permitem apenas funes puras. Existe alguma evidncia que programas que usam funes puras so desenvolvidos mais rapidamente e so menos propensos a erros que programas que usam modicadores. No entanto, modicadores as vezes so convenientes, e em alguns casos, programao funcional menos eciente. Em geral, recomendamos que voc escreva funes puras sempre que for necessrio e recorrer para modicadores somente se existir uma grande vantagem. Esta aproximao poderia ser chamada de um estilo de programao funcional. Clique aqui para feedback 15.5 13.5 Desenvolvimento Prototipado versus Desenvolvimento Planejamento Neste captulo, demonstramos uma aproximao para o desenvolvimento de programas que chamamos de desenvolvimento prototipado. Em cada caso, escrevemos um rascunho tosco (ou prototipo) que executou os clculos bsicos e ento, o testamos em uns poucos casos, corrigindo ento, as falhas que fomos encontrando. Embora esta aproximao possa ser ecaz, ela pode conduzir para cdigo que desnecessariamente complicado desde que trata de muitos casos especiais e unreliable desde que difcil saber se voc encontrou todos os erros. An alternative is planned development, in which high-level insight into the problem can make the programming much easier. In this case, the insight is that a Time object is really a three-digit number in base 60! The second component is the ones column, the minute component is the sixties column, and the hourcomponent is the thirty-six hundreds column. When we wrote addTime and increment, we were effectively doing addition in base 60, which is why we had to carry from one column to the next. This observation suggests another approach to the whole problem we can convert a Time object into a single number and take advantage of the fact that the computer knows how to do arithmetic with numbers. The following function converts a Timeobject into an integer: def converterParaSegundos(t): minutos = t.horas * 60 + t.minutos segundos = minutos * 60 + t.segundos return segundos 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 Captulo 15. Captulo 13: Classes e funes
  • 128. Aprenda Computao com Python Documentation, Verso 1.1 horario.minutos = segundos/60 segundos = segundos - horario.minutos * 60 horario.segundos = segundos return horario 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 15.6 13.6 Generalizao Algumas vezes, converter de base 60 para base 10 e voltar mais difcil do que simplesmente lidar com horrios. Converso de base mais abstrata; nossa intuio para lidar com horrios melhor. Mas se XXXX But if we have the insight to treat times as base 60 numbers and make the investment of writing the conversion functions (converterParaSeguntos e criarHorario), ns conseguimos um programa que menor, fcil de ler e depurar, e mais convel. tambm fcil para adicionar funcionalidades depois. Por exemplo, imagine subtrair dois Horarios para encontrar a durao entre eles. Uma aproximao ingnua seria implementar subtrao com emprstimo (?? borrowing - Isso mesmo ??). Usando as funes de converso ser mais fcil e provavelmente estar correto. Ironicamente, algumas vezes fazer um problema mais difcil (ou mais genrico) o torna mais simples (porque existem alguns poucos casos especiais e poucas oportunidades para errar). Clique aqui para feedback 15.7 13.7 Algoritmos Quando voc escreve uma soluo genrica para uma classe de problemas, ao contrrio de uma soluo especca para um nico problema, voc escreveu um algortmo. Ns mencionamos isso antes mas no denimos cuidadosamente. Isso no fcil para denir, ento ns vamos tentar //a couple of approaches//. Primeiramente, considere alguma coisa que no seja um algortmo. Quando voc aprendeu a multiplicar nmeros de um dgito, voc provavelmente memorizou a tabela de multiplicao. Como resultado, voc memorizou 100 solues especcas. Esse tipo de conhecimento no algortmo. Mas se voc preguioso, voc provavelmente trapaceou por ter aprendido alguns truques. Por exemplo, para encontrar o produto de n e 9, voc pode escrever n-1 como o primeiro dgito e 10-n como o segundo dgito. Este truque um soluo genrica para multiplicar qualquer nmero de um dgito por 9. Isso um algoritmo! De modo parecido, as tcnicas que voc aprendeu para adicionar //com transporte//, //subtraction with borrowing//, e diviso longa so todas algortmos. Uma das caractersticas dos algortmos que eles no requerem nenhuma inteligncia para serem executados ( carry out ). Eles so processos mecnicos no qual cada passo segue o ltimo de acordo com um conjunto simples de regras. Na nossa opinio, preocupante que humanos gastem tanto tempo na escola aprendendo a executar algoritmos que, literalmente, no requerem inteligncia. 15.6. 13.6 Generalizao 121
  • 129. Aprenda Computao com Python Documentation, Verso 1.1 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. 15.8 13.8 Glossrio funo pura (pure function) Uma funo que no modica nenhum dos objetos que ela recebe como parmetro. A maioria das funes puras frutfera. modicador (modier) Uma funo que muda um ou mais dos objetos que ela recebe como parmetros. A maioria dos modicadores nula. estilo de programao funcional (functional programming style) Um estilo de programao onde a maioria das funes so puras. desenvolvimento prototipado (prototype development) Uma maneira de desenvolver programas comeando com um prottipo e gradualmente melhorando-o. desenvolvimento planejado (planned development) Uma maneira de desenvolver programas que envolvem uma percepo de alto nvel do problema e mais planejamento do que desenvolvimento incremental ou desenvolvimento prototipado. algoritmo (algorithm) Um conjunto de instrues para resolver uma classe de problemas usando um processo mecnico, no inteligente. 122 Captulo 15. Captulo 13: Classes e funes
  • 130. CAPTULO 16 Captulo 14: Classes e mtodos 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... 16.1 14.1 Caractersticas da orientao a objetos Python uma linguagem de programao orientada a objetos, o que signica que ela tem caractersticas que suportam a programao orientada a objetos. No fcil denir programao orientada a objetos, mas temos visto already algumas de suas caractersticas: Programas so construdos sobre denies de objetos e denies de funes, e a maioria das computaes expressa em termos de operaes sobre objetos. Cada denio de objeto corresponde a algum objeto ou conceito do mundo real, e as funes que operam com aqueles objetos correspondem maneira como os objetos do mundo real interagem. Por exemplo, a classe Tempo, denida no captulo 13 corresponde maneira como as pessoas registram as horas do dia, e as funes que denimos correspondem aos tipos de coisas que as pessoas fazem com times. Do mesmo modo, as classes Ponto e Retngulo correspondem aos conceitos matemticos de um ponto e de um retngulo. At aqui, no tiramos vantagem das caractersticas fornecidas por Python que suportam a programao orientada a objetos. Estritamente falando, estas caractersticas no so necessrias. Na maior parte das vezes, elas fornecem uma sintaxe alternativa para as coisas que j zemos, mas em muitos casos, a alternativa mais concisa e convm mais acuradamente estrutura do programa. Por exemplo, no programa Time, no existe uma conexo bvia entre a denio da classe e a denio da funo que segue. Com alguma investigao, ca aparente que toda funo toma pelo menos um objeto Time como um parmetro. 123
  • 131. Aprenda Computao com Python Documentation, Verso 1.1 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. 16.2 14.2 exibeHora (printTime) No captulo 13, denimos uma classe chamada Horrio (Time) e voc escreveu uma funo chamada exibeHora (printTime), que deve ter cado mais ou menos assim: class Horario: pass def exibeHora(time) print str(time.horas) + ?:? + str(time.minutos) + ?:? + str(time.segundos) Para chamar esta funo, passamos um objeto Time como um parmetro: >>> >>> >>> >>> >>> horaCorrente = Hora() horaCorrente.horas = 9 horaCorrente.minutos = 14 horaCorrente.segundos = 30 exibeHora(horaCorrente) 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) Agora podemos chamar exibeHora usando a natao de ponto: >>> horaCorrente.exibeHora() 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
  • 132. Aprenda Computao com Python Documentation, Verso 1.1 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. 16.3 14.3 Um outro exemplo Vamos converter incremento (da Seo 13.3) em um mtodo. Para poupar espao, deixaremos de fora mtodos denidos previamente(anteriormente?), mas voc deve mant-los em sua verso: class Time: #previous method definitions here... def increment(self, segundos): self.seconds = seconds + self.seconds while self.segundos >= 60: self.seconds = self.segundos - 60 self.minutes = self.minutos + 1 while self.minutes >= 60: self.minutes = self.minutos - 60 self.hours = self.horas + 1 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?. 16.4 14.4 Um exemplo mais complicado ... 16.5 14.10 Glossrio linguagem orientada a objetos Uma linguagem que prov caractersticas tais como classes denidas pelo usurio e herana, que facilitam a programao orientada a objetos. programao orientada a objetos Um estilo de programao na qual os dados e as operaes que os manipulam esto organizados em classes e mtodos. mtodo Uma funo que denida dentro de uma denio de classe e chamada em instncias desta classe. 16.3. 14.3 Um outro exemplo 125
  • 133. Aprenda Computao com Python Documentation, Verso 1.1 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 (+, -, *, >, 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) O primeiro argumento, 0, representa o naipe de Paus. 17.3 15.3 Atributos de classe e o mtodo __str__ Para imprimir objetos Carta de uma maneira que as pessoas possam facilmente ler, ns gostaramos de mapear os cdigos inteiros para palavras. Uma forma natural de fazer isso usar listas de strings. Ns atribumos estas listas para atributos de classe no topo da denio de classe: class Carta: listaDeNaipes = ["Paus", "Ouros", "Copas", "Espadas"] listaDePosicoes = ["narf", "s", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Valete", "Rainha", "Rei"] # mtodo init omitido def __str__(self): 128 Captulo 17. Captulo 15: Conjuntos de objetos
  • 136. Aprenda Computao com Python Documentation, Verso 1.1 return (self.listaDePosicoes[self.posicao] + " de " + self.ListaDeNaipes[self.naipe]) 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 O problema que todos os Ouros se tornam Baleias Rodopiantes: >>> print carta2 Valete de Baleias Rodopiantes Normalmente, no uma boa idia modicar atributos de classe. 17.4 15.4 Comparando cartas Para tipos primitivos, existem operadores condicionais (, ==, etc.) que comparam valores e determinam quando um maior que, menor que ou igual a outro. Para tipos denidos pelo usurio, ns podemos sobrescrever o comportamento dos operadores pr-denidos fornecendo um mtodo __cmp__. Por conveno, __cmp__ recebe dois parmetros, self e other, e retorna 1 se o primeiro objeto for maior, -1 se o segundo objeto for maior, e 0 se eles forem iguais. Alguns tipos so totalmente ordenados, o que signica que ns podemos comparar quaisquer dois elementos e dizer qual o maior. Por exemplo, os inteiros e os nmeros de ponto utuante so totalmente ordenados. Alguns conjuntos so no-ordenados, o que signica que no existe maneira signicativa de dizer que um elemento maior que o outro. Por exemplo, as frutas so no-ordenadas, e por isso que no podemos comparar mas e laranjas. 17.4. 15.4 Comparando cartas 129
  • 137. Aprenda Computao com Python Documentation, Verso 1.1 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. 17.5 15.5 Baralhos Agora que ns temos objetos para representar Cartas, o prximo passo lgico denir uma classe para representar um Baralho. claro que um baralho formado por cartas; portanto, cada objeto Baralho ir conter uma lista de cartas como um atributo. A seguir, damos uma denio para a classe Baralho. O mtodo de inicializao cria o atributo cartas e gera o conjunto padro de 52 cartas: classe Baralho def __init__(self): self.cartas = [] for naipe in range(4): for posicao in range(1, 14): self.cartas.append(Carta(naipe, posicao)) 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. 17.6 15.6 Imprimindo o baralho Como sempre, quando ns denimos um novo tipo de objeto, ns gostaramos de ter um mtodo para imprimir o contedo de um objeto. Para imprimir um Baralho, ns percorremos a lista e imprimimos cada Carta: class Baralho: ... def imprimirBaralho(self): 130 Captulo 17. Captulo 15: Conjuntos de objetos
  • 138. Aprenda Computao com Python Documentation, Verso 1.1 for carta in self.cartas: print carta 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. 17.7 15.7 Embaralhando Se um baralho estiver perfeitamente embaralhado, ento cada carta tem a mesma probabilidade de aparecer em qualquer lugar no baralho, e qualquer localizao no baralho tem a mesma probabilidade de conter qualquer carta. 17.7. 15.7 Embaralhando 131
  • 139. Aprenda Computao com Python Documentation, Verso 1.1 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 >> baralho = Baralho() >>> baralho.embaralhar() >>> mao = Mao("fabio") >>> baralho.distribuir([mao], 5) >>> print mao Mo fabio contm 2 de espadas 3 de espadas 4 de espadas s de copas 9 de paus 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 vazian" else: return s + " contmn" + 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. 18.4. 16.4 Exibindo a mao 137
  • 145. Aprenda Computao com Python Documentation, Verso 1.1 18.5 16.5 A classe JogoDeCartas A classe JogoDeCartas toma conta de algumas tarefas bsicas comuns a todos os jogos, como, criar o baralho e embaralh-lo: class JogoDeCartas: def __init__(self): self.baralho = Baralho() self.baralho.embaralhar() 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... 18.6 16.6 Classe MaoDeMico Uma mo para jogar Mico requer algumas habilidades para alem das habilidades gerais de uma Mao. Vamos denir uma nova classe, MaoDeMico, que herda de Mao e prov um mtodo adicional chamado descartarCasais: class MaoDeMico(Mao): def descartarCasais(self): conta = 0 cartasIniciais = self.cartas[:] for carta in cartasIniciais: casal = Carta(3 - carta.naipe, carta.valor) if casal in self.cartas: self.cartas.remove(carta) self.cartas.remove(casal) print "Mao %s: %s casais %s" % (self.nome,carta,casal) conta = conta + 1 return conta 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
  • 146. Aprenda Computao com Python Documentation, Verso 1.1 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. 18.7 16.7 Classe Mico Agora podemos focar nossa ateno no jogo em si. Mico uma subclasse de JogoDeCartas com um novo mtodo chamado jogar que recebe uma lista de jogadores como argumento. J que __init__ herdado de JogoDeCartas, um novo objeto Mico contm um novo baralho embaralhado: class Mico(JogoDeCartas): def jogar(self, nomes): # remover a Dama de paus self.baralho.removerCarta(Carta(0,12)) # fazer uma mo para cada jogador self.maos = [] 18.7. 16.7 Classe Mico 139
  • 147. Aprenda Computao com Python Documentation, Verso 1.1 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 Captulo 18. Capitulo 16: Herana
  • 148. Aprenda Computao com Python Documentation, Verso 1.1 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 18.7. 16.7 Classe Mico 141
  • 149. Aprenda Computao com Python Documentation, Verso 1.1 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 Ento, o Jair perdeu. 18.8 16.8 Glossrio herana (inheritance) Habilidade de denir uma nova classe que a verso modicada de uma classe denida anteriormente. classe me (parent class) A classe de quem a classe lha herda. classe lho (child class) Um nova classe criada herdando de uma classe existente; tambm chamada de subclasse. 142 Captulo 18. Capitulo 16: Herana
  • 150. CAPTULO 19 Captulo 17: Listas encadeadas 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 19.1 17.1 Referncias Embutidas Ns temos visto exemplos de atributos que referenciam outros objetos, que so chamados referncias embutidas (veja a Seo 12.8). Uma estrutura de dados comum, a lista ligada, tira vantagem desta caracterstica. Listas ligadas so constitudas de ns (nodos), onde cada n contm uma referncia para o prximo n na lista. Alm disto, cada n contm uma unidade de dados chamada a carga. Uma lista ligada considerada uma estrutura de dados recorrente porque ela tem uma denio recorrente. Uma lista ligada : Uma lista vazia, representada por None, ou Um n que contm um objeto carga e uma referncia para uma lista ligada. Estruturas de dados recorrentes so adequadas para mtodos recorrentes. 143
  • 151. Aprenda Computao com Python Documentation, Verso 1.1 19.2 17.2 A classe No (Node) Como usual quando se escreve uma nova classe, ns comearemos com os mtodos de inicializao e __str__ de modo que podemos testar o mecanismo bsico de se criar e mostrar o novo tipo: class No: def __init__(self, carga=None, proximo=None): self.carga = carga self.proximo = proximo def __str__(self): return str(self.carga) 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 Para car interessante, ns precisamos uma lista com mais do que um n: >>> no1 = No(1) >>> no2 = No(2) >>> no3 = No(3) 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 Captulo 19. Captulo 17: Listas encadeadas
  • 152. Aprenda Computao com Python Documentation, Verso 1.1 Agora voc sabe como criar ns e lig-los em uma lista. O que pode estar menos claro neste ponto por qu. 19.3 17.3 Listas como Colees Listas so teis porque elas provm um modo de montar mltiplos objetos em uma nica entidade, algumas vezes chamada uma coleo. No exemplo, o primeiro n da lista serve como uma referncia para toda a lista. Para passar uma lista como um parmetro, voc apenas tem que passar uma referncia ao primeiro n. Por exemplo, a funo imprimeLista toma um nico n como um argumento. Iniciando com o cabea da lista, ela imprime cada n at que chegue ao m: def imprimeLista(no): while no: print no, no = no.proximo print Para chamar este mtodo, ns passamos uma referncia ao primeiro no: >>> imprimeLista(no1) 1 2 3 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. 19.3. 17.3 Listas como Colees 145
  • 153. Aprenda Computao com Python Documentation, Verso 1.1 19.4 17.4 Listas e Recorrncia natural expressar muitas operaes de listas utilizando mtodos recorrentes. Por exemplo, o seguinte um algoritmo recorrente para imprimir uma lista de trs para frente. 1. Separe a lista em dois pedaos: o primeiro n (chamado a cabea); e o resto (chamado o rabo). 2. Imprima o rabo de trs para frente. 3. Imprima a cabea. Logicamente, o Passo 2, a chamada recorrente, assume que ns temos um modo de imprimir a lista de trs para frente. Mas se ns assumimos que a chamada recorrente funciona o passo de f ento podemos nos convencer de que o algoritmo funciona. Tudo o que precisamos so um caso base e um modo de provar que para qualquer lista, ns iremos, ao nal, chegar no caso base. Dada a denio recorrente de uma lista, um caso base natural a lista vazia, representada por None: def imprimeDeTrasParaFrente(lista): if lista == None : return cabeca = lista rabo = lista.proximo imprimeDeTrasParaFrente(rabo) print cabeca, 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. 19.5 17.5 Listas Innitas No h nada que impea um n de referenciar de volta um n anterior na lista, incluindo ele mesmo. Por exemplo, esta gura mostra uma lista com dois ns, um dos quais refere-se a si mesmo: 146 Captulo 19. Captulo 17: Listas encadeadas
  • 154. Aprenda Computao com Python Documentation, Verso 1.1 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. 19.6 17.6 O Teorema da Ambigidade Fundamental Uma parte de imprimeDeTrasParaFrente pode ter gerado surpresa: cabeca = lista rabo = lista.proximo 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: 19.6. 17.6 O Teorema da Ambigidade Fundamental 147
  • 155. Aprenda Computao com Python Documentation, Verso 1.1 Uma varivel que refere a um n pode tratar o n como um objeto nico ou como o primeiro em uma lista de ns. 19.7 17.7 Modicando Listas Existem duas maneiras de se modicar uma lista ligada. Obviamente, podemos modicar a carga dos ns, mas as operaes mais interessantes so aquelas que adicionam, removem ou reordenam os ns. Como um exemplo, vamos escrever um mtodo que remove o segundo n na lista e retorna uma referncia ao n removido: def removeSegundo(lista): if lista == None : return primeiro = lista segundo = lista.proximo # faz o primeiro no referir ao terceiro primeiro.proximo = segundo.proximo # separa o segundo no do resto da lista segundo.proximo = None return segundo 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) Este diagrama de estado mostra o efeito da operao: 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. 19.8 17.8 Envoltrios e Ajudadores Freqentemente til dividir uma operao de lista em dois mtodos. Por exemplo, para imprimir uma lista de trs para frente no formato convencional de lista [3, 2, 1], podemos usar o mtodo imprimeDeTrasParaFrente para 148 Captulo 19. Captulo 17: Listas encadeadas
  • 156. Aprenda Computao com Python Documentation, Verso 1.1 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. 19.9 17.9 A Classe ListaLigada Existem alguns problemas sutis com o modo que implementamos listas. Em um inverso de causa e efeito, proporemos uma implementao alternativa primeiro e ento explicaremos qual problema ela resolve. Primeiro, criaremos uma nova classe chamada ListaLigada. Seus atributos so um inteiro que contm o comprimento da lista e uma referncia para o primeiro n. Objetos do tipo ListaLigada servem como cabos (handles) para se manipular listas de objetos No: class ListaLigada: def __init__(self): self.comprimento = 0 self.cabeca = None 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 19.9. 17.9 A Classe ListaLigada 149
  • 157. Aprenda Computao com Python Documentation, Verso 1.1 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? 19.10 17.10 Invariantes Algumas listas so bem formadas; outras no o so. Por exemplo, se uma lista contm um lao, ela far muitos de nossos mtodos falharem, de modo que podemos querer requerer que listas no contenham laos. Outro requerimento que o valor de comprimento no objeto ListaLigada seja igual ao nmero real de ns da lista. Requerimentos como estes so chamados de invariantes porque, idealmente, eles deveriam ser verdade para cada objeto o tempo todo. Especicar invariantes para objetos um prtica de programao til porque torna mais fcil provar a correo do cdigo, vericar a integridade das estruturas de dados e detectar erros. Uma coisa que algumas vezes confusa acerca de invariantes que existem momentos em que eles so violados. Por exemplo, no meio de adicionaPrimeiro, aps termos adicionado o n mas antes de termos incrementado comprimento, o invariante violado. Este tipo de violao aceitvel; de fato, freqentemente impossvel modicar um objeto sem violar um invariante por, no mnimo, um pequeno instante. Normalmente, requeremos que cada mtodo que viola um invariante deve restaurar este invariante. Se h qualquer aumento signicativo de cdigo no qual o invariante violado, importante tornar isto claro nos comentrios, de modo que nenhuma operao seja feita que dependa daquele invariante. 19.11 17.11 Glossrio referncia embutida (embedded reference) Uma referncia armazenada/associada em/a um atributo de um objeto. lista ligada (linked list) Uma estrutura de dados que implementa uma coleo usando uma sequncia de ns ligados. n ou nodo (node) Um elemento de uma lista, usualmente implementado como um objeto que contm uma referncia para outro objeto do mesmo tipo. carga (cargo) Um item de dado contido em um n. ligao (link) Uma referncia embutida usada para ligar/conectar um objeto a outro. pr-condio (precondition) Uma assero que precisa/deve ser verdadeira para que um mtodo trabalhe corretamante. teorema da ambigidade fundamental (fundamental ambiguity theorem) Uma referncia para um n de uma lista pode ser tratada como um objeto nico ou como o primeiro em uma lista de ns. singleton (singleton) Uma lista ligada com somente um n. 150 Captulo 19. Captulo 17: Listas encadeadas
  • 158. Aprenda Computao com Python Documentation, Verso 1.1 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). 19.11. 17.11 Glossrio 151
  • 159. Aprenda Computao com Python Documentation, Verso 1.1 152 Captulo 19. Captulo 17: Listas encadeadas
  • 160. CAPTULO 20 Captulo 18: Pilhas 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 20.1 18.1 Tipos abstratos de dados Os tipos de dados que voc viu at agora so todos concretos, no sentido que ns especicamos completamente como eles so implementados. Por exemplo, a classe Card(XXX ver como foi traduzido) representa uma carta utilizando dois inteiros. Como discutimos no momento, esta no a nica maneira de representar uma carta; existem muitas implementaes alternativas. Um tipo abstrato de dado, ou TAD, especica um conjunto de operaes (ou mtodos) e a semntica das operaes (o que elas fazem), mas no especica a implementao das operaes. Isto o que o faz abstrato. Por que isto til? Simplica a tarefa dde especicar um algoritmo se voc pode XXXdenotar(denote) as operaes que voc precisa sem ter que pensar, ao mesmo tempo, como as operaes so executadas. Uma vez que existem geralmente muitas maneiras de implementar um TAD, pode ser til escrever um algritmo que pode ser usado com qualquer das possveis implementaes. TADs bastante conhecidos, como o TAD Pilha deste captulo, j esto implementados em bibliotecas padro, ento eles podem ser escritos uma vez e usado por muitos programadores. As operaes em TADs provm uma linguagem de alto nvel comum para especicar e falar sobre algoritmos. Quando falamos sobre TADs, geralmente distinguimos o cdigo que usa o TAD, chamado cliente, do cdigo que implementa o TAD, chamado cdigo fornecedor. 153
  • 161. Aprenda Computao com Python Documentation, Verso 1.1 20.2 18.2 O TAD Pilha Neste captulo, iremos olhar um TAD comum, a pilha. Uma pilha uma coleo, ou seja, uma estrutura de dados que contei mltiplos elementos. Outras colees que vimos incluem dicionrios e listas. Um TAd denido pelo conjunto de operaes que podem ser executadas nele, que chamado interface. A interface para uma pilha consiste nestas operaes: __init__ : Inicializa uma nova pilha vazia. push : Adiciona um novo item na pilha pop : Remove um tem da pilha e o retorna, O tem que retornadao sempre o ltimo adicionado. isEmpty : Verica se a pilha est vazia. Uma s vezes chamada uma estrutura de dados last in, rst out ou LIFO, porque o ltimo tem adicionad o primeiro a ser removido. 20.3 18.3 Implementando pilhas com listas de Python As operaes de lista que Python oferecem so similares s operaes que denem uma pilha. A interface no exatamente o que se supe ser, mas podemos escrever um cdigo para traduzir do TAD Pilha para as operaes nativas. Este cdigo chamado uma implementao do TAD Pilha. Geralmente, uma implementaa um conjunto de mtodos que satisfazem os requisitos sintticos e semnticos de uma interface. Aqui est uma implementao do TAD Pilha que usa uma lista do Python: class Stack : def __init__(self) : self.items = [] def push(self, item) : self.items.apend(item) def pop(self) : return self.items.pop() def isEmpty(self) : return (self.items == []) 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 Captulo 20. Captulo 18: Pilhas
  • 162. Aprenda Computao com Python Documentation, Verso 1.1 20.4 18.4 Empilhando e desempilhando Uma pilha uma estrutura de dados genrica, o que signica que podemos adicionar qualquer tipo de tem a ela. O exemplo a seguir empilha dois inteiros e uma XXXstring na pilha: >>> >>> >>> >>> s = Stack() s.push(54) s.push(45) s.push("+") 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. 20.5 18.5 Usando uma pilha para avaliar expresses ps-xas Em muitas linguagens de programao, expresses matemticas so executadas com o poerador entre os roid operandos, como em 1 + 2. Este formato chamado notao inxa. Uma alternativa usada por algumas calculadoras chamada notao ps-xa. Em notao ps-xa, o operador segue os operandos, como em 1 2 +. A razo pela qual a notao ps-xa til algumas vezes que existe uma maneira natural de avaliar uma expresso ps-xa usando uma pilha: comeando no incio da expresso, peque um termo (operador ou operando) de cada vez. Se o termo um operando, empilhe-o Se o termo um operador, desempilhe dois operandos, execute a operao neles, e empilhe o resultado. Quando chegar ao m da expresso, deve existir exatamente um operando sobrando na pilha. Este operando o resultado. Como um exerccio, aplique este algoritmo expresso 1 2 + 3 * . Este exemplo demonstra uma das vantagens da notao ps-xa - no necessrio usar parnteses para controlar a ordem das operaes. Para ter o mesmo resultado em notao inxa, deveramos escrever (1 + 2) * 3. Como um exerccio, escreva a expresso ps-xa que equivalente a 1 + 2 * 3. 20.6 18.6 Anlise sinttica Para implementar o algoritmo anterior, necessitamos estar prontos para percorrer uma string e quebr-la em operandos e operadores. Este process um exemplo de XXXparsing, e o resultado - os pedaos da string - so chamados XXXtokens. Voc deve lembrar estas palavras do captulo 1. Python fornece um mtodo split nos mdulos string e re (expresses regulares). A funo string.split separa uma string numa lista usando um nico caracter como delimitador. Por exemplo: 20.4. 18.4 Empilhando e desempilhando 155
  • 163. Aprenda Computao com Python Documentation, Verso 1.1 >>> 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. 20.7 18.7 Avaliando em ps-xo. Para avaliar uma expresso ps-xa, usaremos o parser da seo anterior e o algoritmo da seo anterior a ela. Para manter as coisas simples, comearemos com um avaliador que implementa somente os operadores + e . def evalPostfix (expr) : import re tokenList = re.split ("([^0-9])", expr) stack = Stack() for token in tokenList if token == or token = : continue if token == + : sum = stack.pop() + stack.pop() stack.push(sum) if token == * : product = stack.pop() * stack.pop() stack.push(product) else: stack.push(int(token)) return stack.pop() 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 Captulo 20. Captulo 18: Pilhas
  • 164. Aprenda Computao com Python Documentation, Verso 1.1 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. 20.7.1 18.9 Glossrio tipo abstrato de dados (TAD) (abstract data type(ADT)): Um tipo de dado(geralmente uma coleo de objetos) que denidopor um conjunto de operaes, que podem ser implementadas de vrias maneiras. interface (interface): o conjunto de operaes que denem um TDA. implementao (implementation): Cdigo que satisfaz(preenche?) os requisitos sintticos e semnticos de uma interface. cliente (client): Um programa (ou o programador que o escreveu) que faz utilizao de um TDA. fornecedor (provider): Cdigo (ou o programador que o escreveu) que implementa um TDA. revestimento (veneer): Denio de classe que implementa um TDA com denies de mtodos que so chamadas a outros mtodos, s vezes com pequenas modicaes. A lmina faz um trabalho insignicante, mas melhora ou padroniza a interface dada ao cliente. estrutura de dados genrica (generic data structure): Tipo de estrutura de dados que contem data de um tipo qualquer(tipo genrico). inxa (inx): Notao matemtica em que os operadores se situam entre os operandos. ps-xa (postx): Notao matemtica em que os operadores se situam aps os operandos. XXX parse (parse): Ler um conjunto de caracteres(string de caracteres) ou tokens(smbolos atmicos) e analisar sua estrutura gramatical. XXX token (token): Conjunto de caracteres que so tratados como uma unidade atmica para ns de anlise gramatical, como as palavras na linguagem natural. delimitador (delimiter): Um caracter que utilizado para separar os smbolos atmicos(tokens), como a pontuao na linguagem natural. 20.7. 18.7 Avaliando em ps-xo. 157
  • 165. Aprenda Computao com Python Documentation, Verso 1.1 158 Captulo 20. Captulo 18: Pilhas
  • 166. CAPTULO 21 Captulo 19: Filas 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. 21.1 19.1 Um TDA Fila O TDA Fila denido pelas seguintes operaes: __init__ Inicializar uma nova la vazia. insert Adicionar um novo item la. remove Remover e retornar um item da la. O item retornado o que foi adicionado primeiro. isEmpty Checar se a la est vazia. 21.2 19.2 Fila encadeada A primeira implementao que vamos ver de um TDA Fila chama-se la encadeada porque feita de objetos Ns encadeados. A denio da classe a seguinte: 159
  • 167. Aprenda Computao com Python Documentation, Verso 1.1 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. 21.3 19.3 Caractersticas de performance Quando invocamos um mtodo, normalmente no estamos preocupados com os detalhes da sua implementao. Porm, h um certo detalhe que pode ser bom conhecer: as caractersticas de performance do mtodo. Quanto tempo leva, e como o tempo de execuo muda medida em que aumenta o nmero de itens da coleo? Primeiro, olhe para remove. No h laos ou chamadas de funo aqui, o que sugere que o tempo de execuo desse mtodo sempre o mesmo, toda vez. Um mtodo assim chamado de operao de tempo constante. Na verdade, o mtodo pode ser ligeiramente mais rpido quando a lista est vazia, uma vez que ele pula o corpo da condicional, mas essa diferena no signicativa. XXX: o condicional s aparece na re-implementao do mtodo na classe ImprovedQueue, p.200; essa inconsistncia pode ser conferida tambm nas pginas 198-199 do livro original (PDF e impresso). A performance de insert muito diferente. No caso geral, temos de percorrer a lista para achar o ltimo elemento. Este percurso leva um tempo proporcional extenso da lista. Uma vez que o tempo de execuo uma funo linear da extenso, dizemos que este mtodo opera em tempo linear. Isso bem ruim, se comparado com o tempo constante. 160 Captulo 21. Captulo 19: Filas
  • 168. Aprenda Computao com Python Documentation, Verso 1.1 21.4 19.4 Fila encadeada aprimorada Queremos uma implementao do TDA Fila que possa realizar todas as operaes em tempo constante. Uma maneira de fazer isso modicar a classe Fila, de modo que ela mantenha a referncia tanto ao primeiro quanto ao ltimo n, como mostra a gura: A implementao de ImprovedQueue tem essa cara: class ImprovedQueue: def __init__(self): self.length = 0 self.head = None self.last = None def isEmpty(self): return (self.length == 0) 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: 21.4. 19.4 Fila encadeada aprimorada 161
  • 169. Aprenda Computao com Python Documentation, Verso 1.1 self.last = None return cargo 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. 21.5 19.5 Fila por prioridade O TDA Fila por Prioridade tem a mesma interface que o TDA Fila, mas semntica diferente. Mais uma vez, a interface a seguinte: __init__ Inicializar uma nova la vazia. insert Adicionar um novo item la. remove Remover e retornar um item da la. O item retornado aquele que tiver maior prioridade. isEmpty Checar se a la est vazia. A diferena semntica que o item removido da la no necessariamente o que foi includo primeiro e, sim, o que tem maior prioridade. Que prioridades so essas e como elas se comparam umas com as outras no especicado pela implementao Fila por Prioridade. Isso depende de quais itens esto na la. Por exemplo, se os itens da la tiverem nome, podemos escolh-los por ordem alfabtica. Se for a pontuao de um jogo de boliche, podemos ir da maior para a menor, mas se for pontuao de golfe, teramos que ir da menor para a maior. Se possvel comparar os itens da la, possvel achar e remover o que tem maior prioridade. Essa implementao da Fila por Prioridade tem como atributo uma lista Python chamada items, que contm os itens da la. class PriorityQueue: def __init__(self): self.items = [] def isEmpty(self): return self.items == [] def insert(self, item): self.items.append(item) 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
  • 170. Aprenda Computao com Python Documentation, Verso 1.1 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. 21.6 19.6 A classe Golfer Como exemplo de um objeto com uma denio no-usual de prioridade, vamos implementar uma classe chamada Golfer (golsta), que mantm registro dos nomes e da pontuao de golstas. Como sempre, comeamos denindo __init__ e __str__: class Golfer: def __init__(self, name, score): self.name = name self.score= score def __str__(self): return "%-16s: %d" % (self.name, self.score) 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) 21.6. 19.6 A classe Golfer 163
  • 171. Aprenda Computao com Python Documentation, Verso 1.1 >>> 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. 21.7 19.7 Glossrio la (queue) Conjunto de objetos ordenados esperando algum tipo de servio. Fila (Queue) TAD (Tipo Abstrato de Dado) que realiza operaes comuns de acontecerem em uma la. poltica de enleiramento (queueing policy) As regras que determinam qual membro de uma la o prximo a ser removido. FIFO First In, First Out, (primeiro a entrar, primeiro a sair) poltica de enleiramento em que o primeiro membro a chegar o primeiro a ser removido. la por prioridade (priority queue) Poltica de enleiramento em que cada membro tem uma prioridade, determinada por fatores externos. O membro com a maior prioridade o primeiro a ser removido. Fila por Prioridade (Priority Queue) TAD que dene as operaes comuns de acontecerem em uma la por prioridade. la encadeada (linked queue) Implementao de uma la usando uma lista encadeada. tempo constante (constant time) Operao cujo tempo de execuo no depende do tamanho da estrutura de dados. tempo linear (linear time) Operao cujo tempo de execuo uma funo linear do tamanho da estrutura de dados. 164 Captulo 21. Captulo 19: Filas
  • 172. CAPTULO 22 Captulo 20: rvores 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
  • 173. Aprenda Computao com Python Documentation, Verso 1.1 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. 22.1 20.1 Construindo rvores O processo de montar uma rvore similar ao processo de montar uma lista ligada. cada invocao do construtor cria uma clula. 166 Captulo 22. Captulo 20: rvores
  • 174. Aprenda Computao com Python Documentation, Verso 1.1 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) Em seguida crie a clula pai e ligue ela a seus lhos: tree = Tree(1, left, right); Podemos escrever este cdigo mais concisamente encaixando as invocaes do construtor: >>> tree = Tree(1, Tree(2), Tree(3)) De qualquer forma, o resultado a rvore que apareceu no incio do captulo. 22.2 20.2 Percorrendo rvores Cada vez que Voc v uma nova estrutura de dados, sua primeira pergunta deveria ser Como eu percorro esta estrutura? A forma mais natural de percorrer uma rvore fazer o percurso recursivamente. Por exemplo, se a rvore contm inteiros na carga, a funo abaixo retorna a soma das cargas: def total(tree) : if tree == None : return 0 return total(tree.left) + total(tree.right) + tree.cargo 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. 22.3 20.3 rvores de expresses Uma rvore uma forma natural para representar a estrutura de uma expresso. Ao contrrio de outras notaes, a rvore pode representar a computao de forma no ambgua. Por exemplo, a expresso inxa 1 + 2 * 3 ambgua, a menos que saibamos que a multiplicao feita antes da adio. A rvore de expresso seguinte representa a mesma computao: 22.2. 20.2 Percorrendo rvores 167
  • 175. Aprenda Computao com Python Documentation, Verso 1.1 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. 22.4 20.4 Percurso de rvores Podemos percorrer uma rvore de expresso e imprimir o seu contedo como segue: def printTree(tree) : if tree == None : return print tree.cargo, printTree(tree.left) printTree(tree.right) 168 Captulo 22. Captulo 20: rvores
  • 176. Aprenda Computao com Python Documentation, Verso 1.1 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 22.4. 20.4 Percurso de rvores 169
  • 177. Aprenda Computao com Python Documentation, Verso 1.1 + 1 Se Voc deitar a sada acima Voc enxerga uma verso simplicada da gura original. 22.5 20.5 Construindo uma rvore de expresso Nesta seo analisamos expresses inxas e construmos as rvores de express correspondentes. Por exemplo, para a expresso (3+7)*9 resultar a seguinte rvore: Figura 3 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 Captulo 22. Captulo 20: rvores
  • 178. Aprenda Computao com Python Documentation, Verso 1.1 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 : 22.5. 20.5 Construindo uma rvore de expresso 171
  • 179. Aprenda Computao com Python Documentation, Verso 1.1 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 # this line changed 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 Captulo 22. Captulo 20: rvores
  • 180. Aprenda Computao com Python Documentation, Verso 1.1 else : return a Vamos testar o algoritmo com 9 * 11 + 5 * 7: >>> tokenList = [9, *, 11, +, 5, *, 7, end] >>> tree = getSum(tokenList) >>> printTreePostorder(tree) 9 11 * 5 7 * + 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 Testemos este cdigo com 9 * (11 + 5) * 7: >>> tokenList = [9, *, (, 11, +, 5, ), *, 7, end] >>> tree = getSum(tokenList) >>> printTreePostorder(tree) 9 11 5 + 7 * * 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. 22.6 20.6 Manipulando erros Ao longo do analisador sinttico tnhamos suposto que as expresses (de entrada) so bem formadas. Por exemplo, quando atingimos o m de uma subexpresso, supomos que o prximo caractere um facha parntesis. Caso haja um erro e o prximo caractere seja algo diferente, devemos tratar disto. def getNumber(tokenList) : if getToken(tokenList, () : x = getSum(tokenList) if not getToken(tokenList, )): raise BadExpressionError, missing parenthesis return x else : # the rest of the function omitted 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
  • 181. Aprenda Computao com Python Documentation, Verso 1.1 22.7 20.7 A rvore dos animais Nesta seo, desenvolvemos um pequeno programa que usa uma rvore para representar uma base de conhecimento. O programa interage com o usurio para criar uma rvore de perguntas e de nomes de animais. Segue uma amostra da funcionalidade: Are you thinking of an animal? y Is it a bird? n What is the animals name? dog What question would distinguish a dog from a bird? Can it fly If the animal were dog the answer would be? n Are you thinking of an animal? y Can it fly? n Is it a dog? n What is the animals name? cat What question would distinguish a cat from a dog? Does it bark If the animal were cat the answer would be? n Are you thinking of an animal? y Can it fly? n Does it bark? y Is it a dog? y I rule! Are you thinking of an animal? n Aqui est a rvore que este dilogo constri: Figura 5 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 Captulo 22. Captulo 20: rvores
  • 182. Aprenda Computao com Python Documentation, Verso 1.1 Aqui est o cdigo: def animal() : # start with a singleton root = Tree("bird") # loop until the user quits while 1 : print if not yes("Are you thinking of an animal? ") : break # walk the tree tree = root while tree.getLeft() != None : prompt = tree.getCargo() + "? " if yes(prompt): tree = tree.getRight() else: tree = tree.getLeft() # make a guess guess = tree.getCargo() prompt = "Is it a " + guess + "? " if yes(prompt) : print "I rule!" continue # get new information prompt = "What is the animals name? " animal = raw_input(prompt) prompt = "What question would distinguish a %s from a %s? " question = raw_input(prompt % (animal,guess)) # add new information to the tree tree.setCargo(question) prompt = "If the animal were %s the answer would be? " if yes(prompt % animal) : tree.setLeft(Tree(guess)) tree.setRight(Tree(animal)) else : tree.setLeft(Tree(animal)) tree.setRight(Tree(guess)) 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! 22.7. 20.7 A rvore dos animais 175
  • 183. Aprenda Computao com Python Documentation, Verso 1.1 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. 22.8 20.8 Glossrio rvore binria (binary tree) Uma rvore em que cada clula tem zero, um ou dois descendentes. raiz (root) A clula mais alta de uma rvore, a (nica) clula de uma rvore que no tem pai. folha (leaf ) Uma clula mais baixa numa rvore; uma clula que no tem descendentes. pai (parent) A clula que aponta para uma clula dada. lho (child) Uma clula apontada por uma clula dada. irmos (siebling) Clulas que tem o mesmo pai. nvel (level) Um conjunto de clulas equidistantes da raiz. operador binrio (binary operator) Um operador sobre dois operandos. subexpresso (subexpression) Uma expresso entre parntesis que se comporta como um operando simples numa expresso maior. pr-ordem (preorder) Uma forma de percorrer uma rvore visitando cada clula antes dos seus lhos. notao prexa (prex notation) Uma forma de escrever uma expresso matemtica em que cada operador aparece antes dos seus operandos. ps-ordem (postorder) Uma forma de percorrer uma rvore visitando os lhos de cada clula antes da prpria clula. in-ordem (inorder) Uma forma de percorrer uma rvore visitando a subrvore esquerda, seguida da raiz e nalmente da subrvore direita. 176 Captulo 22. Captulo 20: rvores
  • 184. 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
  • 185. Aprenda Computao com Python Documentation, Verso 1.1 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 A.1 Erros de sintaxe Erros de sintaxe so geralmente fceis de corrigir, bastando apenas que voc descubra onde eles esto. Infelizmente, as mensagens de erro geralmente ajudam pouco. As mensagens mais comuns so: SyntaxError: invalid syntax (Erro de sintaxe: sintaxe invlida) e SyntaxError: invalid token (Erro de sintaxe: objeto invlido), nenhuma das duas muito informativa. Por outro lado, a mensagem diz a voc onde, no cdigo do programa, ocorreu o problema. Na verdade, ela diz a voc onde o Python encontrou o problema, que no necessariamente onde o erro est. s vezes o erro anterior localizao da mensagem de erro, geralmente na linha precedente. Se voc est construindo o programa incrementalmente, voc ter uma boa ideia sobre onde estar o erro. Estar na ltima linha adicionada. Se voc est copiando cdigo de um livro, comece comparando seu cdigo ao cdigo do livro de forma muito cuidadosa. Verique cada caracter. Ao mesmo tempo, lembre-se que o livro pode estar errado, ento voc pode perfeitamente encontrar um erro de sintaxe em um livro. Aqui esto algumas maneiras de evitar os erros de sintaxe mais comuns: 1. Certique-se que voc no est utilizando uma palavra reservada de Python para um nome de varivel. 2. Verique a existncia do sinal de dois pontos no nal do cabealho de cada declarao composta, incluindo as declaraes for, while, if, e def. 3. Verique se a endentao est consistente. Voc pode endentar com espaos ou com tabulaes, mas melhor no mistur-los. Cada nvel deve ser aninhado com a mesma quantidade. 4. Assegure-se de que cada string no cdigo tenha as aspas correspondentes. 5. Se voc tem strings de multilinhas criadas usando trs aspas (simples ou duplas), assegure-se de que voc terminou a string apropriadamente. Uma string terminada de forma inapropriada ou no terminada pode gerar um erro de invalid token (objeto invlido) no nal do seu programa, ou ele pode tratar a parte seguinte do programa como uma string at chegar prxima string. No segundo caso, pode ser que o interpretador nem mesmo produza uma mensagem de erro! 6. Um conjunto de parnteses, colchetes ou chaves no fechados corretamente faz com que o Python continue com a prxima linha como parte da declarao anterior. Geralmente, um erro ocorre quase imediatamente na linha seguinte. 7. Verique o clssico = ao invs de == dentro de uma condicional. Se nada funcionar, v para a prxima seo. 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 Captulo 23. Apndice A: Depurao
  • 186. Aprenda Computao com Python Documentation, Verso 1.1 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. 23.2 A.2 Erros de tempo de execuo Uma vez que seu programa est sintaticamente correto, o interpretador do Python pode import-lo e comear a executlo. O que poderia dar errado? 23.2.1 A.2.1 Meu programa no faz absolutamente nada Este o problema mais comum quando um arquivo consiste de funes e classes mas no chama nada realmente para iniciar a execuo. Isto pode ser intencional se voc planeja apenas importar este mdulo para fornecer classes e funes. Se isto no intencional, assegure-se de que voc est chamando uma funo para iniciar a execuo ou execute uma manualmente do prompt interativo. Veja tambm a seo Fluxo de Execuo abaixo. 23.2.2 A.2.2 Meu programa trava Se um programa para e parece no estar fazendo nada, dizemos que ele travou. Geralmente isto signica que ele foi pego num lao innito ou numa recurso innita. Se existe um lao em particular aonde voc suspeita estar o problema, adicione uma declarao print imediatamente antes do lao que diga entrando no lao e uma outra imediatamente depois que diga saindo do lao. Execute o programa. Se voc receber a primeira mensagem e no a segunda, voc tem um lao innito. V para a seo Lao Innito abaixo. Na maioria das vezes, uma recurso innita far com que o programa execute por um tempo e ento ele produzir um erro do tipo Runtime Error: Maximum recursion depth exceeded (Erro de tempo de execuo: Excedida a profundidade mxima de recurso). Se isto acontecer, v para a seo Recurso Innita abaixo. Se voc no est recebendo este erro, mas suspeita que h um problema com um mtodo ou funo recursiva, voc ainda pode utilizar as tcnicas da seo Recurso Innita. Se nenhum destes passos funcionar, comecea testar outros laos ou mtodos ou funes recursivas. Se isso no funcionar, ento possvel que voc no entenda o uxo de execuo do seu programa. V para a seo Fluxo de Execuo abaixo. Lao Innito Se voc acha que tem um lao innito e descona de qual seja lao causador do problema, adicione uma declarao print no nal do lao que imprima os valores das variveis na condio e o valor da condio. Por exemplo: >>> while # # print print print x > 0 and y < faz algo para faz algo para "x: ", x "y: ", y "condio: ", 0: x y (x > 0 and y < 0) 23.2. A.2 Erros de tempo de execuo 179
  • 187. Aprenda Computao com Python Documentation, Verso 1.1 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. 23.2.3 A.2.3 Quando eu executo o programa, recebo uma exceo Se algo vai errado durante a execuo, o Python exibe uma mensagem que inclui o nome da exceo, a linha do cdigo do programa onde o problema ocorre, e dados para investigao do erro, onde descrita a pilha de execuo de funes e mtodos. Tais dados identicam a funo que est sendo executada no momento, e ento a funo que a invocou, e ento a funo que invocou aquela, e assim por diante (a pilha de execuo de funes). Em outras palavras, ele traa o caminho das invocaes da funo que te levou at onde voc est. Ele tambm inclui o nmero da linha no respectivo arquivo onde cada uma dessas chamadas ocorre. O primeiro passo examinar o lugar no programa onde ocorreu o erro e tentar descobrir o que aconteceu. Esses so alguns dos erros de tempo de execuo mais comuns: NameError (Erro de Nome): Voc est tentando utilizar uma varivel que no existe no ambiente atual. Lembre-se que variveis locais so locais. Voc no pode referenci-la fora da funo onde ela foi denida. TypeError (Erro de Tipo): Existem vrias causas possveis: Voc est tentando utilizar um valor de forma imprpria. Exemplo: indexando uma string, lista ou tupla com alguma coisa que no um inteiro. H uma incompatibilidade entre os itens em um formato de string e os itens passados para converso. Isto pode acontecer se o nmero de itens no for igual ou se uma converso invlida chamada. Por exemplo: Passar uma string para a formatao de converso %f. Voc est passando o nmero errado de argumentos para uma funo ou mtodo. Para mtodos, verique sua denio e conra se o primeiro parmetro chama-se self. Ento verique a chamada ao mtodo; certifque-se de que voc est chamando o mtodo em um objeto com o tipo correto e fornecendo os argumentos adequados. 180 Captulo 23. Apndice A: Depurao
  • 188. Aprenda Computao com Python Documentation, Verso 1.1 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. 23.3 A.3 Erros de semntica De certa forma, os erros de semntica so os mais difceis de depurar, porque o compilador e o sistema de tempo de execuo no fornecem informaes sobre o que est errado. Somente voc sabe o que o programa deve fazer, e somente voc sabe que ele no est fazendo isto. O primeiro passo fazer uma conexo entre o texto do programa e o comportamento que voc est vendo. Voc precisa de uma hiptese sobre o que o programa est realmente fazendo. Uma das coisas que dicultam que os computadores trabalham muito rpido. Voc sempre desejar que a velocidade do programa pudesse ser diminuda para a velocidade humana, e com alguns depuradores voc pode. Mas o tempo que leva para inserir umas poucas declaraes print bem colocadas geralmente mais curto quando comparado a congurar um depurador, inserir e remover breakpoints, e caminhar pelo programa at onde o erro ocorre. 23.3.1 A.3.1 Meu programa no funciona Voc deveria se fazer as seguintes perguntas: H alguma coisa que o programa deveria fazer, mas que no parece que estar acontecendo? 23.3. A.3 Erros de semntica 181
  • 189. Aprenda Computao com Python Documentation, Verso 1.1 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()) Isto pode ser escrito como: >>> vizinho = self.encontraVizinho(i) >>> cartaPega = self.mao[vizinho].popCarta() >>> self.mao[i].adicionaCarta(cartaPega) 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 Captulo 23. Apndice A: Depurao
  • 190. Aprenda Computao com Python Documentation, Verso 1.1 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() voc poderia escrever: >>> def pega_encontrados(self): qtEncontrados = self.mao[i].devolveEncontrados() return qtEncontrados Agora voc tem a oportunidade de mostrar o valor de qtEncontrados antes de devolv-lo. 23.3.4 A.3.4 Eu estou empacado mesmo e eu preciso de ajuda Primeiro,tente sair da frente do computador por alguns minutos. Computadores emitem ondas que afetam o crebro, causando estes efeitos: Frustao e/ou raiva. Crenas superticiosas (o computador me odeia) e pensamentos mgicos (o prorama s funciona quando eu coloco meu chapu de trs pra frente). Programao pelo caminhar aleatrio (a tentativa de programar escrevendo cada programa possvel e escolhendo aquele que faz a coisa certa). Se voc estiver sofrendo de qualquer um destes sintomas, levante-se e v dar uma caminhada. Quando voc estiver calmo, pense no programa. O que ele est fazendo? Quais so as possveis causas do comportamento inadequado? Quando foi a ltima vez que voc teve um programa funcionando, e o que voc fez depois disto? s vezes uma questo de tempo para encontrar um erro. Ns geralmente encontramos os erros quando estamos longe do computador e deixamos nossa mente vaguear. Alguns dos melhores lugares para encontrar erros so trens, chuveiros e na cama, logo antes de pegar no sono. 23.3.5 A.3.5 No, eu preciso mesmo de ajuda Isto acontece. Mesmo os melhores programadores empacam de vez em quando. s vezes voc trabalha num programa por tanto tempo que no consegue ver o erro. Um par fresco de olhos o que se precisa. Antes de trazer mais algum, certique-se de que voc tenha esgotado as tcnicas descritas aqui. Seu programa deve ser to simples quanto possvel, e voc deve estar trabalhando com a mais simples das entradas que causam o erro. Voc deve ter declaraes print nos lugares apropriados (sem comprometer a compreensividade da sada do programa). Voc tem que entender o problema o suciente para descrev-lo concisamente. Quando voc trouxer algum pra ajudar, assegure-se de dar a este algum a informao que ele precisa: Se existe uma mensagem de erro, o que ela e que parte do programa ela indica? Qual foi a ltima coisa que voc fez antes deste erro acontecer? Quais foram as ltimas linhas de cdigo que voc escreveu, ou qual o novo caso de teste que falha? O que voc j tentou at o momento, e o que voc aprendeu? Quando voc encontrar um erro, gaste um segundo para pensar sobre o que voc poderia fazer para encontr-lo mais rpido. Da prxima vez que voc ver algo similar, voc ter condies de encontrar o erro mais rapidamente. 23.3. A.3 Erros de semntica 183
  • 191. Aprenda Computao com Python Documentation, Verso 1.1 Lembre-se, o objetivo no apenas fazer o programa funcionar. O objetivo aprender como fazer o programa funcionar. 184 Captulo 23. Apndice A: Depurao
  • 192. CAPTULO 24 Apndice B: Criando um novo tipo de dado 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
  • 193. Aprenda Computao com Python Documentation, Verso 1.1 def __str__(self): return "%d/%d" %(self.numerador, self.denominador) 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 Como de costume, o comando print chama implicitamente o mtodo __str__. 24.1 B.1 Multiplicao de fraes interessante que nossas fraes possam ser somadas, subtradas, multiplicadas, divididas, etc. Enm, todas as operaes matemticas das fraes. Para que isso seja possvel, vamos usar o recurso de sobrecarga de operadores. Comearemos pela multiplicao por que a operao mais fcil de ser implementada. Para multiplicar duas fraes, criamos uma nova frao, onde o numerador o produto dos numeradores das fraes multiplicadas e o denominador o produto dos numeradores das fraes multiplicadas. O mtodo utilizado em Python para sobrecarga do operador * chama-se __mul__: class Fracao: ... def __mul__(self, other): return Fracao(self.numerador * other.numerador, self.denominador * ohter.denominador) Vamos testar este mtodo criando e multiplicando duas fraes: >>> print Fracao(5, 6) * Fracao(3, 4) 15/24 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
  • 194. Aprenda Computao com Python Documentation, Verso 1.1 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. 24.2 B.2 Soma de fraes Somar mais complicado do que multiplicar, pelo menos quando estamos somando fraes e temos que implementar isso em uma linguagem de programao. Mas no se assuste, no to complicado assim. A soma de a/b com c/d (a*d+c*b)/(b*d). Tomando a multiplicao como base, podemos facilmente implementar os mtodos __add__ e __radd__: class Fracao: ... def __add__(self, other): if isinstance(other, int): other = Fracao(other) return Fracao(self.numerador * other.denominador + self.denominador * other.numerador, self.denominador * other.denominador) __radd__ = __add__ Podemos testar o mtodo usando fraes e inteiros: >>> print Fracao(5, 6) + Fracao(5, 6) 60/36 >>> print Fracao(5, 6) + 3 23/6 >>> print 2 + Fracao(5, 6) 17/6 Os dois primeiros exemplos chamam o mtodo __add__, enquanto o ltimo exemplo chama o mtodo __radd__. 24.3 B.3 Simplicando fraes: O algoritmo de Euclides No exemplo anterior, calculamos a soma de 5/6 com 5/6 e obtivemos o resultado 60/36. O resultado est correto, porm no est representado na melhor forma possvel. O ideal simplicarmos a frao. Para simplicar ao mximo esta frao, devemos dividir o numerador e o denominador pelo mximo divisor comum (MDC) deles, que 12. Fazendo isso, chegamos forma mais simples da frao, que 5/3. 24.2. B.2 Soma de fraes 187
  • 195. Aprenda Computao com Python Documentation, Verso 1.1 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 Agora, sempre que criarmos uma frao, ela ser reduzida: >>> Fracao(100, -36) -25/9 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 Captulo 24. Apndice B: Criando um novo tipo de dado
  • 196. Aprenda Computao com Python Documentation, Verso 1.1 self.denominador * other.denominador) __radd__ = __add__ 24.4 B.4 Comparando fraes Suponha que tenhamos duas fraes (instncias da classe Fracao), a e b, e ns fazemos a comparao a == b. A implementao padro do operador == realiza um teste raso, ou seja, verica se a e b so o mesmo objeto. Queremos personalizar este retorno, de forma que a comparao retorne True se a e b tiverem o mesmo valor, o que chamado de teste profundo. Temos que ensinar s fraes como elas devem se comparar. Como foi visto na seo 15.4, todos os operadores de comparao podem ser sobrecarregados por apenas um mtodo: __cmp__. Por conveno, o mtodo __cmp__ retorna um nmero negativo se self for menor que other, zero se eles forem iguais e um nmero positivo se self for maior que other. A forma mais simples de comparar fraes atravs da multiplicao cruzada (denominador * numerador e viceversa). Se a/b > c/d, ento ad > bc. Tendo isso em mente, vamos criar o __cmp__: class Fracao: ... def __cmp__(self, other): diferenca = (self.numerador * other.denominador self.denominador * other.numerador) return diferenca 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. 24.5 B.5 Indo mais alm... Obviamente, no terminamos de representar uma frao. Ainda temos que implementar a subtrao sobrescrevendo o mtodo __sub__ e a diviso sobrescrevendo o mtodo __div__. Uma forma de tratar tais operaes sobrescrever os mtodos de negao (__neg__) e de inverso (__invert__). Assim, podemos fazer a subtrao atravs da negao do elemento direita do operador e somando, e podemos fazer a diviso atravs da inverso do elemento direita do operador e multiplicando. Depois, temos que implementar os mtodos __rsub__ e __rdiv__. Infelizmente, no podemos utilizar o mesmo macete utilizado para multiplicao e soma, por que a diviso e a subtrao no so comutativas, ou seja, a ordem dos fatores altera o resultado nal. As negaes unrias, que representam o uso do sinal de negao com apenas um elemento, so implementadas atravs da sobrescrita do mtodo __neg__. Potncias podem ser calculadas atravs do mtodo __pow__, mas a implementao um pouco complicada. Se o expoente da potncia no for um inteiro, o resultado provavelmente no poder ser representado como uma frao. Por exemplo, Fracao(2) ** Fracao(2) a raiz quadrada de 2, que um nmero irracional, e nmeros irracionais no pode ser representados por fraes. Logo, uma tarefa complicada implementar uma verso genrica do mtodo __pow__. Existe, ainda, uma outra extenso para a classe Fracao que pode vir mente. At aqui, assumimos que o numerador e o denominador so nmeros inteiros. 24.4. B.4 Comparando fraes 189
  • 197. Aprenda Computao com Python Documentation, Verso 1.1 24.5.1 B.5.1 Exerccio Como exerccio, nalize a implementao da classe Fracao, tornando-a capaz de tratar subtrao, diviso e potenciao. 24.6 B.6 Glossrio mximo divisor comum (MDC) O maior inteiro positivo que tem como mltiplo o numerador e o denominador de uma frao. negao unria a operao que calcula a inverso de um elemento, usualmente representada com um sinal de menos - esquerda do elemento. chamada unria pelo contraste com a operao binria que usa o sinal de menos, a subtrao. simplicar Transformar uma frao em sua equivalente com o MDC valendo 1 190 Captulo 24. Apndice B: Criando um novo tipo de dado
  • 198. CAPTULO 25 Apndice C: Leituras recomendadas 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 25.1 C.1 Recomendaes para leitura E agora, para onde voc vai? Existem diversas direes que podem ser seguidas, aumentando o seu conhecimento especicamente em Python e/ou na cincia da computao em geral. Os exemplos contidos neste livro foram deliberadamente simples, ou seja, eles podem no ter mostrado todo o potencial do Python. Abaixo uma lista de extenses e sugestes para projetos que usem Python: Programar utilizando interfaces grcas (GUI - graphical user interface) permite ao seu programa usar um ambiente de janelas para interagir com o usurio e exibir contedos grcos (janelas, imagens, etc.) A biblioteca grca mais antiga para Python o Tkinter, que baseado no Tcl criado por Jon Ousterhout e na linguagem de script Tk. O Tkinter est incluso na distribuio padro do Python, ou seja, quando instalamos o Python, o mdulo do Tkinter instalado tambm. Outra biblioteca famosa o wxPython, que essencialmente uma folheagem do Python sobre o wxWindows, uma biblioteca C++ que, por sua vez, implementa janelas usando a interfaces nativas nas plataformas Windows e Unix (incluindo Linux). As janelas e controles no wxPython tendem a ter um visual mais nativo do que no Tkinter e so mais um pouco mais simples de usar. Em qualquer biblioteca GUI que voc usar, voc usar a programao orientada a eventos, onde o usurio, e no o programador, determina o uxo de execuo. Este estilo de programao exige um novo costume que, por vezes, o obrigar a repensar toda a estrutura de um programa. A programao web um modelo de programao que integra o Python com a Internet. Por exemplo, voc pode criar um cliente web que abre e l uma pgina remota (quase) to facilmente como voc pode abrir um arquivo local. Existem, ainda, mdulos para Python que permitem acesso remoto a arquivos utilizando FTP, e mdulos que permitem voc enviar e receber e-mails. Python tambm usado (amplamente) para construir programas em servidores web com o intuito de tratar dados fornecidos por formulrios. 191
  • 199. Aprenda Computao com Python Documentation, Verso 1.1 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. 25.2 C.2 Sites e livros sobre Python Aqui esto algumas recomendaes do autor de informaes sobre Python na Internet: A pgina ocial do Python (www.python.org) o ponto de partida para pesquisa sobre qualquer material ligado a Python. L voc encontrar ajuda, documentao, links para outros sites e listas de discusso nas quais voc pode participar. O Open Book Project (www.ibiblio.com/obp) contm no apenas este livro, mas tambm livros similares que abordam Java e C++, escritos por Allen Downey. Alm disso, h aulas sobre Circuitos Eltricos feitas por Tony R. Kuphaldt; Get down with ..., um conjunto de tutoriais sobre uma gama de tpicos em cincia da computao, escrito e editado por alunos de ensino mdio; Python for Fun, um conjunto de estudos de caso em Python, feito por Chris Meyers; e The Linux Cookbook, escrito por Michael Stultz, com 300 pginas de dicas e tcnicas. Por ltimo, se voc for ao Google e buscar por python -snake -monty, voc encontrar cerca de 750 mil resultados. E aqui esto alguns livros que contm material sobre Python: Core Python Programming, escrito por Wesley Chun, um grande livro com cerca de 750 pginas. A primeira prte do livro apresenta os recursos bsicos do Python. A segunda parte traz uma introduo aos tpicos mais avanados, incluindo muitos dos mencionados acima. Python Essential Reference, escrito por David M. Beazley, um livro pequeno, mas que contm informaes tanto da linguagem em si quanto dos mdulos da biblioteca padro. tambm muito bem indexado. Python Pocket Reference, escrito por Mark Lutz, este livro realmente cabe no seu bolso. Embora no seja to abrangente quanto o Python Essential Reference, Python Pocket Reference uma referncia para se ter em mos o tempo todo, capaz de atender muito bem explicao das funes e mtodos mais comuns. Mark Luiz tambm autor do livro Programming Python, um dos primeiros (e maiores) livros sobre Python, e no visa o programador iniciante. Seu ltimo livro, Learning Python, menor e mais acessvel. Python Programming on Win32, escrito por Mark Hammond e Andy Robinson, um tem que ter para qualquer pessoa utilizando Python seriamente para desenvolver aplicaes Windows. Entre outras coisas, o livro apresenta a integrao entre Python e COM, cria uma pequena aplicao com wxPython, e ainda usar Python para criar scripts para aplicaes como Word e Excel. 192 Captulo 25. Apndice C: Leituras recomendadas
  • 200. Aprenda Computao com Python Documentation, Verso 1.1 25.3 C.3 Livros de cincia da computao recomendados As seguintes sugestes de leitura incluem muitos dos livros favoritos do autor. Eles lidam com as boas prticas de programao e cincia da computao em geral. The Practice of Programming, escrito por Kernighan e Pike, abrange no apenas o projeto e a implementao de algoritmos e estrutura de dados, mas tambm depura, testa e melhora o desempenho de programas. Os exemplos so principalmente em C++ e Java, nenhum em Python. The Elements of Java Style, editado por Al Vermeulen, outro livro pequeno que discute alguns dos mais nos pontos de boas prticas de programao, como o bom uso de convees, comentrios, e ainda espaos em branco e endentao (o que no um problema em Python). O livro tambm abrange programao por contrato, usando asseres para capturar erros testando precondies e poscondies, e programao adequada utilizando threads e sua sincronizao. Programming Pearls, escrito por Jon Bentley, um livro clssico. O livro consiste de casos de estudo que originalmente apareceram na coluna do autor no site Communications of ACM (Association for Computing Machinery). Os estudos lidam com trade-offs em programao e por que isto , muitas vezes, uma pssima idia, especialmente para usar na sua primeira ideia para um programa. O livro um pouco mais velho que os outros acima (1986), ento os exemplos esto em linguagens antigas. Existem vrios problemas para resolver, uns com soluo e outros com dicas. O livro foi muito famoso e foi seguido por um segundo volume. The New Turing Omnibus, escrito por A.K Dewdney, fornece uma leve introduo a 66 tpicos de cincia da computao, indo de computao paralela aos vrus de computador, de tomograas computadorizadas a algoritmos genticos. Todos os tpicos so curtos e agradveis. Um livro anterior escrito por Dewdney, The Armchair Universe, uma coleo de sua coluna Computer Recreations (Brincadeiras computacionais) na revista Scientic American. Ambos os livros representam uma rica fonte de ideias para projetos. Turtles, Termites and Trafc Jams, escrito por Mitchel Resnick, trata do poder de descentralizao e como um comportamento complexo pode ocorrer a partir de simples atividades coordenadas, com um grande nmero de agentes. A execuo do programa demonstra o comportamento complexo, que , muitas vezes, contraintuitivo. Gdel, Escher and Bach, escrito por Douglas Hofstadter. Simplicando, se voc encontrar a magia na recurso, voc vai encontrar tambm neste best-seller. Um dos temas abordados por Hofstadter envolve loops estranhos onde os padres evoluem e ascendem at se encontrarem novamente. Esta a controvrsia de Hofstadter, de que tais loops estranhos representam o elemento essencial que separa o animado do inanimado. Ele demonstra tais padres na msica de Bach, nos quadros de Escher e na incompletude dos teoremas de Gdel. 25.3. C.3 Livros de cincia da computao recomendados 193
  • 201. Aprenda Computao com Python Documentation, Verso 1.1 194 Captulo 25. Apndice C: Leituras recomendadas
  • 202. CAPTULO 26 Apndice D: GNU Free Documentation License 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
  • 203. Aprenda Computao com Python Documentation, Verso 1.1 196 Captulo 26. Apndice D: GNU Free Documentation License
  • 204. CAPTULO 27 Indices and tables genindex modindex search 197