SlideShare uma empresa Scribd logo
Como pensar como um cientista da
  Computação usando Python

                            Allen Downey

                            Jeffrey Elkner

                            Chris Meyers



 Tradução: Equipe do projeto http://pensarpython.incubadora.fapesp.br

                      Edição: Cárlisson Galdino
Como pensar como um cientista da Computação usando Python




  Copyright (c) 2002 Allen Downey, Jeffrey Elkner, and Chris Meyers.
  Edited by Shannon Turlington and Lisa Cutler. Cover design by Rebecca
  Gimenez.
  Printing history:
  April 2002: First edition.
  Green Tea Press
  1 Grove St.
  P.O. Box 812901
  Wellesley, MA 02482
  Permission is granted to copy, distribute, and/or modify this document under the
  terms of the GNU Free Documentation License, Version 1.1 or any later version
  published by the Free Software Foundation; with the Invariant Sections being
  “Foreword,” “Preface,” and “Contributor List,” with no Front-Cover Texts, and
  with no BackCover Texts. A copy of the license is included in the appendix
  entitled “GNU Free Documentation License.”
  The GNU Free Documentation License is available from www.gnu.org or by
  writing to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA 02111-1307, USA.




    Como pensar como um cientista da Computação usando Python
  Versão atual: 1.0
  Data de Publicação: 16 de janeiro de 2008
  Classificação: Tutorial
  Autor: Jeffrey Elkner (traduzido pelo            projeto   Pensar    Python    -
  http://pensarpython.incubadora.fapesp.br)
  Publicação: Cyaneus.net




                                         #2
Como pensar como um cientista da Computação usando Python



                                                                              Índice

Foreword...................................................................5
                                                    .                                     4.8 A instrução return....................................................28
Apresentação..................................................   ............6            4.9 Recursividade..........................................................29
Preface.........................................................
                                                              ...............7            4.10 Diagramas de pilha para funções recursivas........29
   How and why I came to use Python...............................7                       4.11 Recursividade infinita...........................................30
   Finding a textb o ok........................................................7          4.12 Entrada pelo teclado..............................................30
   Intro ducing programming with Python.........................7                         4.13 Glossário...............................................................30
   Building a community.....................................................8 Capítulo 5: Funções frutíferas.......................... ......31                .
Prefácio...................................................................       ....9 5.1 Valores de retorno...................................................31
   Como e porque eu vim a usar Python.............................9                     5.2 Desenvolvimento de programas.............................31
   Encontrando um livro texto............................................9              5.3 Composição.............................................................32
   Introduzindo programação com Python.......................10                         5.4 Funções booleanas..................................................32
   Construindo uma comunidade......................................10                   5.5 Mais recursividade..................................................33
                                                                                        5.6 Voto de confiança (Leap of faith)...........................33
Contributor List......................................................            ..11  5.7 Mais um exemplo....................................................34
Versão Brasileira.....................................................13                5.8 Checagem de tipos..................................................34
Capítulo 1: O caminho do programa......................14                               5.9 Glossário.................................................................34
   1.1 A linguagem de programação Python....................14 Capítulo 6: Iteração.................................................36
   1.2 O que é um programa?............................................15               6.1 Reatribuições...........................................................36
   1.3 O que é depuração (debugging)?............................15                     6.2 O comando while....................................................36
       1.3.1 Erros de sintaxe.........................................15                6.3 Tabelas....................................................................37
       1.3.2 Erros em tempo de execução (runtime errors)                                6.4 Tabelas de duas dimensões (ou bi-dimensionais). .38
       ........................................................................ ....15  6.5 Encapsulamento e generalização............................38
       1.3.3 Erros de semântica.....................................15                  6.6 Mais encapsulamento..............................................38
       1.3.4 Depuração experimental (Debugging)........16
   1.4 Linguagens naturais e linguagens formais.............16                          6.7 Variáveis locais.......................................................38
   1.5 O primeiro programa...............................................17             6.8 Mais generalização..................................................39
   1.6 Glossário.................................................................17     6.9 Funções...................................................................39
                                                                                        6.10 Glossário...............................................................40
Capítulo 2: Variáveis, expressões e comandos......18 Capítulo 7: Strings..................................................41
                                                                                                          .
  2.1 Valores e tipos.........................................................18
    2.2 Variáveis.................................................................18      7.1 Um tipo de dado composto.....................................41
    2.3 Nomes de variáveis e palavras reservadas..............18                          7.2 Comprimento..........................................................41
    2.4 Comandos................................................................19        7.3 Travessia e o loop for..............................................41
    2.5 Avaliando expressões..............................................19              7.4 Fatias de strings.......................................................42
    2.6 Operadores e operandos..........................................19                7.5 Comparação de strings............................................42
    2.7 Ordem dos operadores............................................20                7.6 Strings são imutáveis..............................................42
    2.8 Operações com strings............................................20               7.7 Uma função find (encontrar)...................................42
    2.9 Composição.............................................................20         7.8 Iterando e contando.................................................43
    2.11 Glossário...............................................................20       7.9 O módulo string......................................................43
                                                                                          7.10 Classificação de caracteres...................................43
Capítulo 3: Funções................................................22
                                                    .                                     7.11 Glossário...............................................................43
  3.1 Chamadas de funções..............................................22                 7.11 Glossário2.............................................................44
  3.2 Conversão entre tipos..............................................22 Capítulo 8: Listas....................................................45
                                                                                                                                                 .
  3.3 Coerção entre tipos.................................................22
  3.4 Funções matemáticas..............................................22         8.1 Valores da lista........................................................45
  3.5 Composição.............................................................23   8.2 Acessado elementos................................................45
  3.6 Adicionando novas funções....................................23             8.3 Comprimento da lista..............................................45
  3.7 Definições e uso......................................................24    8.4 Membros de uma lista.............................................46
  3.8 Fluxo de execução...................................................24      8.5 Listas e laços for.....................................................46
  3.9 Parâmetros e argumentos........................................24           8.6 Operações em listas.................................................46
  3.10 Variáveis e parâmetros são locais.........................25               8.7 Fatiamento de listas.................................................46
  3.11 Diagramas da pilha...............................................25        8.8 Listas são mutáveis.................................................46
  3.12 Funções com resultados........................................25           8.9 Remoção em lista....................................................47
  3.13 Glossário...............................................................26 8.10 Ojetos e valores.....................................................47
                                                                                  8.11 Apelidos................................................................47
Capítulo 4: Condicionais e recursividade...............27                         8.12 Clonando listas......................................................48
  4.1 O operador módulo.................................................27        8.13 Lista como parâmetro...........................................48
  4.2 Expressões booleanas..............................................27        8.14 Lista aninhadas......................................................48
  4.3 Operadores lógicos..................................................27      8.15 Matrizes.................................................................48
  4.4 Execução condicional.............................................27         8.16 Strings e listas.......................................................49
  4.5 Execução alternativa...............................................28       8.17 Glossário...............................................................49
  4.6 Condicionais encadeados........................................28 Capítulo 9: Tuplas...................................................                 .50
  4.7 Condicionais aninhados..........................................28

                                                                                   #3
Como pensar como um cientista da Computação usando Python

  9.1 Mutabilidade e tuplas..............................................50           16.1 Herança.................................................................75
  9.2 Atribuições de tupla................................................50          16.2 Uma mão de cartas................................................75
  9.3 Tuplas como valores de retorno..............................50                  16.3 Dando as cartas.....................................................75
  9.4 Números aleatórios.................................................50           16.4 Exibindo a mao.....................................................76
  9.5 Lista de números aleatórios....................................51               16.5 A classe JogoDeCartas..........................................76
  9.6 Contando.................................................................51     16.6 Classe MaoDeMico...............................................76
  9.7 Vários intervalos.....................................................51        16.7 Classe Mico...........................................................77
  9.8 Uma solução em um só passo.................................52                   16.8 Glossário...............................................................78
  9.9 Glossário.................................................................52 Capítulo 17: Listas encadeadas...............................79
Capítulo 10: Dicionários.........................................54                   17.1 Referências Embutidas.........................................79
  10.1 Operações dos Dicionários...................................54                 17.2 A classe No (Node)...............................................79
  10.2 Métodos dos Dicionários......................................54                17.3 Listas como Coleções...........................................79
  10.3 Aliasing (XXX) e Copiar......................................55                17.4 Listas e Recorrência..............................................80
  10.4 Matrizes Esparsas..................................................55          17.5 Listas Infinitas.......................................................80
  10.5 Hint........................................................................55 17.6 O Teorema da Ambigüidade Fundamental...........80
  10.6 Inteiros Longos.....................................................56         17.7 Modificando Listas...............................................81
  10.7 Contando Letras....................................................56          17.8 Envoltórios e Ajudadores......................................81
  10.8 Glossário...............................................................56     17.9 A Classe ListaLigada............................................81
Capítulo 11: Arquivos e exceções..........................58                          17.10 Invariantes...........................................................82
  Arquivos e exceções......................................................58         17.11 Glossário.............................................................82
  11.1 Arquivos texto.......................................................58 Capítulo 18: Pilhas................................................                    ...83
  11.2 Gravando variáveis...............................................59            18.1 Tipos abstratos de dados.......................................83
  11.3 Diretórios...............................................................60    18.2 O TAD Pilha.........................................................83
  11.4 Pickling.................................................................60    18.3 Implementando pilhas com listas de Python........83
  11.5 Exceções................................................................60     18.4 Empilhando e desempilhando...............................83
  11.6 Glossário...............................................................61     18.5 Usando uma pilha para avaliar expressões pós-
Capítulo 12: Classes e objetos................................62                      fixas...............................................................................84
  12.1 Tipos compostos definidos pelo usuário..............62                         18.6 Análise sintática....................................................84
  12.2 Atributos................................................................62    18.7 Avaliando em pós-fixo..........................................84
  12.3 Instâncias como parâmetros..................................63                 18.8 Clientes de fornecedores.......................................84
  12.4 O significado de "mesmo"....................................63                 18.9 Glossário...............................................................85
  12.5 Retângulos.............................................................63 Capítulo 19: Filas................................................                 .....86
  12.6 Instancias como valores retornados......................64                     19.1 Um TDA Fila........................................................86
  12.7 Objetos são mutáveis............................................64             19.2 Fila encadeada.......................................................86
  12.8 Copiando...............................................................64      19.3 Características de performance.............................86
  12.9 Glossário...............................................................65     19.4 Fila encadeada aprimorada...................................86
Capítulo 13: Classes e funções................................ 6                  6   19.5 Fila por prioridade.................................................87
  13.1 Horario..................................................................66    19.6 A classe Golfer......................................................88
  13.2 Funções Puras.......................................................66         19.7 Glossário...............................................................88
  13.3 Modificadores.......................................................66 Capítulo 20: Árvores...............................................89
  13.4 O que é melhor ?...................................................67          20.1 Construindo árvores..............................................89
  13.5 Desenvolvimento Prototipado versus                                             20.2 Percorrendo árvores..............................................89
  Desenvolvimento Planejado.........................................67                20.3 Árvores de expressões...........................................89
  13.6 Generalização........................................................67        20.4 Percurso de árvores...............................................90
  13.7 Algoritmos.............................................................68      20.5 Construindo uma árvore de expressão..................90
  13.8 Glossário...............................................................68     20.6 Manipulando erros................................................92
Capítulo 14: Classes e métodos..............................69                        20.7 A árvore dos animais............................................92
  14.1 Características da orientação a objetos.................69                     20.8 Glossário...............................................................93
  14.2 exibeHora (printTime)..........................................69 GNU Free Documentation License........................ 94                                      .
  14.3 Um outro exemplo................................................70             0. PREAMBLE ............................................................94
  14.10 Glossário.............................................................70      1. APPLICABILITY AND DEFINITIONS ................94
Capítulo 15: Conjuntos de objetos..........................71                         2. VERBATIM COPYING ..........................................95
  15.1 Composição...........................................................71        3. COPYING IN QUANTITY .....................................95
  15.2 Objetos Carta.........................................................71       4. MODIFICATIONS ..................................................95
  15.3 Atributos de classe e o método __str__................71                       5. COMBINING DOCUMENTS .................................96
  15.4 Comparando cartas................................................72            6. COLLECTIONS OF DOCUMENTS ......................96
  15.5 Baralhos.................................................................72    7. AGGREGATION WITH INDEPENDENT WORKS .
  15.6 Imprimindo o baralho...........................................72              96
  15.7 Embaralhando.......................................................73          8. TRANSLATION ......................................................96
  15.8 Removendo e distribuindo cartas.........................73                     9. TERMINATION ......................................................96
  15.9 Glossário...............................................................74     10. FUTURE REVISIONS OF THIS LICENSE.........96
Capitulo 16: Herança..............................................75
                                                                   .                      How to use this License for your documents.......96




                                                                                    #4
Como pensar como um cientista da Computação usando Python


                                                        Foreword


By David Beazley                                                               One of the reasons why I like Python is that it
                                                                provides a really nice balance between the practical and the
              As an educator, researcher, and book author, I    conceptual. Since Python is interpreted, beginners can pick up
am delighted to see the completion of this book. Python is a    the language and start doing neat things almost immediately
fun and extremely easy-to-use programming language that has     without getting lost in the problems of compilation and
steadily gained in popularity over the last few years.          linking. Furthermore, Python comes with a large library of
Developed over ten years ago by Guido van Rossum,               modules that can be used to do all sorts of tasks ranging from
Python’s simple syntax and overall feel is largely derived      web-programming to graphics. Having such a practical focus
from ABC, a teaching language that was developed in the         is a great way to engage students and it allows them to
1980’s. However, Python was also created to solve real          complete significant pro jects. However, Python can also
problems and it borrows a wide variety of features from         serve as an excellent foundation for introducing important
programming languages such as C++, Java, Modula-3, and          computer science concepts. Since Python fully supports
Scheme. Because of this, one of Python’s most remarkable        procedures and classes, students can be gradually introduced
features is its broad appeal to professional software           to topics such as procedural abstraction, data structures, and
developers, scientists, researchers, artists, and educators.    ob ject-oriented programming—all of which are applicable to
               Despite Python’s appeal to many different         later courses on Java or C++. Python even borrows a number
communities, you may still wonder “why Python?” or “why         of features from functional programming languages and can
teach programming with Python?” Answering these questions       be used to introduce concepts that would be covered in more
is no simple task—especially when popular opinion is on the     detail in courses on Scheme and Lisp.
side of more masochistic alternatives such as C++ and Java.                    In reading Jeffrey’s preface, I am struck by his
However, I think the most direct answer is that programming comments that Python allowed him to see a “higher level of
in Python is simply a lot of fun and more productive.           success and a lower level of frustration” and that he was able
               When I teach computer science courses, I want to “move faster with better results.”sometimes thesePython for
                                                                                                     Although         comments
to cover important concepts in addition to making the material refer to his same reasons in advanced graduate use computer
                                                                             introductory course, I
interesting and engaging to students. Unfortunately, there is a these exact                                      level
tendency for introductory programming courses to focus far science courses at the University of Chicago. of covering a lot
                                                                                                               In these courses,
too much attention on mathematical abstraction and for I am constantly faced with the a blistering nine week quarter.
                                                                                                 daunting task
students to be come frustrated with annoying problems related of difficult course material in for me to inflict a lot of pain
to low-level details of syntax, compilation, and the Although it is certainly possible
enforcement of seemingly arcane rules. Although such and suffering by using a be counterproductive— have often
                                                                                             language like C++, I
abstraction and formalism is important to professional found this approachisto about a topic unrelatedespecially
software engineers and students who plan to continue their when the coursefind that using Python allows me to better
                                                                “programming.” I
                                                                                                                        to just
study of computer science, taking such an approach in an focus on the actual topic at hand while allowing students to
introductory course mostly succeeds in making computer complete substantial class pro jects.
science boring. When I teach a course, I don’t want to have a
room of uninspired students. I would much rather see them                      Although Python is still a young and evolving
trying to solve interesting problems by exploring different language, I believe that it has a bright future in education. This
ideas, taking unconventional approaches, breaking the rules, book is an important step in that direction.
and learning from their mistakes. In doing so, I don’t want to
waste half of the semester trying to sort out obscure syntax                   David Beazley
problems, unintelligible compiler error messages, or the
several hundred ways that a program might generate a general                   University of Chicago
protection fault.                                                              Author of the Python Essential Reference




                                                         Foreword #5
Como pensar como um cientista da Computação usando Python


                                                    Apresentação

     Tradução do capítulo anterior, que foi mantido por ter sido marcado como seção invariante pelo autor original.
Como educador, pesquisador e autor de livros, regozija-me       é que ele oferece um equilíbrio realmente bom entre o lado
ver completo este trabalho. Python é uma linguagem de           prático e o lado conceitual. Sendo Python interpretado, os
programação divertida e extremamente fácil de usar que tem      iniciantes podem pegar a linguagem e começar a fazer coisas
ganho forte popularidade nestes últimos poucos anos.            legais quase imediatamente sem se perderem em problemas de
Desenvolvida dez anos atrás por Guido van Rossun, a sintaxe     compilação e ligação. Além disso, Python vem com uma
simples do Python e seu sentido geral são grandemente           grande biblioteca de módulos que podem ser utilizados para
derivados do ABC, uma linguagem didática que foi                fazer todo tipo de tarefa, desde a programação para a web até
desenvolvida nos anos 80. Entretanto, Python também foi         gráficos. Com tal enfoque prático temos uma bela maneira de
criado para solucionar problemas reais e tomou emprestado       alcançar o engajamento dos alunos e permitir que eles
uma grande quantidade de características de linguagens de       finalizem projetos significativos. Entretanto, Python também
programação como C++, Java, Modula-3 e Scheme. Por causa        pode servir de excelente embasamento para a introdução de
disso, uma das mais notáveis características do Python é o      conceitos importantes em ciência da computação. Já que
grande apelo que tem junto a desenvolvedores profissionais de   Python suporta plenamente procedimentos (procedures) e
software, cientistas, pesquisadores, artistas e educadores.     classes, os alunos podem ser gradualmente introduzidos a
                                                                tópicos como abstração procedural, estruturas de dados, e
              A Despeito deste apelo do Python junto às mais    programação orientada a objetos ? todos aplicáveis em cursos
variadas comunidades, você pode ainda estar pensando ?por       posteriores de Java ou C++. Python ainda toma emprestado
que Python?? ou ?por que ensinar programação com                certas características de linguagens de programação funcionais
Python??. Responder à estas perguntas não é uma tarefa fácil ?  e pode ser usado para introduzir conceitos cujos detalhes
especialmente se a opinião pública está do lado de alternativas poderiam ser aprofundados em cursos de Scheme e Lisp.
mais masoquistas como C++ e Java. Entretanto, eu acho que a
resposta mais direta é que programar com Python é um                         Lendo o prefácio de Jeffrey, fiquei
bocado divertido e mais produtivo.                              impressionado com seu comentário de que Python o fez ver
                                                                um ?maior nível de sucesso e um menor nível de frustração? o
              Quando ministro cursos de ciências da que lhe permitiu ?progredir mais depressa com resultados
computação, o que desejo é cobrir conceitos importantes além melhores?. Embora estes comentários refiram-se aos seus
de tornar a matéria interessante e os alunos participativos. cursos introdutórios, eu às vezes uso Python exatamente pelas
Infelizmente, existe uma tendência entre os cursos mesmas razões em cursos avançados de pós-graduação em
introdutórios de programação a focar atenção demais em ciência da computação na Universidade de Chicago. Nestes
abstrações matemáticas, e de frustração entre os alunos com cursos, enfrento constantemente a assustadora tarefa de cobrir
problemas enfadonhos e inoportunos relacionados a detalhes muitos tópicos difíceis em um rapidíssimo trimestre de nove
de sintaxe em baixo nível, compilação e a imposição de regras semanas. Embora me seja possível inflingir um bocado de dor
que aparentemente só um expert pode compreender. Embora e sofrimento pelo uso de uma linguagem como C++, tenho
alguma abstração e formalismo sejam importantes para percebido muitas vezes que este enfoque é contraproducente ?
engenheiros profissionais de software e estudantes que especialmente quando o curso é sobre um tópico não
planejam continuar seus estudos em ciências da computação, relacionado apenas com ?programar?. Acho que usar Python
escolher tal abordagem em um curso introdutório faz da me permite um melhor foco no tópico em questão, enquanto
ciência da computação algo entediante. Quando ministro um permite que os alunos completem projetos substanciais em
curso, não desejo uma sala cheia de alunos sem inspiração. classe.
Em vez disso, preferiria muito mais vê-los tentando solucionar
problemas interessantes explorando idéias diferentes,                        Embora Python seja ainda uma linguagem
trilhando caminhos não convencionais, quebrando regras, e jovem e em evolução, acredito que tem um futuro brilhante
aprendendo a partir de seus erros. Fazendo assim, não em educação. Este livro é um passo importante nessa direção.
pretendo desperdiçar metade de um semestre tentando explicar
problemas obscuros de sintaxe, mensagens ininteligíveis de                   David Beazley
compiladores ou as várias centenas de maneiras pelas quais                   Universidade de Chicago
um programa pode gerar uma falha geral de proteção.
             Uma das razões pelas quais eu gosto de Python                   Autor de Python Essencial Reference




                                                    Apresentação #6
Como pensar como um cientista da Computação usando Python


                                                          Preface


By Jeff Elkner                                                    computer science classes the following year, the most pressing
                                                                 problem was the lack of an available textbook.
              This book owes its existence to the
collaboration made possible by the Internet and the free                       Free content came to the rescue. Earlier in the
software movement. Its three authors—a college professor, a     year, Richard Stallman had introduced me to Allen Downey.
high school teacher, and a professional programmer—have yet     Both of us had written to Richard expressing an interest in
to meet face to face, but we have been able to work closely     developing free educational content. Allen had already written
together and have been aided by many wonderful folks who        a first-year computer science textbook, How to Think Like a
have donated their time and energy to helping make this book    Computer Scientist. When I read this book, I knew
better.                                                         immediately that I wanted to use it in my class. It was the
                                                                clearest and most helpful computer science text I had seen. It
               We think this book is a testament to the benefits emphasized the processes of thought involved in
and future possibilities of this kind of collaboration, the programming rather than the features of a particular language.
framework for which has been put in place by Richard Reading it immediately made me a better teacher.
Stallman and the Free Software Foundation.
                                                                               How to Think Like a Computer Scientist was not
                                                                just an excellent book, but it had been released under a GNU
                                                                public license, which meant it could be used freely and
How and why I came to use Python                                modified to meet the needs of its user. Once I decided to use
                                                                Python, it occurred to me that I could translate Allen’s original
In 1999, the College Board’s Advanced Placement (AP) Java version of the book into the new language. While I would
Computer Science exam was given in C++ for the first time. not have been able to write a textbook on my own, having
As in many high schools throughout the country, the decision Allen’s book to work from made it possible for me to do so, at
to change languages had a direct impact on the computer the same time demonstrating that the cooperative development
science curriculum at Yorktown High School in Arlington, model used so well in software could also work for
Virginia, where I teach. Up to this point, Pascal was the educational content.
language of instruction in both our first-year and AP courses.
In keeping with past practice of giving students two years of                  Working on this book for the last two years has
exposure to the same language, we made the decision to been rewarding for both my students and me, and my students
switch to C++ in the first-year course for the 1997-98 school played a big part in the process. Since I could make instant
year so that we would be in step with the College Board’s changes whenever someone found a spelling error or difficult
change for the AP course the following year.                    passage, I encouraged them to look for mistakes in the book
                                                                by giving them a bonus point each time they made a
               Two years later, I was convinced that C++ was suggestion that resulted in a change in the text. This had the
a poor choice to use for introducing students to computer double benefit of encouraging them to read the text more
science. While it is certainly a very powerful programming carefully and of getting the text thoroughly reviewed by its
language, it is also an extremely difficult language to learn and most important critics, students using it to learn computer
teach. I found myself constantly fighting with C++’s difficult science.
syntax and multiple ways of doing things, and I was losing
many students unnecessarily as a result. Convinced there had                   For the second half of the book on ob ject-
to be a better language choice for our first-year class, I went  oriented programming, I knew that someone with more real
looking for an alternative to C++.                              programming experience than I had would be needed to do it
                                                                right. The book sat in an unfinished state for the better part of
               I needed a language that would run on the a year until the free software community once again provided
machines in our Linux lab as well as on the Windows and the needed means for its completion.
Macintosh platforms most students have at home. I wanted it
to be free and available electronically, so that students could                I received an email from Chris Meyers
use it at home regardless of their income. I wanted a language expressing interest in the book. Chris is a professional
that was used by professional programmers, and one that had programmer who started teaching a programming course last
an active developer community around it. It had to support year using Python at Lane Community College in Eugene,
both procedural and ob ject-oriented programming. And most Oregon. The prospect of teaching the course had led Chris to
importantly, it had to be easy to learn and teach. When I the book, and he started helping out with it immediately. By
investigated the choices with these goals in mind, Python the end of the school year he had created a companion project
stood out as the best candidate for the job.                    on our Website at http://www.ibiblio.org/obp called Python
                                                                for Fun and was working with some of my most advanced
               I asked one of Yorktown’s talented students, students as a master teacher, guiding them beyond where I
Matt Ahrens, to give Python a try. In two months he not only could take them.
learned the language but wrote an application called pyTicket
that enabled our staff to report technology problems via the
Web. I knew that Matt could not have finished an application
of that scale in so short a time in C++, and this Intro ducing programming with Python
accomplishment, combined with Matt’s positive assessment of
Python, suggested that Python was the solution I was looking The process of translating and using How to Think Like a
for.                                                            Computer Scientist for the past two years has confirmed
                                                                Python’s suitability for teaching beginning students. Python
                                                                greatly simplifies programming examples and makes
                                                                important programming ideas easier to teach.
Finding a textb o ok
                                                                        The first example from the text illustrates this
Having decided to use Python in both of my introductory point. It is the traditional “hello, world” program, which in the

                                                          Preface #7
Como pensar como um cientista da Computação usando Python

C++ version of the book looks like this:                              they learned in their math courses. I had much less difficulty
                                                                      teaching variables this year than I did in the past, and I spent
     #include <iostream.h>                                            less time helping students with problems using them.
     void main()
     {
                                                                                     Another example of how Python aids in the
                                                                      teaching and learning of programming is in its syntax for
        cout << "Hello, world." << endl;                              functions. My students have always had a great deal of
     }                                                                difficulty understanding functions. The main problem centers
               in the Python version it becomes:                      around the difference between a function definition and a
                                                                      function call, and the related distinction between a parameter
     print "Hello, World!"                                            and an argument. Python comes to the rescue with syntax that
               Even though this is a trivial example, the             is nothing short of beautiful. Function definitions begin with
advantages of Python stand out. Yorktown’s Computer                   the keyword def, so I simply tell my students, “When you
Science I course has no prerequisites, so many of the students        define a function, begin with def, followed by the name of the
seeing this example are looking at their first program. Some of        function that you are defining; when you call a function,
them are undoubtedly a little nervous, having heard that              simply call (type) out its name.” Parameters go with
computer programming is difficult to learn. The C++ version             definitions; arguments go with calls. There are no return types,
has always forced me to choose between two unsatisfying               parameter types, or reference and value parameters to get in
options: either to explain the #include, void main(), {, and }        the way, so I am now able to teach functions in less than half
statements and risk confusing or intimidating some of the             the time that it previously took me, with better
students right at the start, or to tell them, “Just don’t worry       comprehension.
about all of that stuff now; we will talk about it later,” and risk             Using Python has improved the effectiveness of
the same thing. The educational ob jectives at this point in theour computer science program for all students. I see a higher
course are to introduce students to the idea of a programming   general level of success and a lower level of frustration than I
statement and to get them to write their first program, thereby  experienced during the two years I taught C++. I move faster
introducing them to the programming environment. The            with better results. More students leave the course with the
Python program has exactly what is needed to do these things,   ability to create meaningful programs and with the positive
and nothing more.                                               attitude toward the experience of programming that this
              Comparing the explanatory text of the program engenders.
in each version of the book further illustrates what this means
to the beginning student. There are thirteen paragraphs of
explanation of “Hello, world!” in the C++ version; in the Building a community
Python version, there are only two. More importantly, the
missing eleven paragraphs do not deal with the “big ideas” in
computer programming but with the minutia of C++ syntax. I I have received email from all over the globe from people
found this same thing happening throughout the book. Whole using this book to learn or to teach programming. A user
paragraphs simply disappear from the Python version of the community has begun to emerge, and many people have been
text because Python’s much clearer syntax renders them contributing to the pro ject by sending in materials for the
unnecessary.                                                    companion Website at http://www.thinkpython.com.
                Using a very high-level language like Python                         With the publication of the book in print form, I
allows a teacher to postpone talking about low-level details of       expect the growth in the user community to continue and
the machine until students have the background that they need         accelerate. The emergence of this user community and the
to better make sense of the details. It thus creates the ability to   possibility it suggests for similar collaboration among
put “first things first” pedagogically. One of the best examples        educators have been the most exciting parts of working on this
of this is the way in which Python handles variables. In C++ a        pro ject for me. By working together, we can increase the
variable is a name for a place that holds a thing. Variables          quality of materials available for our use and save valuable
have to be declared with types at least in part because the size      time. I invite you to join our community and look forward to
of the place to which they refer needs to be predetermined.           hearing from you. Please write to the authors at
Thus, the idea of a variable is bound up with the hardware of         feedback@thinkpython.com.
the machine. The powerful and fundamental concept of a
variable is already difficult enough for beginning students (in                       Jeffrey Elkner
both computer science and algebra). Bytes and addresses do                          Yorktown High School
not help the matter. In Python a variable is a name that refers
to a thing. This is a far more intuitive concept for beginning                      Arlington, Virginia
students and is much closer to the meaning of “variable” that




                                                             Preface #8
Como pensar como um cientista da Computação usando Python


                                                         Prefácio

     Tradução do capítulo anterior, que foi mantido por ter sido marcado como seção invariante pelo autor original.
Este livro deve sua existência à colaboração tornada possível
pela Internet e pelo movimento do software livre. Seus três       Encontrando um livro texto
autores ? um professor universitário, um secundário e um
programador profissional ? ainda não se encontraram               Tendo decidido usar Python em ambas as minhas classes
pessoalmente, mas temos podido trabalhar bem de perto e           introdutórias de ciência da computação do ano seguinte, o
temos sido ajudados por muitos colegas maravilhosos que têm       problema mais urgente era a falta de um livro texto disponível.
dedicado seu tempo e energia a ajudar a fazer deste um livro
cada vez melhor.                                                                 O conteúdo livre veio em socorro.
                                                                  Anteriormente naquele ano, Richard Stallman tinha me
              Achamos que este livro é um testemunho dos          apresentado a Allen Downey. Ambos havíamos escrito a
benefícios e possibilidades futuras deste tipo de colaboração,    Richard expressando interesse em desenvolver conteúdo
cujo modelo tem sido colocado por Richard Stallman e pela         educacional livre. Allen já tinha escrito um livro texto para o
Free Software Foundation.                                         primeiro ano de ciência da computação, How to Think Like a
                                                                  Computer Scientist. Quando li este livro, soube imediatamente
                                                                  que queria utilizá-lo nas minhas aulas. Era o texto mais claro e
                                                                  proveitoso em ciência da computação que eu tinha visto. Ele
Como e porque eu vim a usar Python                                enfatizava o processo de reflexão envolvido em programação
                                                                  em vez de características de uma linguagem em particular. Lê-
Em 1999, o Exame de Colocação Avançada em Ciência da              lo fez de mim imediatamente um melhor professor.
Computação da Comissão de Faculdades (College Board?s
Advanced Placement (AP) Computer Science) foi aplicado em                        O How to Think Like a Computer Scientist era
C++ pela primeira vez. Como em muitas escolas secundárias         não só um excelente livro, como também fora lançado sob
através do país, a decisão de mudar linguagens teve um            uma licença pública GNU, o que significava que ele poderia
impacto direto no currículo de ciência da computação na           ser usado livremente e modificado para atender as
Yorktown High School em Arlington, Virginia, onde leciono.        necessidades de seu usuário. Uma vez que eu havia decidido
Até então, Pascal era a linguagem didática para nossos cursos     usar Python, me ocorreu que eu poderia traduzir a versão
de primeiro ano e avançado. Mantendo a prática corrente de        original do livro de Allen do Java para a nova linguagem.
dar aos estudantes dois anos de exposição à mesma                 Apesar de não estar capacitado para escrever eu mesmo um
linguagem, tomamos a decisão de mudar para C++ no curso           livro texto, tendo o livro de Allen a partir do qual trabalhar
de primeiro ano para o ano letivo de 1997-98 de modo que          tornou possível para mim fazê-lo, ao mesmo tempo
estaríamos em sincronismo com a mudança da Comissão de            demonstrando que o modelo de desenvolvimento cooperativo
Faculdades (College Board?s) em relação ao curso avançado         tão bem utilizado em software poderia também funcionar para
para o ano seguinte.                                              conteúdo educacional.
               Dois anos depois, eu estava convencido que                       Trabalhar neste livro pelos últimos dois anos
C++ foi uma escolha infeliz para introduzir os alunos em          tem sido recompensador para mim e meus alunos, e eles
ciência da computação. Ao mesmo tempo em que é                    tiveram um grande papel neste processo. A partir do momento
certamente uma linguagem de programação muito poderosa,           em que eu podia fazer mudanças instantâneas assim que
também é uma linguagem extremamente difícil de aprender e         alguém encontrasse um erro ortográfico ou um trecho difícil,
de ensinar. Eu me encontrava constantemente lutando com a         eu os encorajei a procurar por erros no livro, dando a eles
sintaxe difícil do C++ e as múltiplas maneiras de fazer a         pontos de bonificação cada vez que eles fizessem uma
mesma coisa, e estava, como resultado, perdendo muitos            sugestão que resultasse em uma mudança no texto. Isto teve o
alunos desnecessariamente. Convencido de que deveria existir      duplo benefício de encorajá-los a ler o texto mais
uma linguagem melhor para a nossa classe de primeiro ano,         cuidadosamente e de ter o texto totalmente revisado por seus
fui procurar por uma alternativa ao C++.                          críticos mais importantes: alunos utilizando-o para aprender
                                                                  ciência da computação.
               Eu precisava de uma linguagem que pudesse
rodar nas máquinas em nosso laboratório Linux bem como nas                      Para a segunda metade do livro, sobre
plataformas Windows e Macintosh que a maioria dos alunos          programação orientada a objetos, eu sabia que seria preciso
tinha em casa. Eu precisava que ela fosse gratuita e disponível   alguém com uma maior experiência do que a minha em
eletronicamente, assim os alunos poderiam utilizá-la em casa      programação real para fazê-lo corretamente. O livro esteve em
independentemente de suas rendas. Eu queria uma linguagem         estado inacabado por quase um ano até que a comunidade de
que fosse utilizada por programadores profissionais, e que        software livre providenciasse mais uma vez os meios
tivesse uma comunidade de desenvolvimento ativa em torno          necessários para sua conclusão.
dela. Ela teria que suportar ambas, programação procedural e                     Eu recebi um e-mail de Chris Meyers mostrando
orientada a objetos. E, mais importante, deveria ser fácil de     interesse no livro. Chris é um programador profissional que
aprender e de ensinar. Quando considerei as alternativas tendo    começou a dar um curso de programação no ano anterior
em mente aquelas metas, Python sobressaiu-se como a melhor        usando Python no Lane Community College em Eugene,
candidata para a tarefa.                                          Oregon. A perspectiva de dar aquele curso ligou Chris ao
             Pedi para um dos talentosos estudantes de            livro, e ele começou a ajudar o trabalho imediatamente. Pelo
Yorktown, Matt Ahrens, que experimentasse Python. Em dois         final do ano letivo ele tinha criado um projeto colaborativo em
meses ele não só aprendeu a linguagem como também                 nosso Website em http://www.ibiblio.org/obp chamado
escreveu uma aplicação chamada pyTicket que possibilitou à        Python for Fun e estava trabalhando com alguns dos meus
nossa equipe reportar problemas de tecnologia pela Web. Eu        alunos mais avançados como um guru, guiando-os além de
sabia que Matt não poderia ter finalizado uma aplicação           onde eu poderia levá-los.
daquele porte em período tão curto em C++, esta realização,
combinada com a avaliação positiva do Python dada por Matt,
sugeriam que Python era a solução que eu estava procurando.


                                                         Prefácio #9
Como pensar como um cientista da Computação usando Python


                                                                 poderoso e fundamental de variável já é bastante difícil para o
Introduzindo programação com Python                              aluno iniciante (em ambas, ciência da computação e álgebra).
                                                                 Bytes e endereços não ajudam neste caso. Em Python uma
O processo de traduzir e utilizar How to Think Like a            variável é um nome que se refere a uma coisa. Este é um
Computer Scientist pelos últimos dois anos tem confirmado a      conceito muito mais intuitivo para alunos iniciantes e está
conveniência de Python no ensino de alunos iniciantes.           muito mais próximo do significado de ?variável? que eles
Python simplifica tremendamente os programas exemplo e           aprenderam em seus cursos de matemática. Eu tive muito
torna idéias importantes de programação mais fáceis de           menos dificuldade em ensinar variáveis este ano do que tive
ensinar.                                                         no passado, e gastei menos tempo ajudando aos alunos com
                                                                 problemas no uso delas.
               O primeiro exemplo do texto ilustra este ponto.
É o tradicional programa ?Alô mundo?, do qual na versão                      Um outro exemplo de como Python ajuda no
C++ do livro se parece com isto:                               ensino e aprendizagem de programação é em sua sintaxe para
                                                               função. Meus alunos têm sempre tido grande dificuldade na
#include <iostream.h>                                          compreensão de funções. O problema principal gira em torno
                                                               da diferença entre a definição de uma função e a chamada de
void main()                                                    uma função, e a distinção relacionada entre um parâmetro e
                                                               um argumento. Python vem em auxílio com uma sintaxe não
{                                                              apenas curta quanto bela. As definições de função começam
   cout << "Alô, mundo." << endl;                              com def, então eu simplesmente digo aos meus alunos ?
}                                                              Quando você define uma função, comece com def, seguido do
               Na versão Python, ele se transforma em:         nome da função que você está definindo; quando você chama
                                                               uma função, simplesmente chame-a digitando o nome dela?.
print "Alô, Mundo!"                                            Parâmetros ficam nas definições; argumentos vão com as
               Mesmo sendo um exemplo trivial, as vantagens chamadas. Não existem tipos de retorno, tipos de parâmetro
do Python saltam aos olhos. O curso de Ciência da ou passagem de parâmetros por valor ou por referência no
Computação I que ministro em Yorktown não tem pré- meio do caminho, permitindo-me ensinar funções em menos
requisitos, assim, muitos dos alunos que veem esse exemplo da metade do tempo que isto me tomava anteriormente, com
estão olhando para o seu primeiro programa. Alguns deles uma melhor compreensão.
estão indubitavelmente nervosos, por já terem ouvido falar
que programação de computadores é difícil de aprender. A efetividade deA utilização do Python da computação para
                                                                             nosso programa em ciência
                                                                                                         tem melhorado a
versão C++ tem sempre me forçado a escolher entre duas todos os estudantes. Eu vejo um nível geral de sucesso muito
opções insatisfatórias: ou explicar os comandos #include, void mais alto e um nível mais baixo de frustração do que
main(), {, e } e arriscar confundir ou intimidar alguns dos experimentei durante os dois anos em que ensinei C++. Eu
alunos logo assim que iniciam, ou dizer a eles ?Não se avanço mais rápido com melhores resultados. Mais alunos
preocupem com todas estas coisas agora; falaremos sobre elas deixam o curso com a habilidade de criar programas
mais tarde?, e correr o mesmo risco. O objetivo educacional significativos e com uma atitude positiva em relação a
neste ponto do curso é introduzir os alunos à idéia de experiência de programação que isso traz.
comando em programação e vê-los escrever seu primeiro
programa, deste modo introduzindo-os ao ambiente de
programação. O programa em Python tem exatamente o que é
necessário para conseguir isto, e nada mais.                   Construindo uma comunidade
              Comparar o texto explicativo do programa em
cada versão do livro ilustra ainda mais o que significa para o   Tenho recebido e-mails de todo o planeta de pessoas
aluno iniciante. Existem treze parágrafos de explicação do ?     utilizando este livro para aprender ou ensinar programação.
Alô, mundo!? na versão C++; na versão Python existem             Uma comunidade de usuários tem começado a emergir e
apenas dois. Mais importante, os onze parágrafos perdidos não    muitas pessoas têm contribuído com o projeto enviando seus
se ocupam das ?idéias chave? da programação de                   materiais para o Website cooperativo em:
computadores, mas com a minúcia da sintaxe C++. Vejo a
mesma coisa acontecendo através de todo o livro. Parágrafos                    http://www.thinkpython.com
inteiros simplesmente desaparecem da versão do texto para                      Com a publicação do livro em formato
Python porque a sintaxe muito mais clara do Python os torna      impresso, minha expectativa quanto ao crescimento da
desnecessários.                                                  comunidade de usuários é que ela seja contínua e acelerada. O
               Utilizar uma linguagem de tão alto nível como     surgimento desta comunidade de usuários e a possibilidade
Python, permite ao professor deixar para falar mais tarde        que sugere de colaboração semelhante entre educadores tem
sobre os níveis mais baixos, próximos à máquina, quando os       sido para mim a parte mais excitante do trabalho neste projeto.
alunos já terão a experiência necessária para ver com mais       Trabalhando juntos, podemos aumentar a qualidade do
sentido os detalhes. Desta maneira podemos ?por em primeiro      material disponível para o nosso uso e poupar tempo valioso.
lugar as primeiras coisas?, pedagogicamente. Um dos              Eu convido você a se juntar a nossa comunidade e espero
melhores exemplos disto é a maneira com que Python lida          ouvir algo de você. Por favor, escreva para os autores em
com variáveis. Em C++ uma variável é um nome para um             feedback@thinkpython.com.
lugar que guarda uma coisa. Variáveis têm de ser declaradas                    Jeffrey Elkner
com seu tipo pelo menos em parte por que o tamanho do lugar
a que se referem precisa ser predeterminado. Assim, a idéia de                 Yorktown High School
variável fica amarrada ao hardware da máquina. O conceito
                                                                               Arlington, Virginia




                                                       Prefácio #10
Como pensar como um cientista da Computação usando Python


                                                      Contributor List


To paraphrase the philosophy of the Free Software                         “unconsciously” in Chapter 1 needed to be changed
Foundation, this book is free like free speech, but not                   to “subconsciously”.
necessarily free like free pizza. It came about because of a
collaboration that would not have been possible without the          ●    Chris McAloon sent in several corrections to
GNU Free Documentation License. So we thank the Free                      Sections 3.9 and 3.10.
Software Foundation for developing this license and, of              ●    Matthew J. Moelter has been a long-time contributor
course, making it available to us.                                        who sent in numerous corrections and suggestions to
               We also thank the more than 100 sharp-eyed                 the book.
and thoughtful readers who have sent us suggestions and              ●    Simon Dicon Montford reported a missing function
corrections over the past few years. In the spirit of free                definition and several typos in Chapter 3. He also
software, we decided to express our gratitude in the form of a            found errors in the increment function in Chapter 13.
contributor list. Unfortunately, this list is not complete, but we
are doing our best to keep it up to date.                            ●    John Ouzts corrected the definition of “return value”
              If you have a chance to look through the list,              in Chapter 3.
you should realize that each person here has spared you and          ●    Kevin Parks sent in valuable comments and
all subsequent readers from the confusion of a technical error            suggestions as to how to improve the distribution of
or a less-than-transparent explanation, just by sending us a              the book.
note.
                                                                     ●    David Pool sent in a typo in the glossary of Chapter
               Impossible as it may seem after so many                    1, as well as kind words of encouragement.
corrections, there may still be errors in this book. If you
should stumble across one, please check the online version of        ●    Michael Schmitt sent in a correction to the chapter on
the book at http://thinkpython.com, which is the most up-to-              files and exceptions.
date version. If the error has not been corrected, please take a
minute to send us email at feedback@thinkpython.com. If we           ●    Robin Shaw pointed out an error in Section 13.1,
make a change due to your suggestion, you will appear in the              where the printTime function was used in an example
next version of the contributor list (unless you ask to be                without being defined.
omitted). Thank you!                                                 ●    Paul Sleigh found an error in Chapter 7 and a bug in
    ●    Lloyd Hugh Allen sent in a correction to Section 8.4.            Jonah Cohen’s Perlscript that generates HTML from
                                                                          LaTeX.
    ●    Yvon Boulianne sent in a correction of a semantic
         error in Chapter 5.                                         ●    Craig T. Snydal is testing the text in a course at Drew
                                                                          University. He has contributed several valuable
    ●    Fred Bremmer submitted a correction in Section 2.1.              suggestions and corrections.
    ●    Jonah Cohen wrote the Perl scripts to convert the           ●    Ian Thomas and his students are using the text in a
         LaTeX source for this book into beautiful HTML.                  programming course. They are the first ones to test
                                                                          the chapters in the latter half of the book, and they
    ●    Michael Conlon sent in a grammar correction in                   have made numerous corrections and suggestions.
         Chapter 2 and an improvement in style in Chapter 1,
         and he initiated discussion on the technical aspects of     ●    Keith Verheyden sent in a correction in Chapter 3.
         interpreters.
                                                                     ●    Peter Winstanley let us know about a longstanding
    ●    Benoit Girard sent in a correction to a humorous                 error in our Latin in Chapter 3.
         mistake in Section 5.6.
                                                                     ●    Chris Wrobel made corrections to the code in the
    ●    Courtney Gleason and Katherine Smith wrote                       chapter on file I/O and exceptions.
         horsebet.py, which was used as a case study in an
         earlier version of the book. Their program can now          ●    Moshe Zadka has made invaluable contributions to
         be found on the website.                                         this pro ject. In addition to writing the first draft of
                                                                          the chapter on Dictionaries, he provided continual
    ●    Lee Harr submitted more corrections than we have                 guidance in the early stages of the book.
         room to list here, and indeed he should be listed as
         one of the principal editors of the text.                   ●    Christoph Zwerschke sent several corrections and
                                                                          pedagogic suggestions, and explained the difference
    ●    James Kaylin is a student using the text. He has                 between gleich and selbe.
         submitted numerous corrections.
                                                                     ●    James Mayer sent us a whole slew of spelling and
    ●    David Kershaw fixed the broken catTwice function in               typographical errors, including two in the contributor
         Section 3.10.                                                    list.
    ●    Eddie Lam has sent in numerous corrections to               ●    Hayden McAfee caught a potentially confusing
         Chapters 1, 2, and 3. He also fixed the Makefile so                inconsistency between two examples.
         that it creates an index the first time it is run and
         helped us set up a versioning scheme.                       ●    Angel Arnal is part of an international team of
                                                                          translators working on the Spanish version of the
    ●    Man-Yong Lee sent in a correction to the example                 text. He has also found several errors in the English
         code in Section 2.4.                                             version.
    ●    David     Mayo     pointed    out    that    the   word     ●    Tauhidul Hoque and Lex Berezhny created the


                                                       Contributor List #11
Como pensar como um cientista da Computação usando Python

    illustrations in Chapter 1 and improved many of the        ●    Ben Logan sent in a number of typos and problems
    other illustrations.                                            with translating the book into HTML.
●   Dr. Michele Alzetta caught an error in Chapter 8 and       ●    Jason Armstrong saw the missing word in Chapter 2.
    sent some interesting pedagogic comments and
    suggestions about Fibonacci and Old Maid.                  ●    Louis Cordier noticed a spot in Chapter 16 where the
                                                                    code didn’t match the text.
●   Andy Mitchell caught a typo in Chapter 1 and a
    broken example in Chapter 2.                               ●    Brian Cain suggested several clarifications in
                                                                    Chapters 2 and 3.
●   Kalin Harvey suggested a clarification in Chapter 7
    and caught some typos.                                     ●    Rob Black sent in a passel of corrections, including
                                                                    some changes for Python 2.2.
●   Christopher P. Smith caught several typos and is
    helping us prepare to update the book for Python 2.2.      ●    Jean-Philippe Rey at Ecole Centrale Paris sent a
                                                                    number of patches, including some updates for
●   David Hutchins caught a typo in the Foreword.                   Python 2.2 and other thoughtful improvements.
●   Gregor Lingl is teaching Python at a high school in        ●    Jason Mader at George Washington University made
    Vienna, Austria. He is working on a German                      a number of useful suggestions and corrections.
    translation of the book, and he caught a couple of bad
    errors in Chapter 5.                                       ●    Jan Gundtofte-Bruun reminded us that “a error” is an
                                                                    error.
●   Julie Peters caught a typo in the Preface.
                                                               ●    Abel David and Alexis Dinno reminded us that the
●   Florin Oprina sent in an improvement in makeTime,               plural of “matrix” is “matrices”, not “matrixes”. This
    a correction in printTime, and a nice typo.                     error was in the book for years, but two readers with
                                                                    the same initials reported it on the same day. Weird.
●   D. J. Webre suggested a clarification in Chapter 3.
                                                               ●    Charles Thayer encouraged us to get rid of the semi-
●   Ken found a fistful of errors in Chapters 8, 9 and 11.           colons we had put at the ends of some statements and
●   Ivo Wever caught a typo in Chapter 5 and suggested              to clean up our use of “argument” and “parameter”.
    a clarification in Chapter 3.                               ●    Roger Sperberg pointed out a twisted piece of logic
●   Curtis Yanko suggested a clarification in Chapter 2.             in Chapter 3.




                                                 Contributor List #12
Como pensar como um cientista da Computação usando Python


                                                  Versão Brasileira


A versão traduzida para Português Brasileiro foi feita pela
equipe do site http://pensarpython.incubadora.fapesp.br,
abaixo relacionada:
     •   Adrovane Kade (adrovane)
     •   Alex Augusto da Luz dos Santos (nowayx)
     •   Claudio Fernando Berrondo Soares (cl-audio)
     •   Daniel Rosa Franzini (danielt3)
     •   Douglas Soares de Andrade (dsa)
     •   Fabio Rizzo Matos (fabrizmat)
     •   Imre Simon (imres)
     •   Joao Paulo Liberato (jpliberato)
     •   João Paulo Gomes Vanzuita (taken)
     •   Julio Monteiro (jmonteiro)
     •   Luciano Ramalho (luciano)
     •   Marcus Pereira (mvmendes)
     •   Mario O. de Menezes (modemene)
     •   Paulo J. S. Silva (pjssilva)
     •   Victor Rafael da Paixão Lopes (reije)
     •   marta mello (martamello)
     •   vitor gurgel (vitor_gurgel)
             Esta diagramação foi feita por Cárlisson
Galdino <bardo@castelodotempo.com>, com algumas
modificações visando a facilitar a publicação em duas colunas.
Alguns comentários de depuração feitos pela equipe de
tradução foram omitidos.




                                                   Versão Brasileira #13
Como pensar como um cientista da Computação usando Python


                                     Capítulo 1: O caminho do programa


O objetivo deste livro é ensinar o leitor a pensar como um
cientista da computação. Essa maneira de pensar combina
algumas das melhores características da matemática, da
engenharia e das ciências naturais. Como os matemáticos, os
cientistas da computação usam linguagens formais para
representar idéias (especificamente, computações). Como os
engenheiros, eles projetam coisas, montando sistemas a partir
de componentes e avaliando as vantagens e desvantagens de
diferentes alternativas. Como os cientistas naturais, eles                 O compilador lê o programa e o traduz
observam o comportamento de sistemas complexos, completamente antes que o programa comece a rodar. Neste
formulam hipóteses e testam previsões.                        caso, o programa escrito em linguagem de alto nível é
              A habilidade mais importante de um cientista da chamado de código fonte, e o programa traduzido programa é
                                                                                                              é chamado
                                                              de código objeto ou executável. Uma vez que um
computação é a solução de problemas. Solução de problemas compilado, você pode executá-lo repetidamente, sem que
é a habilidade de formular questões, pensar criativamente precise de nova tradução.
sobre soluções possíveis e expressar uma solução de forma
clara e precisa. Ocorre que aprender a programar é uma
excelente oportunidade de praticar a habilidade da solução de
problemas. É por isso que este capítulo se chama "O caminho
do programa".
               Em certo nível, você estará aprendendo a
programar, habilidade que é útil em si mesma. Em outro
nível, você usará a programação como um meio para atingir                Python é considerada uma linguagem
um objetivo. À medida que você for avançando na leitura, interpretada, porque os programas em Python são executados
esse objetivo ficará mais claro.                          por um interpretador. Existem duas maneiras de usar o
                                                          interpretador: no modo de linha de comando e no modo de
                                                          script. No modo de linha de comando, você digita programas
                                                          em Python e o interpretador mostra o resultado:
1.1 A linguagem de programação Python
                                                                $ python
                                                                Python 2.4.3 (#2, Oct 6 2006, 07:49:22)
Python é a linguagem de programação que você vai estudar
neste livro. Python é um exemplo de linguagem de                [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2
programação de alto nível; outras linguagens de alto nível      Type "help", "copyright", "credits" or "license" for
de que você já pode ter ouvido falar são C, C++, Perl e Java.   more information.
                                                                >>> print 1 + 1
              Como você pode deduzir a partir da expressão      2
"linguagem de alto nível", também existem as "linguagens de
baixo nível", às vezes chamadas de "linguagens de máquina"                     A primeira linha deste exemplo é o comando
ou "linguagem assembly" ("linguagens de montagem"). Dito        que inicia o interpretador Python. As três linhas seguintes são
de maneira simples, o computador só consegue executar           mensagens do interpretador. A quarta linha começa com >>>,
programas escritos em linguagens de baixo nível. Deste          que é o sinal usado pelo interpretador para indicar que ele está
modo, programas escritos em linguagens de alto nível            pronto. No exemplo anterior, digitamos print 1 + 1 e o
precisam ser processados antes que possam rodar. Esse           interpretador respondeu 2.
processamento extra toma algum tempo, o que é uma pequena                   Você também pode escrever um programa em
desvantagem em relação às linguagens de alto nível.           um arquivo e usar o interpretador para executar o conteúdo
              Mas as vantagens são enormes. Primeiro, é desse arquivo. Um arquivo como este é chamado de script.
muito mais fácil programar em uma linguagem de alto nível. Por exemplo, usamos um editor de texto para criar um
É mais rápido escrever programas em uma linguagem de alto arquivo chamado leticia.py com o seguinte conteúdo:
nível; eles são mais curtos e mais fáceis de ler e é mais
provável que estejam corretos. Segundo, as linguagens de alto print 1 + 1
nível são portáveis, o que significa que podem rodar em                     Por convenção, arquivos que contenham
diferentes tipos de computador, com pouca ou nenhuma programas em Python têm nomes que terminam com .py.
modificação. Programas em baixo nível só podem rodar em
um único tipo de computador e precisam ser re-escritos para                 Para executar o programa, temos de dizer ao
rodar em outro tipo.                                          interpretador o nome do script:
              Devido a essas vantagens, quase todos os $ python leticia.py
programas são escritos em linguagens de alto nível. As de 2
baixo nível são utilizadas somente para umas poucas                       Em outros ambientes de desenvolvimento, os
aplicações especializadas.                                   detalhes da execução de programas podem ser diferentes.
                                                             Além disso, a maioria dos programas são mais interessantes
              Dois tipos de programas processam linguagens do que esse...
de alto nível, traduzindo-as em linguagens de baixo nível:
interpretadores e compiladores. O interpretador lê um                     A maioria dos exemplos neste livro são
programa escrito em linguagem de alto nível e o executa, ou executados a partir da linha de comando. Trabalhar com a
seja, faz o que o programa diz. Ele processa o programa um linha de comando é conveniente no desenvolvimento e
pouco de cada vez, alternadamente: ora lendo algumas linhas, testagem de programas, porque você pode digitar os
ora realizando computações.                                  programas e executá-los imediatamente. Uma vez que você


                                        Capítulo 1: O caminho do programa #14
Como pensar como um cientista da Computação usando Python

tem um programa que funciona, deve guardá-lo em um script, lugar, o interpretador Python vai exibir uma mensagem de
de forma a poder executá-lo ou modificá-lo no futuro.      erro e vai terminar - e o programa não 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 experiência, entretanto,
1.2 O que é um programa?                                   cometerá menos erros e os localizará mais rápido.
Um programa é uma seqüência de instruções que                  1.3.2 Erros em tempo de execução (runtime errors)
especificam como executar uma computação. A computação
pode ser algo matemático, como solucionar um sistema de        O segundo tipo de erro é o erro de runtime, ou erro em tempo
equações ou encontrar as raízes de um polinômio, mas           de execução, assim chamado porque só aparece quando você
também pode ser uma computação simbólica, como buscar e        roda o programa. Esses erros são também conhecidos como
substituir uma palavra em um documento ou (estranhamente)      exceções, porque normalmente indicam que alguma coisa
compilar um programa.                                          excepcional (e ruim) aconteceu.
               Os detalhes são diferentes em diferentes                     Erros de runtime são raros nos programas
linguagens, mas algumas instruções básicas aparecem em simples que você vai ver nos primeiros capítulos - então, vai
praticamente todas as linguagens:                            demorar um pouco até você se deparar com um erro desse
                                                             tipo.
              entrar Pegar dados do teclado, de um arquivo
                      ou de algum outro dispositivo.
                                                             1.3.3 Erros de semântica
                 sair Mostrar dados na tela ou enviar dados
                      para um arquivo ou outro dispositivo.  O terceiro tipo de erro é o erro de semântica (mais
                                                             comumente chamado erro de lógica). Mesmo que o seu
            calcular Executar      operações     matemáticas programa tenha um erro de semântica, ele vai rodar com
                      básicas, como adição e multiplicação.  sucesso, no sentido de que o computador não vai gerar
                                                             nenhuma mensagem de erro. Só que o programa não vai fazer
           executar Checar certas condições e executar a a coisa certa, vai fazer alguma outra coisa. Especificamente,
  condicionalmente seqüência apropriada de instruções.       aquilo que você tiver dito para ele fazer.
             repetir Executar alguma ação repetidamente,                    O problema é que o programa que você
                      normalmente com alguma variação.       escreveu não é aquele que você queria escrever. O significado
               Acredite se quiser: isso é praticamente tudo. do programa (sua semântica ou lógica) está errado. Identificar
Todos os programas que você já usou, não importa quão erros semânticos pode ser complicado, porque requer que
complicados, são feitos de instruções mais ou menos você trabalhe de trás para frente, olhando a saída do programa
parecidas com essas. Assim, poderíamos definir programação e tentando imaginar o que ele está fazendo.
como o processo de dividir uma tarefa grande e complexa em
sub-tarefas cada vez menores, até que as sub-tarefas sejam 1.3.4 Depuração experimental (Debugging)
simples o suficiente para serem executadas com uma dessas
instruções básicas.                                          Uma das habilidades mais importantes que você vai adquirir é
               Isso pode parecer um pouco vago, mas vamos a de depurar. Embora possa ser frustrante, depurar é uma das
voltar a esse tópico mais adiante, quando falarmos sobre partes intelectualmente mais ricas, desafiadoras e
algoritmos.                                                  interessantes da programação.
                                                                            De certa maneira, a depuração é como um
                                                             trabalho de detetive. Você se depara com pistas, e tem que
1.3 O que é depuração (debugging)?                           deduzir os processos e eventos que levaram aos resultados
                                                             que aparecem.
Programar é um processo complicado e, como é feito por                        Depurar também é como uma ciência
seres humanos, freqüentemente conduz a erros. Por mero          experimental. Uma vez que você tem uma idéia do que está
capricho, erros em programas são chamados de bugs e o           errado, você modifica o seu programa e tenta de novo. Se a
processo de encontrá-los e corrigi-los é chamado de             sua hipótese estava correta, então você consegue prever o
depuração (debugging).                                          resultado da modificação e fica um passo mais perto de um
                                                                programa que funciona. Se a sua hipótese estava errada, você
               Três tipos de erro podem acontecer em um tem que tentar uma nova. Como Sherlock Holmes mostrou,
programa: erros de sintaxe, erros em tempo de execução "Quando você tiver eliminado o impossível, aquilo que
(runtime errors) e erros de semântica. Distinguir os três tipos restou, ainda que improvável, deve ser a verdade." (Arthur
ajuda a localizá-los mais rápido:                               Conan Doyle, O signo dos quatro).

1.3.1 Erros de sintaxe                                                      Para algumas pessoas, programação e
                                                               depuração são a mesma coisa. Ou seja, programar é o
Python só executa um programa se ele estiver sintaticamente    processo de gradualmente depurar um programa, até que ele
correto; caso contrário, o processo falha e retorna uma        faça o que você quer. A idéia é começar com um programa
mensagem de erro. Sintaxe se refere à estrutura de um          que faça alguma coisa e ir fazendo pequenas modificações,
programa e às regras sobre esta estrutura. Por exemplo, em     depurando-as conforme avança, de modo que você tenha
português, uma frase deve começar com uma letra maiúscula      sempre um programa que funciona.
e terminar com um ponto.                                                   Por exemplo, o Linux é um sistema operacional
              esta frase contém um erro de sintaxe. Assim que programa simples, que Linus Torvaldsmas começou como
                                                            um
                                                                contém milhares de linhas de código,
                                                                                                      usou para explorar
como esta                                                   o chip Intel 80386. De acordo com Larry Greenfield, "Um dos
              Para a maioria dos leitores, uns errinhos de primeiros projetos de Linus Torvalds foi um programa que
sintaxe não chegam a ser um problema significativo e é por deveria alternar entre imprimir AAAA e BBBB. Isso depois
isso que conseguimos ler a poesia moderna de e. e. cummings evoluiu até o Linux". (The Linux User's Guide Versão Beta 1)
sem cuspir mensagens de erro. Python não é tão indulgente.                 Capítulos posteriores farão mais sugestões
Se o seu programa tiver um único erro de sintaxe em algum

                                        Capítulo 1: O caminho do programa #15
Como pensar como um cientista da Computação usando Python

sobre depuração e outras práticas de programação.                              redundância nas linguagens naturais, o que
                                                                               freqüentemente as torna prolixas. As
                                                                               linguagens formais são menos redundantes e
1.4 Linguagens naturais e linguagens formais                                   mais concisas.
                                                                 literalidade As linguagens naturais estão cheias de
Linguagens naturais são as linguagens que as pessoas falam,                     expressões idiomáticas e metáforas. Se eu
como o português, o inglês e o espanhol. Elas não foram                         digo "Caiu a ficha", é possível que não exista
projetadas pelas pessoas (muito embora as pessoas tentem                        ficha nenhuma, nem nada que tenha caído.
colocar alguma ordem nelas); elas evoluíram naturalmente.                       Nas linguagens formais, não há sentido
                                                                                ambíguo.
              Linguagens formais são linguagens que foram                     Pessoas que crescem falando uma linguagem
projetadas por pessoas, para aplicações específicas. Por       natural -- ou seja, todo mundo - muitas vezes têm dificuldade
exemplo, a notação que os matemáticos usam é uma               de se acostumar com uma linguagem formal. De certa
linguagem formal, que é particularmente boa em denotar         maneira, a diferença entre linguagens formais e naturais é
relações entre números e símbolos. Os químicos usam uma        como a diferença entre poesia e prosa, porém mais acentuada:
linguagem formal para representar a estrutura química das
moléculas. E, mais importante:
                                                                     poesia As palavras são usadas pela sua sonoridade,
           Linguagens de programação são linguagens                         além de seus sentidos, e o poema como um
 formais que foram desenvolvidas para expressar                             todo cria um efeito ou uma reação emocional.
 computações.                                                               A ambigüidade não é apenas freqüente, mas na
                                                                            maioria das vezes, proposital.
              As linguagens formais tendem a ter regras
estritas quanto à sintaxe. Por exemplo, 3 + 3 = 6 é uma              prosa O sentido literal das palavras é mais
expressão matemática sintaticamente correta, mas 3=+6$ não                 importante, e a estrutura contribui mais para o
é. H2O é um nome químico sintaticamente correto, mas 2Zz                   significado. A prosa é mais fácil de analisar do
não é.                                                                     que a poesia, mas ainda é muitas vezes
                                                                           ambígua.
              As regras de sintaxe são de dois tipos, um
relacionado aos tokens, outro à estrutura. "Tokens" são os programas O significado de um programa de computador
elementos básicos da linguagem, como as palavras, números,                 é exato e literal, e pode ser inteiramente
e elementos químicos. Um dos problemas com 3=+6$ é que $                   entendido pela análise de seus tokens e de sua
não é um token válido em linguagem matemática (pelo menos                  estrutura.
até onde sabemos). Do mesmo modo, 2Zz é inválida porque                    Aqui vão algumas sugestões para a leitura de
não existe nenhum elemento cuja abreviatura seja Zz.          programas (e de outras linguagens formais). Primeiro, lembre-
              O segundo tipo de erro de sintaxe está se de que linguagens por       formais são muito mais densas do que
relacionado à estrutura de uma expressão -- quer dizer, ao linguagens naturais, muito isso, é mais demorado lê-las. A
modo como os tokens estão arrumados. A expressão 3=+6$ é estrutura, também, éde cima para baixo, da geralmente não a
                                                                                          importante, logo,                é
estruturalmente inválida, porque você não pode colocar um uma boa idéia ler                                  esquerda para
sinal de "mais" imediatamente após um sinal de "igual". Do direita. Em vez disso, os tokens e interpretando a estrutura.
                                                                                      aprenda a analisar o programa na sua
mesmo modo, fórmulas moleculares devem ter índices cabeça, identificando
subscritos colocados depois do nome do elemento, não antes. Finalmente, ortográficos e má importantes.com as quais você
                                                              como, erros
                                                                          os detalhes são
                                                                                             pontuação,
                                                                                                            Pequenas coisas,
              Faça este exercício: crie o que pareça ser uma pode se safar nas linguagens naturais, podem fazer uma
  frase bem estruturada em português com "tokens" grande diferença em uma linguagem formal.
  irreconhecíveis dentro dela. Depois escreva outra frase com
  todos os "tokens" válidos, mas com uma estrutura inválida.
              Quando você lê uma frase em português ou 1.5 O primeiro programa
uma expressão em uma linguagem formal, você tem de
imaginar como é a estrutura da frase (embora, em uma                          Tradicionalmente, o primeiro programa escrito
linguagem natural, você faça isso inconscientemente). Este em uma nova linguagem de programação é chamado de "Alô,
processo é chamado parsing (análise sintática).                Mundo!" porque tudo que ele faz é apresentar as palavras
              Por exemplo, quando você ouve a frase, "Caiu a "Alô, Mundo!". Em Python, ele é assim:
ficha", entende que "a ficha" é o sujeito e "caiu" é o verbo. print "Alô, Mundo!"
Uma vez que você analisou a frase, consegue entender o seu
significado, ou a semântica da frase. Assumindo que você                      Isso é um exemplo de um comando print, que,
saiba o que é uma ficha e o que significa cair, você entenderá na realidade, não "imprime" nada em papel. Ele apresenta o
o sentido geral dessa frase.                                   valor na tela. Neste caso, o resultado são as palavras:
               Muito embora as linguagens formais e as Alô, Mundo!
naturais tenham muitas características em comum -- tokens,                    As aspas no programa marcam o começo e o
estrutura, sintaxe e semântica -- existem muitas diferenças:    fim do valor; elas não aparecem no resultado final.
 ambigüidade As linguagens naturais estão cheias de                           Algumas pessoas julgam a qualidade de uma
                 ambigüidades, que as pessoas contornam linguagem de programação pela simplicidade do programa
                 usando pistas contextuais e outras "Alô, Mundo!". Por esse padrão, Python se sai tão bem
                 informações. Já as linguagens formais são quanto possível.
                 desenvolvidas para serem quase ou
                 totalmente desprovidas de ambigüidade, o
                 que significa que qualquer expressão tem 1.6 Glossário
                 precisamente        só      um        sentido,
                 independentemente do contexto.
                                                                        solução de O processo de formular um problema,
 redundância Para compensar a ambigüidade e reduzir                     problemas encontrar uma solução e expressar esta
                 mal-entendidos,        emprega-se       muita

                                        Capítulo 1: O caminho do programa #16
Como pensar como um cientista da Computação usando Python

      (problem solução.                                          (algorithm) categoria de problemas.
       solving)
                                                                         bug Erro em um programa.
   linguagem de Uma linguagem de programação como
alto nível (high- Python: projetada para ser fácil para os        depuração O processo de encontrar e remover
 level language) seres humanos a utilizarem.                     (debugging) qualquer um dos três tipos de erros de
                                                                             programação.
   linguagem de Uma linguagem de programação que é
baixo nível (low- concebida para ser fácil para um           sintaxe (syntax) A estrutura de um programa.
 level language) computador, tal como a linguagem de          erro de sintaxe Erro em um programa, que torna
                  máquina ou a linguagem montagem              (syntax error) impossível a análise sintática (logo,
                  (assembly language)                                         também impossível a interpretação).
  portabilidade Propriedade que um programa tem, de           erro em tempo Erro que não ocorre até que o programa
   (portability) rodar em mais de um tipo de                     de execução seja executado, mas que impede que o
                 computador.                                 (runtime error) programa continue.
    interpretar Executar um programa escrito em uma                  exceção Um outro nome para um erro em tempo
     (interpret) linguagem de alto nível, traduzindo-o            (exception) de execução ou erro de runtime.
                 uma linha de cada vez.
                                                                     erro de Erro em um programa, que o leva a fazer
       compilar Traduzir todo um programa escrito em              semântica algo diferente do que pretendia o
      (compile) uma linguagem de alto nível para uma de      (semantic error) programador.
                baixo nível de um só vez, em preparação
                para uma execução posterior.                       semântica O significado de um programa.
    código fonte Um programa em uma linguagem de alto            (semantics)
   (source code) nível, antes de ter sido compilado.              linguagem Qualquer língua falada pelos seres
                                                             natural (natural humanos      que tenha   evoluído
  código objeto A saída do compilador, depois que ele              language) naturalmente.
   (object code) traduziu o programa.
      executável Um outro nome para código objeto que             linguagem Qualquer linguagem desenvolvida pelas
    (executable) está pronto para ser executado.              formal (formal pessoas para propósitos específicos, tais
                                                                   language) como, a representação de idéias
          script Um programa guardado em um arquivo                          matemáticas     ou     programas      de
                 (normalmente um que será interpretado).                     computadores; todas as linguagens de
                                                                             programação são linguagens formais.
      programa Conjunto de instruções que especifica
     (program) uma computação.                                 átomo (token) Um elemento básico da estrutura
                                                                             sintática de um programa, análogo a uma
                                                                             palavra em uma linguagem natural.
                                                             análise sintática Examinar um programa e analisar sua
                                                                       (parse) estrutura sintática.
                                                              comando print Instrução    que   leva    o   interpretador




                                      Capítulo 1: O caminho do programa #17
Como pensar como um cientista da Computação usando Python

   (`print` Python a apresentar um valor na tela.
statement)




                                 Capítulo 1: O caminho do programa #18
Como pensar como um cientista da Computação usando Python


                                  Capítulo 2: Variáveis, expressões e comandos



                                                                  variável é um nome que se refere a um valor.
2.1 Valores e tipos
                                                                        O comando de atribuição cria novas variáveis e
O valor (por exemplo, letras e números) é uma das coisas dá a elas valores:
fundamentais que um programa manipula. Os valores que já >>> mensagem = "E aí, Doutor?"
vimos até agora foram o 2 (como resultado, quando
adicionamos 1 + 1) e "Alô, Mundo!".                      >>> n = 17
                                                                  >>> pi = 3.14159
              Esses valores pertencem a tipos diferentes: 2 é                    Este exemplo faz três atribuições. A primeira
um inteiro, e "Alô, Mundo!" é uma string, assim chamada           atribui a string "E aí, Doutor?" a uma nova variável chamada
porque "string", em inglês, quer dizer seqüência, série, cadeia   mensagem. A segunda dá o valor inteiro 17 a n, e a terceira
(de caracteres), ou neste caso, "série de letras". Você (e o      atribui o número de ponto flutuante 3.14159 à variável
interpretador) consegue identificar strings porque elas           chamada pi.
aparecem entre aspas.
                                                                            Uma maneira comum de representar variáveis
             O comando print também funciona com no papel é escrever o nome delas com uma seta apontando
inteiros:                                                     para o valor da variável. Esse tipo de figura é chamado de
 >>> print 4
                                                              diagrama de estado porque mostra em que estado cada
                                                              variável está (pense nisso como o estado de espírito da
 4                                                            variável). O diagrama a seguir mostra o resultado das
             Se você estiver em dúvida sobre qual é o tipo de instruções de atribuição:
um determinado valor, o interpretador pode revelar:
>>> type("Alô, Mundo!")
<type 'string'>
>>> type(17)
<type 'int'>
               Nenhuma surpresa: strings pertencem ao tipo
string e inteiros pertencem ao tipo int. Menos obviamente,
números com um ponto decimal pertencem a um tipo
chamado float, porque estes números são representados em
um formato chamado ponto flutuante1:
>>> type(3.2)                                                                  O comando print também funciona com
<type 'float'>                                                    variáveis:
            O que dizer de valores como "17" e "3.2"? Eles >>> print mensagem
parecem números, mas estão entre aspas, como strings:      E aí, Doutor?
>>> type("17")                                                    >>> print n
<type 'string'>                                                   17
>>> type("3.2")                                                   >>> print pi
<type 'string'>                                                   3.14159
              Eles são strings.                                               Em cada um dos casos, o resultado é o valor da
                                                                variável. Variáveis também têm tipo; novamente, podemos
              Ao digitar um número grande, é tentador usar perguntar ao interpretador quais são eles:
pontos entre grupos de três dígitos, assim: 1.000.000. Isso não
funciona porque Python usa o ponto como separador decimal. >>> type(mensagem)
Usar a vírgula, como se faz em inglês, resulta numa expressão <type 'string'>
válida, mas não no número que queríamos representar:            >>> type(n)
>>> print 1,000,000                                               <type 'int'>
1 0 0                                                             >>> type(pi)
                                                                  <type 'float'>
              Não é nada do que se esperava! Python
interpreta 1,000,000 como uma tupla, algo que veremos no                      O tipo de uma variável é o tipo do valor ao qual
Capítulo 9. Por hora, lembre-se apenas de não colocar vírgulas ela se refere.
nos números.

                                                                  2.3 Nomes de variáveis e palavras reservadas
2.2 Variáveis
                                                           Os programadores geralmente escolhem nomes significativos
Uma das características mais poderosas de uma linguagem de para suas variáveis -- eles documentam para o quê a variável é
programação é a habilidade de manipular variáveis. Uma usada.
                                                                         Nomes de variáveis podem ser arbitrariamente
1 N.T.: Observe o uso de ponto no lugar da vírgula para    longos. Eles podem conter tanto letras quanto números, mas
   separar a parte inteira da parte fracionária.

                                     Capítulo 2: Variáveis, expressões e comandos #19
Como pensar como um cientista da Computação usando Python

têm de começar com uma letra. Embora seja válida a comando, o interpretador avalia e exibe o resultado:
utilização de letras maiúsculas, por convenção, não usamos.
Se você o fizer, lembre-se de que maiúsculas e minúsculas são >>> 1 + 1
diferentes. Bruno e bruno são variáveis diferentes.            2
               O caractere para sublinhado ( _ ) pode aparecer           Embora expressões contenham valores,
em um nome. Ele é muito utilizado em nomes com múltiplas variáveis e operadores, nem toda expressão contém todos estes
palavras, tal como em meu_nome ou preco_do_cha_na_china. elementos. modovalor uma variável:
                                                               do mesmo
                                                                        Um
                                                                           que
                                                                               por si só é considerado uma expressão,
               Se você der a uma variável um nome inválido,
causará um erro de sintaxe:                                    >>> 17
                                                               17
>>> 76trombones = "grande parada"                              >>> x
SyntaxError: invalid syntax                                    2
>>> muito$ = 1000000                                                        Avaliar uma expressão não é exatamente a
SyntaxError: invalid syntax                                    mesma coisa que imprimir um valor:
>>> class = "Ciencias da Computacao 101"
                                                               >>> mensagem = "E aí, Doutor?"
SyntaxError: invalid syntax
                                                               >>> mensagem
                76trombones é inválida porque não começa com
uma letra. muito$ é inválida porque contém um caractere        'E aí, Doutor?'
ilegal, o cifrão. Mas o que está errado com class?             >>> print mensagem
                                                               E aí, Doutor?
              Ocorre que class é uma das palavras
reservadas em Python. Palavras reservadas definem as regras usa o mesmo formato que você usaria valorentrar com o valor.
                                                                           Quando Python exibe o
                                                                                                 para
                                                                                                       de uma expressão,
e a estrutura da linguagem e não podem ser usadas como No caso de strings, isso significa que as aspas são incluídas
nomes de variáveis.                                         [#]_. Mas o comando print imprime o valor da expressão, que,
              Python tem 29 palavras reservadas:            neste caso, é o conteúdo da string.
and         def     exec      if        not     return                     Num script, uma expressão sozinha é um
assert      del     finally import or           try
                                                            comando válido, porém sem efeito. O script:
break      elif        for        in       pass    while       17
class      else        from       is       print   yield       3.2
continue   except      global     lambda   raise               "Alô, Mundo!"
              Pode ser útil ter essa lista à mão. Se o         1 + 1
interpretador acusar erro sobre um de seus nomes de variável                não produz qualquer saída. Como você mudaria
e você não souber porquê, veja se o nome está na lista.      o "script" para exibir os valores destas quatro expressões?


2.4 Comandos                                                   2.6 Operadores e operandos
Um comando é uma instrução que o interpretador Python Operadores são símbolos especiais que representam
pode executar. Vimos até agora dois tipos de comandos: de computações como adição e multiplicação. Os valores que o
exibição (print) e de atribuição.                           operador usa são chamados operandos.
              Quando você digita um comando na linha de                   Todas as expressões seguintes são válidas em
comando, o Python o executa e mostra o resultado, se houver Python e seus significados são mais ou menos claros:
um. O resultado de um comando print é a exibição de um
valor. Comandos de atribuição não produzem um resultado 20+32 hora-1 hora*60+minuto minuto/60 5**2 
visível.                                                    (5+9)*(15-7)
              Um script normalmente contém uma seqüência                  Em Python, os símbolos +, -, / e o uso de
de comandos. Se houver mais de um comando, os resultados parênteses para agrupamento têm o mesmo significado que em
aparecerão um de cada vez, conforme cada comando seja matemática. O asterisco (*) é o símbolo para multiplicação e
executado.                                                  ** é o símbolo para potenciação.
                Por exemplo, o "script":                                    Quando um nome de variável aparece no lugar
                                                               de um operando, ele é substituído pelo valor da variável, antes
print 1                                                        da operação ser executada.
x = 2
                                                                             Adição, subtração, multiplicação e potenciação
print 2                                                        fazem o que se espera, mas você pode ficar surpreso com a
                produz a saída:                                divisão. A operação seguinte tem um resultado inesperado:
1                                                              >>> minuto = 59
2                                                              >>> minuto/60
                Novamente, o comando de atribuição não         0
produz saída.                                                                O valor de minuto é 59 e, em aritmética
                                                               convencional, 59 dividido por 60 é 0,98333, não 0. A razão
                                                               para a discrepância é que Python está realizando uma divisão
2.5 Avaliando expressões                                       inteira.
                                                                     Quando ambos os operandos são inteiros, o
Uma expressão é uma combinação de valores, variáveis e resultado tem de ser também um inteiro e, por convenção, a
operadores. Se você digitar uma expressão na linha de divisão inteira sempre arredonda para baixo, mesmo em casos


                                     Capítulo 2: Variáveis, expressões e comandos #20
Como pensar como um cientista da Computação usando Python

como este, em que o inteiro seguinte está muito próximo:    como 4*3 equivale a 4+4+4, não é de estranhar que "Legal"*3
                                                            seja o mesmo que "Legal"+"Legal"+"Legal". Por outro lado,
>>> minuto*100/60                                           uma diferença significativa separa concatenação e repetição de
98                                                          adição e multiplicação. Você saberia mencionar uma
             De novo, o resultado é arredondado para baixo, propriedade da adição e da multiplicação que não ocorre na
mas agora pelo menos a resposta é aproximadamente correta. concatenação e na repetição?
A alternativa é usar a divisão em ponto flutuante, o que
veremos no capítulo 3.
                                                               2.9 Composição
2.7 Ordem dos operadores                                     Até agora, vimos os elementos de um programa -- variáveis,
                                                             expressões, e instruções ou comandos -- isoladamente, sem
Quando mais de um operador aparece em uma expressão, a mencionar como combiná-los.
ordem de avaliação depende das regras de precedência.                      Uma das características mais práticas das
Python segue as mesmas regras de precedência para seus linguagens de programação é a possibilidade de pegar
operadores matemáticos que a matemática. O acrônimo pequenos blocos e combiná-los numa composição. Por
PEMDAS é uma maneira prática de lembrar a ordem das exemplo, nós sabemos como somar números e sabemos como
operações:                                                   exibi-los; acontece que podemos fazer as duas coisas ao
    ● Parênteses têm a mais alta precedência e podem ser mesmo tempo:
        usados para forçar uma expressão a ser avaliada na >>> print 17 + 3
        ordem que você quiser. Já que expressões entre
        parênteses são avaliadas primeiro, 2 * (3-1) é 4, e 20
        (1+1)**(5-2) é 8. Você também pode usar parênteses                 Na realidade, a soma tem que acontecer antes da
        para tornar uma expressão mais fácil de ler, como em impressão, assim, as ações não estão na realidade acontecendo
        (minuto * 100) / 60, ainda que isso não altere o ao mesmo tempo. O ponto é que qualquer expressão
        resultado.                                           envolvendo números, strings, e variáveis pode ser usada
                                                             dentro de um comando print. Você já tinha visto um exemplo
    ● Exponenciação ou potenciação tem a próxima disto:
        precedência mais alta, assim 2**1+1 é 3 e não 4, e
        3*1**3 é 3 e não 27.                                  print "Número de minutos desde a meia-noite: ", 
                                                               hora*60+minuto
    ●   Multiplicação e Divisão têm a mesma precedência,
        que é mais alta do que a da Adição e da Subtração,                  Esta possibilidade pode não parecer muito
        que também têm a mesma precedência. Assim 2*3-1        impressionante agora, mas você verá outros exemplos em que
        dá 5 em vez de 4, e 2/3-1 é -1, não 1 (lembre-se de    a composição torna possível expressar computações
        que na divisão inteira, 2/3=0).                        complexas de modo limpo e conciso.
    ●   Operadores com a mesma precedência são avaliados                     Atenção: Existem limites quanto ao lugar onde
        da esquerda para a direita. Assim, na expressão        você pode usar certos tipos de expressão. Por exemplo, o lado
        minuto*100/60, a multiplicação acontece primeiro,      esquerdo de um comando de atribuição tem que ser um nome
        resultando em 5900/60, o que se transforma             de variável, e não uma expressão. Assim, o seguinte não é
        produzindo 98. Se as operações tivessem sido           válido: minuto+1 = hora.
        avaliadas da direita para a esquerda, o resultado
        poderia ter sido 59*1, que é 59, que está errado.
                                                               2.11 Glossário
2.8 Operações com strings                                         valor (value) Um número ou string (ou outra coisa que
                                                                                ainda vamos conhecer) que pode ser
De maneira geral, você não pode executar operações                              atribuída a uma variável ou computada em
matemáticas em strings, ainda que as strings se pareçam com                     uma expressão.
números. O que segue é inválido (assumindo que mensagem é
do tipo string):                                                     tipo (type) Um conjunto de valores. O tipo de um
                                                                                 valor determina como ele pode ser usado
mensagem-1     "Alô"/123    mensagem*"Alô"      "15"+2                           em expressões. Até agora, os tipos vistos
              Interessante é o operador +, que funciona com                      são: inteiros (tipo int), números em ponto-
strings, embora ele não faça exatamente o que você poderia                       flutuante (tipo float) e strings (tipo string).
esperar. Para strings, o operador + representa concatenação,           ponto- Formato para representar números que
que significa juntar os dois operandos ligando-os pelos             flutuante possuem partes fracionárias.
extremos. Por exemplo:                                       (floating-point)
fruta = "banana"                                                       variável Nome que se refere a um valor.
assada = " com canela"                                               (variable)
print fruta + assada
              A saída deste programa é banana com canela. O            comando Trecho de código que representa uma
espaço antes da palavra com é parte da string e é necessário        (statement) instrução ou ação. Até agora, os comandos
para produzir o espaço entre as strings concatenadas.                           vistos foram de atribuição e exibição.

               O operador * também funciona com strings; ele         atribuição Comando que atribui um valor a uma
realiza     repetição.    Por     exemplo,  "Legal"*3      é      (assignment) variável.
"LegalLegaLegal". Um dos operadores tem que ser uma               diagrama de Representação gráfica de um conjunto de
string; o outro tem que ser um inteiro.                           estado (state variáveis e os valores aos quais elas se
                                                                     diagram) referem.
             Por um lado, esta interpretação de + e * faz
sentido pela analogia entre adição e multiplicação. Assim

                                   Capítulo 2: Variáveis, expressões e comandos #21
Como pensar como um cientista da Computação usando Python

palavra-chave Palavra reservada usada pelo compilador
    (keyword) para analisar o programa; você não pode
              usar palavras-chave como if, def, e while
              como nomes de variáveis.
     operador Símbolo especial que representa uma
    (operator) computação simples, como adição,
               multiplicação ou concatenação de strings.
    operando Um dos valores sobre o qual o operador
    (operand) opera.
    expressão Combinação de variáveis, operadores e
  (expression) valores, que representa um resultado único.
       avaliar Simplificar uma expressão através da
    (evaluate) realização de operações, para produzir um
               valor único.
divisão inteira Operação que divide um inteiro por outro e
       (integer resulta em um inteiro. A divisão inteira
      division) resulta no número de vezes que o
                numerador é divisível pelo denominador e
                descarta qualquer resto.
    regras de O conjunto de regras que governa a ordem
  precedência em que expressões envolvendo múltiplos
     (rules of operadores e operandos são avaliadas.
  precedence)
  concatenar Juntar dois operandos lado a lado.
(concatenate)
  composição Habilidade de combinar expressões          e
(composition) comandos simples em expressões            e
              comandos compostos, de forma              a
              representar computações complexas        de
              forma concisa.
   comentário Informação em um programa dirigida a
   (comment) outros programadores (ou qualquer pessoa
              que esteja lendo o código fonte) e que não
              tem efeito na execução do programa.




                                Capítulo 2: Variáveis, expressões e comandos #22
Como pensar como um cientista da Computação usando Python


                                               Capítulo 3: Funções




                                                                '32'
3.1 Chamadas de funções                                         >>> str(3.14149)
                                                                '3.14149'
Você já viu um exemplo de uma chamada de função:
                                                                          Pode parecer curioso que Python faça distinção
>>> type('32')                                              entre o valor inteiro 1 e o valor em ponto flutuante 1.0. Eles
<type 'str'>
                                                            podem representar o mesmo número, mas pertencem a tipos
                                                            diferentes. A razão é que eles são representados de modo
              O nome da função é type e ela exibe o tipo de diferente dentro do computador.
um valor ou variável. O valor ou variável, que é chamado de
argumento da função, tem que vir entre parênteses. É comum
se dizer que uma função 'recebe' um valor e 'retorna' um
resultado. O resultado é chamado de valor de retorno.       3.3 Coerção entre tipos
               Em vez de imprimir um valor de retorno,
podemos atribui-lo a uma variável:                           Agora que podemos converter entre tipos, temos outra
                                                             maneira de lidar com a divisão inteira. Voltando ao exemplo
 >>> bia = type('32')                                        do capítulo anterior, suponha que queiramos calcular a fração
 >>> print bia                                               de hora que já passou. A expressão mais óbvia, minuto / 60,
 <type 'str'>
                                                             faz aritmética inteira, assim, o resultado é sempre 0, mesmo
                                                             aos 59 minutos passados da hora.
               Como outro exemplo, a função id recebe um
valor ou uma variável e retorna um inteiro, que atua como um                Uma solução é converter minuto para ponto
identificador único para aquele valor:                       flutuante e fazer a divisão em ponto flutuante:
>>> id(3)                                                       >>> minuto = 59
134882108                                                       >>> float(minuto) / 60
>>> bia = 3                                                     0.983333333333
>>> bia(beth)                                                                Opcionalmente, podemos tirar vantagem das
134882108                                                      regras de conversão automática entre tipos, chamada de
             Todo valor tem um id, que é um número único coerção de tipos. Para os operadores matemáticos, se
relacionado ao local onde ele está guardado na memória do qualquer operando for um float, o outro é automaticamente
computador. O id de uma variável é o id do valor a qual ela se convertido para float:
refere.                                                        >>> minuto = 59
                                                                >>> minuto / 60.0
                                                                0.983333333333
3.2 Conversão entre tipos                                                     Fazendo o denominador um float, forçamos o
                                                                Python a fazer a divisão em ponto flutuante.
Python provê uma coleção de funções nativas que convertem
valores de um tipo em outro. A função int recebe um valor e o
converte para inteiro, se possível, ou, se não, reclama:
                                                                3.4 Funções matemáticas
>>> int('32')
32                                                         Em matemática, você provavelmente já viu funções como
>>> int('Alô')                                             seno (sin) e log, e aprendeu a resolver expressões como
ValueError: invalid literal for int() : Alô                sin(pi/2) e log(1/x). Primeiro você resolve e expressão entre
                                                           parênteses (o argumento). Por exemplo, pi/2 é
              int também pode converter valores em ponto aproximadamente 1,571, e 1/x é 0.1 (se x for 10,0).
flutuante para inteiro, mas lembre que isso trunca a parte
fracionária:                                                              Aí você avalia a função propriamente dita, seja
                                                           procurando numa tabela ou realizando vários cálculos. O sin
>>> int(3.99999)                                           de 1,571 é 1 e o log de 0,1 é -1 (assumindo que log indica o
3                                                          logaritmo na base 10).
>>> int(-2.3)
-2
                                                                         Este processo pode ser aplicado repetidamente
                                                          para avaliar expressões mais complicadas, como
            A função float converte inteiros e strings em log(1/sin(pi/2)). Primeiro você avalia o argumento na função
números em ponto flutuante:                               mais interna, depois avalia a função e assim por diante.
>>> float(32)                                                                 Python tem um módulo matemático que provê a
32.0                                                            maioria das funções matemáticas mais familiares. Um módulo
>>> float('3.14159')                                            é um arquivo que contém uma coleção de funções
3.14159                                                         relacionadas agrupadas juntas.
              Finalmente, a função str converte para o tipo                  Antes de podermos usar as funções contidas em
string:                                                         um módulo, temos de importá-lo:
>>> str(32)                                                     >>> import math


                                                Capítulo 3: Funções #23
Como pensar como um cientista da Computação usando Python


              Para chamar uma das funções, temos que lista de parâmetros especifica que informação, se houver
especificar o nome do módulo e o nome da função, separados alguma, você tem que fornecer para poder usar a nova função.
por um ponto. Esse formato é chamado de notação de ponto:
                                                                        Uma função pode ter quantos comandos forem
>>> decibel = math.log10(17.0)                             necessários, mas eles precisam ser endentados a partir da
>>> angulo = 1.5                                           margem esquerda. Nos exemplos deste livro, usaremos uma
>>> altura = math.sin(angulo)
                                                           endentação de dois espaços.
             A primeira instrução atribui a decibel o              As primeiras funções que vamos mostrar não
logaritmo de 17 na base 10. Existe também uma função terão parâmetros, então, a sintaxe terá esta aparência:
chamada log, que pega o logaritmo na base e.
                                                                  def novaLinha():
               A terceira instrução encontra o seno do valor da     print
variável angulo. sin e as outras funções trigonométricas (cós,
tan, etc.) recebem argumentos em radianos. Para converter de                    Esta função é chamada de novaLinha. Os
graus em radianos, divida por 360 e multiplique por 2*pi. Por     parênteses vazios indicam que ela não tem parâmetros.
exemplo, para encontrar o seno de 45 graus, primeiro calcule      Contém apenas um único comando, que gera como saída um
o ângulo em radianos e depois ache o seno:                        caractere de nova linha (isso é o que acontece quando você
                                                                  usa um comando print sem qualquer argumento).
>>> graus = 45
                                                                              A sintaxe para a chamada desta nova função é a
>>> angulo = graus * 2 * math.pi / 360.0                          mesma sintaxe para as funções nativas:
>>> math.sin(angulo)
0.707106781187                                                    print 'Primeira Linha.'
            A constante pi também é parte do módulo math.         novaLinha()
Se você sabe geometria, pode checar o resultado anterior          print 'Segunda Linha.'
comparando-o com a raiz quadrada de dois dividido por dois:                    A saída deste programa é:
>>> math.sqrt(2) / 2.0                                            Primeira Linha.
0.707106781187
                                                                  Segunda Linha.
                                                                              Observe o espaço extra entre as duas linhas. E
3.5 Composição                                                    se quiséssemos mais espaço entre as linhas? Poderíamos
                                                                  chamar a mesma função repetidamente:
Do mesmo modo como nas funções matemáticas, as funções            print 'Primeira Linha.'
do Python podem ser compostas, o que significa que você           novaLinha()
pode usar uma expressão como parte de outra. Por exemplo,         novaLinha()
você pode usar qualquer expressão como um argumento para
uma função:                                                       novaLinha()
                                                                  print 'Segunda Linha.'
>>> x = math.cos(angulo + pi/2)                                          Ou poderíamos escrever uma nova função
            Esta instrução toma o valor de pi, divide-o por chamada tresLinhas, que produzisse três novas linhas:
2, e soma o resultado ao valor de angulo. A soma é então
passada como um argumento para a função cos.                def tresLinhas() :
                                                                    novaLinha()
              Você também pode pegar o resultado de uma             novaLinha()
função e passá-lo como um argumento para outra:
                                                                    novaLinha()
>>> x = math.exp(math.log(10.0))
              Esta instrução encontra o logaritmo base e de 10 print 'Primeira Linha.'
e então eleva e àquela potência. O resultado é atribuído a x.  tresLinhas()
                                                                  print 'Segunda Linha.'
                                                                                Esta função contém três comandos, todos com
3.6 Adicionando novas funções                                     recuo de dois espaços a partir da margem esquerda. Já que o
                                                                  próximo comando não está endentado, Python reconhece que
                                                                  ele não faz parte da função.
Até aqui, temos utilizado somente as funções que vêm com
Python, mas também é possível adicionar novas funções. Criar                Algumas coisas que devem ser observadas sobre
novas funções para resolver seus próprios problemas é uma este programa:
das coisas mais úteis de uma linguagem de programação de
propósito geral.                                                   1. Você pode chamar o mesmo procedimento
                                                                       repetidamente. Isso é muito comum, além de útil.
              No contexto de programação, função é uma
seqüência nomeada de instruções ou comandos, que realizam          2. Você pode ter uma função chamando outra função;
uma operação desejada. Esta operação é especificada numa               neste caso tresLinhas chama novaLinha.
definição de função. Até agora, as funções que usamos neste
livro são pré-definidas e suas definições não foram                         Pode não estar claro, até agora, de que vale o
apresentadas. Isso demonstra que podemos usar funções sem esforço de criar novas funções - existem várias razões, mas
ter que nos preocupar com os detalhes de suas definições.     este exemplo demonstra duas delas:
              A sintaxe para uma definição de função é:            ● Criar uma nova função permite que você coloque
                                                                       nome em um grupo de comandos. As funções podem
 def NOME( LISTA DE PARAMETROS ) :                                     simplificar um programa ao ocultar uma computação
   COMANDOS                                                            complexa por trás de um simples comando cujo
                                                                       nome pode ser uma palavra em português, em vez de
              Você pode usar o nome que quiser para as                 algum código misterioso.
funções que criar, exceto as palavras reservadas do Python. A

                                                  Capítulo 3: Funções #24
Como pensar como um cientista da Computação usando Python

    ●   Criar uma nova função pode tornar o programa            lembra que uma função pode chamar outra. Enquanto estiver
        menor, por eliminar código repetido. Por exemplo,       no meio de uma função, o programa poderia ter de executar os
        um atalho para 'imprimir' nove novas linhas             comandos em uma outra função. Mas enquanto estivesse
        consecutivas é chamar tresLinhas três vezes.            executando esta nova função, o programa poderia ter de
                                                                executar ainda outra função!
            Como exercício, escreva uma função chamada
 noveLinhas que use tresLinhas para imprimir nove linhas               Felizmente, Python é adepto de monitorar a
 em branco. Como você poderia imprimir vinte e sete novas posição onde está, assim, cada vez que uma função se
 linhas?                                                  completa, o programa retoma de onde tinha parado na função
                                                          que a chamou. Quando chega ao fim do programa, ele
                                                          termina.
3.7 Definições e uso                                                      Qual a moral dessa história sórdida? Quando
                                                          você ler um programa, não o leia de cima para baixo. Em vez
Reunindo os fragmentos de código da Seção 3.6, o programa disso, siga o fluxo de execução.
completo fica assim:
def novaLinha() :
                                                                3.9 Parâmetros e argumentos
  print
                                                                Algumas das funções nativas que você já usou requerem
def tresLinhas() :                                              argumentos, aqueles valores que controlam como a função faz
  novaLinha()                                                   seu trabalho. Por exemplo, se você quer achar o seno de um
  novaLinha()                                                   número, você tem que indicar qual número é. Deste modo, sin
  novaLinha()                                                   recebe um valor numérico como um argumento.
                                                                              Algumas funções recebem mais de um
print 'Primeira Linha.'                                         argumento. Por exemplo, pow recebe dois argumentos, a base
tresLinhas()                                                    e o expoente. Dentro da função, os valores que lhe são
print 'Segunda Linha.'                                          passados são atribuídos a variáveis chamadas parâmetros.
               Esse programa contém duas definições de                      Veja um exemplo de uma função definida pelo
funções: novaLinha e tresLinhas. Definições de funções são usuário, que recebe um parâmetro:
executadas como quaisquer outros comandos, mas o efeito é
criar a nova função. Os comandos dentro da definição da def imprimeDobrado(bruno):
função não são executados até que a função seja chamada,        print bruno, bruno
logo, a definição da função não gera nenhuma saída.                         Esta função recebe um único argumento e o
               Como você já deve ter imaginado, é preciso atribui a um parâmetro chamado bruno. O valor do parâmetro
criar uma função antes de poder executá-la. Em outras (a essa altura, não sabemos qual será) é impresso duas vezes,
palavras, a definição da função tem que ser executada antes seguido de uma nova linha. Estamos usando bruno para
que ela seja chamada pela primeira vez.                       mostrar que o nome do parâmetro é decisão sua, mas claro que
                                                              é melhor escolher um nome que seja mais ilustrativo.
               Como exercício, mova as últimas três linhas
  deste programa para o topo, de modo que a chamada da                      A função imprimeDobrado funciona para
  função apareça antes das definições. Rode o programa e veja qualquer tipo que possa ser impresso:
  que mensagem de erro você terá.
                                                                >>> imprimeDoobrado('Spam')
            Também a título de exercício, comece com a Spam Spam
 versão que funciona do programa e mova a definição de >>> imprimeDobrado(5)
 novaLinha para depois da definição de tresLinhas. O que 5 5
 acontece quando você roda este programa?
                                                                >>> imprimeDobrado(3.14159)
                                                                3.14159 3.14159
                                                                             Na primeira chamada da função, o argumento é
3.8 Fluxo de execução                                           uma string. Na segunda, é um inteiro. Na terceira é um float.
Para assegurar que uma função esteja definida antes do seu               As mesmas regras de composição que se
primeiro uso, é preciso saber em que ordem os comandos são aplicam a funções nativas também se aplicam às funções
executados, o que é chamado de fluxo de execução.          definidas pelo usuário, assim, podemos usar qualquer tipo de
                                                           expressão como um argumento para imprimeDobrado:
              A execução sempre começa com o primeiro
comando do programa. Os comandos são executados um de >>> imprimeDobrado('Spam'*4)
cada vez, pela ordem, de cima para baixo.                  SpamSpamSpamSpam SpamSpamSpamSpam
                                                                >>> imprimeDobrado(math.cos(math.pi))
              As definições de função não alteram o fluxo de
execução do programa, mas lembre-se que comandos dentro         -1.0 -1.0
da função não são executados até a função ser chamada.                        Como acontece normalmente, a expressão é
Embora não seja comum, você pode definir uma função             avaliada antes da execução da função, assim imprimeDobrado
dentro de outra. Neste caso, a definição mais interna não é     imprime SpamSpamSpamSpam SpamSpamSpamSpam em
executada até que a função mais externa seja chamada.           vez de 'Spam'*4 'Spam'*4.
              Chamadas de função são como um desvio no                       Como exercício, escreva um chamada a
fluxo de execução. Em vez de ir para o próximo comando, o        imprimeDobrado que imprima 'Spam'*4 'Spam'*4. Dica:
fluxo salta para a primeira linha da função chamada, executa     strings podem ser colocadas tanto entre aspas simples
todos os comandos lá e então volta atrás para retomar de onde    quanto duplas e o tipo de aspas que não for usado para
havia deixado.                                                   envolver a string pode ser usado dentro da string, como
                                                                 parte dela.
             Parece muito simples, até a hora em que você


                                                Capítulo 3: Funções #25
Como pensar como um cientista da Computação usando Python

             Também podemos usar uma variável 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 música do
grupo humorístico britânico 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
variável que passamos como um argumento (miguel) não tem
nada a ver com o nome do parâmetro (bruno). Não importa de
que modo o valor foi chamado de onde veio (do 'chamador');              A ordem da pilha mostra o fluxo de execução.
aqui, em imprimeDobrado, chamamos a todo mundo de imprimeDobrado foi chamado por concatDupla, e
bruno.                                                     concatDupla foi chamado por __main__ (principal), que é um
                                                           nome especial para a função mais no topo. Quando você cria
                                                           uma variável fora de qualquer função, ela pertence à
                                                           __main__.
3.10 Variáveis e parâmetros são locais
                                                                            Cada parâmetro se refere ao mesmo valor que o
Quando você cria uma variável local dentro de uma função,     seu argumento correspondente. Assim, parte1 tem o mesmo
ela só existe dentro da função e você não pode usá-la fora de valor de canto1, parte2 tem o mesmo valor de canto2 e bruno
lá. Por exemplo:                                              tem o mesmo valor de concat.

def concatDupla(parte1, parte2)
                                                                             Se um erro acontece durante uma chamada de
                                                                função, Python imprime o nome da função, e o nome da
  concat = parte1 + parte2                                      função que a chamou, e o nome da função que chamou a que
  imprimeDobrado(concat)                                        chamou, percorrendo todo o caminho de volta a __main__.
              Esta função recebe dois argumentos, concatena-
os, e então imprime o resultado duas vezes. Podemos chamar                Por exemplo, se tentássemos acessar concat de
a função com duas strings:                                   dentro de imprimeDobrado, teríamos um NameError:

>>> canto1 = 'Pie Jesu domine, '                                Traceback (innermost last):
>>> canto2 = 'dona eis requiem. '                                 File "teste.py", line 13, in __main__
>>> concatDupla(canto1, canto2)                                     concatDupla(canto1, canto2)
Pie Jesu domine, Dona eis requiem. Pie Jesu domine,              File "teste.py", line 5, in concatDupla
Dona eis requiem.                                                   imprimeDobrado(concat)
                                                                  File "teste.py", line 9, in imprimeDobrado
             Quando a função concatDupla termina, a
variável concat é destruída. Se tentarmos imprimi-la, teremos       print concat
um erro:                                                        NameError: concat
                                                                             Esta lista de funções é chamada de traceback.
>>> print concat                                               Ela mostra em qual arquivo de programa o erro ocorreu, em
NameError: concat                                              que linha, e quais funções estavam sendo executadas naquele
            Parâmetros são sempre locais. Por exemplo, momento. Mostra também a linha de código que causou o
fora da função imprimeDobrado, não existe alguma coisa erro.
chamada bruno. Se você tentar utilizá-la, Python vai reclamar.
                                                                             Note a similaridade entre o traceback e o
                                                               diagrama da pilha; não é coincidência.
3.11 Diagramas da pilha
                                                                3.12 Funções com resultados
Para entender que variáveis 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 variável, A essa altura, você deve ter percebido que algumas das
mas também a função à qual cada variável pertence.              funções que estamos usando, tais como as funções
                                                                matemáticas, produzem resultados. Outras funções, como
              Cada função é representada por um frame novaLinha, executam uma ação, mas não retornam um valor.
(quadro). Um frame é uma caixa com o nome de uma função O que levanta algumas questões:
ao lado dela e os parâmetros e variáveis da função dentro dela.
O diagrama de pilha para o exemplo anterior tem a seguinte          1. O que acontece se você chama uma função e não faz
aparência:                                                              nada com o resultado (por exemplo, não atribui o
                                                                        resultado a uma variável ou o usa como parte de uma
                                                                        expressão maior)?
                                                                    2.   O que acontece se você usa uma função que não
                                                                         produz resultado em uma expressão tal como
                                                                         novaLinha() + 7?
                                                                    3.   Você pode escrever funções que produzem
                                                                         resultados, ou está preso a funções como novaLinha e
                                                                         imprimeDobrado?
                                                                             A resposta para a terceira questão é afirmativa e
                                                                nós vamos fazer isso no Capítulo 5.

                                                Capítulo 3: Funções #26
Como pensar como um cientista da Computação usando Python

             A título de exercício, responda as outras duas
 questões testando-as. Se tiver dúvida sobre o que é válido ou
 inválido em Python, tente buscar a resposta perguntando ao
 interpretador.


3.13 Glossário

    chamada de Comando que executa uma função.
         função Consiste do nome da função seguido de
  (function call) uma lista de argumentos entre parênteses.
     argumento Valor fornecido a uma função quando ela
     (argument) é chamada. Este valor é atribuído ao
                parâmetro correspondente na função.
        valor de O resultado da função. Se uma chamada
 retorno (return de função é usada como expressão, o
          value) valor de retorno é o valor da expressão.
   conversão de Comando explícito que pega um valor de
      tipo (type um tipo e devolve o valor correspondente
    conversion) em outro tipo.
 coercividade de Uma conversão de tipo que ocorre
       tipo (type automaticamente, de acordo com as regras
       coercion) de coercividade do Python.
        módulo Arquivo que contém uma coleção de
       (module) funções e classes relacionadas entre si.
     notação de A sintaxe para chamar uma função que
     ponto (dot está em outro módulo, especificando o
      notation) nome do módulo, seguido por um ponto
                (.) e o nome da função.
         função Seqüência de comandos nomeada, que
      (function) realiza alguma tarefa útil. As funções
                 podem ou não receber parâmetros e
                 podem ou não retornar valores.
    definição de Comando que cria uma nova função,
          função especificando seu nome, parâmetros e
       (function comandos que ela executa.
     definition)
        fluxo de A ordem na qual os comandos são
 execução (flow executados durante a execução do
   of execution) programa.
      parâmetro Nome usado numa função para referir-se
    (parameter) ao valor passado como argumento.
   variável local Variável definida dentro da função. Uma
 (local variable) variável local só pode ser usada dentro da
                  função onde foi definida.
   diagrama da Representação gráfica da pilha de funções,
    pilha (stack suas variáveis e os valores aos quais elas
       diagram) se referem.
          frame Retângulo no diagrama da pilha que
                representa uma chamada de função.
                Contém as variáveis locais e os
                parâmetros da função.
      traceback Lista de funções que estão em execução,
                impressa quando um erro de execução
                ocorre.




                                                 Capítulo 3: Funções #27
Como pensar como um cientista da Computação usando Python


                                       Capítulo 4: Condicionais e recursividade




4.1 O operador módulo                                            4.3 Operadores lógicos
O operador módulo trabalha com inteiros (e expressões que        Existem três operadores lógicos: and, or, not (e, ou, não). A
têm inteiros como resultado) e produz o resto da divisão do      semântica (significado) destes operadores é similar aos seus
primeiro pelo segundo. Em Python, o operador módulo é um         significados em inglês (ou português). Por exemplo, x > 0 and
símbolo de porcentagem (%). A sintaxe é a mesma que a de         x < 10 é verdadeiro somente se x for maior que 0 e menor que
outros operadores:                                               10.
>>>   quociente = 7 / 3                                                         n%2 == 0 or n%3 == 0 é verdadeiro se qualquer
>>>   print quociente                                            das condições for verdadeira, quer dizer, se o número n for
2
                                                                 divisível por 2 ou por 3.
>>>   resto = 7 % 3                                                             Finalmente, o operador lógico not nega uma
>>>   print resto                                                expressão booleana, assim, not(x > y) é verdadeiro se (x > y)
1                                                                for falso, quer dizer, se x for menor ou igual a y.
             Então, 7 dividido por 3 é 2 e o resto é 1.                    A rigor, os operandos de operadores lógicos
                                                              deveriam ser expressões booleanas, mas Python não é muito
               O   operador        módulo      se      revela
surpreendentemente útil. Por exemplo, você pode checar se rigoroso. Qualquer número diferente de zero é interpretado
um número é divisível por outro - se x % y dá zero, então x é como verdadeiro (True):
divisível por y.                                              >>> x = 5
             Você também pode extrair o algarismo ou             >>> x and 1
algarismos mais à direita de um número. Por exemplo, x % 10      1
resulta o algarismo mais à direita de x (na base 10).            >>> y = 0
Similarmente, x % 100 resulta nos dois dígitos mais à direita.   >>> y and 1
                                                                 0
                                                                               Em geral, esse tipo de coisa não é considerado
4.2 Expressões booleanas                                         de bom estilo. Se você precisa comparar um valor com zero,
                                                                 deve fazê-lo explicitamente.
Uma expressão booleana é uma expressão que é verdadeira
(true) ou é falsa (false). Em Python, uma expressão que é
verdadeira tem o valor 1, e uma expressão que é falsa tem o 4.4 Execução condicional
valor 0.
            O operador == compara dois valores e produz Para poder escrever programas úteis, quase sempre
uma expressão booleana:                                 precisamos da habilidade de checar condições e mudar o
                                                        comportamento do programa de acordo com elas. As
>>> 5 == 5                                              instruções condicionais nos dão essa habilidade. A forma
True                                                    mais simples é a instrução if (se):
>>> 5 == 6
                                                                 if x > 0
False
                                                                   print "x é positivo"
              No primeiro comando, os dois operadores são
iguais, então a expressão avalia como True (verdadeiro); no                   A expressão booleana depois da instrução if é
segundo comando, 5 não é igual a 6, então temos False (falso). chamada de condição. Se ela é verdadeira (true), então a
                                                               instrução endentada é executada. Se não, nada acontece.
              O operador == é um dos operadores de
comparação; os outros são:                                                    Assim como outras instruções compostas, a
                                                               instrução if é constituída de um cabeçalho e de um bloco de
 x != y          # x é diferente de y                          instruções:
x   > y         #   x   é   maior   que y                        CABECALHO:
x   < y         #   x   é   menor   que y                          PRIMEIRO COMANDO
x   >= y        #   x   é   maior   ou igual a y                   ...
x   <= y        #   x   é   menor   ou igual a y                   ULTIMO COMANDO
              Embora esses operadores provavelmente sejam                      O cabeçalho começa com uma nova linha e
familiares a você, os símbolos em Python são diferentes dos      termina com dois pontos (:). Os comandos ou instruções
símbolos da matemática. Um erro comum é usar um sinal de         endentados que seguem são chamados de bloco. A primeira
igual sozinho (=) em vez de um duplo (==). Lembre-se de que      instrução não endentada marca o fim do bloco. Um bloco de
= é um operador de atribuição e == é um operador de              comandos dentro de um comando composto ou instrução
comparação. Também não existem coisas como =< ou =>.             composta é chamado de corpo do comando.
                                                                             Não existe limite para o número de instruções
                                                                 que podem aparecer no corpo de uma instrução if, mas tem
                                                                 que haver pelo menos uma. Ocasionalmente, é útil ter um


                                           Capítulo 4: Condicionais e recursividade #28
Como pensar como um cientista da Computação usando Python

corpo sem nenhuma instrução (usualmente, como um uma delas é verdadeira, o ramo correspondente é executado, e
delimitador de espaço para código que você ainda não a instrução termina. Mesmo que mais de uma condição seja
escreveu). Nesse caso, você pode usar o comando pass, que verdadeira, apenas o primeiro ramo verdadeiro executa.
não faz nada.
                                                                        Como exercício, coloque os exemplos acima em
                                                            funções chamadas comparar(x, y) e executar(escolha).
4.5 Execução alternativa
                                                                4.7 Condicionais aninhados
Um segundo formato da instrução if é a execução alternativa,
na qual existem duas possibilidades e a condição determina
qual delas será executada. A sintaxe se parece com:          Um condicional também pode ser aninhado dentro de outra.
                                                             Poderíamos ter escrito o exemplo tricotômico (dividido em
if x % 2 == 0:                                               três) como segue:
  print x, "é par"
                                                                if x == y:
else:
                                                                  print x, "e", y, "são iguais"
  print x, "é impar"
                                                                else:
              Se o resto da divisão de x por 2 for 0, então       if x < y:
sabemos que x é par, e o programa exibe a mensagem para
esta condição. Se a condição é falsa, o segundo grupo de            print x, "é menor que", y
instruções é executado. Desde que a condição deva ser             else:
verdadeira (true) ou falsa (false), precisamente uma das            print x, "é maior que", y
alternativas vai ser executada. As alternativas são chamadas                O condicional mais externo tem dois ramos. O
ramos (branches), porque existem ramificações no fluxo de primeiro ramo contém uma única instrução de saída. O
execução.                                                     segundo ramo contém outra instrução if, que por sua vez tem
              Por sinal, se você precisa checar a paridade de dois ramos. Os dois ramos são ambos instruções de saída,
números com freqüência, pode colocar este código dentro de embora pudessem conter instruções condicionais também.
uma função:                                                                 Embora a endentação das instruções torne a
 def imprimeParidade(x):
                                                              estrutura aparente, condicionais aninhados tornam-se difíceis
                                                              de ler rapidamente. Em geral, é uma boa idéia evitar o
   if x % 2 == 0:                                             aninhamento quando for possível.
    print x, "é par"
  else:                                                              Operadores lógicos freqüentemente fornecem
    print x, "é impar"
                                                         uma maneira de simplificar instruções condicionais aninhadas.
                                                         Por exemplo, podemos reescrever o código a seguir usando
            Para qualquer valor de x, imprimeParidade uma única condicional:
exibe uma mensagem apropriada. Quando você a chama, pode
fornecer uma expressão de resultado inteiro como um if 0 < x:
argumento:                                                 if x < 10:
>>> imprimeParidade(17)                                             print "x é um número positivo de um só
                                                                algarismo."
>>> imprimeParidade(y+1)
                                                                             A instrução print é executada somente se a
                                                                fizermos passar por ambos os condicionais, então, podemos
                                                                usar um operador and:
4.6 Condicionais encadeados                                     if 0 < x and x < 10:
                                                                  print "x é um número positivo de um só algarismo."
Às vezes existem mais de duas possibilidades e precisamos de
mais que dois ramos. Uma condicional encadeada é uma                     Esses tipos de condição são comuns, assim,
maneira de expressar uma computação dessas:                  Phython provê uma sintaxe alternativa que é similar à notação
                                                             matemática:
if x < y:
                                                                if 0 < x < 10:
  print x, "é menor que", y
                                                                  print "x é um número positivo de um só algarismo."
elif x > y:
  print x, "é maior que", y
else:
  print x, "e", y, "são iguais"                                 4.8 A instrução return
               elif é uma abreviação de "else if" ("então se").
De novo, precisamente um ramo será executado. Não existe O comando return permite terminar a execução de uma função
limite para o número de instruções elif, mas se existir uma antes que ela alcance seu fim. Uma razão para usá-lo é se você
instrução else ela tem que vir por último:                      detectar uma condição de erro:
if escolha == 'A':                                              import math
  funcaoA()
elif escolha == 'B':                                            def imprimeLogaritmo(x):
  funcaoB()                                                       if x <= 0:
elif escolha == 'C':                                                print "Somente números positivos, por favor."
  funcaoC()                                                         return
else:
  print "Escolha inválida."                                       resultado = math.log(x)
              Cada condição é checada na ordem. Se a              print "O log de x é ", resultado
primeira é falsa, a próxima é checada, e assim por diante. Se                 A   função   imprimeLogaritmo   recebe   um


                                      Capítulo 4: Condicionais e recursividade #29
Como pensar como um cientista da Computação usando Python

parâmetro de nome x. A primeira coisa que ela faz é checar se   novaLinha()
x é menor ou igual a 0, neste caso ela exibe uma mensagem de              Muito embora isso funcione, não seria muito
erro e então usa return para sair da função. O fluxo de útil se precisássemos gerar como saída 2 novas linhas, ou 106.
execução imediatamente retorna ao ponto chamador, quer Uma alternativa melhor seria esta:
dizer, de onde a função foi chamada, e as linhas restantes da
função não são executadas.                                    def nLinhas(n):
             Lembre-se que para usar uma função do módulo        if n > 0:
de matemática, math, você tem de importá-lo.                       print
                                                                   nLinhas(n-1)
                                                                           Esse programa é similar a contagemRegressiva;
4.9 Recursividade                                            sempre que n for maior que 0, ele gera como saída uma nova
                                                             linha e então chama a si mesmo para gerar como saída n-1
                                                             linhas adicionais. Deste modo, o número total de novas linhas
Já mencionamos que é válido uma função chamar outra é 1 + (n-1) que, se você estudou álgebra direitinho, vem a ser
função, e você viu vários exemplos disso. Mas ainda não o próprio n.
tínhamos dito que também é válido uma função chamar a si
mesma. Talvez não seja óbvio porque isso é bom, mas trata-se               O processo de uma função chamando a si
de uma das coisas mais mágicas e interessantes que um mesma é chamado de recursividade, e tais funções são ditas
programa pode fazer. Por exemplo, dê uma olhada na seguinte recursivas.
função:
def contagemRegressiva(n):
  if n == 0:                                                   4.10 Diagramas de pilha para funções recursivas
    print "Fogo!"
  else:                                                        Na Seção 3.11, usamos um diagrama de pilha para representar
                                                               o estado de um programa durante uma chamada de função. O
    print n                                                    mesmo tipo de diagrama pode ajudar a interpretar uma função
    contagemRegressiva(n-1)                                    recursiva.
              contagemRegressiva espera que o parâmetro, n,
seja um inteiro positivo. Se n for 0, ela produz como saída a               Toda vez que uma função é chamada, Python
palavra "Fogo!". De outro modo, ela produz como saída n e cria um novo quadro (frame) para a função, que contém as
então chama uma função de nome contagemRegressiva -- ela variáveis locais e parâmetros da função. Para uma função
mesma -- passando n-1 como argumento.                         recursiva, terá que existir mais de um quadro na pilha ao
                                                              mesmo tempo.
              O que acontece se chamarmos essa função da
seguinte maneira:                                                           Esta figura mostra um diagrama de pilha para
                                                              contagemRegressiva, chamada com n = 3:
>>> contagemRegressiva(3)
             A execução de contagemRegressiva começa
com n=3, e desde que n não é 0, produz como saída o valor 3,
e então chama a si mesma...
             A execução de contagemRegressiva começa
com n=2, e desde que n não é 0, produz como saída o valor 2,
e então chama a si mesma...
             A execução de contagemRegressiva começa
com n=1, e desde que n não é 0, produz como saída o valor 1,
e então chama a si mesma...
              A execução de contagemRegressiva começa
com n=0, e desde que n é 0, produz como saída a palavra
"Fogo!" e então retorna.
             A contagemRegressiva que tem n=1 retorna.
             A contagemRegressiva que tem n=2 retorna.
             A contagemRegressiva que tem n=1 retorna.
             E então estamos de volta em __main__ (que
viagem!). Assim, a saída completa se parece com:                             Como de costume, no topo da pilha está o
                                                               quadro para __main__. Ele está vazio porque nem criamos
3                                                              qualquer variável em __main__ nem passamos qualquer valor
2                                                              para ele.
1                                                                Os quatro quadros contagemRegressiva têm
Fogo!                                              valores diferentes para o parâmetro n. A parte mais em baixo
            Como um segundo exemplo, dê uma olhada na pilha, onde n=0, é chamada de caso base. Ele não faz uma
novamente nas funções novaLinha e tresLinhas:      chamada recursiva, então não há mais quadros.
def novaLinha():                                                 Como exercício, desenhe um diagrama de pilha
  print                                              para nLinhas chamada com n=4.

def tresLinhas():
  novaLinha()
  novaLinha()


                                     Capítulo 4: Condicionais e recursividade #30
Como pensar como um cientista da Computação usando Python


                                                                 >>> velocidade = input(deixa)
4.11 Recursividade infinita                                      Qual... é a velocidade de vôo de uma andorinha?
                                                                 De qual você fala, uma andorinha Africana ou uma 
Se uma recursividade nunca chega ao caso base, ela prossegue     Européia?
fazendo chamadas recursivas para sempre, e o programa
nunca termina. Isto é conhecido como recursividade infinita, e   SyntaxError: invalid syntax
geralmente não é considerada uma boa idéia. Aqui está um                    Para evitar esse tipo de erro, geralmente é bom
programa mínimo com uma recursividade infinita:                usar raw_input para pegar uma string e, então, usar funções de
                                                               conversão para converter para outros tipos.
def recursiva():
  recursiva()
             Na maioria dos ambientes de programação, um 4.13 Glossário
programa com recursividade infinita na verdade não roda para
sempre. Python reporta uma mensagem de erro quando a
profundidade máxima de recursividade é alcançada:               operador            Operador denotado por um símbolo de
                                                                 módulo             porcentagem (%), que trabalha com
  File "<stdin>", line 2, in recursiva                         (modulus             inteiros e retorna o resto da divisão de um
  (98 repetitions omitted)                                     operator)            número por outro.
  File "<stdin>", line 2, in recursiva
RuntimeError: Maximum recursion depth exceeded
                                                                       expressão Uma expressão que é verdadeira ou falsa.
                                                                        booleana
                                                                        (boolean
             Este traceback é um pouco maior do que aquele            expression)
que vimos no capítulo anterior. Quando o erro ocorre, existem        operador de Um dos operadores que compara dois
100 quadros recursiva na pilha!                                      comparação valores: ==, !=, >, <, >=, e <=.
             Como exercício, escreva uma função com                  (comparison
 recursividade infinita e rode-a no interpretador Python.               operator)
                                                                  operador lógico Um dos operadores que combina
                                                                         (logical expressões booleanas: and, or, e not.
                                                                       operator)
4.12 Entrada pelo teclado
                                                                         comando Comando que controla o fluxo de
Os programas que temos escrito até agora são um pouco crus,           condicional execução dependendo de alguma
no sentido de não aceitarem dados entrados pelo usuário. Eles        (conditional condição.
simplesmente fazem a mesma coisa todas as vezes.                       statement)
              Python fornece funções nativas que pegam                  condição A expressão booleana que determina qual
entradas pelo teclado. A mais simples é chamada raw_input.            (condition) bloco será executado num comando
Quando esta função é chamada, o programa pára e espera que                        condicional.
o usuário digite alguma coisa. Quando o usuário aperta a tecla           comando    Comando que consiste de um cabeçalho e
Enter ou Return, o programa prossegue e a função raw_input              composto    um corpo. O cabeçalho termina com um
retorna o que o usuário digitou como uma string:                      (compound     dois-pontos (:). O corpo é endentado em
>>> entrada = raw_input()                                              statement)   relação ao cabeçalho.
O que você está esperando?                                          bloco (block) Grupo de comandos consecutivos com a
>>> print entrada                                                                 mesma endentação.
O que você está esperando?
                                                                    corpo (body) O bloco que se segue ao cabeçalho em
              Antes de chamar raw_input, é uma boa idéia                         um comando composto.
exibir uma mensagem dizendo ao usuário o que ele deve
entrar. Esta mensagem é uma deixa (prompt). Podemos suprir          aninhamento Estrutura de programa dentro da outra,
uma deixa como um argumento para raw_input:                             (nesting) como um comando condicional dentro de
                                                                                  um bloco de outro comando condicional.
>>> nome = raw_input("Qual... é o seu nome? ")
Qual... é o seu nome? Arthur, Rei dos Bretões!                      recursividade O processo de chamar a própria função
>>> print nome
                                                                      (recursion) que está sendo executada.
Arthur, Rei dos Bretões!                                          caso base (base Bloco de comando condicional numa
            Se esperamos que a entrada seja um inteiro,                     case) função recursiva que não resulta em uma
podemos usar a função input:                                                      chamada recursiva.
deixa = "Qual... é a velocidade de vôo de uma                   recursão infinita Função que chama a si mesma
andorinha?n"                                                            (infinite recursivamente sem nunca chegar ao caso
velocidade = input(deixa)
                                                                       recursion) base. Após algum tempo, uma recursão
                                                                                   infinita causa um erro de execução.
              Se o usuário digita uma string de números, ela é
convertida para um inteiro e atribuída a velocidade.              deixa (prompt) Indicação visual que diz ao usuário que o
Infelizmente, se o usuário digitar um caractere que não seja                     programa está esperando uma entrada de
um número, o programa trava:                                                     dados.




                                      Capítulo 4: Condicionais e recursividade #31
Como pensar como um cientista da Computação usando Python


                                        Capítulo 5: Funções frutíferas



                                                             terminará sem encontrar um comando return. Neste caso, o
5.1 Valores de retorno                                       valor de retorno será um valor especial chamado None:
Algumas das funções nativas do Python que temos usado, >>> print valorAbsoluto(0)
como as funções matemáticas, produziram resultados. Chamar None
a função gerou um novo valor, o qual geralmente atribuímos à               Como exercício, escreva uma função compare
uma variável ou usamos como parte de uma expressão:          que retorne 1 se x > y, 0 se x == y e -1 se x < y.
e = math.exp(1.0)
altura = raio * math.sin(angulo)
              Mas até agora, nenhuma das funções que nós 5.2 Desenvolvimento de programas
mesmos escrevemos retornou um valor.
                                                             Neste ponto, você deve estar apto a olhar para funções
              Neste capítulo, iremos escrever funções que completas e dizer o que elas fazem. Também, se você vem
retornam valores, as quais chamaremos de funções frutíferas, fazendo os exercícios, você escreveu algumas pequenas
ou funções que dão frutos, na falta de um nome melhor. O funções. Conforme escrever funções maiores, você pode
primeiro exemplo é area, que retorna a área de um círculo começar a ter mais dificuldade, especialmente com erros em
dado o seu raio:                                             tempo de execução (erros de runtime) ou erros semânticos.
import math                                                                Para lidar com programas de crescente
                                                             complexidade, vamos sugerir uma técnica chamada
def area(raio):                                              desenvolvimento incremental. A meta do desenvolvimento
  temp = math.pi * raio**2                                   incremental é evitar seções de depuração (debugging) muito
                                                             longas pela adição e teste de somente uma pequena quantidade
  return temp                                                de código de cada vez.
              Já vimos a instrução return antes, mas em uma
função frutífera a instrução return inclui um valor de retorno.           Como exemplo, suponha que você queira
Esta instrução significa: "Retorne imediatamente desta função encontrar a distância entre dois pontos, dados pelas
e use a expressão em seguida como um valor de retorno". A coordenadas (x1,y1) e (x2,y2). Pelo teorema de Pitágoras, a
expressão fornecida pode ser arbitrariamente complicada, de distância é:
modo que poderíamos ter escrito esta função de maneira mais
concisa:                                                                          
                                                                    distancia=  x2− x12  y2−y12 ¿
def area(raio):                                                      O primeiro passo é considerar como deveria ser
  return math.pi * raio**2                              uma função distancia em Python. Em outras palavras, quais
             Por outro lado, variáveis temporárias como são as entradas (parâmetros) e qual é a saída (valor de
temp muitas vezes tornam a depuração mais fácil.        retorno)?
             Às vezes é útil ter múltiplos comandos return,                Neste caso, os dois pontos são as entradas, os
um em cada ramo de uma condicional:                         quais podemos representar usando quatro parâmetros. O valor
                                                            de retorno é a distância, que é um valor em ponto flutuante.
def valorAbsoluto(x):
  if x < 0:                                                               Já podemos escrever um esboço da função:
    return -x                                                def distancia(x1, y1, x2, y2):
  else:                                                        return 0.0
    return x                                                             Obviamente, esta versão da função não computa
              Já que estes comandos return estão em ramos distâncias; ela sempre retorna zero. Mas ela está
alternativos da condicional, apenas um será executado. Tão sintaticamente correta, e vai rodar, o que significa que
logo um seja executado, a função termina sem executar podemos testá-la antes de torná-la mais complicada.
qualquer instrução ou comando subseqüente.
                                                                         Para testar a nova função, vamos chamá-la com
              O código que aparece depois de uma instrução valores hipotéticos:
return, ou em qualquer outro lugar que o fluxo de execução
jamais alcance, é chamado código morto (dead code).        >>> distancia(1, 2, 4, 6)
                                                             0.0
             Em uma função frutífera, é uma boa idéia
assegurar que todo caminho possível dentro do programa               Escolhemos estes valores de modo que a
encontre uma instrução return. Por exemplo:            distância horizontal seja igual a 3 e a distância vertical seja
                                                       igual a 4; deste modo, o resultado é 5 (a hipotenusa de um
def valorAbsoluto(x):                                  triângulo 3-4-5). Quando testamos uma função, é útil
   if x < 0:                                           sabermos qual o resultado correto.
    return -x                                                           Neste ponto, já confirmamos que a função está
  elif x > 0:                                             sintaticamente correta, e podemos começar a adicionar linhas
    return x                                              de código. Depois de cada mudança adicionada, testamos a
           Este programa não está correto porque se x for função de novo. Se um estar: nas linhas adicionadasponto,
                                                                                      erro ocorre em qualquer
0, nenhuma das condições será verdadeira, e a função sabemos aonde ele deve                                       mais
                                                          recentemente.

                                          Capítulo 5: Funções frutíferas #32
Como pensar como um cientista da Computação usando Python

              Um primeiro passo lógico nesta computação é        parâmetros. Registre cada estágio do desenvolvimento
encontrar as diferenças x2 - x1 e y2 - y1. Nós iremos guardar    incremental conforme você avance.
estes valores em variáveis temporárias chamadas dx e dy e
imprimi-las:
def distancia(x1, y1, x2, y2):                                  5.3 Composição
  dx = x2 - x1
  dy = y2 - y1                                                  Conforme você poderia esperar agora, você pode chamar uma
  print "dx vale", dx                                           função de dentro de outra. Esta habilidade é chamada de
  print "dy vale", dy                                           composição.
  return 0.0                                                              Como um exemplo, vamos escrever uma função
             Se a função estiver funcionando, as saídas que recebe dois pontos, o centro de um círculo e um ponto em
deverão ser 3 e 4. Se é assim, sabemos que a função está seu perímetro, e calcula a área do círculo.
recebendo os parâmetros corretos e realizando a primeira
computação corretamente. Se não, existem poucas linhas para               Assuma que o ponto do centro está guardado
checar.                                                     nas variáveis xc e yc, e que o ponto do perímetro está nas
                                                            variáveis xp e yp. O primeiro passo é encontrar o raio do
             Em seguida, computaremos a soma dos círculo, o qual é a distância entre os dois pontos. Felizmente,
quadrados de dx e dy:                                       temos uma função, distancia, que faz isto:
def distancia(x1, y1, x2, y2):                                  Raio = distancia(xc, yc, xp, yp)
  dx = x2 - x1                                                                O segundo passo é encontrar a área de um
  dy = y2 - y1                                                  círculo com o raio dado e retorná-la:
  dquadrado = dx**2 + dy**2                                     resultado = area(raio)
  print "dquadrado vale: ", dquadrado                           return resultado
  return 0.0
                                                                   Juntando tudo numa função, temos:
             Note que removemos os comandos print que
havíamos escrito no passo anterior. Código como este é def area2(xc, yc, xp, yp):
chamado de andaime porque ajuda a escrever o programa,   raio = distancia(xc, yc, xp, yp)
mas não é parte do produto final.                        resultado = area(raio)
              De novo, nós vamos rodar o programa neste           return resultado
estágio e checar a saída (que deveria ser 25).                        Chamamos à esta função de area2 para
                                                        distinguir da função area, definida anteriormente. Só pode
              Finalmente, se nós tínhamos importado o existir uma única função com um determinado nome em um
módulo matemático math, podemos usar a função sqrt para determinado módulo.
computar e retornar o resultado:
                                                                      As variáveis temporárias raio e resultado são
def distancia(x1, x2, y1, y2):                          úteis para o desenvolvimento e para depuração (debugging),
  dx = x2 - x1                                          mas uma vez que o programa esteja funcionando, podemos
  dy = y2 - y1                                          torná-lo mais conciso através da composição das chamadas de
  dquadrado = dx**2 + dy**2                             função:
  resultado = math.sqrt(dquadrado)                              def area2(xc, yc, xp, yp):
  return resultado                                                return area(distancia(xc, yc, xp, yp))
              Se isto funcionar corretamente, você conseguiu.                Como exercício, escreva uma função
Caso contrário, talvez fosse preciso imprimir (exibir) o valor inclinacao(x1, y1, x2, y2) que retorne a inclinação (ou
de resultado antes da instrução return.                        coeficienteAngular?) de uma linha dados os pontos (x1, y1) e
              Enquanto for iniciante, você deve acrescentar (x2, y2). Depois use esta função em uma função chamada
apenas uma ou duas linhas de código de cada vez. Conforme cortaY(x1, y1, x2, y2) que retorne a interseção da linha com o
ganhar mais experiência, você se verá escrevendo e depurando eixo y, dados os pontos (x1, y1) e (x2, y2).
pedaços maiores. De qualquer modo, o processo de
desenvolvimento incremental pode poupar um bocado de
tempo de depuração.                                            5.4 Funções booleanas
             Os aspectos chave do processo são:
                                                           Funções podem retornar valores booleanos, o que muitas
    1.   Comece com um programa que funciona e faça vezes é conveniente por ocultar testes complicados dentro de
         pequenas mudanças incrementais. Em qualquer ponto funções. Por exemplo:
         do processo, se houver um erro, você saberá
         exatamente onde ele está.                         def ehDivisivel(x, y):
    2.   Use variáveis temporárias para manter valores            If x % y == 0:
         intermediários de modo que você possa exibi-los e          return True # é verdadeiro (true), é divisível
         checá-los.                                               else:
                                                                    return False # é falso (false), não é divisível
    3.  Uma vez que o programa funcione, você pode querer
        remover algum código muleta, ou andaime                           O nome desta função é ehDivisivel ("é
        (scaffolding) ou consolidar múltiplos comandos divisível"). É comum dar a uma função booleana nomes que
        dentro de expressões compostas, mas somente se isto soem como perguntas sim/não. ehDivisivel retorna ou True ou
        não tornar o programa difícil de ler.               False para indicar se x é ou não é divisível por y.
             Como um exercício, use o desenvolvimento                     Podemos tornar a função mais concisa se
 incremental para escrever uma função chamada hipotenusa tirarmos vantagem do fato de a condição da instrução if ser ela
 que retorna a medida da hipotenusa de um triângulo mesma uma expressão booleana. Podemos retorná-la
 retângulo dadas as medidas dos dois catetos como diretamente, evitando totalmente o if:

                                           Capítulo 5: Funções frutíferas #33
Como pensar como um cientista da Computação usando Python

def ehDivisivel(x, y):                                             def fatorial(n):
  return x % y == 0                                                             Se acontece de o argumento ser 0, tudo o que
              Esta sessão mostra a nova função em ação:            temos de fazer é retornar 1:
>>> ehDivisivel(6, 4)                                              def fatorial(n):
False                                                                if n == 0:
>>> ehDivisivel(6, 3)                                                  return 1
True                                                                  Por outro lado, e esta é a parte interessante,
           Funções booleanas são freqüentemente usadas temos que fazer uma chamada recursiva para encontrar o
em comandos condicionais:                              fatorial de n-1 e então multiplicá-lo por n:
if ehDivisivel(x, y):                                              def fatorial(n):
  print "x é divisível por y"                                        if n == 0:
else:                                                                  return 1
  print "x não é divisível por y"                                    else:
                                                                       recursivo = fatorial(n-1)
              Mas a comparação extra é desnecessária.
                                                                       resultado = n * recursivo
              Como exercício, escreva uma função                       return resultado
  estaEntre(x, y, z) que retorne True se y < x < z ou False se               O fluxo de execução para este programa é
  não.                                                         similar ao fluxo de contagemRegressiva na Seção 4.9. Se
                                                               chamarmos fatorial com o valor 3:
                                                                                Já que 3 não é 0, tomamos o segundo ramo e
5.5 Mais recursividade                                             calculamos o fatorial de n-1 ...
Até aqui, você aprendeu apenas um pequeno subconjunto da                        Já que 2 não é 0, tomamos o segundo ramo e
linguagem Python, mas pode ser que te interesse saber que          calculamos o fatorial de n-1 ...
este pequeno subconjunto é uma linguagem de programação                         Já que 1 não é 0, tomamos o segundo ramo e
completa, o que significa que qualquer coisa que possa ser         calculamos o fatorial de n-1 ...
computada pode ser expressa nesta linguagem. Qualquer
programa já escrito pode ser reescrito usando somente os                        Já que 0 é 0, tomamos o primeiro ramo e
aspectos da linguagem que você aprendeu até agora                  retornamos 1 sem fazer mais qualquer chamada recursiva.
(usualmente, você precisaria de uns poucos comandos para
controlar dispositivos como o teclado, mouse, discos, etc., mas                    O valor retornado (1) é multiplicado por n, que
isto é tudo).                                                      é 1, e o resultado é retornado.
               Provar esta afirmação é um exercício nada                           O valor retornado (1) é multiplicado por n, que
trivial, que foi alcançado pela primeira vez por Alan Turing,      é 2, e o resultado é retornado.
um dos primeiros cientistas da computação (alguém poderia
dizer que ele foi um matemático, mas muitos dos primeiros                          O valor retornado (2) é multiplicado por n, que
cientistas da computação começaram como matemáticos). Por          é 1, e o resultado, 6, se torna o valor de retorno da chamada de
isso, ficou conhecido como Tese de Turing. Se você fizer um        função que iniciou todo o processo.
curso em Teoria da Computação, você terá chance de ver a                        Eis o diagrama de pilha para esta seqüência de
prova.                                                             chamadas de função:
              Para te dar uma idéia do que você pode fazer
com as ferramentas que aprendeu a usar até agora, vamos
avaliar algumas funções matemáticas recursivamente
definidas. Uma definição recursiva é similar à uma definição
circular, no sentido de que a definição faz referência à coisa
que está sendo definida. Uma verdadeira definição circular
não é muito útil:
              vorpal: adjetivo usado para descrever algo que
é vorpal.
              Se você visse esta definição em um dicionário,
ficaria confuso. Por outro lado, se você procurasse pela
definição da função matemática fatorial, você encontraria algo
assim:
                                                                                 Os valores de retorno são mostrados sendo
0! = 1                                                             passados de volta para cima da pilha. Em cada quadro, o valor
n! = n.(n-1)!                                                      de retorno é o valor de resultado, o qual é o produto de n por
                                                                   recursivo.
               Esta definição 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") é
                                                                   5.6 Voto de confiança (Leap of faith)
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. Seguir o fluxo de execução é uma maneira de ler programas,
                                                                   mas que pode rapidamente se transformar em um labirinto.
               Se você pode escrever uma definição recursiva Uma alternativa é o que chamamos de "voto de confiança".
de alguma coisa, você geralmente pode escrever um programa Quando você tem uma chamada de função, em vez de seguir o
em Python para executá-la. O primeiro passo é decidir quais fluxo de execução, você assume que a função funciona
são os parâmetros para esta função. Com pouco esforço, você corretamente e retorna o valor apropriado.
deverá concluir que fatorial recebe um único parâmetro:

                                             Capítulo 5: Funções frutíferas #34
Como pensar como um cientista da Computação usando Python

               De fato, você está agora mesmo praticando este RuntimeError: Maximum recursion depth exceeded
voto de confiança ao usar as funções nativas. Quando você                     Parece um caso de recursividade infinita. Mas o
chama math.cos ou math.exp, você não examina a que será que é de fato? Existe um caso base -- quando n == 0.
implementação destas funções. Você apenas assume que elas O problema é que o valor de n nunca encontra o caso base.
funcionam porque as pessoas que escreveram as bibliotecas
nativas eram bons programadores.                                              Na primeira chamada recursiva, o valor de n é
                                                                0.5. Na próxima, ele é igual a -0.5. Daí em diante, ele se torna
               O mesmo também é verdade quando você cada vez menor, mas jamais será 0.
chama uma de suas próprias funções. Por exemplo, na Seção
5.4, escrevemos a função chamada ehDivisivel que determina                    Temos então duas alternativas. Podemos tentar
se um número é divisível por outro. Uma vez que nos generalizar a função fatorial para que funcione com números
convencemos que esta função está correta -- ao testar e em ponto flutuante, ou fazemos fatorial realizar a checagem
examinar o código -- podemos usar a função sem examinar o de tipo de seus parâmetros. A primeira é chamada função
código novamente.                                               gamma e está um pouco além do escopo deste livro. Sendo
                                                                assim, ficaremos com a segunda.
               O mesmo também é verdadeiro para programas
recursivos. Quando você tem uma chamada recursiva, em vez                     Podemos usar type para comparar o tipo do
de seguir o fluxo de execução, você poderia assumir que a parâmetro com o tipo de um valor inteiro conhecido (como 1).
chamada recursiva funciona (produz o resultado correto) e Ao mesmo tempo em que fazemos isto, podemos nos
então perguntar-se, "Assumindo que eu possa encontrar o certificar também de que o parâmetro seja positivo:
fatorial de n-1, posso calcular o fatorial de n?" Neste caso, é
claro que você pode, multiplicando por n.                       def fatorial (n):
                                                                   if type(n) != type(1):
              Naturalmente, é um pouco estranho que uma        print "Fatorial somente é definido para
função funcione corretamente se você ainda nem terminou de
escrevê-la, mas é por isso que se chama voto de confiança! inteiros."
                                                                     return -1
                                                                   elif n < 0:
                                                                     print "Fatorial somente é definido para inteiros
5.7 Mais um exemplo                                               positivos."
                                                                     return -1
No exemplo anterior, usamos variáveis temporárias para             elif n ==0:
explicitar (spell out XXX) os passos e tornar o código mais
fácil de depurar, mas poderíamos ter economizado algumas             return 1
linhas:                                                            else:
                                                                     return n * fatorial(n-1)
def fatorial(n):
                                                                              Agora temos três casos base. O primeiro pega os
  if n == 0:                                                    não-inteiros. O segundo pega os inteiros negativos. Em ambos
    return 1                                                    os casos, o programa exibe uma mensagem de erro e retorna
  else:                                                         um valor especial, -1, para indicar que alguma coisa saiu
    return n * fatorial(n-1)                                    errada:
               De agora em diante, tenderemos a utilizar um      >>> fatorial ("Fred")
formato mais conciso, mas recomendamos que você use a            Fatorial somente é definido para inteiros.
versão mais explícita enquanto estiver desenvolvendo código.
Quando ele estiver funcionando, você pode enxugá-lo se           -1
estiver se sentindo inspirado.                                   >>> fatorial (-2)
                                                                 Fatorial somente é definido para inteiros positivos.
              Depois de fatorial, o exemplo mais comum de
uma função matemática definida recursivamente é fibonacci, a     -1
qual tem a seguinte definição:                                                Se passarmos pelas duas checagens, então
                                                                saberemos que n é um inteiro positivo, e poderemos provar
fibonacci(0) = 1                                                que a recursividade encontra seu término.
fibonacci(1) = 1
                                                                              Este programa demonstra um padrão (pattern)
fibonacci(n) = fibonacci(n-1) + fibonacci(n-2);                 chamado às vezes de guardião. As duas primeiras
              Traduzido em Python, parecerá assim:              condicionais atuam como guardiãs, protegendo o código que
                                                                vem em seguida de valores que poderiam causar um erro. Os
def fibonacci(n):                                               guardiões tornam possível garantir a correção do código.
  if n == 0 or n == 1:
    return 1
  else:
                                                                5.9 Glossário
    return fibonacci(n-1) + fibonacci(n-2)
              Se você tentar seguir o fluxo de execução aqui,
mesmo para valores bem pequenos de n, sua cabeça explodirá.   função frutífera Uma função que produz um valor de
Mas, de acordo com o voto de confiança, se você assume que (fruitful function) retorno.
as duas chamadas recursivas funcionam corretamente, então é   valor de retorno O valor entregue como resultado de uma
claro que você terá o resultado correto ao juntá-las.           (return value) chamada de função.
                                                                            variável Uma variável usada para guardar um
                                                                        temporária valor intermediário em um cálculo
5.8 Checagem de tipos                                                   (temporary complexo.
                                                                           variable)
O que acontece se chamamos fatorial e damos a ela 1.5 como
argumento?:                                                           código morto Parte de um programa que nunca pode
                                                                        (dead code) ser executada, muitas vezes por que ela
>>> fatorial (1.5)                                                                  aparece depois de uma instrução return.


                                             Capítulo 5: Funções frutíferas #35
Como pensar como um cientista da Computação usando Python

          None Um valor especial em Python, retornado
               por funções que não possuem uma
               instrução return ou têm uma instrução
               return sem argumento.
desenvolvimento    Uma estratégia de desenvolvimento de
     incremental   programas que evita a depuração ao
    (incremental   adicionar e testar somente uma pequena
   development)    quantidade de código de cada vez.
       andaime Código          usado       durante  o
   (scaffolding) desenvolvimento do programa, mas que
                 não faz parte do produto final.
       guardião Uma condição que checa e manipula
     (guardian) circunstâncias que poderiam causar um
                erro.




                                         Capítulo 5: Funções frutíferas #36
Como pensar como um cientista da Computação usando Python


                                                 Capítulo 6: Iteração



                                                                            Vimos     dois   programas,    nLinhas    e
6.1 Reatribuições                                            contagemRegressiva, que usam recursividade (recursão) para
                                                             fazer a repetição, que também é chamada iteração. Porque a
Como você talvez já tenha descoberto, é permitido fazer mais iteração é muito comum, Python tem várias características
de uma atribuição à mesma variável. Uma nova atribuição faz para torná-la mais fácil. A primeira delas em que vamos dar
uma variável existente referir-se a um novo valor (sem se uma olhada é o comando while.
referir mais ao antigo).:
                                                                            Aqui está como fica contagemRegressiva com
bruno = 5                                                    um comando while:
print bruno,                                                      def contagemRegressiva(n):
bruno = 7                                                           while n > 0:
print bruno                                                           print n
              A saída deste programa é 5 7, porque na                 n = n-1
primeira vez que bruno é impresso, seu valor é 5 e na segunda       print "Fogo!"
vez, seu valor é 7. A vírgula no final do primeiro comando
print suprime a nova linha no final da saída, que é o motivo
pelo qual as duas saídas aparecem na mesma linha.                   Desde que removemos a chamada recursiva,
              Veja uma reatribuição em um diagrama de esta função não é recursiva.
estado:                                                             Você quase pode ler o comando while como se
                                                      fosse Inglês. Ele significa, "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 fluxo de
                                                                  execução para um comando while:
                                                                     1.   Teste a condição, resultando 0 ou 1.
              Com a reatribuição torna-se ainda mais                 2.   Se a condição for falsa (0), saia do comando while e
importante distinguir entre uma operação de atribuição e um               continue a execução a partir do próximo comando.
comando de igualdade. Como Python usa o sinal de igual ( = )
para atribuição, existe a tendência de lermos um comando             3.   Se a condição for verdadeira (1), execute cada um
como a = b como um comando de igualdade. Mas não é!                       dos comandos dentro do corpo e volte ao passo 1.
              Em primeiro lugar, igualdade é comutativa e                   O corpo consiste de todos os comandos abaixo
atribuição não é. Por exemplo, em matemática, se a = 7 então do cabeçalho, com a mesma endentação.
7 = a. Mas em Python, o comando a = 7 é permitido e 7 = a                   Este tipo de fluxo é chamado de um loop (ou
não é.                                                        laço) porque o terceiro passo cria um "loop" ou um laço de
              Além disso, em matemática, uma expressão de volta ao topo. Note que se a condição for falsa na primeira vez
igualdade é sempre verdadeira. Se a = b agora, então, a será que entrarmos no loop, os comandos dentro do loop jamais
sempre igual a b. Em Python, um comando de atribuição pode serão executados.
tornar duas variáveis iguais, mas elas não têm que permanecer               O corpo do loop poderia alterar o valor de uma
assim:                                                        ou mais variáveis de modo que eventualmente a condição se
 a = 5
                                                              torne falsa e o loop termine. Se não for assim, o loop se
                                                              repetirá para sempre, o que é chamado de um loop infinito.
 b = a # a e b agora são iguais                               Uma fonte de diversão sem fim para os cientistas da
 b = 3 # a e b não são mais iguais                            computação é a observação de que as instruções da
              A terceira linha muda o valor de a mas não embalagem de shampoo, "Lave, enxágüe, repita" é um loop
muda o valor de b, então, elas não são mais iguais. (Em infinito.
algumas linguagens de programação, um símbolo diferente é
usado para atribuição, como <- ou :=, para evitar confusão.)                No caso de contagemRegressiva, podemos
                                                              provar que o loop terminará porque sabemos que o valor de n
              Embora a reatribuição seja freqüentemente útil, é finito, e podemos ver que o valor de n diminui dentro de
você deve usá-la com cautela. Se o valor das variáveis muda cada repetição (iteração) do loop, então, eventualmente
freqüentemente, isto pode fazer o código difícil de ler e de chegaremos ao 0. Em outros casos, isto não é tão simples de
depurar.                                                      afirmar:
                                                                  def sequencia(n):
                                                                    while n != 1:
6.2 O comando while                                                   print n,
                                                                      if n%2 == 0:                  # n é par
Os computadores são muito utilizados para automatizar tarefas           n = n/2
repetitivas. Repetir tarefas idênticas ou similares sem cometer       else:                         # n é impar
erros é uma coisa que os computadores fazem bem e que as
pessoas fazem poorly.                                                   n = n*3+1



                                                  Capítulo 6: Iteração #37
Como pensar como um cientista da Computação usando Python

              A condição para este loop é n != 1, então o loop 1.0        0.0
vai continuar até que n seja 1, o que tornará a condição falsa. 2.0       0.69314718056
               Dentro de cada repetição (iteração) do loop, o      3.0    1.09861228867
programa gera o valor de n e então checa se ele é par ou           4.0    1.38629436112
impar. Se ele for par, o valor de n é dividido por 2. Se ele for   5.0    1.60943791243
impar, o valor é substituído por n*3+1. Por exemplo, se o          6.0    1.79175946923
valor inicial (o argumento passado para seqüência) for 3, a        7.0    1.94591014906
seqüência resultante será 3, 10, 5, 16, 8, 4, 2, 1.
                                                                   8.0    2.07944154168
              Já que n às vezes aumenta e às vezes diminui,        9.0    2.19722457734
não existe uma prova óbvia de que n jamais venha a alcançar                     Se estes valores parecem odd, lembre-se que a
1, ou de que o programa termine. Para alguns valores               função log usa a base e. Já que potências de dois são tão
particulares de n, podemos provar o término. Por exemplo, se       importantes em ciência da computação, nós freqüentemente
o valor inicial for uma potência de dois, então o valor de n       temos que achar logaritmos referentes à base 2. Para fazermos
será par dentro de cada repetição (iteração) do loop até que       isso, podemos usar a seguinte fórmula:
alcance 1. O exemplo anterior termina com uma dessas
seqüências começando em 16.
              Valores específicos à parte, A questão
interessante é se há como provarmos que este programa
termina para todos os valores de n. Até hoje, ninguém foi
capaz de provar que sim ou que não!                                             Alterando o comando de saída para:
              Como um exercício, reescreva a função nLinhas print x, 't', math.log(x)/math.log(2.0)
da seção 4.9 usando iteração em vez de recursão.                        o que resultará em:
                                                                   1.0    0.0
                                                                   2.0    1.0
6.3 Tabelas                                                        3.0    1.58496250072
                                                                   4.0    2.0
Uma das coisas para qual os loops são bons é para gerar dados      5.0    2.32192809489
tabulares. Antes que os computadores estivessem readily
disponíveis, as pessoas tinham que calcular logaritmos, senos,     6.0    2.58496250072
cossenos e outras funções matemáticas à mão. Para tornar isto      7.0    2.80735492206
mais fácil, os livros de matemática continham longas tabelas       8.0    3.0
listando os valores destas funções. Criar as tabelas era           9.0    3.16992500144
demorado e entediante, e elas tendiam a ser cheias de erros.
                                                                                 Podemos ver que 1, 2, 4 e 8 são potências de
              Quando os computadores entraram em cena,             dois porque seus logaritmos na base 2 são números redondos.
uma das reações iniciais foi "Isto é ótimo! Podemos usar           Se precisássemos encontrar os logaritmos de outras potências
computadores para geras as tabelas, assim não haverá erros."       de dois, poderíamos modificar o programa deste modo:
Isto veio a se tornar verdade (na maioria das vezes) mas
shortsighted. Rapidamente, porém, computadores e                   x = 1.0
calculadoras tornaram-se tão pervasivos que as tabelas ficaram     while x < 100.0:
obsoletas.                                                           print x, 't', math.log(x)/math.log(2.0)
                                                                     x = x * 2.0
              Bem, quase. Para algumas operações, os
computadores usam tabelas de valores para conseguir uma                         Agora, em vez de somar algo a x a cada iteração
resposta aproximada e então realizar cálculos para melhorar a      do loop, o que resulta numa seqüência aritmética, nós
aproximação. Em alguns casos, têm havido erros nas tabelas         multiplicamos x por algo, resultando numa seqüência
underlying, o caso mais famoso sendo o da tabela usada pelo        geométrica. O resultado é:
processador Pentium da Intel para executar a divisão em
ponto-flutuante.                                                   1.0    0.0
                                                                   2.0    1.0
               Embora uma tabela de logaritmos não seja mais       4.0    2.0
tão útil quanto já foi um dia, ela ainda dá um bom exemplo de      8.0    3.0
iteração. O seguinte programa gera uma seqüência de valores
na coluna da esquerda e seus respectivos logaritmos na coluna      16.0   4.0
da direita:                                                        32.0   5.0
                                                                   64.0   6.0
x = 1.0                                                                           Por causa do caractere de tabulação entre as
while x < 10.0:                                                    colunas, a posição da segunda coluna não depende do número
  print x, 't', math.log(x)                                       de dígitos na primeira coluna.
  x = x + 1.0
                                                                            Tabelas de logaritmos podem não ser mais úteis,
              A   string   't'   representa   um   caracterde mas para cientistas da computação, conhecer as potências de
tabulação.                                                     dois é!
              Conforme caracteres e strings vão sendo                       Como um exercício, modifique este programa
mostrados na tela, um ponteiro invisível chamado cursor de modo que ele produza as potências de dois acima de 65.535
marca aonde aparecerá o próximo caractere. Depois de um (ou seja, 216). Imprima e memorize-as.
comando print, o cursor normalmente vai para o início de uma
nova linha.                                                                 O caractere de barra invertida em 't' indica o
                                                               início de uma seqüência de escape. Seqüências de escape são
              O caractere de tabulação desloca o cursor para a usadas para representar caracteres invisíveis como de
direita até que ele encontre uma das marcas de tabulação. tabulação e de nova linha. A seqüência n representa uma nova
Tabulação é útil para fazer colunas de texto line up, como na linha.
saída do programa anterior:

                                                    Capítulo 6: Iteração #38
Como pensar como um cientista da Computação usando Python

              Uma seqüência de escape pode aparecer em                Se chamarmos esta função com o argumento 2,
qualquer lugar em uma string; no exemplo, a seqüência de teremos a mesma saída que antes. Com o argumento 3, a saída
escape de tabulação é a única coisa dentro da string.    é:
              Como você acha que se representa uma barra 3            6       9         12       15         18
invertida em uma string?                                                    Com o argumento 4, a saída é:
             Como um exercício, escreva um única string 4             8       12        16       20         24
que
                                                                           Agora você provavelmente pode adivinhar
produza                                                       como imprimir uma tabela de multiplicação - chamando
    esta                                                      imprimeMultiplos repetidamente com argumentos diferentes.
        saída.
                                                              De fato, podemos usar um outro loop:
                                                               i = 1
                                                               while i <= 6:
6.4 Tabelas de duas dimensões (ou bi-dimensionais)               imprimeMultiplos(i)
                                                                 i = i + 1
Uma tabela de duas dimensões é uma tabela em que você lê o                Note o quanto este loop é parecido com aquele
valor na interseção entre uma linha e uma coluna. Uma tabela dentro de imprimeMultiplos. Tudo o que fiz foi substituir o
de multiplicação é um bom exemplo. Digamos que você comando print pela chamada à função.
queira imprimir uma tabela de multiplicação de 1 a 6.
                                                                          A saída deste programa é uma tabela de
               Uma boa maneira de começar é escrever um multiplicação:
loop que imprima os múltiplos de 2, todos em uma linha:
                                                               1      2       3         4        5          6
i = 1                                                          2      4       6         8        10         12
while i <= 6:                                                  3      6       9         12       15         18
  print 2*i, '     ',                                          4      8       12        16       20         24
  i = i + 1                                                    5      10      15        20       25         30
print                                                          6      12      18        24       30         36
              A primeira linha inicializa a variável chamada i,
a qual age como um contador ou variável de controle do
loop. Conforme o loop é executado, o valor de i é
incrementado de 1 a 6. Quando i for 7, o loop termina. A cada 6.6 Mais encapsulamento
repetição (iteração) do loop, é mostrado o valor de 2*i,
seguido de três espaços.                                        Para demonstrar de novo o encapsulamento, vamos pegar o
                                                                código do final da seção 6.5 e acondicioná-lo, envolvê-lo em
              De novo, a vírgula no comando print suprime a uma função:
nova linha. Depois que o loop se completa, o segundo
comando print inicia uma nova linha.                            def imprimeTabMult():
             A saída do programa é:                                i = 1
                                                                   while i <= 6:
2      4     6      8      10      12                                imprimeMultiplos(i)
             Até aqui, tudo bem. O próximo passo é                   i = i + 1
encapsular e generalizar.                                                    Este processo é um plano de desenvolvimento
                                                              comum. Nós desenvolvemos código escrevendo linhas de
                                                              código fora de qualquer função, ou digitando-as no
6.5 Encapsulamento e generalização                            interpretador. Quando temos o código funcionando, extraímos
                                                              ele e o embalamos em uma função.
Encapsulamento é o processo de wrapping um pedaço de                        Este     plano    de   desenvolvimento      é
código em uma função, permitindo que você tire vantagem de particularmente útil se você não sabe, quando você começa a
todas as coisas para as quais as funções são boas. Você já viu escrever, como dividir o programa em funções. Esta técnica
dois exemplos de encapsulamento: imprimeParidade na seção permite a você projetar enquanto desenvolve.
4.5; e eDivisivel na seção 5.4
               Generalização significa tomar algo que é
específico, tal como imprimir os múltiplos de 2, e torná-lo 6.7 Variáveis locais
mais geral, tal como imprimir os múltiplos de qualquer inteiro.
               Esta função encapsula o loop anterior e Você pode estar pensando como podemos utilizar a mesma
generaliza-o para imprimir múltiplos de n:                      variável, i, em ambos, imprimeMultiplos e imprimeTabMult.
                                                                Isto não causaria problemas quando uma das funções mudasse
def imprimeMultiplos(n):                                        o valor da variável?
i = 1                                                                     A resposta é não, porque o i em
while i <= 6:                                                 imprimeMultiplos e o i em imprimeTabMult não são a mesma
  print n*i, 't ',                                           variável.
  i = i + 1
                                                                             Variáveis criadas dentro de uma definição de
print                                                          função são locais; você não pode acessar uma variável local
              Para encapsular, tudo o que tivemos que fazer de fora da função em que ela "mora". Isto significa que você é
foi adicionar a primeira linha, que declara o nome de uma livre para ter múltiplas variáveis com o mesmo nome, desde
função e sua lista de parâmetros. Para generalizar, tudo o que que elas não estejam dentro da mesma função.
tivemos que fazer foi substituir o valor 2 pelo parâmetro n.
                                                                             O diagrama de pilha para este programa mostra

                                                 Capítulo 6: Iteração #39
Como pensar como um cientista da Computação usando Python

que duas variáveis chamadas i não são a mesma variável. Elas def imprimeMultiplos(n, altura):
podem se referir a valores diferentes e alterar o valor de uma i = 1
não afeta à outra.                                             while i <= altura:
                                                                       print n*i, 't',
                                                                       i = i + 1
                                                                     print

                                                                 def imprimeTabMult(altura):
                                                                   i = 1
                                                                   while i <= altura:
                                                                   imprimeMultiplos(i, altura)
                                                                   i = i + 1
                                                                               Note que quando adicionamos um novo
                                                                 parâmetro, temos que mudar a primeira linha da função (o
                                                                 cabeçalho da função), e nós também temos que mudar o lugar
                                                                 de onde a função é chamada em imprimeTabMult.
                                                                              Como esperado, este programa gera uma tabela
                                                                 quadrada de sete por sete:
               O valor de i em imprimeTabMult vai de 1 a 6.      1        2       3            4     5        6        7
No diagrama, i agora é 3. Na próxima iteração do loop i será     2        4       6            8     10       12       14
4. A cada iteração do loop, imprimeTabMult chama                 3        6       9            12    15       18       21
imprimeMultiplos com o valor corrente de i como argumento.
O valor é atribuído ao parâmetro n.                              4        8       12           16    20       24       28
                                                                 5        10      15           20    25       30       35
               Dentro de imprimeMultiplos, o valor de i vai de   6        12      18           24    30       36       42
1 a 6. No diagrama, i agora é 2. Mudar esta variável não tem     7        14      21           28    35       42       49
efeito sobre o valor de i em imprimeTabMult.
                                                                              Quando você generaliza uma função
              É comum e perfeitamente legal ter variáveis        apropriadamente, você muitas vezes tem um programa com
locais diferentes com o mesmo nome. Em particular, nomes         capacidades que você não planejou. Por exemplo, você pode
como i e j são muito usados para variáveis de controle de        ter notado que, porque ab = ba, todas as entradas na tabela
loop. Se você evitar utilizá-los em uma função só porque você    aparecem duas vezes. Você poderia economizar tinta
já os usou em outro lugar, você provavelmente tornará seu        imprimindo somente a metade da tabela. Para fazer isso, você
programa mais difícil de ler.                                    tem que mudar apenas uma linha em imprimeTabMult. Mude:
                                                                 imprimeTabMult(i, altura)
                                                                                para:
6.8 Mais generalização
                                                                 imprimeTabMult(i, i)
Como um outro exemplo de generalização, imagine que você                        e você terá:
precise de um programa que possa imprimir uma tabela de
multiplicação de qualquer tamanho, não apenas uma tabela de 1
seis por seis. Você poderia adicionar um parâmetro a 2                    4
imprimeTabMult:                                             3             6       9
def imprimeTabMult(altura):                                      4        8       12           16
  i = 1                                                          5        10      15           20    25
  while i <= altura:                                             6        12      18           24    30       36
    imprimeMultiplos(i)                                          7        14      21           28    35       42       49
    i = i + 1                                                        Como um exercício, trace a execução desta
              Nós substituímos o valor 6 pelo parâmetro versão de imprimeTabMult e explique como ela funciona.
altura. Se chamarmos imprimeTabMult com o argumento 7,
ela mostra:
1      2        3        4        5         6
                                                                 6.9 Funções
2      4        6        8        10        12
                                                                      ●    Há pouco tempo mencionamos "todas as coisas para
3      6        9        12       15        18                             as quais as funções são boas." Agora, você pode estar
4      8        12       16       20        24                             pensando que coisas exatamente são estas. Aqui estão
5      10       15       20       25        30                             algumas delas:
6      12       18       24       30        36
                                                                      ●    Dar um nome para uma seqüência de comandos torna
7      14       21       28       35        42                             seu programa mais fácil de ler e de depurar.
              Isto é bom, exceto que nós provavelmente
quereríamos que a tabela fosse quadrada - com o mesmo                 ●    Dividir um programa longo em funções permite que
número de linhas e colunas. Para fazer isso, adicionamos                   você separe partes do programa, depure-as
outro parâmetro a imprimeMultiplos para especificar quantas                isoladamente, e então as componha em um todo.
colunas a tabela deveria ter.
                                                                      ●    Funções facilitam tanto recursão quanto iteração.
             Só para confundir, chamamos este novo
parâmetro de altura, demonstrando que diferentes funções              ●    Funções bem projetadas são freqüentemente úteis
podem ter parâmetros com o mesmo nome (como acontece                       para muitos programas. Uma vez que você escreva e
com as variáveis locais). Aqui está o programa completo:                   depure uma, você pode reutilizá-la.


                                                 Capítulo 6: Iteração #40
Como pensar como um cientista da Computação usando Python


6.10 Glossário

       reatribuição quando mais de um valor é atribuído a
          (multiple mesma variável durante a execução do
      assignment 1) programa.
            iteração execução repetida de um conjunto de
         (iteration) comandos/instruções (statements) usando
                     uma chamada recursiva de função ou um
                     laço (loop).
        laço (loop) um comando/instrução ou conjunto de
                    comandos/instruções        que executam
                    repetidamente até que uma condição de
                    interrupção seja atingida.
       laço infinito um laço em que a condição de interrupção
     (infinite loop) nunca será atingida.
      corpo (body) o conjunto de comandos/instruções que
                   pertencem a um laço.
    variável de laço uma variável usada como parte da
     (loop variable) condição de interrupção do laço.
    tabulação (tab) um carácter especial que faz com que o
                    cursor mova-se para a próxima parada
                    estabelecida de tabulação na linha atual.
        nova-linha um carácter especial que faz com que o
         (newline) cursor mova-se para o início da próxima
                   linha.
    cursor (cursor) um marcador invisível que determina
                    onde o próximo carácter var ser impresso.
       sequência de um carácter de escape () seguido por um
     escape (escape ou mais caracteres imprimíveis, usados
          sequence) para definir um carácter não imprimível.
        encapsular quando um programa grande e complexo
      (encapsulate) é dividido em componentes (como
                    funções) e estes são isolados um do outro
                    (pelo uso de variáveis locais, por
                    exemplo).
        generalizar quando algo que é desnecessariamente
       (generalize) específico (como um valor constante) é
                    substituído por algo apropriadamente
                    geral (como uma variável ou um
                    parâmetro). Generalizações dão maior
                    versatilidade    ao   código,      maior
                    possibilidade de reuso, e em algumas
                    situações até mesmo maior facilidade para
                    escrevê-lo.
           plano de   um        processo     definido    para
    desenvolviment    desenvolvimento de um programa. Neste
    o (development    capítulo, nós demonstramos um estilo de
              plan)   desenvolvimento baseado em escrever
                      código para executar tarefas simples e
                      específicas, usando encapsulamento e
                      generalização.




1    N.T.: O termo multiple assignment (ou atribuição múltipla) é
     usado com mais frequência para descrever a sintaxe a = b
     = c. Por este motivo optamos pelo termo reatribuição no
     contexto da seção 6.1 desse capítulo.

                                                    Capítulo 6: Iteração #41
Como pensar como um cientista da Computação usando Python


                                                   Capítulo 7: Strings



                                                                 de comprimento:
7.1 Um tipo de dado composto
                                                                  comprimento = len(fruta)
Até aqui, vimos três diferentes tipos de dado: int, float e ultima = fruta[comprimento-1]
string. Strings são qualitativamente diferentes dos outros dois               Como alternativa, podemos usar índices
tipos porque são feitas de pedaços menores - caracteres.        negativos, os quais contam de trás pra frente os elementos da
                                                                string. A expressão fruta[-1] resulta a última letra, fruta[-2]
               Tipos que consistem de pedaços menores são resulta a penúltima (a segunda de trás para frente), e assim por
chamados tipos de dados compostos. Dependendo do que diante.
estejamos fazendo, pode ser que precisemos tratar um tipo de
dado composto como uma coisa única, ou pode ser que
queiramos acessar suas partes. Esta ambigüidade é útil.
              O operador colchete seleciona um único
                                                                 7.3 Travessia e o loop for
caractere de uma string.:
                                                               Várias computações envolvem o processamento de uma string
>>> fruta = "banana"                                           um caractere de cada vez. Muitas vezes elas começam com o
>>> letra = fruta[1]                                           primeiro, selecionam um de cada vez, fazem alguma coisa
>>> print letra                                                com ele, e continuam até o fim. Este padrão de processamento
                                                               é chamado uma travessia (traversal, com a idéia de
             A expressão fruta[1] seleciona o caractere "percorrimento"). Uma maneira de codificar uma travessia é
número 1 de fruta. A variável letra referencia ou refere-se ao com um comando while:
resultado da expressão. Quando exibimos letra, temos uma
surpresa:                                                      indice = 0
                                                                  while indice < len(fruta):
a
                                                                      letra = fruta[indice]
               A primeira letra de "banana" não é a. A menos
que você seja um cientista da computação. Neste caso, você            print letra
deve entender a expressão dentro dos colchetes como um                indice = indice + 1
deslocamento (offset,) a partir do começo da string, e o                         Este loop percorre a string e exibe cada letra em
deslocamento da primeira letra é zero. Assim, b é a 0ª ("zero- sua própria linha. A condição do loop é indice < len(fruta),
ésima") letra de "banana", a é a 1ª ("um-ésima", diferente de assim, quando índice é igual ao comprimento da string, a
primeira), e n é a 2ª ("dois-ésima", diferente de segunda) letra. condição se torna falsa, e o corpo do loop não é executado. O
                                                                  último caractere acessado é aquele com o índice len(fruta)-1,
               Para pegar a primeira letra de uma string, você que vem a ser o último caractere da string.
simplesmente põe 0, ou qualquer expressão que resulte o valor
0, dentro dos colchetes:                                                         Como um exercício, escreva uma função que
                                                                    tome uma string como argumento e devolva suas letras de
>>> letra = fruta[0]                                                trás para frente, uma por linha.
>>> print letra
b                                                                           Usar um índice para percorrer um conjunto de
                                                             valores é tão comum que Python oferece uma sintaxe
               A expressão entre colchetes é chamada de alternativa simplificada - o loop for:
índice. Um índice especifica um membro de um conjunto
ordenado, neste caso o conjunto de caracteres da string. O for char in fruta:
índice indica aquele membro que você quer, daí seu nome. Ele      print char
pode ser qualquer expressão inteira.
                                                                            A cada vez através do loop, o próximo caractere
                                                             da string é atribuído à variável char. O loop continua até que
                                                             não reste mais caracteres.
7.2 Comprimento                                                           O exemplo seguinte mostra como usar
                                                             concatenação e um loop for para gerar uma série abecedário.
A função len retorna o número de caracteres de uma string:   "Abecedário" se refere a uma série ou lista na qual os
                                                             elementos aparecem em ordem alfabética. Por exemplo, no
>>> fruta = "banana"                                         livro de Robert McCloskey's Make Way for Ducklings, os
>>> len(fruta)                                               nomes dos "ducklings" são Jack, Kack, Lack, Mack, Nack,
6                                                            Ouack, Pack e Quack. O loop seguinte, produz como saída
               Para pegar a última letra de uma string, você aqueles nomes, em ordem:
pode ficar tentado a fazer alguma coisa assim:
                                                                  prefixos = "JKLMNOPQ"
comprimento = len(fruta)                                          sufixo = "ack"
ultima = fruta[comprimento]         #   ERRO!
              Não vai funcionar. Isto vai causar o seguinte for letra in prefixos:
erro em tempo de execução (runtime error): IndexError: string     print letra + sufixo
index out of range. (ErroDeIndice: índice da string fora do               A saída deste programa é:
intervalo). A razão é que não existe 6ª letra em "banana". Já
que começamos a contar do zero, as seis letras são numeradas Jack
de 0 a 5. Para pegar o último caractere, temos que subtrair 1 Kack

                                                    Capítulo 7: Strings #42
Como pensar como um cientista da Computação usando Python

Lack                                                           else:
Mack                                                               print "Sim, nós não temos bananas!"
Nack                                                                       Entretanto, você deve atentar para o fato de que
Oack                                                          Pyhton não manipula letras maiúsculas e minúsculas da
Pack                                                          mesma maneira que as pessoas o fazem. Todas as letras
Qack
                                                              maiúsculas vêm antes das minúsculas. Como resultado:
               Naturalmente, esta saída não está cem por cento Sua palavra, Zebra, vem antes de banana.
certa porque "Ouack" e "Quack" estão escritos de maneira                     Uma maneira comum de resolver este problema
errada.                                                        é converter as strings para um formato padrão, seja todas
               Como um exercício, modifique o programa para minúsculas, ou todas maiúsculas, antes de realizar a
  corrigir este erro.                                          comparação. Um problema mais difícil é fazer o programa
                                                               perceber que zebras não são frutas.

7.4 Fatias de strings                                         7.6 Strings são imutáveis
Um segmento de uma string é chamado de uma fatia.
Selecionar uma fatia é similar a selecionar um caractere: É tentador usar o operador [] no lado esquerdo de uma
                                                          expressão de atribuição, com a intenção de alterar um
>>> s = "Pedro, Paulo e Maria"                            caractere em uma string. Por exemplo:
>>> print s[0:5]                                               saudacao = "Alô, mundo!"
Pedro                                                          saudacao[0] = 'E'                 #   ERRO!
>>> print s[7:12]                                              print saudacao
Paulo
                                                                          Em vez de produzir a saída Elô, Mundo!, este
>>> print s[16:21]                                           código produz o erro em tempo de execução (runtime error):
Maria                                                        TypeError: object doesn't support item assignment
               O operador [n:m] retorna a parte da string do (ErroDeTipo: objeto não dá suporte à atribuição de item.)
"n-ésimo" caractere ao "m-ésimo" caractere, incluindo o
primeiro mas excluindo o último. Este comportamento não é                 Strings são imutáveis, o que significa que você
intuitivo; ele faz mais sentido se você imaginar os índices não pode mudar uma string que já existe. O melhor que você
apontando para os intervalos entre os caracteres, como no pode fazer é criar uma nova string que seja uma variação da
seguinte diagrama:                                           original:
                                                               saudacao = "Alô, mundo!"
                                                               novaSaudacao = 'E' + saudação[1:]
                                                               print novaSaudacao
                                                                           A solução aqui é concatenar uma nova primeira
                                                              letra com uma fatia de saudação. Esta operação não tem
                                                              nenhum efeito sobre a string original.


               Se você omitir o primeiro índice (antes dos dois 7.7 Uma função find (encontrar)
pontos ":"), a fatia começa do início da string. Se você omitir
o segundo índice, a fatia vai até o final da string. Assim:     O que faz a seguinte função?:
>>> fruta = "banana"                                           def find(str, ch):
>>> fruta[:3]                                                      indice = 0
'ban'                                                              while indice < len(str):
>>> fruta[3:]                                                          if str[indice] == ch:
'ana'                                                                      return indice
             O que você acha de s[:] significa?                        indice = indice + 1
                                                                   return -1
                                                                           Num certo sentido, find (encontrar) é o oposto
7.5 Comparação de strings                                    do operador []. Em vez de pegar um índice e extrair o
                                                             caractere correspondente, ela pega um caractere e encontra
                                                             (finds) em qual índice aquele caractere aparece. Se o caractere
O operador de comparação funciona com strings. Para ver se não é encontrado, a função retorna -1.
duas strings são iguais:
                                                                           Este é o primeiro exemplo que vemos de uma
if palavra == "banana":                                      instrução return dentro de um loop. Se str[indice] == ch, a
     print "Sim, nós não temos bananas!"                     função retorna imediatamente, abandonando o loop
               Outras operações de comparação são úteis para prematuramente.
colocar palavras em ordem alfabética:
                                                                           Se o caractere não aparece na string, então o
if palavra < "banana":                                       programa sai do loop normalmente e retorna -1.
    print "Sua palavra," + palavra + ", vem antes de                       Este padrão de computação é às vezes chamado
 banana."                                                     de travessia "eureka", porque tão logo ele encontra (find) o
elif palavra > "banana":                                      que está procurando, ele pode gritar "Eureka!" e parar de
    print "Sua palavra," + palavra + ", vem depois           procurar.
de banana."                                                                 Como um exercício, modifique a função find

                                                  Capítulo 7: Strings #43
Como pensar como um cientista da Computação usando Python


    (encontrar) de modo que ela receba um terceiro parâmetro, o              Neste exemplo, a busca falha porque a letra b
    índice da string por onde ela deve começar sua procura.     não aparece no intervalo entre 1 e 2 (não incluindo o 2) do
                                                                índice.

7.8 Iterando e contando
                                                               7.10 Classificação de caracteres
O programa seguinte conta o número e vezes que a letra a
aparece em uma string:                                   Muitas vezes é útil examinar um caractere e testar se ele é
                                                         maiúsculo ou minúsculo, ou se ele é um caractere ou um
fruta = "banana"                                         dígito. O módulo string oferece várias constantes que são úteis
contador = 0                                             para esses propósitos.
for letra in fruta:
                                                                            A string string.lowercase contém todas as letras
    if letra == 'a'                                            que o sistema considera como sendo minúsculas.
        contador = contador + 1                                Similarmente, string.uppercase contém todas as letras
print contador                                                 maiúsculas. Tente o seguinte e veja o que você obtém:
              Este programa demonstra um outro padrão de       >>> print string.lowercase
computação chamado de contador. A variável contador é
inicializada em 0 e então incrementada cada vez que um a é     >>> print string.uppercase
encontrado. (Incrementar é o mesmo que aumentar em um; é       >>> print string.digits
o oposto de decrementar, e não tem relação com excremento,                  Nós podemos usar essas constantes e find
que é um substantivo.) Quando se sai do loop, contador         (encontrar) para classificar caracteres. Por exemplo, se
guarda o resultado - o número total de a's.                    find(lowercase, ch) retorna um valor outro que não -1, então
                                                               ch deve ser minúsculo:
                Como um exercício, encapsule este código em
    uma função chamada contaLetras, e generalize-a de modo def eMinusculo(ch):
    que possa aceitar uma string e uma letra como parâmetros.      return string.find(string.lowercase, ch) != -1
                Como um segundo exercício, reescreva esta                   Como uma alternativa, podemos tirar vantagem
    função de modo que em vez de percorrer a string, ela use a do operador in, que determina se um caractere aparece em
    versão com três parâmetros de find (encontrar) da seção uma string:
    anterior.
                                                               def eMinusculo(ch):
                                                                   return ch in string.lowercase
7.9 O módulo string                                                          Ainda, como uma outra alternativa, podemos
                                                               usar o operador de comparação:
O módulo string contém funções úteis que manipulam strings. def eMinusculo(ch):
Conforme é usual, nós temos que importar o módulo antes que     return 'a' <= ch <= 'z'
possamos utilizá-lo:                                                    Se ch estiver entre a e z, ele deve ser uma letra
>>> import string
                                                            minúscula.
             O módulo string inclui uma função chamada                     Como um exercício, discuta que versão de
find (encontrar) que faz a mesma coisa que a função que          eMinusculo você acha que será a mais rápida. Você pode
escrevemos. Para chamá-la, temos que especificar o nome do       pensar em outras razões além da velocidade para preferir
módulo e o nome da função usando a notação de ponto.:            uma em vez de outra?
>>> fruta = "banana"                                                         Outra constante definida no módulo string pode
>>> indice = string.find(fruta, "a")                           te surpreender quando você executar um print sobre ela:
>>> print indice                                               >>> print string.whitespace
1                                                                            Caracteres de espaçamento (ou espaços em
              Este exemplo demonstra um dos benefícios dos     branco) movem o cursor sem "imprimir" qualquer coisa. Eles
módulos - eles ajudam a evitar colisões entre nomes de         criam os espaços em branco entre os caracteres visíveis (pelo
funções nativas e nomes de funções definidas pelo usuário.     menos numa folha de papel branco). A string constante
Usando a notação de ponto podemos especificar que versão de    string.whitespace contém todos os caracteres de espaçamento,
find (encontrar) nós queremos.                                 incluindo espaço, tabulação (t) e nova linha (n).
             De fato, string.find é mais generalizada que a               Existem outras funções úteis no módulo string,
nossa versão. Primeiramente, ela pode encontrar substrings, mas este livro não pretende ser um manual de referência. Por
não apenas caracteres:                                      outro lado, Python Library Reference é exatamente isto. Em
                                                            meio a uma abundante documentação, ele está disponível no
>>> string.find("banana", "na")                             site da web do Python, www.python.org.
2
             Além disso, ela recebe um argumento adicional
que especifica o índice pelo qual ela deve começar sua
procura:                                                   7.11 Glossário
>>> string.find("banana", "na", 3)
                                                                 tipo de dado Um tipo de dado no qual o valor consiste de
4                                                                   composto componentes, ou elementos, que são eles
              Ou ela pode receber dois argumentos adicionais      (compound mesmos valores.
que especificam o intervalo de índices:                             data type)
>>> string.find("bob", "b", 1, 2)                                    travessia Iterar através dos elementos de um conjunto,
-1                                                                  (traverse) realizando uma operação similar em cada um


                                                  Capítulo 7: Strings #44
Como pensar como um cientista da Computação usando Python

                  deles.
índice (index) Uma variável ou valor usados para
               selecionar um membro de um conjunto
               ordenado, como um caractere em uma string.
   fatia (slice) Uma parte de uma string especificada por
                 um intervalo de índices.
     mutável Um tipo de dado composto a cujos
    (mutable) elementos podem ser atribuídos novos
              valores.
    contador Uma variável utilizada para contar alguma
    (counter) coisa, usualmente inicializada em zero e
              então incrementada.
 incrementar aumentar o valor de uma variável em 1.
  (increment)
 decrementar diminuir o valor de uma variável em 1.
  (decrement)
 espaçamento Qualquer um dos caracteres que move o
 (whitespace) cursor sem imprimir caracteres visíveis. A
              constante string.whitespace contém todos os
              caracteres de espaçamento.


7.11 Glossário2

  tipo de dado    Um tipo de dado em que os valores são
     composto     compostos de componentes, ou elementos,
   (compound      que podem ser tratados como valores
     data type)   separados.
    atravessar Iterar através dos elementos definidos,
     (traverse) executando uma operação similar em cada.
 índice (index) Uma variável ou valor usado para selecionar
                um membro de uma definição ordenada,
                como um caractere de uma string.
   fatia (slice) Uma parte da string especificada por um
                 intervalo de índice.
     mutável Um tipo de dado composto do qual
    (mutable) elementos podem atribuir novos valores.
     contador Uma variável usada para contar alguma
     (counter) coisa, geralmente iniciada em zero e
               incrementada.
   incremento Para aumentar o valor da variável.
  (increment)
  decremento Para dimiuir o valor da variável.
  (decrement)
    espaço em Qualquer caractere que move o cursor sem
       branco imprimir caracteres visíveis. A constante
  (whitespace) string.whitespace    contém     todos os
               caracteres de espaço em branco.




                                                 Capítulo 7: Strings #45
Como pensar como um cientista da Computação usando Python


                                                 Capítulo 8: Listas


Uma lista é um conjunto ordenado de valores, onde cada valor
é identificado por um índice. Os valores que compõem uma        8.2 Acessado elementos
lista são chamados elementos. Listas são similares a strings,
que são conjuntos ordenados de caracteres, com a diferença      A sintaxe para acessar os elementos de uma lista é a mesma
que os elementos de uma lista podem possuir qualquer tipo.      que a sintaxe para acessar os caracteres de uma string XXX o
Listas e strings XXX e outras coisas que se comportam como      operator colchete ([]). A expressão dentro dos colchetes
conjuntos ordenados XXX são chamados seqüências.                especifica o índice. Lembre-se que os índices iniciam em 0:
                                                                >>> print numeros[0]
                                                                >>> numeros[1]= 5
8.1 Valores da lista
                                                                      O operador colchete pode aparecer em qualquer
                                                        lugar em uma expressão. Quando ele aparece no lado
Existem várias maneiras de criar uma nova lista; a mais esquerdo de uma atribuição, ele modifica um dos elementos
simples é envolver os elementos em colchetes ([ e ]):   em uma lista, de forma que o um-ésimo elemento de numeros,
>>> [10, 20, 30, 40]
                                                        que era 123, é agora 5.
>>> ['spam', 'bungee', 'swallow']                                           Qualquer expressão inteira pode ser utilizada
              O primeiro exemplo á uma lista de quatro          como um índice:
inteiros. O segundo é uma lista de três strings. Os elementos
de uma lista não necessitam ser do mesmo tipo. A lista a        >>> numeros[3-2]
seguir contém uma string, um valor float, um valor inteiro, e   5
mirabile dictu uma outra lista:                                 >>> numeros[1.0]
                                                                TypeError: sequence index must be integer
>>> ['alo', 2.0, 5, [10,20]]
                                                                        Se você tentar ler ou escrever um elemento que
             Uma lista dentro de outra lista é dita estar não existe, você recebe um erro de tempo de execução
aninhada.                                                 (runtime error):
             Listas que contém inteiros consecutivos são >>> numeros[2]=5
comuns, então Python fornece uma maneira simples de criá-
los:                                                      IndexError: list assignment index out of range
                                                                        Se um índice possui um valor negativo, ele
 >>> range(1,5)                                           conta ao contrário a partir do final da lista:
[1, 2, 3, 4]
                                                                >>> numeros[-1]
              A função range pega dois argumentos e devolve
uma lista que contém todos os inteiros do primeiro até o 5
segundo, incluindo o primeiro mas não incluindo o segundo!  >>> numeros[-2]
                                                                17
             Existem outras formas de range. Com um >>> numeros[-3]
argumento simples, ela cria uma lista que inicia em 0:
                                                                IndexError: list index out of range
>>> range(10)                                                                numeros[-1] é o último elemento da lista,
[0,1, 2, 3, 4, 5, 6, 7, 8, 9]                                   numeros[-2] é o penúltimo e numeros[-3] não existe.
            Se existe um terceiro argumento, ele especifica             É comum utilizar uma variável de laço como um
o espaço entre os valores sucessivos, que é chamado de índice da lista:
tamanho do passo. Este exemplo conta de 1 até 10 em passos
de 2:                                                       >>> cavaleiros = ['guerra', 'fome', 'peste', 'morte']
                                                                i = 0
>>> range(1, 10, 2)
                                                                while i < 4:
[1, 3, 5, 7, 9]
                                                                        print cavaleiros[i]
            Finalmente, existe uma lista especial que não
contém elementos. Ela é chamada lista vazia, e sua notação é            i = i + 1
[].                                                                        Este laço while conta de 0 até 4. Quando a
                                                             variável do laço i é 4, a condição falha e o laço se encerra.
             Com todas estas formas de criar listas, seria Desta forma o corpo do laço é executado somente quando i é
decepcionante se não pudéssemos atribuir valores de listas a 0, 1, 2 e 3.
variáveis ou passar listas como parâmetros a funções.
Felizmente, podemos.                                                       Em cada vez dentro do laço, a variável i é
                                                             utilizada como um índice para a lista, exibindo o i-ésimo
>>> vocabulario = ['melhorar', 'castigar',                  elemento. Este padrão de computação é chamado de percurso
'defenestrar']                                               na lista.
>>> numeros = [17, 123]
>>> vazio = []
>>> print vocabulario, numeros, vazio                           8.3 Comprimento da lista
['melhorar', 'castigar', 'defenestrar'] [17, 123] []
                                                                A função len devolve o comprimento de uma lista. É uma boa
                                                                idéia utilizar este valor como o limite superior de um laço ao
                                                                invés de uma constante. Desta forma, se o tamanho da lista


                                                  Capítulo 8: Listas #46
Como pensar como um cientista da Computação usando Python

mudar, você não precisará ir através de todo o programa                  Quase se lê como Português: "For (para cada)
modificando todos os laços; eles funcionarão corretamente cavaleiro in (na lista de) cavaleiros, print (imprima o nome do)
para qualquer tamanho de lista:                           cavaleiro."
>>> cavaleiros = ['guerra', 'fome', 'peste', 'morte']                               Qualquer expressão de lista pode ser utilizada
i = 0                                                               num laço for:
while i < len(cavaleiros):                                          >>> for numero in range(20):
        print cavaleiros[i]                                                         if numero % 2 == 0:
        i = i + 1                                                                           print numero
              A última vez que o corpo do laço é executado, i
é len(cavaleiros) - 1, que é o índice do último elemento. >>> for fruta in ["banana", "abacaxi", "laranja"]:
Quando i é igual a len(cavaleiros), a condição falha e o corpo
não é executado, o que é uma boa coisa, porque                           print "Eu gosto de comer " + fruta + "s!"
len(cavaleiros) não é um índice legal.                                       O primeiro exemplo exibe todos os números
                                                               pares entre zero e dezenove. O segundo exemplo expressa o
              Embora uma lista possa conter uma outra lista, a entusiasmo por várias frutas.
lista aninhada ainda conta como um elemento simples. O
comprimento desta lista é quatro:
>>> [`spam!', 1, ['Brie', 'Roquefort',                             8.6 Operações em listas
'Pol lê Veq'], [1, 2 3]]
             Como um exercício, escreva um laço que O operador + concatena listas:
  percorra a lista anterior e exiba o comprimento de cada
  elemento. O que acontece se você manda um inteiro para >>> a = [1, 2, 3]
  len?                                                    >>> b = [4, 5, 6]
                                                                    >>> c = a + b
                                                                    >>> print c
8.4 Membros de uma lista                                            [1, 2, 3, 4, 5, 6]
                                                                      Similarmente, o operador * repete uma lista um
in é um operador lógico que testa se um elemento é membro número dado de vezes:
de uma seqüência. Nós o utilizamos na Seção 7.10 com
strings, mas ele também funciona com listas e outras >>> [0] * 4
seqüências:                                               [0, 0, 0, 0]
                                                                    >>> [1, 2, 3] * 3
>>> cavaleiros = ['guerra', 'fome', 'peste', 'morte']               [1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> 'peste' in cavaleiros                                                       O primeiro exemplo repete [0] quatro vezes. O
True                                                                segundo exemplo repete a lista [1, 2, 3] três vezes.
>>> 'depravação' in cavaleiros
False
              Uma vez que 'peste' é um membro da lista 8.7 Fatiamento de listas
cavaleiros, o operador in devolve verdadeiro. Uma vez que
depravação não está na lista, in devolve falso.
                                                           A operação de fatiamento que vimos na Seção 7.4 também
              Podemos utilizar também o not em combinação funciona sobre listas:
com o in para testar se um elemento não é um membro de uma
lista:                                                     >>> lista = ['a', 'b', 'c', 'd', 'e', 'f']
                                                                    >>> lista[1:3]
>>> ``depravação`` not in cavaleiros                                ['b', 'c']
True                                                                >>> lista[:4]
                                                                    ['a', 'b', 'c', 'd']
                                                                    >>> lista[3:]
8.5 Listas e laços for                                              ['d', 'e', 'f']
                                                                    >>> lista[:]
O laço for que vimos na Seção 7.3 também funciona com               ['a', 'b', 'c', 'd', 'e', 'f']
listas. A sintaxe generalizada de um laço for é:
for VARIÁVEL in LISTA:
        CORPO                                                       8.8 Listas são mutáveis
              Esta declaração é equivalente a:
                                                                    Diferente das strings, as listas são mutáveis, o que significa
>>> i = 0                                                           que podemos modificar seus elementos. Utilizando o operador
    while i < len(LIST):                                            colchete no lado esquerdo de uma atribuição, podemos
        VARIABLE = LIST[i]                                          atualizar um de seus elementos:
        XXX BODY
                                                                    >>> fruta = ["banana", "abacaxi", "laranja"]
    i = i + 1
                                                                    >>> fruta[0] = "abacate"
              O laço for é mais conciso porque podemos              >>> fruta[-1] = "tangerina"
eliminar a variável do laço, i. Aqui está o laço anterior escrito
com um`laço for:                                                    >>> print fruta
                                                                    ['abacate', 'abacaxi', 'tangerina']
>>> for cavaleiro in cavaleiros:                                                   Com o operador de fatiamento podemos
        print cavaleiro                                             atualizar vários elementos de uma vez:


                                                     Capítulo 8: Listas #47
Como pensar como um cientista da Computação usando Python

>>> lista = ['a', 'b', 'c', 'd', 'e', 'f']                    se referem à mesma coisa. Estas "coisas" possume nomes -
>>> lista[1:3] = ['x', 'y']                                   elas são chamadas objetos. Um objeto é algo ao qual uma
>>> print lista
                                                              variável pode se referenciar.
['a', 'x', 'y', 'd', 'e', 'f']                                      Todo objeto possui um identificador único, que
               Também podemos remover elementos de uma podemos obter com a função id. Exibindo o identificador de a
lista atribuindo a lista vazia a eles:                 e b, podemos dizer se elas se referem ao mesmo objeto.
>>> lista = ['a', 'b', 'c', 'd', 'e', 'f']                    >>> id(a)
>>> lista[1:3] = []                                           135044008
>>> print lista                                               >>> id(b)
['a', 'd', 'e', 'f']                                          135044008
             E podemos adicionar elementos a uma lista               De fato, obtivemos o mesmo identificador duas
enfiando-os numa fatia vazia na posição desejada:      vezes, o que significa que Python criou apenas uma string, e
                                                       tanto a quanto b se referem a ela.
>>> lista = ['a', 'd', 'f']
>>> lista[1:1] = ['b', 'c']
                                                                            Interessantemente, listas se comportam de forma
                                                              diferente. Quando criamos duas listas, obtemos dois objetos:
>>> print lista
['a', 'b', 'c', 'd', 'f']                                     >>> a = [1, 2, 3]
>>> lista[4:4] = ['e']                                        >>> b = [1, 2, 3]
>>> print lista                                               >>> id(a)
['a', 'b', 'c', 'd', 'e', 'f']                                135045528
                                                              >>> id(b)
                                                              135041704
8.9 Remoção em lista                                                        Então o diagrama de estado fica assim:

Utilizando fatias para remover elementos pode ser
complicado, e desta forma propenso a erro. Python fornece
uma alternativa que é mais legível.
             del remove um elemento de uma lista:
>>> a = ['um', 'dois', 'tres']
>>> del a[1]                                                               a e b possuem o mesmo valor mas não se
>>> a                                                         referem ao mesmo objeto.
['um', 'tres']
               Como você deveria esperar, del trata valores
negativos e causa erros de tempo de execução se o índice 8.11 Apelidos
estiver fora da faixa.
              Você também pode utilizar uma faixa como um Uma vez que variáveis se referem a objetos, se atribuimos
índice para del:                                          uma variável a uma outra, ambas as variáveis se referem ao
                                                          mesmo objeto:
>>> lista = ['a', 'b', 'c', 'd', 'e', 'f']
>>> del lista[1:5]                                            >>> a = [1, 2, 3]
>>> print lista                                               >>> b = a
['a', 'f']                                                                  Neste caso, o diagrama de estado se parece com
              Como de costume, fatias selecionam todos os isto:
elementos até, mas não incluindo, o segundo índice.


8.10 Ojetos e valores
Se executamos estas declarações de atribuição:
>>> a = "banana"
>>> b = "banana"
                                                                            Uma vez que a lista possui dois nomes
                                                             diferentes, a e b, dizemos que ela está "apelidada" (aliased).
              sabemos que a e b se referem a uma string com Mudanças feitas em um apelido afetam o outro nome:
as letras banana. Mas não podemos dizer se elas apontam para
a mesma string.                                              >>> b[0] = 5
                                                              >>> print a
             Existem dois possíveis estados:
                                                              [5, 2, 3]
                                                                            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
                                                              mutáveis. É claro, para objetos imutáveis, não há problema. É
                                                              por isto que Python é livre para apelidar cadeias de caracteres
                                                              quando vê uma oportunidade de economizar.
              Em um caso, a e b se referem a duas coisas
diferentes que possuem o mesmo valor. No segundo caso, elas

                                                  Capítulo 8: Listas #48
Como pensar como um cientista da Computação usando Python


                                                              que contém todos menos o primeiro elemento de uma
8.12 Clonando listas                                          determinada lista:
Se queremos modificar uma lista e também manter uma cópia >>> def cauda(lista):
da original, preciamos ter condições de fazer uma cópia da         return lista[1:]
própria lista, não apenas uma referência. Este processo é              Aqui está a maneira como ela é utilizada:
algumas vezes chamado clonagem, para evitar a ambigüidade
da palavra "cópia".                                        >>> numeros = [1, 2, 3]
                                                              >>> resto = cauda(numeros)
               A maneira mas fácil de clonar uma lista é
utilizar o operador de fatia:                            >>> print resto
                                                              [2, 3]
>>>   a = [1, 2, 3]                                                         Uma vez que o valor de retorno foi criado com
>>>   b = a[:]                                                o operador de fatia, ele é uma nova lista. A criação de resto, e
>>>   print b                                                 qualquer alteração subseqüente a resto, não tem efeito sobre
[1,   2, 3]                                                   numeros.
             Pegar qualquer fatia de a cria uma nova lista.
Neste caso acontece da fatia consistir da lista inteira.
             Agora estamos livres para fazer alterações a b
                                                              8.14 Lista aninhadas
sem nos preocuparmos com``a``:
                                                              Uma lista aninhada é uma lista que aparece como um
>>> b[0] = 5                                                  elemento de uma outra lista. Nesta lista, o terceiro elemento é
>>> print a                                                   uma lista aninhada:
[1, 2, 3]                                                     >>> lista = ["alo", 2.0, 5, [10, 20]]
             Como exercício, desenhe um diagrama de              Se exibimos lista[3], obtemos [10, 20]. Para
 estado para``a`` e b antes e depois desta mudança. extrairmos um elemento de uma lista aninhada, podemos agir
                                                    em duas etapas:
                                                              >>> elem = lista[3]
8.13 Lista como parâmetro
                                                              >>> elem[0]
                                                              10
Passar uma lista como um argumento passa realmente uma
referência à lista, não uma cópia da lista. Por exemplo, a          Ou podemos combiná-las:
função cabeca pega uma lista como parâmetro e devolve a >>> lista[3][1]
cabeça da lista, ou seja, seu primeiro elemento:
                                                              20
>>> def cabeca(lista):                                                        Os operadores colchete avaliam da esquerda
        return lista[0]                                       para a direita, então a expressão pega o terceiro elemento de
             Eis como ela é utilizada:                        lista e extrai o primeiro elemento dela.
>>> numeros = [1, 2, 3]
>>> cabeca(numeros)
                                                              8.15 Matrizes
1
              O parâmetro lista e a variável numeros são Listas aninhadas são freqüentemente utilizadas                  para
apelidos para o mesmo objeto. O diagrama de estado se parece representar matrizes. Por exemplo, a matriz:
com isto:




                                                                            poderia ser representada como:
                                                              >>> matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
              Uma vez que o objeto é compartilhado pelos                   matriz é uma lista com três elementos, onde
dois quadros, o desenhamos entre eles.                      cada elemento é uma linha da matriz. Podemos selecionar uma
              Se a função modifica um parâmetro da lista, a linha inteira da matriz da maneira habitual:
função chamadora vê a mudança. Por exemplo, >>> matriz[1]
removeCabeca remove o primeiro elemento da lista:
                                                              [4, 5, 6]
>>> def removecabeca(lista):                                                 Ou podemos extrair um único elemento da
        del lista[0]                                          matriz utilinado a forma de duplo índice:
             Aqui está a maneira como ela é utilizada:        >>> matriz[1][1]
>>>   numeros = [1, 2, 3]                                     5
>>>   removeCabeca(numeros)                                                O primeiro índice seleciona a linha, e o segundo
>>>   print numeros                                          índice seleciona a coluna. Embora esta maneira de representar
[2,   3]                                                     matrizes seja comum, ela não é a única possibilidade. Uma
                                                             pequena variação é utilizar uma lista de colunas ao invés de
             Se uma função devolve uma lista, ela devolve uma lista de linhas.
uma referência à lista. Por exemplo, cauda devolve uma lista

                                                  Capítulo 8: Listas #49
Como pensar como um cientista da Computação usando Python

               Mais adiante veremos uma alternativa mais          mesmo para qualquer string? Quando eles seriam diferentes?
radical utilizando um dicionário.

                                                                 8.17 Glossário
8.16 Strings e listas
                                                                      lista (list) Uma coleção denominada de objetos, onde
Duas das mais úteis funções no módulo string envolvem listas                       cada objeto é identificado por um índice.
de strings. A função split (separar) quebra uma string em uma
lista de palavras. Por padrão, qualquer número de caracteres      índice (index) Uma variável inteira ou valor que indica um
espaço em branco é considerado um limite de uma palavra:                         elemento de uma lista.
>>> import string                                                      elemento Um dos valores em uma lista(ou outra
>>> poesia = "O orvalho no carvalho..."                               (element) seqüência). O operador colchete seleciona
>>> string.split(poesia)                                                        elementos de uma lista.
['O', 'orvalho', 'no', 'carvalho...']                                 seqüência Qualquer um dos tipos de dados que
              Um argumento opcional chamado um                       (sequence) consiste de um conjunto ordenado de
delimitador pode ser utilizado para especificar qual caracter                   elementos, com cada elemento identificado
utilizar como limites da palavra. O exemplo a seguir utiliza a                  por um índice.
string va:
                                                                 lista aninhada Uma lista que é um elemento de uma outra
>>> string.split(poesia, 'va')                                       (nested list) lista.
['O or', 'lho no car', 'lho...']                                   percurso na O acesso seqüencial de cada elemento em
             Perceba que o delimitador não aparece na lista.          lista (list uma lista.
                                                                     traversal)
              A função join (juntar) é o inverso de split. Ela
pega uma lista de strings e concatena os elementos com um                 objeto Um coisa a qual uma variável pode se
espaço entre cada par:                                                  (object) referir.
>>> lista = ['O', 'orvalho', 'no', 'carvalho...']                      apelidos Múltiplas variáveis que contém referências
>>> string.join(lista)                                                 (aliases) ao mesmo objeto.
'O orvalho no carvalho...'                                        clonar (clone) Criar um novo objeto que possui o mesmo
               Como split, join recebe um delimitador que é                      valor de um objeto existente. Copiar a
inserido entre os elementos:                                                     referência a um objeto cria um apelido
                                                                                 (alias) mas não clona o objeto.
>>> string.join(lista, '_')
'O_orvalho_no_carvalho...'                                         delimitador Um caracter uma string utilizados para
                                                                    (delimiter) indicar onde uma string deveria ser
             Como um execício, descreva o relacionamento                        dividida(split).
 entre string.join(string.split(poesia)) e poesia. Eles são o




                                                   Capítulo 8: Listas #50
Como pensar como um cientista da Computação usando Python


                                                  Capítulo 9: Tuplas




                                                                >>> temp = a
9.1 Mutabilidade e tuplas                                       >>> a = b
                                                                >>> b = temp
Até agora, você tem visto dois tipos compostos: strings, que
são compostos de caracteres; e listas, que são compostas de                  Se você tiver que fazer isso com frequência, esta
elementos de qualquer tipo. Uma das diferenças que notamos      abordagem se torna incômoda. Python fornece uma forma de
é que os elementos de uma lista podem ser modificados, mas      atribuição de tupla que resolve esse problema
os caracteres em uma string não. Em outras palavras, strings    elegantemente:
são imutáveis e listas são mutáveis.                            >>> a, b = b, a
              Há um outro tipo em Python chamado tupla                        O lado esquedo é uma tupla de variáveis; o lado
(tuple) que é similar a uma lista exceto por ele ser imutável. direito é uma tupla de valores. Cada valor é atribuído à sua
Sintaticamente, uma tupla é uma lista de valores separados por respectiva variável. Todas as expressões do lado direito são
vírgulas:                                                      avaliadas antes de qualquer das atribuições. Esta característica
                                                               torna as atribuições de tupla bastante versáteis.
>>> tupla = 'a', 'b', 'c', 'd', 'e'
              Embora não seja necessário, é convencional esquerda e o número de valores nanúmero deve ser igual: na
                                                                       Naturalmente, o             de variáveis
colocar tuplas entre parênteses:                                                           direita

>>> tupla = ('a', 'b', 'c', 'd', 'e')                           >>> a, b, c, d = 1, 2, 3
                                                                ValueError: unpack tuple of wrong size
             Para criar uma tupla com um único elemento,
temos que incluir uma vírgula final:
>>> t1 = ('a',)
>>> type(t1)
                                                                9.3 Tuplas como valores de retorno
<type 'tuple'>
                                                            Funções podem retornar tuplas como valor de retorno. Por
               Sem a vírgula, Python entende ('a') como uma Exemplo, nós poderíamos escrever uma função que troca dois
string entre parênteses:                                    parâmetros entre si:
>>> t2 = ('a')                                                  def troca(x, y):
>>> type(t2)                                                      return y, x
<type 'string'>                                                               Então nós poderíamos atribuir o valor de retorno
              Questões de sintaxe de lado, as operações em para uma tupla com duas variáveis:
tuplas são as mesmas operações das listas. O operador índice
seleciona um elemento da tupla.                                a, b = troca(a, b)
                                                                              Neste caso, não existe uma grande vantagem em
 >>> tupla = ('a', 'b', 'c', 'd', 'e')                         fazer de troca (swap) uma função. De fato, existe um perigo
 >>> tupla[0]                                                  em tentar encapsular troca, o qual é a tentação de cometer o
 'a'                                                           seguinte erro:
              E o operador slice (fatia) seleciona uma "faixa"
(range) de elementos.                                          def troca(x, y):           # versao incorreta
                                                                  x, y = y, x
>>> tupla[1:3]                                                                Se nós chamarmos esta função desta forma:
('b', 'c')
              Mas se tentarmos modificar um dos elementos troca(a, b)
de uma tupla, teremos um erro:                                             então a e x são apelidos para um mesmo valor.
                                                              Mudar x dentro da função troca, faz com que x se referencie a
>>> tupla[0] = 'A'                                            um valor diferente, mas sem efeito sobre a dentro de
TypeError: object doesn't support item assignment             __main__. Do mesmo modo, a mudança em y não tem efeito
              Naturalmente, mesmo que não possamos sobre b.
modificar os elementos de uma tupla, podemos substituí-la por              Esta função roda sem produzir uma mensagem
uma tupla diferente:                                          de erro, mas ela não faz o que pretendemos. Este é um
>>> tupla = ('A',) + tupla[1:]
                                                              exemplo de um erro semântico.
>>> tupla                                                                     Como exercício, desenhe um diagrama de
('A', 'b', 'c', 'd', 'e')                                         estado pra esta função de modo que você possa ver porque
                                                                  ela não funciona.

9.2 Atribuições de tupla
                                                                9.4 Números aleatórios
De vez em quando, é necessário trocar entre si os valores de
duas variáveis. Com operações de atribuição convencionais, A maioria dos programas de computador fazem a mesma coisa
temos que utilizar uma variável temporária. Por exemplo, para sempre que são executados, então, podemos dizer que eles são
fazer a troca entre a e b:                                    determinísticos. Determinismo em geral é uma coisa boa, se


                                                   Capítulo 9: Tuplas #51
Como pensar como um cientista da Computação usando Python

nós esperamos que um cálculo dê sempre o mesmo resultado. programa que divida a faixa de valores em intervalos e conte o
Entretanto, para algumas aplicações queremos que o número de valores de cada intervalo.
computador se torne imprevisível. Jogos são um exemplo
óbvio, mas existem outros.
              Fazer     um    programa    realmente    não-      9.6 Contando
determinístico se mostra não ser tão fácil, mas existem
maneiras de fazê-lo ao menos parecer não-determinístico.         Uma boa maneira de abordar problemas como esse é dividir o
Uma dessas maneiras é gerar números aleatórios e usá-los         problema em subproblemas, e encontrar um subproblema que
para determinar o resultado de um programa. Python tem uma       se enquadre em um padrão de solução computacional que
função nativa que gera números pseudo aleatórios, os quais       você já tenha visto antes.
não são verdadeiramente aleatórios no sentido matemático,
mas para os nossos propósitos eles são.                                      Neste caso, queremos       percorrer uma lista de
                                                                 números e contar o número de vezes    que valor se encaixa em
             O módulo random contém uma função chamada           um determinado intervalo. Isso soa    familiar. Na Seção 7.8,
random que retorna um número em ponto flutuante (floating-       nós escrevemos um programa que        percorria uma string e
point number) entre 0.0 e 1.0. Cada vez que você chama           contava o número de vezes que         uma determinada letra
random, você recebe o próximo número de uma longa série.         aparecia.
Para ver uma amostra, execute este loop:
                                                                              Assim, podemos prosseguir copiando o
import random                                                    programa original e adaptando-o para o problema atual. O
                                                                 programa original era:
for i in range(10):
  x = random.random()                                            contador = 0
  print x                                                        for letra in fruta:
                                                                   if letra == 'a':
              Para gerar um número aleatório ente 0.0 e um
limite superior, digamos superior, multiplique x por superior.       contador = contador + 1
                                                                 print contador
                Como exercício, gere um número aleatório entre                    O primeiro passo é substituir fruta por lista e
 'inferior' e 'superior'.                                           letra por numero. Isso não muda o programa, apenas o ajusta
                Como exercício adicional, gere um número para que ele se torne mais fácil de ler e entender.
 inteiro aleatório entre 'inferior' e 'superior', inclusive os dois               O segundo passo é mudar o teste. Nós não
 extremos.                                                          estamos interessados em procurar letras. Nós queremos ver se
                                                                    numero está entre inferior e superior.:
                                                                 contador = 0
9.5 Lista de números aleatórios
                                                                   for numero in lista
                                                                     if inferior < numero < superior:
O primeiro passo é gerar uma lista aleatória de valores.
listaAleatoria pega um parâmetro inteiro e retorna uma lista de        contador = contador + 1
números aleatórios com o comprimento dado. Inicia-se com           print contador
uma lista de n zeros. A cada iteração do loop, ele substitui um                O último passo é encapsular este código em
dos elementos por um número aleatório. O valor retornado é uma função chamada noIntervalo. Os parâmetros são a lista e
uma referência para a lista completa:                           os valores inferior e superior:
def listaAleatoria(n):                                           def noIntervalo(lista, inferior, superior):
  s = [0] * n                                                        contador = 0
  for i in range(n):                                                 for numero in lista:
    s[i] = random.random()                                             if inferior < numero < superior:
  return s                                                               contador = contador + 1
               Vamos realizar um teste desta função com uma          return contador
lista de oito elementos. Para efeitos de depuração, é uma boa               Através da cópia e da modificação de um
idéia começar com uma lista pequena.                          programa existente, estamos aptos a escrever esta função
 >>> listaAleatoria(8)
                                                              rapidamente e economizar um bocado de tempo de depuração.
                                                              Este plano de desenvolvimento é chamado de casamento de
 0.15156642489                                                padrões. Se você se encontrar trabalhando em um problema
 0.498048560109                                               que você já solucionou antes, reuse a solução.
0.810894847068
0.360371157682
0.275119183077                                                   9.7 Vários intervalos
0.328578797631
0.759199803101                                                   Conforme o número de intervalos aumenta, noIntervalo torna-
0.800367163582                                                   se intragável. Com dois intervalos, não é tão ruim:
              Os números gerados por random são
supostamente uniformemente distribuídos, o que significa que inferior = noIntervalo(a, 0.0, 0.5)
cada valor tem uma probabilidade igual de acontecer.         superior = noIntervalo(a, 0.5, 1)
                                                                          Mas com quatro intervalos, começa a ficar
              Se nós dividirmos a faixa de valores possíveis desconfortável.:
em intervalos do mesmo tamanho, e contarmos o número de
vezes que um determinado valor aleatório caiu em seu intervalo1 = noIntervalo(a, 0.0, 0.25)
respectivo intervalo, nós devemos obter o mesmo número intervalo2 = noIntervalo(a, 0.25, 0.5)
aproximado de valores em cada um dos intervalos.
                                                                 intervalo3 = noIntervalo(a, 0.5, 0.75)
             Nós podemos testar esta teoria escrevendo um

                                                   Capítulo 9: Tuplas #52
Como pensar como um cientista da Computação usando Python

intervalo4 = noIntervalo(a, 0.75, 1.0)
              Existem aqui dois problemas. Um é que temos
                                                                9.8 Uma solução em um só passo
que criar novos nomes de variável para cada resultado. O
outro é que temos que calcular os limites de cada intervalo.     Embora este programa funcione, ele não é tão eficiente quanto
                                                                 poderia ser. Toda vez que ele chama noIntervalo, ele percorre
              Vamos resolver o segundo problema primeiro. a lista inteira. Conforme o número de intervalos aumenta, a
Se o número de intervalos é numeroDeIntervalos, então a lista será percorrida um bocado de vezes.
largura de cada intervalo é 1.0 / numeroDeIntervalos.
                                                                               Seria melhor fazer uma única passagem pela
              Vamos usar um laço (loop) para calcular a faixa, lista e calcular para cada valor o índice do intervalo ao qual o
ou largura, de cada intervalo. A variável do loop, i, conta de 0 valor pertença. Então podemos incrementar o contador
até numeroDeIntervalos-1:                                        apropriado.
larguraDoIntervalo = 1.0 / numeroDeIntervalos                                  Na seção anterior, pegamos um índice, i, e o
for i in range(numeroDeIntervalos):                             multiplicamos pela larguraDoIntervalo para encontrar o limite
  inferior = i * larguraDoIntervalo                             inferior daquele intervalo. Agora queremos pegar um valor
                                                                entre 0.0 e 1.0 e encontrar o índice do intervalo ao qual ele se
  superior = inferior + larguraDoIntervalo                      encaixa.
  print "do" inferior, "ao", superior
               Para calcular o limite inferior (inferior) de cada               Já que este problema é o inverso do problema
intervalo, nós multiplicamos a variável do loop (i) pela largura anterior, podemos imaginar que deveríamos dividir por
do intervalo (larguraDoIntervalo). O limite superior (superior) larguraDoIntervalo em vez de multiplicar. Esta suposição está
está exatamente uma "largura de intervalo" acima.                 correta.
               Com numeroDeIntervalos = 8, o resultado é:                       Já que larguraDoIntervalo = 1.0                /
                                                                  numeroDeIntervalos, dividir por larguraDoIntervalo é o
 0.0 to 0.125                                                     mesmo que multiplicar por numeroDeIntervalos. Se
 0.125 to 0.25                                                    multiplicarmos um número na faixa entre 0.0 e 1.0 por
 0.25 to 0.375
                                                                  numeroDeIntervalos, obtemos um número na faixa entre 0.0 e
                                                                  numeroDeIntervalos. Se arredondarmos este número para
 0.375 to 0.5                                                     baixo, ou seja, para o menor inteiro mais próximo, obtemos
 0.5 to 0.625                                                     exatamente o que estamos procurando - o índice do intervalo:
0.625 to 0.75
0.75 to 0.875                                                    numeroDeIntervalos = 8
0.875 to 1.0                                                     intervalos = [0] * numeroDeIntervalos
                                                                 for i in lista:
               Você pode confirmar que cada intervalo tem a
mesma largura, que eles não se sobrepõe, e que eles cobrem         indice = int(i * numeroDeIntervalos)
toda a faixa de valores de 0.0 a 1.0.                              intervalos[indice] = intervalos[indice] + 1
                                                                              Usamos a função int para converter um número
              Agora, de volta ao primeiro problema. Nós em ponto flutuante (float) para um inteiro.
precisamos de uma maneira de guardar oito inteiros, usando a
váriavel do loop para indicar cada um destes inteiros. Você                   Existe a possibilidade deste cálculo produzir um
deve estar pensando, "Lista!"                                   índice que esteja fora dos limites (seja negativo ou maior que
                                                                len(intervalos)-1)?
              Nós temos que criar a lista de intervalos fora do
loop, porque queremos fazer isto apenas uma vez. Dentro do                    Uma lista como intervalos que contém uma
loop, nós vamos chamar noIntervalo repetidamente e atualizar contagem do número de valores em cada intervalo é chamada
o i-ésimo elemento da lista:                                    de histograma.
numeroDeIntervalos = 8                                                        Como exercício, escreva uma função chamada
intervalos = [0] * numeroDeIntervalos                             ``histograma`` que receba uma lista e um número de
larguraDoIntervalo = 1.0 / numeroDeIntervalos                     intervalos como argumentos e retorne um histograma com o
for i in range(numeroDeIntervalos):
                                                                  número de intervalos solicitado.
  inferior = i * larguraDoIntervalo
  superior = inferior + larguraDoIntervalo
  intervalos[i] = noIntervalo(lista, inferior,                  9.9 Glossário
superior)
print intervalos                                                   tipo imutável Um tipo de elemento que não pode ser
             Com uma lista de 1000 valores, este código vai          (immutable modificado. Atribuições a um elemento ou
produzir esta lista de quantidades de valores em cada                      type) "fatiamento (slices)" XXX aos tipos
intervalo:                                                                       imutáveis causarão erro.
[138, 124, 128, 118, 130, 117, 114, 131]                           tipo mutável Tipo de dados onde os elementos podem
             Esses números estão razoavelmente póximos de        (mutable type) ser modificados. Todos os tipos mutáveis,
125, o que era o que esperávamos. Pelo menos eles estão                         são tipos compostos. Listas e dicionários
próximos o bastante para nos fazer acreditar que o gerador de                   são exemplos de tipos de dados mutáveis.
número aleatórios está funcionando.                                             String e tuplas não são.
              Como exercício, teste esta função com algumas         tupla (tuple) Tipo sequencial similar as listas com
 listas longas, e veja se o número de valores em cada um dos                      exceção de que ele é imutável. Podem ser
 intervalos tendem a uma distribuição nivelada.                                   usadas Tuplas sempre que um tipo imutável
                                                                                  for necessário, por exemplo uma "chave
                                                                                  (key)" em um dicionário
                                                                    Atribuição a Atribuição a todos os elementos de uma
                                                                     tupla (tuple tupla feita num único comando de


                                                   Capítulo 9: Tuplas #53
Como pensar como um cientista da Computação usando Python

  assignment) atribução. A atribuição aos elementos
              ocorre em paralelo, e não em sequência,
              tornando esta operação útil para swap, ou
              troca recíproca de valores entre variáveis
              (ex: a,b=b,a).
 determinístico Um programa que realiza a mesma coisa
(deterministic) sempre que é executado.
       pseudo    Uma sequência de números que parecem
     aleatório   ser aleatórios mas são na verdade o
(pseudorando     resultado     de   uma    computação
           m)    "determinística"
  histograma Uma lista de inteiros na qual cada elemento
  (histogram) conta o número de vezes que algo acontece.




                                             Capítulo 9: Tuplas #54
Como pensar como um cientista da Computação usando Python


                                           Capítulo 10: Dicionários


Os tipos compostos que voce aprendeu - strings, listas e tuplas >>> del inventario['peras']
- utilizam inteiros como indices. Se voce tentar utilizar >>> print inventario
qualquer outro tipo como indice, voce receberá um erro.         {'laranjas': 525, 'abacaxis': 430, 'bananas': 312}
              Dicionários sao similiares a outros tipos                     Ou se nós esperamos por mais peras em breve,
compostos exceto por eles poderem user qualquer tipo nos podemos simplesmente trocar o valor associoado as peras:
imutavel de dados como indice. Como exemplo, nos
criaremos um dicionário para traduzir palavras em Inglês para >>> inventario['peras'] = 0
Espanhol. Para esse dicionário, os indices serão strings.       >>> print inventario
            Uma maneira de criar um dicionario é {'laranjas': 525, 'abacaxis': 430, 'peras': 0, 
comecando com um dicionário vazio e depois adiconando 'bananas': 312}
elementos. Um dicionário vazio é denotado assim {}:                 A função len também funciona com dicionários;
                                                      retornando o número de pares chave-valor:
>>> ing2esp = {}
>>> ing2esp['one'] = 'uno'                                   >>> len(inventario)
>>> ing2esp['two'] = 'dos'                                   4
              A primeira atribuição cria um dicionario
chamado ing2esp; as outras atribuições adicionam novos
elementos para o dicionário. Nos podemos imprimir o valor 10.2 Métodos dos Dicionários
corrente de um dicionario da maneira usual:
>>> print ing2esp                                            Um método é parecido com uma função - possui parametros e
{'one': 'uno', 'two': 'dos'}                                 retorna valores - mas a sintaxe é diferente. Por exemplo, o
              Os elementos de um dicionário aparecem em      metodo keys recebe um dicionário e retorna uma lista com as
uma lista separada por vírgulas. Cada entrada contêm um      chaves, mas em vez de usarmos a sintaxe de função
indice e um valor separado por dois-pontos. Em um            keys(ing2esp), nos usamos a sintaxe de método
dicionário, os índices sao chamados de chaves, entao os      ing2esp.keys():
elementos são chamados de pares chave-valor.                  >>> ing2esp.keys()
              Outra maneira de criar dicionários é fornecendo ['one', 'three', 'two']
uma lista de pares chaves-valor utilizando a mesma sintaxe da               Dessa forma o ponto especifica o nome da
última saída.                                                 função, keys, e o nome do objeto em que deve ser aplicada a
                                                              função, ing2esp. Os parenteses indicam que esse método não
 >>> ing2esp = {'one': 'uno', 'two': 'dos',                  possui parameteros.
'three': 'tres'}
             Se nos imprimirmos o valor de ing2esp que ele é invocado, nesse caso, nós podemos dizer dizemos
                                                                Ao invés de chamarmos um método,
                                                                                                     que nós
novamente, nos teremos uma surpresa:               estamos invocando keys do objeto ing2esp.
>>> print ing2esp                                                         O método values é parecido; retorna a lista de
{'one': 'uno', 'three': 'tres', 'two': 'dos'}                valores de um dicionário:
              Os pares chave-valor não estão em ordem!
Felizmente, não a motivos para se preocupar com a ordem, >>> ing2esp.values()
desde que os elementos do dicionário nunca sejam indexados ['uno', 'tres', 'dos']
com indices inteiros. Podemos usar as chaves para buscar os                O método items retorna os dois, na forma de
valores correspondentes:                                    uma lista de tuplas - cada tupla com um par chave-valor:
>>> print ing2esp['two']                                     >>> ing2esp.items()
'dos'                                                        [('one','uno'), ('three','tres'), ('two','dos')]
             A chave 'two' retornou o valor 'dos' mesmo               A sintaxe fornece uma informação util. Os
pensando que retornaria o terceiro par chave-valor.     colchetes indicam que isso é uma lista. Os parentêses indicam
                                                        que os elementos da lista são tuplas.
                                                                         Se o método recebe de algum parâmetro, se
10.1 Operações dos Dicionários                             utiliza a mesma sintaxe das funções. Por exemplo, o método
                                                           has_key recebe uma chave e retorna verdadeiro (1) se a chave
O comando del remove um par chave-valor de um dicionário. existe no dicionário:
Por exemplo, o dicionário abaixo contem os nomes de varias
frutas e o numero de cada fruta em no estoque:             >>> ing2esp.has_key('one')
                                                             True
>>> inventario = {'abacaxis': 430, 'bananas': 312,          >>> ing2esp.has_key('deux')
'laranjas': 525, 'peras': 217}                               False
>>> print inventario                                                       Se voce tentar chamar um método sem
{'laranjas': 525, 'abacaxis': 430, 'peras': 217,            especificar em qual objeto, voce obterá um erro. Nesse caso, a
'bananas': 312}                                              mensagem de erro não é muito útil:
             Se alguem comprar todas as peras, podemos >>> has_key('one')
remover a entrada do dicionário:


                                             Capítulo 10: Dicionários #55
Como pensar como um cientista da Computação usando Python

NameError: has_key                                             utilizamos o operador []:
                                                               >>> matriz[0,3]
                                                               1
10.3 Aliasing (XXX) e Copiar                                             Note que a sintaxe da representação de um
                                                            dicionário não é a mesma que a sintaxe usada pela
Uma vez que os dicionários são mutáveis, voce precisa saber representação pelas listas. Em vez de usarmos dois índices
sobre Aliasing. Sempre que duas variáveis referenciarem o inteiros, nós usamos apenas um índice, que é uma tupla de
mesmo objeto, quando uma é alterada, afeta a outra.         inteiros.
             Se você quer modificar um dicionário e                      Mas existe um problema. Se tentarmos buscar
continuar com uma copia original, utilize o método copy. Por um elemento zero, obteremos um erro, pois não existe uma
exemplo, opposites é um dicionário que contêm pares de entrada no dicionário para a chave especificada:
antônimos:
                                                               >>> matriz[1,3]
>>> opposites = {'up': 'down', 'right': 'wrong',              KeyError: (1,3)
'true': 'false'}                                                             O método get resolve esse problema:
>>> alias = opposities
>>> copy = opposities.copy()                                   >>> matriz.get((0,3), 0)
                                                               1
              alias e opposites se referem ao mesmo objeto;
copy se refere a um novo objeto igual ao dicionário opposites.               O primeiro parâmetro é a chave; o segundo é o
Se você modificar o alias, opposites também será alterado.     valor que get retornará caso não existe a chave no dicionário:
>>> alias['right'] = 'left'                                    >>> matriz.get((1,3), 0)
>>> opossites['right']                                         0
'left'                                                                 get definitivamente melhora a semântica e a
                Se modificarmos copy, opposites não será sintaxe do acesso a matrizes esparsas.
modificado:
>>> copy['right'] = 'privilege'
                                                               10.5 Hint
>>> opposites['right']
'left'
                                                               Se você brincou com a função fibonacci da seção 5.7, é
                                                               provável que você notou que quanto maior o número passado
                                                               para a função, mais tempo a função demora para executar.
10.4 Matrizes Esparsas                                         Além disso, o tempo da execução aumenta rapidamente. Em
                                                               uma das nossas máquinas, fibonacci(20) executa
                                                               instantaneamente, fibonacci(30) demora cerca de um
Na seção 8.14, nós usamos uma lista de listas para representar segundo, e fibonacci(40) demora uma eternidade.
uma matriz. Essa é uma boa escolha se a matriz for
principalmente de valores diferentes de zero, mas                            Para entender o porque, considere o gráfico de
considerando uma matriz esparsa como essa:                     chamadas para fibonacci com n=4:




                Uma representação 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 dicionário. Para
as chaves, nós podemos usar tuplas que contêm os números da              O gráfico mostra a estrutura da função, com
linha e a coluna. Abaixo uma representação em um diciónario linhas conectando cada execução com a execução que a
da mesma matriz:                                            chamou. No topo do gráfico, fibonacci tem n=4, que chama
 >>> matriz = {(0,3): 1, (2, 1): 2, (4, 3): 3}
                                                            fibonacci com n=3 e n=2. Em seguida, fibonacci com n=3
                                                            chama fibonacci com n=2 e n=1. E assim por diante.
              Nós precisamos apenas de três pares chave-
valor, cada um sendo um elemento diferente de zero da                    Conte quantas vezes fibonacci(0) e fibonacci(1)
matriz. Cada chave é uma tupla, e cada valor é um número são chamadas. Essa é uma solução ineficiente para o
inteiro.                                                    problema, e torna-se pior quando o parâmetro recebido é um
                                                            número maior.
              Para acessarmos um elemento da matriz, nos

                                               Capítulo 10: Dicionários #56
Como pensar como um cientista da Computação usando Python

              Uma boa solução é guardar os valores que já Os dois primeiros numeros da sequência são long ints, então
foram calculados armazenando-os em um dicionário. Um todos os números subsequentes da sequência também serão.
valor previamente calculado que é guardado para ser utilizado
mais tarde é chamado de hint. Abaixo uma implementação de                 Como exercício, converta fatorial para produzir
fibonacci usando hints:                                       um inteiro longo como resultado.
>>> previous = {0:1, 1:1}
>>> def fibonacci(n):
                                                                10.7 Contando Letras
        if previous.has_key(n):
                return previous[n]
                                                                No capítulo 7, escrevemos uma função que contava o número
        else:                                                   de ocorrências de uma letra em uma string. A versão mais
                newValue = fibonacci(n-1) +                    comum desse problema é fazer um histograma das letras da
fibonacci(n-2)                                                  string, ou seja, quantas vezes cada letra aparece na string.
                previous[n] = newValue
                                                                      Um histograma pode ser util para comprimir um
                return newValue                        arquivo de texto. Pois diferentes letras aparecem com
            O dicionário chamado previous guarda os diferentes frequências, podemos comprimir um arquivo
números de Fibonacci que nós ja conhecemos. Ele começa usando pequenos códigos para letras comuns e longos códigos
com apenas dois pares: 0 possui 1; e 1 possui 1.       para letras que aparecem em menor frequência.
              Sempre que fibonacci é chamada, ela verifica o                  Dicionários fornecem uma maneira elegante de
dicionário para determinar se ele já possui o resultado. Se o   gerar um histograma:
resultado estiver ali, a função pode retornar imediatamente
sempre precisar fazer mais chamadas recursivas. Se o            >>> letterCounts = {}
resultado não estiver ali, ele é calculado no newValue. O       >>> for letter in "Mississippi":
valor de newValue é adicionado no dicionário antes da função    ...     letterCounts[letter] =
retornar.                                                       letterCounts.get(letter,0) + 1
            Usando essa versão de fibonacci, nossa              ...
máquina consegue calcular fibonacci(40) em um piscar de         >>> letterCounts
olhos. Mas quando tentamos calcular fibonacci(50), nós          {'M': 1, 's': 4, 'p': 2, 'i': 4}
veremos um problema diferente:                                                 Começamos com um dicionário vazio. Para
>>> fibonacci(50)
                                                                cada letra da string, achamos o contador (possivelmente zero)
                                                                e o incrementamos. No final, o dicionário contem pares de
OverflowError: integer addition                                 letras e as suas frequências.
             A resposta, que você verá em um minuto, é
20.365.011.074. O problema é que esse número é muito                    É mais atraente mostrarmos o histograma na
grande para guardarmos como um inteiro do Python 1. Isso é ordem alfabética. Podemos fazer isso com os métodos items e
overflow. Felizmente, esse problema tem uma solução sort:
simples.
                                                                >>> letterItems = letterCounts.items()
                                                                >>> letterItems.sort()
                                                                >>> print letterItems
10.6 Inteiros Longos                                            [('M', 1), ('i', 4), ('p', 2), ('s', 4)]
                                                                              Você ja tinha visto o método items antes, mas
Python possui um tipo chamado long int que permite              sort é o primeiro método que você se depara para aplicar em
trabalharmos com qualquer tamanho de inteiros. Existem duas     listas. Existem muitos outros métodos de listas, incluindo
maneiras de criarmos um valor long int. A primeira é escrever   append, extend, e reverse. Consulte a documentação do
um inteiro seguido de um L no final:                            Python para maiores detalhes.
>>> type(1L)
<type 'long int'>
              A outra maneira é usarmos a função long que 10.8 Glossário
converte um valor para um long int. long pode receber
qualquer valor númerico e até mesmo uma string de digitos:      dicionário Uma coleção de pares de chaves-valores
>>> long(1)                                                   (dictionary) que são mapeados pelas chaves, para se
1L
                                                                           obter os valores. As chaves podem ser
                                                                           qualquer tipo de dados imutavel, e os
>>> long(3.9)                                                              valores podem ser de qualquer tipo.
3L
>>> long('57')                                                       chave (key) Um valor que é usado para buscar uma
57L
                                                                                 entrada em um dicionário.
             Todas as operações matemáticas funcionam            par chave-valor Um dos itens de um dicionário.
com long int s, então não precisamos modificar muito para        (key-value pair)
adaptar fibonacci:
                                                                         método Um tipo de função que é chamada com
>>> previous = {0: 1L, 1:1L}                                            (method) uma sintaxe diferente e invocada no
>>> fibonacci(50)                                                                contexto de um objeto.
20365011074L                                                    invocar (invoke) Chamar um método.
             Somente trocando os valores iniciais de
previous, conseguimos mudar o comportamento da fibonacci.                    hint O armazenamento temporário de um valor
                                                                                  pré-computado para evitar a computação
1   N.T. A partir do Python 2. XXX este erro não ocorre mais,                     redundante.
    pois em caso de sobrecarga o valor inteiro é
    automaticamente promovido para o tipo long.                         overflow Um resultado numérico que é muito

                                                Capítulo 10: Dicionários #57
Como pensar como um cientista da Computação usando Python

grande para ser representado no formato
numérico.




                          Capítulo 10: Dicionários #58
Como pensar como um cientista da Computação usando Python


                                         Capítulo 11: Arquivos e exceções



                                                                     temos um erro:
Arquivos e exceções
                                                                     >>> f = open("teste.cat", "r")
Durante a execução de um programa, seus dados ficam na               IOError: [Errno 2] No such file or directory: 
memória. Quando o programa termina, ou o computador é                'teste.cat'
desligado, os dados na memória desaparecem. Para armazenar                       Sem nenhuma surpresa, o método read lê dados
os dados permanentemente, você tem que colocá-los em um              do arquivo. Sem argumentos, ele lê todo o conteúdo do
arquivo. Arquivos usualmente são guardados em um disco               arquivo:
rígido (HD), num disquete ou em um CD-ROM.
                                                                     >>> texto = f.read()
              Quando existe um número muito grande de
arquivos, eles muitas vezes são organizados dentro de >>> print texto
diretórios (também chamados de ?pastas? ou ainda ? Agora é horade fechar o arquivo
*folders*?). Cada arquivo é identificado por um nome único,              Não existe espaço entre ?hora? e ?de? porque
ou uma combinação de um nome de arquivo com um nome de nós não gravamos um espaço entre as strings.
diretório.
                                                                         read também pode receber um argumento que
              Lendo e escrevendo em arquivos, os programas indica quantos caracteres ler:
podem trocar informações uns com os outros e gerar formatos
imprimíveis como PDF.                                       >>> f = open("teste.dat", "r")
                                                                     >>> print f.read(9)
               Trabalhar com arquivos é muito parecido com           Agora é h
trabalhar com livros. Para utilizar um livro, você tem que abrí-
lo. Quando você termina, você tem que fechá-lo. Enquanto o                         Se não houver caracteres suficientes no arquivo,
livro estiver aberto, você pode tanto lê-lo quanto escrever          read retorna os caracteres restantes. Quando chegamos ao final
nele. Em qualquer caso, você sabe onde você está situado no          do arquivo, read retorna a string vazia:
livro. Na maioria das vezes, você lê o livro inteiro em sua
ordem natural, mas você também pode saltar através de alguns         >>> print f.read(1000006)
trechos (skip around).                                               orade fechar o arquivo
                                                                     >>> print f.read()
             Tudo isso se aplica do mesmo modo a arquivos.
Para abrir um arquivo, você especifica o nome dele e indica o
que você quer, seja ler ou escrever (gravar).                 >>>
                                                                         A função seguinte, copia um arquivo, lendo e
             Abrir um arquivo cria um objeto arquivo. Neste gravando até cinqüenta caracteres de uma vez. O primeiro
exemplo, a variável f se referencia ao novo objeto arquivo. argumento é o nome do arquivo original; o segundo é o nome
                                                            do novo arquivo:
>>> f = open("teste.dat", "w")
>>> print f                                                          def copiaArquivo(velhoArquivo, novoArquivo):
<open file "teste.dat", mode "w" at fe820>                             f1 = open(velhoArquivo, "r")
              A função open recebe dois argumentos. O                  f2 = open(novoArquivo, "w")
primeiro é o nome do arquivo, e o segundo é o modo. Modo ?             while 1:
w? significa que estamos abrindo o arquivo para gravação                   texto = f1.read(50)
(?*write*?, escrever).                                                     if texto == "":
                Se não existir nenhum arquivo de nome                          break
teste.dat, ele será criado. Se já existir um, ele será substituído         f2.write(texto)
pelo arquivo que estamos gravando (ou escrevendo).                     f1.close()
              Quando executamos um comando print sobre o               f2.close()
objeto arquivo, visualizamos o nome do arquivo, o modo e a             return
localização do objeto na memória.                                              A comando break é novo. O que ele faz é saltar
                                                                  a execução para fora do loop; o fluxo de execução passa para
            Para colocar dados dentro              do    arquivo, o primeiro comando depois do loop.
invocamos o método write do objeto arquivo:
                                                                            Neste exemplo, o loop while é infinito porque o
>>> f.write("Agora é hora")                                    valor 1 é sempre verdadeiro. O único modo de sair do loop é
>>> f.write("de fechar o arquivo")                             executando o break, o que ocorre quando texto é a string
              Fechar o arquivo diz ao sistema que terminamos vazia, o que ocorre quando alcançamos o fim do arquivo.
de escrever (gravar) e que o arquivo está livre para ser lido:
>>> f.close()
              Agora podemos abrir o arquivo de novo, desta 11.1 Arquivos texto
vez para leitura, e ler o seu conteúdo para uma string. Desta
vez, o argumento modo é ?r? para leitura (?reading?):         Um arquivo texto é um arquivo que contém caracteres
                                                              imprimíveis e espaços, organizados dentro de linhas separadas
>>> f = open("teste.dat", "r")                                por caracteres de nova linha. Já que Pyhton é especialmente
              Se tentarmos abrir um arquivo que não existe, projetado para processar arquivos texto, ele oferece métodos
                                                              que tornam esta tarefa mais fácil.

                                             Capítulo 11: Arquivos e exceções #59
Como pensar como um cientista da Computação usando Python

             Para demonstrar, vamos criar um arquivo texto módulo. Mas quando o primeiro operador é uma string, % é o
com três linhas de texto separadas por caracteres de nova operador de formatação.
linha:
                                                                        O primeiro operando é a string de formatação,
 >>> f = open("teste.dat", "w")                            e o segundo operando é uma tupla de expressões. O resultado
 >>> f.write("linha umnlinha doisnlinha trêsn")         é uma string que contém os valores das expressões,
 >>> f.close()
                                                           formatadas de acordo com a string de formatação.
              O método readline lê todos os caracteres até, e               Num exemplo simples, a seqüência de
incluindo, o próximo caractere de nova linha:                 formatação "??%d??" significa que a primeira expressão na
                                                              tupla deve ser formatada como um inteiro. Aqui a letra d
 >>> f = open("teste.dat", "r")                               representa ?decimal?.
>>> print f.readline()
linha um                                                           >>> carros = 52
                                                                   >>> "%d" % carros
>>>                                                                '52'
               readlines retorna todas as linhas restantes como              O resultado é a string ?52?, que não deve ser
uma lista de strings:                                           confundida com o valor inteiro 52.
>>> print f.readlines()
                                                                             Uma seqüência de formatação pode aparecer em
                                                                qualquer lugar na string de formatação, assim, podemos
['linha dois012', 'linha três012']                            embutir um valor em uma seqüência:
               Neste caso, a saída está em formado de lista, o
que significa que as strings aparecem entre aspas e o caractere >>> carros = 52
de nova linha aparece como a seqüência de escape 012.           >>> "Em julho vendemos %d carros." % carros
               No fim do arquivo, readline retorna a string 'Em julho vendemos 52 carros.'
vazia e readlines retorna a lista vazia:                                 A seqüência de formatação "%f" formata o
                                                            próximo item da tupla como um número em ponto flutuante, e
>>> print f.readline()                                      "%s" formata o próximo como uma string:
                                                                   >>> "Em %d dias fizemos %f milhões %s." % 
>>> print f.readlines()
                                                                   (34,6.1,'reais')
[]
                                                                   'Em 34 dias fizemos 6.100000 milhões de reais.'
            A seguir temos um exemplo de um programa de
processamento de linhas. filtraArquivo faz uma cópia de                   Por padrão, o formato de ponto flutuante exibe
velhoArquivo, omitindo quaisquer linhas que comecem por #: seis casas decimais.
def filtraArquivo(velhoArquivo, novoArquivo):
                                                                          O número de expressões na tupla tem que ser
                                                           igual ao número de seqüências de formatação na string. Além
  f1 = open(velhoArquivo, "r")                             disso, os tipos das expressões têm que iguais aos da seqüência
  f2 = open(novoArquivo, "w")                              de formatação:
  while 1:
      texto = f1.readline()                                        >>> "%d %d   %d" % (1,2)
      if texto == "":                                              TypeError:   not enough arguments for format string
          break                                                    >>> "%d" %   'reais'
      if texto[0] == '#':                                          TypeError:   illegal argument type for built-in 
          continue                                                 operation
      f2.write(texto)                                                             No primeiro exemplo, não existem expressões
  f1.close()
                                                                   suficientes; no segundo, a expressão é do tipo errado.
  f2.close()                                                              Para um controle maior na formatação de
  return                                                    números, podemos especificar o número de dígitos como parte
             O comando continue termina a iteração corrente da seqüência de formatação:
do loop, mas continua iterando o loop. O fluxo de execução >>> "%6d" % 62
passa para o topo do loop, checa a condição e prossegue
conforme o caso.                                            '     62'
                                                                   >>> "%12f" % 6.1
               Assim, se texto for a string vazia, o loop          '    6,100000'
termina. Se o primeiro caractere de texto for o jogo da velha (?                 O número depois do sinal de porcentagem é o
# ?), o fluxo de execução passa para o topo do loop. Somente       número mínimo de espaços que o valor ocupará. Se o valor
se ambas as condições falharem é que texto será copiado para       fornecido tiver um número menor de dígitos, espaços em
dentro do novo arquivo.                                            branco serão adicionados antes para preencher o restante. Se o
                                                                   número de espaços for negativo, os espaços serão adicionados
                                                                   depois:
11.2 Gravando variáveis                                            >>> "%-6d" % 62
                                                                   '62    '
O argumento de write tem que ser uma string, assim se
quisermos colocar outros valores em um arquivo, temos de                Para números em ponto-flutuante, também
convertê-los para strings primeiro. A maneira mais fácil de podemos especificar o número de dígitos depois da vírgula:
fazer isso é com a função str:                              >>> "%12.2f" % 6.1
>>> x = 52                                                         '         6.10'
>>> f.write(str(x))                                                     Neste exemplo, o resultado reserva 12 espaços e
           Uma alternativa é usar o operador de inclui dois dígitos depois da vírgula. Esta formatação é útil
formatação %. Quando aplicado a inteiros, % é o operador para exibir valores monetários com os centavos alinhados.

                                           Capítulo 11: Arquivos e exceções #60
Como pensar como um cientista da Computação usando Python

              Por exemplo, imagine um dicionário que comandos necessários. Para usá-lo, importe pickle e então
contém nomes de estudantes como chaves e salários-hora abra o arquivo da maneira usual:
como valores. Aqui está uma função que imprime o conteúdo
do dicionário como um relatório formatado:                >>> import pickle
                                                                >>> f = open(?test.pck?, ?w?)
def relatorio(salarios):                                                   Para armazenar uma estrutura de dados, use o
  estudantes = salarios.keys()                                  método dump e então feche o arquivo do modo usual:
  estudantes.sort()
  for estudante in estudantes:                                  >>> pickle.dump(12.3, f)
      print "%-20s %12.02f" % (estudante,                      >>> pickle.dump([1,2,3], f)
salarios[estudante])                                            >>> f.close()
              Para testar esta função, criaremos um pequeno              Então, podemos abrir o arquivo para leitura e
dicionário e imprimiremos o conteúdo:                       carregar as estruturas de dados que foram descarregadas
                                                            (dumped):
>>> salarios = {'maria': 6.23, 'joão': 5.45, 
                                                                >>> f = open(?test.pck?, ?r?)
'josué': 4.25}
                                                                >>> x = pickle.load(f)
>>> relatorio(salarios)
                                                                >>> x
joão                          5.45
                                                                12,3
josué                         4.25
                                                                >>> type(x)
maria                         6.23
                                                                <type ?float?>
              Controlando a largura de cada valor, podemos
garantir que as colunas ficarão alinhadas, desde que os nomes   >>> y = pickle.load(f)
contenham menos que vinte e um caracteres e os salários         >>> y
sejam menores do que um bilhão de reais por hora.               [1, 2, 3]
                                                                >>> type(y)
                                                                <type ?list?>
11.3 Diretórios                                                               Cada vez que invocamos load, obtemos um
                                                                único valor do arquivo, completo com seu tipo original.
Quando você cria um novo arquivo abrindo-o e escrevendo
nele, o novo arquivo fica no diretório corrente (seja lá onde
for que você esteja quando rodar o programa). Do mesmo 11.5 Exceções
modo, quando você abre um arquivo para leitura, Python
procura por ele no diretório corrente.
                                                              Whenever que um erro em tempo de execução acontece, ele
              Se você quiser abrir um arquivo que esteja em gera uma exceção. Usualmente, o programa pára e Python
algum outro lugar, você tem que especificar o caminho (path) exibe uma mensagem de erro.
para o arquivo, o qual é o nome do diretório (ou folder) onde
o arquivo está localizado:                                               Por exemplo, dividir por zero gera uma
                                                              exceção:
>>> f = open("/usr/share/dict/words", "r")
                                                                >>> print 55/0
>>> print f.readline()
                                                                ZeroDivisionError: integer division or modulo
Aarhus
               Este exemplo abre um arquivo chamado words                   Do mesmo modo, acessar um item de lista
que reside em um diretório de nome dict, o qual reside em inexistente:
share, o qual reside em usr, o qual reside no diretório de mais >>> a = []
alto nível do sistema, chamado /.                               >>> print a[5]
               Você não pode usar / como parte do nome de IndexError: list index out of range
um arquivo; ela é um caractere reservado como um                            Ou acessar uma chave que não está em um
delimitador entre nomes de diretórios e nomes de arquivos.      dicionário:
               O arquivo /usr/share/dict/words contém uma >>> b = {}
lista de palavras em ordem alfabética, na qual a primeira >>> print b[?what?]
palavra é o nome de uma universidade Dinamarquesa.
                                                                KeyError: what
                                                                             Em cada caso, a mensagem de erro tem duas
                                                              partes: o tipo do erro antes dos dois pontos, e especificidades
11.4 Pickling                                                 do erro depois dos dois pontos. Normalmente Python também
                                                              exibe um ?*traceback*? de onde estava a execução do
Para colocar valores em um arquivo, você tem que convertê- programa, mas nós temos omitido esta parte nos exemplos.
los para strings. Você já viu como fazer isto com str:                       Às vezes queremos executar uma operação que
 >>> f.write (str(12.3))                                      pode causar uma exceção, mas não queremos que o programa
 >>> f.write (str([1,2,3]))
                                                              pare. Nós podemos tratar a exceção usando as instruções try e
                                                              except.
               O problema é que quando você lê de volta o
valor, você tem uma string. O Tipo original da informação foi                Por exemplo, podemos pedir ao usuário um
perdido. De fato, você não pode sequer dizer onde começa um nome de arquivo e então tentar abrí-lo. Se o arquivo não
valor e termina outro:                                        existe, não queremos que o programa trave; queremos tratar a
                                                              exceção:
>>> f.readline()
?12.3[1, 2, 3]?                                                 nomedoarquivo = raw_input(?Entre com o nome do 
              A solução é o pickling, assim chamado porque ? arquivo: ?)
preserva? estruturas de dados. O módulo pickel contém os try:

                                          Capítulo 11: Arquivos e exceções #61
Como pensar como um cientista da Computação usando Python

  f = open (nomedoarquivo, ?r?)                                                 Se a função que chamou entraNumero trata o
except:                                                          erro, então o programa pode continuar; de outro modo, Pyhton
  print ?Não existe arquivo chamado?, nomedoarquivo
                                                                 exibe uma mensagem de erro e sai:
              A instrução try executa os comandos do >>> entraNumero()
primeiro bloco. Se não ocorrerem exceções, ele ignora a Escolha um número: 17
instrução except. Se qualquer exceção acontece, ele executa os ErroNumeroRuim: 17 é um número ruim
comandos do ramo except e continua.
                                                                             A mensagem de erro inclui o tipo da exceção e a
              Podemos encapsular esta habilidade numa informação adicional que você forneceu.
função: existe toma um nome de arquivo e retorna verdadeiro
se o arquivo existe e falso se não existe:                                   Como um exercício, escreva uma função que
                                                                use entraNumero para pegar um número do teclado e que
 def existe(nomedoarquivo)                                      trate a exceção ErroNumeroRuim.
  try:
    f = open(nomedoarquivo)
    f.close()                                                    11.6 Glossário
    return 1
  except:                                                           arquivo (file) Uma entidade nomeada, usualmente
    return 0                                                                       armazenada em um disco rígido (HD),
               Você pode usar múltiplos blocos except para                         disquete ou CD-ROM, que contém uma
tratar diferentes tipos de exceções. O Manual de Referência de                     seqüência de caracteres.
Python (Python Reference Manual) tem os detalhes.
                                                                   diretório      Uma coleção nomeada de arquivos,
             Se o seu programa detecta uma condição de           (directory)      também chamado de pasta ou folder.
erro, você pode fazê-lo lançar uma exceção. Aqui está um
exemplo que toma uma entrada do usuário e testa se o valor é caminho (path)       Uma seqüência de nomes de diretórios que
17. Supondo que 17 não seja uma entrada válida por uma                            especifica a exata localização de um
razão qualquer, nós lançamos uma exceção.                                         arquivo.
def entraNumero():                                             arquivo texto      Um arquivo que contém caracteres
                                                                   (text file)    organizados em linhas separadas por
  x = input (?Escolha um número: ?)                                               caracteres de nova linha.
  if x == 17:
    raise ?ErroNumeroRuim?, ?17 é um número ruim?                       comando Um comando que força a atual iteração de
  return x                                                          break (break um loop a terminar. O fluxo de execução
                                                                      statement) vai para o topo do loop, testa a condição e
             O comando raise toma dois argumentos: o tipo                        prossegue conforme o caso.
da exceção e informações específicas sobre o erro.
ErroNumeroRuim é um novo tipo de exceção que nós
inventamos para esta aplicação.




                                          Capítulo 11: Arquivos e exceções #62
Como pensar como um cientista da Computação usando Python


                                          Capítulo 12: Classes e objetos




                                                                 >>> final.y = 4.0
12.1 Tipos compostos definidos pelo usuário                                  Esta sintaxe é similar à sintaxe para acessar uma
                                                               variável de um módulo, como math.pi ou string.uppercase.
Depois de usarmos alguns tipos nativos do Python, estamos Neste caso, porém, estamos acessando um item de dado de
prontos para criar um tipo de dados: o Ponto.                  uma instância. Estes itens são chamados atributos.
              Considere o conceito matemático de um ponto.                   O seguinte diagrama de estado mostra o
Em duas dimensões, um ponto é um par de números resultado destas atribuições:
(coordenadas) que são tratadas coletivamente como um objeto
simples. Na notação matemática, pontos são freqüentemente
escritos entre parênteses com vírgula 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 numéricos em ponto flutuante.
A questão, então, é como agrupar estes dois valores em um                 A variável final refere a um objeto Ponto, que
objeto composto. A maneira rápida e rasteira é usar uma lista contém dois atributos. Cada atributo faz referência a um
ou uma tupla, e para algumas aplicações, esso pode ser a número em ponto flutuante.
melhor escolha1.
                                                                          Podemos ler o valor de um atributo usando a
              Uma alternativa é definir um novo tipo mesma sintaxe:
composto, também chamado uma classe. Esta abordagem
envolve um pouco mais de esforço, mas ela tem vantagens >>> print final.y
que logo ficarão evidentes.                                   4.0
             Eis a definição de uma classe:                      >>> x = final.x
                                                                 >>> print x
class Ponto:                                                     3.0
  pass                                                                         A expressão final.x significa, "Vá ao objeto
              Definições de classes podem aparecer em            final e pegue o valor de x". Neste caso, atribuímos este valor a
qualquer parte de um programa, mas elas costuma ficar            uma variável cujo nome é 'x'. Não há conflito entre a variável
próximas do começo do programa (após os comandos import).        x e o atributo x. O propósito da notação objeto.atributo é
As regras de sintaxe para a definição de classes são as mesmas   identificar a qual variável você está fazendo referência de
de outros comandos compostos (veja Seção 4.4).                   forma que não é ambíguo.
              A definição acima cria uma nova classe                    Você pode usar a notação objeto.atributo como
chamada Ponto. O comando pass não tem nenhum efeito; aqui parte de qualquer expressão; assim os seguintes comandos são
ele é necessário porque um comando composto precisa ter válidos:
algo no seu corpo.
                                                                 print '(' + str(final.x) + ', ' + str(final.y) + ')'
              Quando criamos a classe Ponto, criamos um distAoQuadrado = final.x * final.x + final.y * 
novo tipo de dado, também chamado Ponto. Os membros
deste novo tipo são chamados instâncias deste tipo ou final.y
objetos. Criar uma nova instância é instanciar. Para                       A primeira linha imprime (3.0, 4.0); a segunda
instanciar o objeto Ponto, invocamos a função (adivinhou?) linha calcula o valor 25.0.
Ponto:
                                                                           É tentador imprimir o valor do próprio objeto
 final = Ponto()                                             final:
              A variável final agora contém uma referência a >>> print final
um novo objeto da classe Ponto. Uma função como Ponto, que <__main__.Ponto instance at 80f8e70>
cria novos objetos, é chamada construtor.
                                                                           O resultado indica que final é uma instância da
                                                             classe Ponto e foi definida no prgrama principal: __main__.
                                                             80f8e70 é o identificador único deste objeto, escrito em
12.2 Atributos                                               hexadecimal (base 16). Esta não é provavelmente a forma
                                                             mais informativa para mostrar um objeto Ponto. Logo você irá
Podemos adicionar novos dados em uma instância usando a ver como mudar isso.
notação de ponto (dot notation):                                           Como exercício, crie e imprima um objeto
 >>> final.x = 3.0
                                                               Ponto, e então use id para imprimir o identificador único do
                                                               objeto. Traduza a forma hexadecimal para a forma decimal e
1 N.T.: A linguagem Python também incorpora um tipo nativo     confirme se são compatíveis.
   complex que representa números complexos. Uma
   instância de complex, como a=3+5j possui dois valores de
   ponto flutuante em seus atributos a.real e a.imag, e pode
   ser utilizada para armazenar pontos em um espaço bi-
   dimensional.

                                              Capítulo 12: Classes e objetos #63
Como pensar como um cientista da Computação usando Python


                                                                                  Para comparar o conteúdo dos objetos --
12.3 Instâncias como parâmetros                                      igualdade profunda -- podemos escrever uma função chamada
                                                                     mesmoPonto:
Você pode passar uma instância como um parâmetro da forma
usual. Por exemplo:                                       def mesmoPonto(p1, p2) :
                                                                       return (p1.x == p2.x) and (p1.y == p2.y)
def mostrarPonto(p):                                                    Agora se criarmos dois diferentes objetos que
  print '(' + str(p.x) + ', ' + str(p.y) + ')'            contém os mesmos dados, podemos usar mesmoPonto para
             A função mostrarPonto pega o ponto (p) como verificar se eles representam o mesmo ponto.
um argumento e mostra-o no formato padrão. Se você chamar
mostrarPonto(final), a saída será (3.0, 4.0).             >>> p1 = Ponto()
                                                                     >>> p1.x = 3
                Como um exercício, re-escreva a função >>> p1.y = 4
    distância da Seção 5.2 para receber dois pontos como >>> p2 = Ponto()
    parâmetros, ao invés de quatro números.
                                                                     >>> p2.x = 3
                                                                     >>> p2.y = 4
                                                                     >>> mesmoPonto(p1, p2)
12.4 O significado de "mesmo"                                        True
                                                                        É claro, se as duas variáveis referirem ao
O significado da palavra "mesmo" parece perfeitamente claro mesmo objeto, elas têm igualdade rasa e igualdade profunda.
até que você pense a respeito, e então você percebe que há
mais nesta palavra do que você esperava.
              Por exemplo, se você diz "Cris e eu temos o            12.5 Retângulos
mesmo carro", você está dizendo que o carro de Cris e o seu
são do mesmo fabricante e modelo, mas são dois carros
diferentes. Se você disser "Cris e eu temos a mesma mãe",            Digamos que desejemos uma classe para representar um
você está dizendo que a mãe de Cris e a sua, são a mesma             retângulo. A questão é, qual informação temos de prover para
pessoa1. Portanto a idéia de 'semelhança' é diferente                especificar um retângulo? Para manter as coisas simples,
dependendo do contexto.                                              assuma que o retângulo é orientado verticalmente ou
                                                                     horizontalmente, nunca em um ângulo.
             Quando falamos de objetos, há uma
ambigüidade similar. Por exemplo, se dois Pontos forem os                   Há algumas possibilidades: poderíamos
mesmos, isto quer dizer que eles contêm os mesmos dados especificar o centro do retângulo (duas coordenadas) e seu
(coordenadas) ou que são realmente o "mesmo" objeto?          tamanho (largura e altura); ou poderíamos especificar um dos
                                                              lados e o tamanho; ou poderíamos especificar dois lados
             Para verificar se duas referências se referem ao opostos. A escolha convencional é especificar o canto superior
'mesmo' objeto, use o operador '==' 2. Por exemplo:           esquerdo do retângulo e o tamanho.
>>> p1 = Ponto()                                                                  Novamente, vamos definir uma nova classe:
>>> p1.x = 3
                                                                     class Rectangle:
>>> p1.y = 4
                                                                       pass
>>> p2 = Ponto()
>>> p2.x = 3
                                                                                  E instanciá-la:
>>> p2.y = 4                                                         box = Rectangle()
>>> p1 == p2                                                         box.width = 100.0
False                                                                box.height = 200.0
             Mesmo que p1 e p2 contenham as mesmas                        Este código cria um novo objeto Retângulo com
coordenadas, os dois não representam o mesmo objeto. Se dois atributos ponto-flutuante. Para especificar o canto
atribuirmos p1 a p2, então as duas variáveis são pseudônimos superior esquerdo, podemos embutir um objeto dentro de um
do mesmo objeto.                                             objeto!
>>> p2 = p1                                                          box.corner = Ponto()
>>> p1 == p2                                                         box.corner.x = 0.0;
True                                                                 box.corner.y = 0.0;
             Este tipo de igualdade é chamado de igualdade             A expressão box.corner.x significa, "vá ao
rasa porque ela compara somente as referências e não o objeto referenciado por 'box' e selecione o atributo 'corner';
conteúdo dos objetos.                                      então vá ao objeto 'corner' e deste, selecione o atributo de
                                                           nome 'x'".
1    Nem todos os idiomas têm este problema. Por exemplo,
     em alemão há palavras diferentes para diferentes sentidos                    A figura mostra o estado deste objeto:
     de "mesmo". "Mesmo carro" nesse contexto seria "gleiche
     Auto", e "mesma mãe" seria "selbe Mutter".
2    LR: Eu não diria que devemos usar == para verificar se
     dois objetos são 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
     referências, e portanto serve para determinar se duas
     variáveis apontam para o mesmo objeto. No entanto, a o
     código acima está correto porque em Python a
     implemetação default de == (método __eq__) é comparar o
     id das instâncias, porém 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).

                                               Capítulo 12: Classes e objetos #64
Como pensar como um cientista da Computação usando Python


                                                               >>>   import copy
12.6 Instancias como valores retornados                        >>>   p1 = Ponto()
                                                               >>>   p1.x = 3
Funções podem retornar instâncias. Por exemplo, findCenter     >>>   p1.y = 4
pega um Retângulo como um argumento e retorna um Ponto
que contem as coordenadas do centro do retângulo:              >>>   p2 = copy.copy(p1)
                                                               >>>   p1 == p2
def findCenter(box):                                           0
      p = Ponto()                                              >>>   mesmoPonto(p1, p2)
      p.x = box.corner.x + box.width/2.0                       1
      p.y = box.corner.y + box.height/2.0                             Uma vez que importamos o modulo 'copy',
             Para chamar esta função, passe 'box' como um podemos usar o método 'copy' para criar um outro 'Ponto'. p1
argumento e coloque o resultado em uma variável.          e p2 não representam o mesmo ponto, mas eles contem os
                                                          mesmo dados.
>>> center = findCenter(box)
>>> print mostrarPonto(center)                                             Para copiar um simples objeto como um 'Ponto',
(50.0, 100.0)                                                 que não contem nenhum objeto embutido, 'copy' é suficiente.
                                                              Isto eh chamado 'shallow' copia.
                                                                             Mas para um objeto como um 'Rectangle', que
                                                              contem uma referencia para um 'Ponto', o método 'copy' não
12.7 Objetos são mutáveis                                     irá executar corretamente a copia. Ele irá copiar a referencia
                                                              para o objeto 'Ponto', portanto o que acontece aqui é que os
Podemos mudar o estado de um objeto fazendo uma dois Rectangle (o novo e o antigo) irão fazer referencia a um
atribuição a um dos seus atributos. Por exemplo, para mudar o simples 'Ponto'.
tamanho de um retângulo sem mudar sua posição, podemos
modificar os valores de sua largura e altura. Veja:                          Em outras palavras, se criarmos um 'box', c1,
                                                              utilizando a forma usual, e depois fazer uma copia, c2, usando
 box.width = box.width + 50                                   o método 'copy', o diagrama de estado resultante ficará assim:
box.height = box.height + 100
             Poderíamos encapsular este código em um
método e generaliza-lo para aumentar o tamanho deste
retângulo em qualquer medida:
def growRect(box, dwidth, dheight) :
      box.width = box.width + dwidth
      box.height = box.height + dheight
             As variáveis dwidth e dheight indicam em                  o resultado não será o que esperamos. Neste
quanto vamos aumentar o tamanho do retângulo em cada caso, invocando 'growRect' em um dos retângulos (c1), isto
direção. Chamando este método, teríamos o mesmo efeito. não irá afetar o outro retângulo (c2, neste exemplo). Mas se
                                                        usarmos o método 'moveRect' em qualquer um deles, isto irá
             Por exemplo, poderíamos criar um novo inevitavelmente afetar o outro. Este comportamento é confuso
Retângulo com o nome de 'bob' e passar este nome para o e propenso a erros!
método growRect:
                                                                       Mas felizmente o modulo 'copy' contem um
>>> bob = Rectangle()                                   método chamado 'deepcopy' que copia não somente o objeto,
>>> bob.width = 100.00                                  mas também copia todo e qualquer objeto 'embutido' neste
>>> bob.height = 200.00
                                                        objeto. Por isto, você não ficará surpreso porque este método
                                                        chama-se 'deepcopy' (copia profunda) não é? Veja como
>>> bob.corner.x = 0.0;                                 funciona:
>>> bob.corner.y = 0.0;
>>> growRect(bob, 50, 100)                                     >>> c2 = copy.deepcopy(c1)
              Enquanto growRect está sendo executado, o                  Agora, c1 e c2 são objetos completamente
parâmetro 'box' é um alias (apelido) para 'bob'. Qualquer separados.
mudança feita em 'box', também irá afetar 'bob'.                         Podemos usar 'deepcopy' para re-escrever
              Como exercício, escreva uma function (método) 'growRect' sendo que ao invés de modificar um Rectangle
com o nome de moveRect que pega um Rectangle e dois existente, ele cria um novo que tem a mesma localização do
parâmetros com o nome de 'dx' e 'dy'. Esta função deverá outro, mas com novas dimensões:
mudar a localização do retângulo através da adição de 'dx' à
coordenada 'x' e da adição de 'dy' à coordenada 'y'.         def growRect(box, dwidth, dheight):
                                                                 import copy
                                                                 newBox = copy.deepcopy(box)
                                                                 newBox.width = newBox.width + dwidth
12.8 Copiando                                                    newBox.height = newBox.height + dheight
                                                                 return newBox
Ao usar 'alias' - como fizemos na seção anterior - podemos
tornar o programa um pouco difícil de ler ou entender, pois as               Como exercício, re-escreva o método
mudanças feitas em um local, podem afetar inesperadamente 'moveRect' para ele criar e retornar um novo Rectangle ao
um outro objeto. E pode se tornar difícil de encontrar todas as invés de apenas modificar o antigo.
variáveis que podem afetar um dado objeto.
              Copiar um objeto é freqüentemente uma
alternativa ao 'alias'. O modulo 'copy' contém uma função
chamada 'copy' que duplica um qualquer objeto. Veja:


                                           Capítulo 12: Classes e objetos #65
Como pensar como um cientista da Computação usando Python


12.9 Glossário

   classe (class) Um tipo composto (XXX compound type)
                  definido pelo usuário. Uma classe também
                  pode ser visualizada como um molde que
                  define a forma dos objetos que serão suas
                  instâncias.
       instanciar Criar uma instância de uma classe.
    (instantiate)
       instância Um objeto que pertence a uma classe.
      (instance)
  objeto (object) Um tipo de dado composto comumente
                  utilizado para representar uma coisa ou
                  um conceito do mundo real.
      construtor Um método utilizado para criar novos
   (constructor) objetos.
        atributo Um dos itens de dados nomeados que
      (attribute) compõem uma instância.
 igualdade rasa Igualdade de referências; ocorre quando
       (shallow duas referências apontam para o mesmo
       equality) objeto.
      igualdade Igualdade de valores; ocorre quando duas
 profunda (deep referências apontam para objetos que têm
       equality) o mesmo valor.
      cópia rasa Ato de copiar o conteúdo de um objeto,
  (shallow copy) incluindo as referências a objetos
                 embutidos       (XXX       embedded);
                 implementada pela função copy do
                 módulo copy.
 cópia profunda Ato de copiar o conteúdo de um objeto,
    (deep copy) bem como dos objetos embutidos (XXX
                embedded), e dos objetos embutidos
                nestes, e assim por diante; implementada
                pela função deepcopy do módulo copy.




                                          Capítulo 12: Classes e objetos #66
Como pensar como um cientista da Computação usando Python


                                        Capítulo 13: Classes e funções



                                                             quantidade de tempo que a máquina de fazer pão gasta para
13.1 Horario                                                 fazer pão. Então vamos usar somaHorario para tentar saber
                                                             quando o pão estará pronto. Se você não tiver terminado de
Como exemplo de outro tipo definido pelo usuário, vamos escrever imprimirHorario ainda, de uma olhada na seção 14.2
definir uma classe chamada Horario que grava os registros de antes de você continuar isso:
horário do dia. Eis a definição da classe:
                                                                >>>   horarioAtual = Horario()
class Horario:                                                  >>>   horarioAtual.horas = 9
        pass                                                    >>>   horarioAtual.minutos = 14
              Podemos criar uma nova instância de Horario e     >>>   horarioAtual.segundos = 30
determinar atributos para horas, minutos e segundos:
                                                                >>>   horarioDoPao = Horario()
horario = Horario()
                                                                >>>   horarioDoPao.horas = 3
horario.horas = 11
                                                                >>>   horarioDoPao.minutos = 35
horario.minutos = 59
                                                                >>>   horarioDoPao.segundos = 0
horario.segundos = 30
             O diagrama de estado para o objeto Horario
parece com isso:                                        >>> horarioTermino = somaHorario(horarioAtual, 
                                                                horarioDoPao)
                                                                >>> imprimirHorario(horarioTermino)
                                                                              A saída deste programa é 12:49:30, o que é
                                                                correto. Por outro lado, existem casos onde o resultado não é
                                                                correto. Você pode pensar em algum ?
                                                                              O problema é que esta função não lida com
                                                                casos onde o número 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.
             Como exercício, escreva uma função                   Aqui está a segunda versão corrigida da função:
 'imprimirHorario' que tenha como argumento um objeto
 Horario e imprima-o na forma horas:minutos:segundos. def somaHorario(t1, t2):
                                                                         soma = Horario()
             Como um segundo exercício, escreva uma
 função booleana que tenha como argumento dois objetos                   soma.horas = t1.horas + t2.horas
 Horario, h1 e h2, e retorne verdadeiro (1) se h1 vem depois             soma.minutos = t1.minutos + t2.minutos
 de h2 cronologicamente, do contrário, retorne falso (0).                soma.segundos = t1.segundos + t2.segundos

                                                                         if soma.segundos >= 60:
13.2 Funções Puras                                                               soma.segundos = soma.segundos - 60
                                                                                 soma.minutos = soma.minutos + 1
Nas próximas sessões, vamos escrever duas versões de uma
função chamada adicionaHorario, que calcula a soma de dois               if soma.minutos >= 60:
horários. Elas vão demonstrar 2 tipos de funções: funções                        soma.minutos = soma.minutos - 60
puras e funções modificadoras.                                                   soma.horas = soma.horas + 1
             Segue uma versão rudimentar de somaHorario:
                                                                         return soma
def somaHorario(h1, h2):
                                                                              Apesar desta função estar correta, ela está
  soma = Horario()                                              começando a ficar grande. Depois vamos sugerir uma
  soma.horas = h1.horas + h2.horas                              aproximação alternativa que rende um código menor. Clique
  soma.minutos = h1.minutos + h2.minutos                        aqui para feedback
  soma.segundos = h1.segundos + h2.segundos
  return soma
               A função cria um novo objeto Horario,            13.3 Modificadores
inicializa os seus atributos, e retorna uma referência para o
novo objeto. Isso é chamado de função pura pois ela não     Existem momentos quando é útil para uma função modificar
modifica nenhum dos objetos que são passados como           um ou mais dos objetos que ela recebe como parâmetro.
parâmetros e não tem nenhum efeito colateral, como imprimir Usualmente, quem está chamando a função mantém uma
um valor ou pegar entrada do usuário.                       referência para os objetos que ele passa, de forma que
             Aqui está um exemplo de como usar esta quaisquer mudanças que a função faz são visíveis para quem
função. Nós vamos criar dois objetos Horario: horarioAtual, está chamando. Funções que trabalham desta forma são
que contém o horário atual; e horarioDoPao, que contém a chamadas modificadores.


                                          Capítulo 13: Classes e funções #67
Como pensar como um cientista da Computação usando Python


              incrementar, que adiciona um número dado de
segundos para um objeto Horario, poderia ser escrito quase 13.5 Desenvolvimento Prototipado versus
naturalmente como um modificador. Um rascunho rudimentar
da função seria algo parecido com isso:                    Desenvolvimento Planejado
def incrementar(horario, segundos):                             Neste capítulo, demonstramos uma aproximação para o
        horario.segundos =                                     desenvolvimento de programas que chamamos de
horario.segundos + segundos                                     desenvolvimento prototipado. Em cada caso, escrevemos um
                                                                rascunho rudimentar (ou protótipo) que executou os cálculos
        if horario.segundos >= 60:                              básicos e então, o testamos em uns poucos casos, corrigindo
                                                                as falhas que fomos encontrando.
                horario.segundos = 
horario.segundos - 60                                                           Embora esta aproximação possa ser eficaz, ela
                horario.minutos = horario.minutos + 1           pode conduzir a código que é desnecessariamente complicado
                                                                pois trata de muitos casos especiais e não é confiável, pois é
                                                                difícil saber se você encontrou todos os erros.
        if horario.minutos >= 60:
                horario.minutos =                                           Uma alternativa é o desenvolvimento planejado,
horario.minutos - 60                                           onde uma visão de alto nível do problema pode tornar a
                horario.horas = horario.horas + 1              codificação muito mais fácil. Nesse caso, a abstração é que
                                                               um objeto Horario é realmente um número de três digitos na
               A primeira linha executa a operação básica; o base 60! O componente segundo é a "coluna dos uns", o
resto lida com os caso especiais que vimos antes.              componente minuto é a "coluna do 60", e o componente hora
               Esta função está correta ? O que aconteceria se é a "coluna do 360".
o parâmetro segundos for muito maior que sessenta ? Nesse                    Quando nós escrevemos somaHorario e
caso, não é suficiente transportar apenas uma vez; teríamos de incrementar, nós estávamos efetivamente fazendo adições em
continuar fazendo isso até que segundos seja menor que base 60, que é o motivo pelo qual tinhamos de transportar de
sessenta. Uma solução seria substituir os comando if por uma coluna para a próxima.
comandos while:
                                                                             Essa observação sugere uma outra aproximação
def incrementar(horario, segundos):                            para todo o problema - nós podemos converter um objeto
           horario.segundos =                                 Horario em um número simples e levar vantagem do fato de
horario.segundos + segundos                                    que um computador sabe como fazer aritmética com números.
                                                               A seguinte função converte o objeto Horario em um inteiro:
         while horario.segundos >= 60:                          def converterParaSegundos(t):
                 horario.segundos = horario.segundos -            minutos = t.horas * 60 + t.minutos
60                                                                segundos = minutos * 60 + t.segundos
                  horario.minutos = horario.minutos + 1           return segundos
                                                                              Agora, tudo que precisamos é uma maneira de
        while horario.minutos >= 60:                            converter de um inteiro para um objeto Horario:
                horario.minutos = 
horario.minutos - 60                                            def criarHorario(segundos):
                horario.horas = horario.horas + 1                   horario = Time()
             Esta função agora esta correta, mas não é a            horario.horas = segundos/3600
solução mais eficiente.                                             segundos = segundos - horario.horas * 3600
                                                                    horario.minutos = segundos/60
             Como um exercício, reescreva esta função de            segundos = segundos - horario.minutos * 60
 maneira que ela não contenha nenhum loop. Como um
 segundo exercício, reescreva incrementar como uma função           horario.segundos = segundos
 pura, e escreva chamadas de funções para as duas funções.          return horario
 Clique aqui para feedback                                                    Você deve ter que pensar um pouco para se
                                                                convencer que esta técnica de converter de uma base para
                                                                outra é correta. Assumindo que você está convencido, você
                                                                pode usar essas funções para reescrever somaHorario:
13.4 O que é melhor ?
                                                                def somaHorario(t1, t2):
Qualquer coisa que pode ser feita com modificadores também          segundos = 
podem ser feitas com funções puras. De fato, algumas            converterParaSegundos(t1) + converterParaSegundos(t2)
linguagens de programação permitem apenas funções puras.            return criarHorario(segundos)
Existe alguma evidência que programas que usam funções
puras são desenvolvidos mais rapidamente e são menos                           Esta versão é muito mais curta que a original, e
propensos a erros que programas que usam modificadores. No      é muito mais fácil para demonstrar que está correta
entanto, modificadores as vezes são convenientes, e em alguns   (assumindo, como sempre, que as funções que são chamadas
casos, programação funcional é menos eficiente.                 estão corretas).
             Em geral, recomendamos que você escreva                  Como um exercício, reescreva incrementar da
funções puras sempre que for necessário e recorrer para mesma forma. Clique aqui para feedback
modificadores somente se existir uma grande vantagem. Esta
aproximação poderia ser chamada de um estilo de
programação funcional. Clique aqui para feedback           13.6 Generalização
                                                                Algumas vezes, converter de base 60 para base 10 e voltar é
                                                                mais difícil do que simplesmente lidar com horários.


                                          Capítulo 13: Classes e funções #68
Como pensar como um cientista da Computação usando Python

Conversão de base é mais abstrata; nossa intuição para lidar                Na nossa opinião, é preocupante que humanos
com horários é melhor.                                       gastem tanto tempo na escola aprendendo a executar
                                                             algoritmos que, literalmente, não requerem inteligência.
              Mas se conseguirmos abstrair horários como
números de base 60 e investirmos em criar as funções de                     Por outro lado, o processo de projetar
conversão (converterParaSeguntos e criarHorario), nós algoritmos é interessante, intelectualmente desafiante, e uma
conseguimos um programa que é menor, fácil de ler e depurar, parte central daquilo que chamamos programação.
e mais confiável.
                                                                            Algumas das coisas que as pessoas fazem
              É também fácil para adicionar funcionalidades naturalmente, sem dificuldade ou consciência, são as mais
depois. Por exemplo, imagine subtrair dois Horarios para difíceis de se expressar através de algoritmos. Entender a
encontrar a duração entre eles. Uma aproximação ingênua linguagem natural é um bom exemplo. Todos nós fazemos
seria implementar subtração com empréstimo. Usando as isso, mas até hoje ninguém conseguiu explicar como fazemos
funções de conversão será mais fácil e provavelmente estará isso, pelo menos não na forma de algoritmo. Clique aqui para
correto.                                                     feedback.
               Ironicamente, algumas vezes fazer um problema
mais difícil (ou mais genérico) o torna mais simples (porque
existem alguns poucos casos especiais e poucas oportunidades 13.8 Glossário
para errar). Clique aqui para feedback
                                                                 função pura (pure Uma função que não modifica
                                                                         function) nenhum dos objetos que ela recebe
13.7 Algoritmos                                                                    como parâmetro. A maioria das
                                                                                   funções puras é frutífera.
Quando você escreve uma solução genérica para uma classe               modificador Uma função que muda um ou mais
de problemas, ao contrário de uma solução específica para um            (modifier) dos objetos que ela recebe como
único problema, você escreveu um algoritmo. Nós                                    parâmetros.    A     maioria dos
mencionamos isso antes mas não definimos cuidadosamente.                           modificadores é nula.
Isso não é fácil para definir, então vamos tentar definir
demonstrando algumas situações.                                             estilo de Um estilo de programação onde a
                                                                      programação maioria das funções são puras.
              Primeiramente, considere alguma coisa que não    funcional (functional
seja um algoritmo. Quando você aprendeu a multiplicar           programming style)
números de um dígito, você provavelmente memorizou a
tabela de multiplicação. Como resultado, você memorizou 100        desenvolvimento    Uma maneira de desenvolver
soluções específicas. Esse tipo de conhecimento não é                  prototipado    programas começando com um
algoritmo.                                                               (prototype   protótipo     e   gradualmente
                                                                      development)    melhorando-o.
              Mas se você é "preguiçoso", você
provavelmente trapaceou por ter aprendido alguns truques.          desenvolvimento Uma maneira de desenvolver
Por exemplo, para encontrar o produto de n e 9, você pode       planejado (planned programas que envolvem uma
escrever n-1 como o primeiro dígito e 10-n como o segundo             development) percepção de alto nível do problema
dígito. Este truque é um solução genérica para multiplicar                         e mais planejamento do que
qualquer número de um dígito por 9. Isso é um algoritmo!                           desenvolvimento incremental ou
                                                                                   desenvolvimento prototipado.
               De modo parecido, as técnicas que você
aprendeu para somar com transporte, subtração com algoritmo (algorithm)               Um conjunto de instruções para
empréstimo, e divisão longa são todas algoritmos. Uma das                             resolver uma classe de problemas
características dos algoritmos é que eles não requerem                                usando um processo mecânico, não
nenhuma inteligência para serem executados. Eles são                                  inteligente.
processos mecânicos no qual cada passo segue o último de
acordo com um conjunto simples de regras.




                                          Capítulo 13: Classes e funções #69
Como pensar como um cientista da Computação usando Python


                                         Capítulo 14: Classes e métodos


ATENÇÃO As referências cruzadas a nomes em códigos de
outros capítulos (especialmente 13) ainda não foram 14.2 exibeHora (printTime)
unificadas...
                                                      No capítulo 13, definimos uma classe chamada Horário
                                                      (Time) e você escreveu uma função chamada exibeHora
                                                      (printTime), que deve ter ficado mais ou menos assim:
14.1 Características da orientação a objetos
                                                                 class Horario:
Python é uma linguagem de programação orientada a            pass
objetos, o que significa que ela tem características que
suportam a programação orientada a objetos.                def exibeHora(time)
               Não é fácil definir programação orientada a   print str(time.horas) + ?:? + 
objetos, mas temos visto already algumas de suas               str(time.minutos) + ?:? + 
características:                                               str(time.segundos)
    ● Programas são construídos sobre definições de                    Para chamar esta função, passamos um objeto
         objetos e definições de funções, e a maioria das Time como um parâmetro:
         computações é expressa em termos de operações
         sobre objetos.                                    >>> horaCorrente = Hora()
                                                                 >>> horaCorrente.horas = 9
    ●    Cada definição de objeto corresponde a algum objeto >>> horaCorrente.minutos = 14
         ou conceito do mundo real, e as funções que operam >>> horaCorrente.segundos = 30
         com aqueles objetos correspondem à maneira como
         os objetos do mundo real interagem.                 >>> exibeHora(horaCorrente)
                                                                          Para fazer de exibeHora um método, tudo o que
              Por exemplo, a classe Tempo, definida no temos a fazer é mover a definição da função para dentro da
capítulo 13 corresponde à maneira como as pessoas registram definição da classe. Note a mudança na endentação:
as horas do dia, e as funções que definimos correspondem aos
tipos de coisas que as pessoas fazem com times. Do mesmo class Horario:
modo, as classes Ponto e Retângulo correspondem aos            def exibeHora(time):
conceitos matemáticos de um ponto e de um retângulo.             print str(time.horas) + ?:? + 
               Até aqui, não tiramos vantagem das                          str(time.minutos) + ?:? + 
características fornecidas por Python que suportam a                       str(time.segundos)
programação orientada a objetos. Estritamente falando, estas                      Agora podemos chamar exibeHora usando a
características não são necessárias. Na maior parte das vezes, natação de ponto:
elas fornecem uma sintaxe alternativa para as coisas que já
fizemos, mas em muitos casos, a alternativa é mais concisa e >>> horaCorrente.exibeHora()
convém mais acuradamente à estrutura do programa.                                 Como é usual, o objeto no qual o método é
               Por exemplo, no programa Time, não existe invocado aparece antes do ponto e o nome do método aparece
uma conexão óbvia entre a definição da classe e a definição da depois do ponto.
função que segue. Com alguma investigação, fica aparente                          O objeto no qual o método é invocado é
que toda função toma pelo menos um objeto Time como um atribuído ao primeiro parâmetro, então, neste caso,
parâmetro.                                                          horaCorrente é atribuído ao parâmetro time.
               Esta observação é a motivação por trás dos                         Por convenção, o primeiro parâmetro de um
métodos. Já temos visto alguns métodos, tais como keys método é chamado self. A razão para isto é um pouco
(chaves) e values (valores), os quais foram invocados em convoluted, mas é baseada numa metáfora útil.
dicionários. Cada método é associado com uma classe e é
intended para ser invocado em instâncias daquela classe.                          A sintaxe para uma chamada de função,
                                                                    exibeHora(horaCorrente), sugere que a função é um agente
               Métodos são simplesmente como funções, com ativo. Diz algo como, ?Ei, exibeHora! Aqui está um objeto
duas diferenças:                                                    para você exibir.?
     ● Métodos são definidos dentro da definição de uma                           Na programação orientada a objetos, os objetos
          classe para tornar explícita a relação entre a classe e o são     agentes    ativos.   Uma      chamado     do    tipo
          método.                                                   horaCorrente.exibeHora() diz ?Ei, horaCorrente! Por favor
     ● A sintaxe para a chamada do método é diferente da exiba-se a si mesmo!?
          sintaxe para a chamada de uma função.                                   Esta mudança de perspectiva pode ser mais
               Nas próximas seções, vamos pegar as funções          polida, mas não fica óbvio que seja útil. Nos exemplos que
dos dois capítulos anteriores e transformá-las em métodos. temos visto até aqui, pode ser que não seja. Mas às vezes,
Esta transformação é puramente mecânica: você pode deslocar a responsabilidade das funções para cima dos objetos
conseguí-la simplesmente seguindo uma seqüência de passos. torna possível escrever funções mais versáteis, e torna mais
Se você se sentir confortável convertendo de uma forma para fácil manter e reutilizar o código.
a outra, você estará apto para escolher a melhor forma para
seja o lá o que for que você estiver fazendo.



                                            Capítulo 14: Classes e métodos #70
Como pensar como um cientista da Computação usando Python


14.3 Um outro exemplo                                                orientada a tais como classes definidas pelo usuário e
                                                                         objetos herança, que facilitam a programação
                                                                                 orientada a objetos.
Vamos converter incremento (da Seção 13.3) em um método.
Para poupar espaço, deixaremos de fora métodos definidos           programação Um estilo de programação na qual os
previamente(anteriormente?), mas você deve mantê-los em              orientada a dados e as operações que os manipulam
sua versão:                                                              objetos estão organizados em classes e métodos.
class Time:                                                              método Uma função que é definida dentro de uma
  #previous method definitions here...                                          definição de classe e é chamada em
                                                                                instâncias desta classe.
  def increment(self, segundos):                                  override (sem     Substituir uma definição já pronta.
  self.seconds = seconds + self.seconds                               traducao;     Exemplos      incluem     substituir  um
                                                                          termo     parâmetro padrão por um argumento
                                                                   consagrado)      particular e substituir um método padrão,
  while self.segundos >= 60:                                                        fornecendo um novo método com o
    self.seconds = self.segundos - 60                                               mesmo nome.
    self.minutes = self.minutos + 1
                                                                      método de     Um método especial que é invocado
                                                                    inicialização   automaticamente quando um novo objeto
  while self.minutes >= 60:                                             (tambem     é criado e que inicializa os atributos deste
    self.minutes = self.minutos - 60                                chamado de      objeto.
    self.hours = self.horas + 1                                      construtor)
             A transformação é puramente mecânica ?               sobrecarga de Estender a funcionalidade dos operadores
movemos a definição do método para dentro da definição da             operador nativos (+, -, *, >, <, etc.) de forma que
classe e mudamos o nome do primeiro parâmetro.                                  eles funcionem também com tipos
             Agora podemos chamar incremento como um                            definidos pelo usuário.
método:                                                          produto escalar Operação definida na álgebra linear que
horaCorrente.incremento(500)                                                     multiplica dois pontos (com coordenadas
                                                                                 (x,y,z)) e retorna um valor numérico.
               De novo, o objeto no qual o método é chamado
gets atribui ao primeiro parâmetro, self. O segundo parâmetro,     multiplicação Operação definida na álgebra linear que
segundo toma(gets) o valor 500.                                     por escalar multiplica cada uma das coordenadas de
                                                                                 um ponto por um valor numérico.
            Como     um      exercício,     converta   ?
 converteParaSegundos? (da Seção 13.5) para um método na            polimórfica Uma função que pode operar com mais de
 classe ?Time?.                                                                 um tipo. Se todas as operações de uma
                                                                                função pode ser aplicadas a um certo tipo,
                                                                                então a função pode ser aplicada a este
                                                                                tipo.
14.10 Glossário

      linguagem Uma linguagem que provê características




                                           Capítulo 14: Classes e métodos #71
Como pensar como um cientista da Computação usando Python


                                       Capítulo 15: Conjuntos de objetos



                                                                     ●    Rainha -> 12
15.1 Composição
                                                                     ●    Rei -> 13
Até agora, você vio diversos exemplos de composição. Um                        O motivo pelo qual nós estamos usando notação
dos primeiros exemplos foi o uso de uma invocação de             matemática para estes mapeamentos é que eles não são parte
método como parte de uma expressão. Outro exemplo é a            do programa Python. Eles são parte do projeto do programa,
estrutura aninhada dos comandos: você pode pôr um comando        mas eles nunca aparecem explicitamente no código. A
if dentro de um laço while, dentro de outro comando if, e        definição de classe para o tipo Carta fica parecida com esta:
assim por diante.
                                                                 class Carta:
               Tendo visto este padrão, e tendo aprendido a
respeito de listas e objetos, você não deveria ficar surpreso em    def __init__(self, naipe=0, posicao=0):
aprender que você pode criar listas de objetos. Você também            self.naipe = naipe
pode criar obejtos que contêm listas (como atritos); você pode         self.posicao = posicao
criar listas que contêm listas; você pode criar objetos que                    Como sempre, nós fornecemos um método de
contêm objetos; e assim por diante.                              inicialização que recebe um parâmetro opcional para cada
               Neste capítulo e no próximo, você irá ver alguns atributo.
exemplos destas combinações, usando objetos Carta como                         Para criar um objeto que representa o 3 de Paus,
exemplo.                                                         usa-se este comando:
                                                                 tresDePaus = Carta(0, 3)
15.2 Objetos Carta                                                             O primeiro argumento, 0, representa o naipe de
                                                                 Paus.
Se você não estiver familiarizado com jogos de cartas, agora é
um bom momento para conseguir um baralho, ou então esse
capítulo pode não fazer muito sentido. Há 52 cartas em um        15.3 Atributos de classe e o método __str__
baralho, cada uma das quais pertence a um dos quatro naipes e
a uma das treze posições. Os naipes são Espadas, Copas,          Para imprimir objetos Carta de uma maneira que as pessoas
Ouros e Paus (em ordem descendente no bridge). As posições       possam facilmente ler, nós gostaríamos de mapear os códigos
são Ás, 2, 3, 4, 5, 6, 7, 8, 9, 10, Valete, Rainha e Rei.        inteiros para palavras. Uma forma natural de fazer isso é usar
Dependendo do jogo, a posição do Ás pode ser maior do que a      listas de strings. Nós atribuímos estas listas para atributos de
do Rei ou menor do que a do 2.                                   classe no topo da definição de classe:
               Se quisermos definir um novo objeto para          class Carta:
representar uma carta, é óbvio que os atributos devem ser
posicao e naipe. Não tão óbvio são os tipos aos quais devem        listaDeNaipes = ["Paus", "Ouros", "Copas",
pertencer os atributos. Uma possibilidade é usar strings         "Espadas"]
contendo palavras como "Espada" para naipes e "Rainha" para        listaDePosicoes = ["narf", "Ás", "2", "3", "4",
posições. Um problema com esta implementação é que não           "5", "6", "7",
seria fácil comparar cartas para ver qual possui o maior naipe                        "8", "9", "10", "Valete",
ou posição.                                                      "Rainha", "Rei"]
               Uma alternativa é usar inteiros para codificar as
posições e naipes. "Codificar", neste caso, não significa o # método init omitido
mesmo que as pessoas normalmente pensam, que é
criptografar ou traduzir para um código secreto. O que um def __str__(self):
cientista da computação quer dizer com "codificar" é "definir        return (self.listaDePosicoes[self.posicao] +
um mapeamento entre uma seqüência de números e os itens " de " + self.ListaDeNaipes[self.naipe])
que eu quero representar". Por exemplo:
                                                                               Um atributo de classe é definido fora de
     ● Espadas -> 3                                              qualquer método, e ele pode ser acessado por quaisquer
                                                                 métodos da classe.
     ● Copas -> 2
                                                                               Dentro de __str__, nós podemos usar
     ● Ouros -> 1                                                listaDeNaipes e listaDePosicoes para mapear os valores
     ● Paus -> 0                                                 numéricos de naipe e posicao para strings. Por exemplo, a
                                                                 expressão self.listaDeNaipes[self.naipe] significa "use o
               Uma característica óbvia deste mapeamento é atributo naipe do objeto self como um índice para o atributo
que os naipes são mapeados para inteiros na ordem, de modo de classe chamado listaDeNaipes, e selecione a string
que nós podemos comparar naipes pela comparação de apropriada".
inteiros. O mapeamento de posições é bastante óbvio. Cada
uma das posições numéricas mapeia para o inteiro em listaDePosicoes é preencher "narf" no primeiro elemento
                                                                               O motivo para o
correspondente e, as cartas com figura são mapeadas da lista, que nunca será usado. As únicasdo 0-ésimo elemento
                                                                                                o lugar
                                                                                                         posições válidas são
conforme abaixo:                                                 de 1 a 13. Este item desperdiçado não é inteiramente
     ● Valete -> 11                                              necessário. Nós poderíamos ter iniciado com 0, como é
                                                                 normal. Porém, é menos confuso codificar 2 como 2, 3 como

                                          Capítulo 15: Conjuntos de objetos #72
Como pensar como um cientista da Computação usando Python

3, e assim por diante.                                             # verificar os naipes
                                                                   if self.naipe > other.naipe: return 1
             Com os métodos que nós temos até agora, nós
podemos criar e imprimir cartas:                                   if self.naipe < other.naipe: return -1
                                                                   # as cartas têm o mesmo naipe... verificar as
>>> carta1 = Carta(1, 11)                                        posições
>>> print carta1                                                   if self.posicao > other.posicao: return 1
Valete de Ouros                                                    if self.posicao < other.posicao> return -1
               Atributos de classe como listaDeNaipes são          # as posições são iguais... é um empate
compartilhados por todos os objetos Carta. A vantagem disso        return 0
é que nós podemos usar qualquer objeto Carta para acessar os                  Nesta ordenação, Ases são menores do que 2.
atributos de classe:
                                                                            Como um exercício, modifique ``__cmp__``, de
>>> carta2 = Carta(1, 3)                                          modo que os Ases sejam maiores do que os Reis.
>>> print carta2
3 de Ouros
>>> print carta2.listaDeNaipes[1]
Ouros
                                                                 15.5 Baralhos
              A desvantagem é que se nós modificarmos um
atributo de classe, isso afetará cada instância da classe. Por   Agora que nós temos objetos para representar Cartas, o
exemplo, se nós decidirmos que "Valete de Ouros" deveria         próximo passo lógico é definir uma classe para representar um
realmente se chamar "Valete de Baleias Rodopiantes", nós         Baralho. É claro que um baralho é formado por cartas;
poderíamos fazer isso:                                           portanto, cada objeto Baralho irá conter uma lista de cartas
                                                                 como um atributo.
>>> carta1.listaDeNaipes = "Baleias Rodopiantes"
                                                                               A seguir, damos uma definição para a classe
>>> print carta1                                                 Baralho. O método de inicialização cria o atributo cartas e
3 de Baleias Rodopiantes                                         gera o conjunto padrão de 52 cartas:
             O problema é que todos os Ouros se tornam
Baleias Rodopiantes:                                   classe Baralho
                                                                   def __init__(self):
>>> print carta2                                                     self.cartas = []
3 de Baleias Rodopiantes                                             for naipe in range(4):
               Normalmente, não é uma boa idéia modificar              for posicao in range(1, 14):
atributos de classe.                                                     self.cartas.append(Carta(naipe, posicao))
                                                                             A maneira mais fácil de popular o baralho é
                                                              com um laço aninhado. O laço externo enumera os naipes de 0
15.4 Comparando cartas                                        até 3. O laço interno enumera as posições de 1 até 13. Como o
                                                              laço externo repete quatro vezes e o laço interno 13 vezes, o
                                                              número total de vezes que o corpo é executado é 52 (13 vezes
Para tipos primitivos, existem operadores condicionais (<, >, quatro). Cada iteração cria uma nova instância de Carta com o
==, etc.) que comparam valores e determinam quando um é naipe e posição atuais e a inclui na lista cartas.
maior que, menor que ou igual a outro. Para tipos definidos
pelo usuário, nós podemos sobrescrever o comportamento dos                   O método append trabalha sobre listas mas não,
operadores pré-definidos fornecendo um método __cmp__. obviamente, sobre tuplas.
Por convenção, __cmp__ recebe dois parâmetros, 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.
                                                                 15.6 Imprimindo o baralho
              Alguns tipos são totalmente ordenados, o que
significa que nós podemos comparar quaisquer dois elementos
e dizer qual é o maior. Por exemplo, os inteiros e os números    Como sempre, quando nós definimos um novo tipo de objeto,
de ponto flutuante são totalmente ordenados. Alguns              nós gostaríamos de ter um método para imprimir o conteúdo
conjuntos são não-ordenados, o que significa que não existe      de um objeto. Para imprimir um Baralho, nós percorremos a
maneira significativa de dizer que um elemento é maior que o     lista e imprimimos cada Carta:
outro. Por exemplo, as frutas são não-ordenadas, e é por isso    class Baralho:
que não podemos comparar maçãs e laranjas.
                                                                   ...
               O conjunto de cartas de jogo é parcialmente         def imprimirBaralho(self):
ordenado, o que significa que às vezes você pode comparar            for carta in self.cartas:
cartas, e às vezes não. Por exemplo, você sabe que o 3 de Paus         print carta
é 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                      Aqui, e a partir daqui, as reticências (...)
Ouros? Um tem uma posição maior, mas o outro tem um indicam que nós omitimos os outros métodos da classe.
naipe maior.                                                                 Como uma alternativa a imprimirBaralho, nós
               Para tornar as cartas comparáveis, você tem que poderíamos escrever um método __str__ para a classe
decidir o que é mais importante: posição ou naipe. Para ser Baralho. A vantagem de __str__ é que ela é mais flexível. Em
honesto, a escolha é arbitrária. Por questão de escolha, nós vez de apenas imprimir o conteúdo de um objeto, ela gera uma
iremos dizer que naipe é mais importante, porque um baralho representação em string que outras partes do programa podem
de cartas novo vem ordenado com todas as cartas de Paus manipular antes de imprimir ou armazenar para uso posterior.
juntas, seguidas pelas de Ouros, e assim por diante.                         Abaixo, uma versão de __str__ que devolve
               Com essa decisão, nós podemos escrever uma representação em string de um Baralho. Para adicionar
__cmp__:                                                       um pouco de estilo, ela distribui as cartas em uma cascata, na
                                                               qual cada carta é indentada um espaço a mais do que a carta
 def __cmp__(self, other):                                     anterior:

                                          Capítulo 15: Conjuntos de objetos #73
Como pensar como um cientista da Computação usando Python

class Baralho:                                                      totalmente aleatória:
  ...
                                                                    class Baralho:
  def __str__(self):
                                                                      ...
    s = ""
                                                                      def embaralhar(self):
    for i in range(len(self.cartas)):
                                                                        import random
      s = s + " "*i + str(self.cartas[i]) + "n"
                                                                        nCartas = len(self.cartas)
    return s
                                                                        for i in range(nCartas):
               Este     exemplo        demonstra         diversas         j = random.randrange(i, nCartas)
características. Primeiro, em vez de percorrer self.cartas e
atribuir cada carta a uma variável, nós estamos usando i como             self.cartas[i], self.cartas[j] =
uma variável de laço e um índice para a lista de cartas.            self.cartas[j], self.cartas[i]
                                                                            Em vez de assumir que existem 52 cartas no
              Segundo, nós estamos usando o operador de baralho, nós obtivemos o comprimento real da lista e o
multiplicação de strings para indentar cada carta com um guardamos na variável nCartas.
espaço adicional com relação à anterior. A expressão " "*i
produz um número de espaços igual ao valor atual de i.                      Para cada carta no baralho, nós escolhemos uma
                                                              carta aleatória dentre as cartas que ainda não foram
              Terceiro, em vez de usar o comando print para embaralhadas. Então, nós trocamos a carta atual (i) pela carta
imprimir as cartas, nós usamos a função str. Passar um objeto selecionada (j). Para trocar as cartas, nós usamos uma
como um argumento para str equivale a invocar o método atribuição de tupla, como visto na Seção 9.2:
__str__ sobre o objeto.
                                                                    self.cartas[i], self.cartas[j] = self.cartas[j],
              Finalmente, nós estamos usando a variável s self.cartas[i]
como um acumulador. Inicialmente, s é a string vazia. A cada
repetição do laço, uma nova string é gerada e concatenada               Como exercício, reescreva esta linha de código
com o valor antigo de s para obter um novo valor. Quando o sem usar uma atribuição de seqüência.
laço termina, s contém a representação em string completa do
Baralho, que se parece com:
>>> baralho = Baralho()
                                                                    15.8 Removendo e distribuindo cartas
>>> print Baralho
Ás de Paus                                                          Outro método que pode ser útil para a classe Baralho é
                                                                    removerCarta. Ele recebe uma carta como parâmetro, remove-
 2 de Paus                                                          a do baralho e retorna verdadeiro (1), se a carta estava no
  3 de Paus                                                         baralho e falso (0), caso contrário:
   4 de Paus
    5 de Paus                                                       class Baralho:
     6 de Paus                                                        ...
      7 de Paus                                                       def removerCarta(self, carta):
       8 de Paus                                                        if carta in self.cartas:
        9 de Paus                                                         self.cartas.remove(carta)
         10 de Paus                                                       return 1
          Valete de Paus                                                else
           Rainha de Paus                                                 return 0
            Rei de Paus                                             O operador in retorna verdadeiro se o primeiro
             Ás de Ouros
                                                     operando estiver contido no segundo, que deve ser uma lista
                                                     ou uma tupla. Se o primeiro operando for um objeto, Python
           E assim por diante. Mesmo que o resultado usa o método __cmp__ do objeto para determinar igualdade
apareça em 52 linhas, é uma string longa que contém com os itens da lista. Como o método __cmp__ da classe
newlines.                                            Carta verifica por igualdade profunda, o método removerCarta
                                                     também testa por igualdade profunda.
                                                                                 Para distribuir as cartas, nós iremos remover e
15.7 Embaralhando                                                   devolver a carta do topo. O método de lista pop fornece uma
                                                                    maneira conveniente de fazer isso:
Se um baralho estiver perfeitamente embaralhado, então cada
carta tem a mesma probabilidade de aparecer em qualquer class Baralho:
lugar no baralho, e qualquer localização no baralho tem a   ...
mesma probabilidade de conter qualquer carta.               def distribuirCarta(self):
                                                                         return self.cards.pop()
              Para embaralhar as cartas, nós usaremos a
função randrange do módulo random. Com dois argumentos                             Na verdade, pop remove a última carta da lista.
inteiros, a e b, randrange escolhe um inteiro aleatório no          Portanto, nós estamos realmente distribuindo as cartas do fim
intervalo a <= x < b. Como o limite superior é estritamente         para o início do baralho.
menor que b, nós podemos usar o comprimento de uma lista                          Uma última operação que nós poderíamos
como o segundo parâmetro, e nós garantimos que o índice             querer é a função booleana estahVazio, que retorna verdadeiro
sempre será válido. Por exemplo, esta expressão escolhe o           se o baralho não contém cartas:
índice de uma carta aleatória em um baralho:
                                                                    class Baralho:
random.randrange(0, len(self.cartas))
                                                                      ...
             Uma maneira fácil de embaralhar as cartas é
percorrer a lista e trocar cada carta por outra escolhida             def estahVazio(self):
aleatoriamente. É possível que a carta seja trocada por ela             return (len(self.cartas) == 0)
mesma, mas isso não é problema. Na verdade, se nós
excluíssemos essa possibilidade, a ordem das cartas não seria

                                            Capítulo 15: Conjuntos de objetos #74
Como pensar como um cientista da Computação usando Python


15.9 Glossário

      codificar Representar um conjunto de valores
      (encode) usando outro conjunto de valores,
                construindo um mapeamento entre eles.
    atributo de Uma variável que é definida dentro de uma
   classe (class definição de classe, mas fora de qualquer
      atribute) método. Atributos de classe podem ser
                 acessados a partir de qualquer método da
                 classe e são compartilhados por todas as
                 instâncias da classe.
   acumulador Uma variável usada em um laço para
 (accumulator) acumular uma série de valores, para, por
               exemplo, concatená-los em uma string ou
               somá-los a uma soma em andamento.




                                       Capítulo 15: Conjuntos de objetos #75
Como pensar como um cientista da Computação usando Python


                                               Capitulo 16: Herança



                                                                              Esse comando indica que a nova classe Mao
16.1 Herança                                                    herda da classe existente Baralho.
Uma das características mais marcantes das linguagens                         O construtor de Mao inicializa os atributos da
orientadas a objetos é a herança. Herança é a habilidade de mão, que são nome e cartas. A string nome identifica essa
definir uma nova classe que é uma versão modificada de uma mão, provavelmente pelo nome do jogador que está segurando
classe existente.                                              as cartas. O nome é um parâmetro opcional com a string vazia
                                                               como valor default. cartas é a lista de cartas da mão,
               A principal vantagem dessa característica é que inicializada com uma lista vazia
você pode adicionar novos métodos a uma classe sem ter que
modificar a classe existente. Chama-se "herança" porque a class Mao(Baralho):
nova classe herda todos os métodos da classe existente.           def __init__(self, nome=""):
Ampliando a metáfora, podemos dizer que a classe existente é         self.cartas = []
às vezes chamada de classe mãe (parent). A nova classe pode          self.nome = nome
ser chamada de classe filha ou, simplesmente, "subclasse".                    Em praticamente todos os jogos de cartas, é
               A herança é uma característica poderosa. necessario adicionar e remover cartas do baralho. Remover
Alguns programas que seriam complicados sem herança cartas já está resolvido, uma vez que Mao herda removerCarta
podem ser escritos de forma simples e concisa graças a ela. E de Baralho. Mas precisamos escrever adicionarCarta:
a herança também pode facilitar o reuso do código, uma vez
que você pode adaptar o comportamento de classes existentes class Mao(Baralho):
sem ter que modificá-las. Em alguns casos, a estrutura da         #...
herança reflete a natureza real do problema, tornando o           def adicionarCarta(self,carta):
programa mais fácil de entender.                                     self.cartas.append(carta)
               Por outro lado, a herança pode tornar um                       De novo, a elipse indica que omitimos outros
programa seja difícil de ler. Quando um método é invocado, métodos. O método de listas append adiciona a nova carta no
às vezes não está claro onde procurar sua definição. A parte final da lista de cartas.
relevante do código pode ser espalhada em vários módulos. E,
também, muitas das coisas que podem ser feitas utilizando
herança também podem ser feitas de forma igualmente 16.3 Dando as cartas
elegante (ou até mais) sem ela. Se a estrutura natural do
problema não se presta a utilizar herança, esse estilo de
programação pode trazer mais problemas que vantagens.          Agora que temos uma classe Mao, queremos distribuir cartas
                                                               de Baralho para mãos de cartas. Não é imediatamente óbvio se
               Nesse capítulo, vamos demonstrar o uso de esse método deve ir na classe Mao ou na classe Baralho, mas
herança como parte de um programa que joga uma variante de como ele opera num único baralho e (possivelmente) em
Mico. Um dos nossos objetivos é escrever um código que várias mãos de cartas, é mais natural colocá-lo em Baralho.
possa ser reutilizado para implementar outros jogos de cartas.
                                                                              O método distribuir deve ser bem geral, já que
                                                               diferentes jogos terão diferentes requerimentos. Podemos
                                                               querer distribuir o baralho inteiro de uma vez só ou adicionar
16.2 Uma mão de cartas                                         uma carta a cada mão.
                                                                               distribuir recebe dois argumentos, uma lista (ou
Para quase todos os jogos de baralho, é preciso representar     tupla) de mãos e o numero total de cartas a serem dadas. Se
uma mão de cartas. Uma mão de cartas é similar a um maço        não houver cartas suficientes no baralho, o método dá todas as
de baralho. Porque ambos são formados por uma série de          cartas e pára:
cartas e ambos requerem operações, como, adicionar e
remover cartas. Fora isso, a habilidade de embaralhar a mão e   class Baralho:
o baralho também são úteis.                                       #...
               Mas, ao mesmo tempo, a mão é também                def distribuir(self, maos, nCartas=999):
diferente do baralho. Dependendo do jogo que está sendo             nMaos = len(maos)
jogado, precisamos realizar algumas operações nas mãos de           for i in range(nCartas):
cartas que não fazem sentido para o baralho inteiro. Por              if self.estahVazia(): break
exemplo, no pôquer, podemos classificar uma mão (trinca,
flush, etc.) ou compará-la com outra mão. No jogo de bridge,          # interromper se acabaram as cartas
podemos querer computar a quantidade de pontos que há                 carta = self.pegarCarta()
numa mão, a fim de fazer um lance.                                    # pegar a carta do topo
                                                                      mao = maos[i % nMaos]
             Essa situação sugere o uso de herança. Se Mao é
uma subclasse de Baralho, terá todos os métodos de Baralho, e         # quem deve receber agora?
novos métodos podem ser adicionados.                                  mao.adicionarCarta(carta)
                                                                      # adicionar a carta à mao
              Na definição de classe, o nome da classe pai               O segundo parâmetro, nCartas, é opcional; o
aparece entre parênteses:                                  default é um número grande, o que na prática significa que
class Mao(Baralho):                                        todas as cartas do baralho serão dadas se este parâmetro for
  pass
                                                           omitido.


                                                Capitulo 16: Herança #76
Como pensar como um cientista da Computação usando Python

             A variável do laço i vai de 0 a nCartas-1. A cada     def __init__(self):
volta do laço, uma carta é removida do baralho, usando o             self.baralho = Baralho()
método de lista pop, que remove e retorna o último item na           self.baralho.embaralhar()
lista.
                                                                          Este é o primeiro dos casos que vimos até agora
             O operador módulo (%) permite dar cartas em em que o método de inicialização realiza uma computação
ao redor da mesa (uma carta de cada vez para cada mão). significativa, para além de inicializar atributos.
Quando i é igual ao numero de mãos na lista, a expressão i %
nMaos volta para o começo da lista (índice 0).                            Para implementar jogos específicos, podemos
                                                             herdar de JogoDeCartas e adicionar caracteristicas para o
                                                             novo jogo. Como exemplo, vamos escrever uma simulação de
                                                             Mico.
16.4 Exibindo a mao
                                                                          O objetivo do jogo é livrar-se das cartas que
                                                             estiverem na mão. Para fazer isso, é preciso combinar cartas
Para exibir o conteúdo de uma mão, podemos tirar vantagem formando pares ou casais que tenham a mesma cor e o mesmo
dos métodos exibirBaralho e __str__ herdados de Baralho. Por número ou figura. Por exemplo, o 4 de paus casa com o 4 de
exemplo:                                                     espadas porque os dois naipes são pretos. O Valete de copas
>>> baralho = Baralho()
                                                             combina com o Valete de ouros porque ambos são vermelhos.
>>> baralho.embaralhar()                                                      Antes de mais nada, a Dama de paus é removida
>>> mao = Mao("fabio")                                           do baralho, para que a Dama de espadas fique sem par. A
>>> baralho.distribuir([mao], 5)                                 Dama de espadas então faz o papel do mico. As 51 cartas que
>>> print mao
                                                                 sobram são distribuidas aos jogadores em ao redor da mesa
                                                                 (uma carta de cada vez para cada mão). Depois que as cartas
Mão fabio contém                                                 foram dadas, os jogadores devem fazer todos os casais
2 de espadas                                                     possíveis que tiverem na mão, e em seguida descartá-los na
 3 de espadas                                                    mesa.
  4 de espadas                                                             Quando ninguém mais tiver nenhum par para
   Ás de copas                                              descartar, o jogo começa. Na sua vez de jogar, o jogador pega
    9 de paus                                               uma carta (sem olhar) do vizinho mais proximo à esquerda,
              Nao é lá uma grande mão, mas tem potencial que ainda tiver cartas. Se a carta escolhida casar com uma
para um straight flush.                                     carta que ele tem na mão, ele descarta esse par. Quando todos
                                                            os casais possíveis tiverem sido feitos, o jogador que tiver
              Embora seja conveniente herdar os métodos sobrado com a Dama de espadas na mão perde o jogo.
existentes, há outras informacoes num objeto Mao que
podemos querer incluir quando ao exibí-lo. Para fazer isso,                Em nossa simulação computacional do jogo, o
podemos fornecer um método __str__ para a classe Mao que computador joga todas as mãos. Infelizmente, algumas
sobrescreva o da classe Baralho:                            nuances do jogo presencial se perdem. Num jogo presencial, o
                                                            jogador que está com o mico na mão pode usar uns truques
class Mao(Baralho)                                          para induzir o vizinho a pegar a carta, por exemplo,
  #...                                                      segurando-a mais alto que as outras, ou mais baixo, ou se
  def __str__(self):                                        esforçando para que ela não fique em destaque. Já o
                                                            computador simplesmente pega a carta do vizinho
     s = "Mao " + self.nome                                 aleatoriamente...
    if self.estahVazia():
      return s + " está vazian"
    else:
      return s + " contémn" + Baralho.__str__(self)
                                                                 16.6 Classe MaoDeMico
               Inicialmente, s é uma string que identifica a
mão. Se a mão estiver vazia, o programa acrescenta as Uma das habilidades gerais de uma algumas habilidades uma
                                                                   mão para jogar Mico requer                      para
palavras está vazia e retorna o resultado.                    alem                              Mao. Vamos definir
                                                              nova classe, MaoDeMico, que herda de Mao e provê um
               Se não, o programa acrescenta a palavra contém método adicional chamado descartarCasais:
e a representação de string do Baralho, computada pela
invocação do método __str__ na classe Baralho em self.        class MaoDeMico(Mao):
                                                                   def descartarCasais(self):
             Pode parecer estranho enviar self, que se refere        conta = 0
à Mao corrente, para um método Baralho, mas isso só até voce         cartasIniciais = self.cartas[:]
se lembrar que um Mao é um tipo de Baralho. Objetos Mao
podem fazer tudo que os objetos Baralho fazem, entao, é              for carta in cartasIniciais:
permitido passar uma instância de Mao para um método                   casal = Carta(3 - carta.naipe, carta.valor)
Baralho.                                                               if casal in self.cartas:
                                                                         self.cartas.remove(carta)
              Em geral, sempre é permitido usar uma
instância de uma subclasse no lugar de uma instância de uma              self.cartas.remove(casal)
classe mãe.                                                              print "Mao %s: %s casais %s" %
                                                                 (self.nome,carta,casal)
                                                                         conta = conta + 1
                                                                     return conta
16.5 A classe JogoDeCartas
                                                                          Começamos fazendo uma cópia da lista de
                                                            cartas, para poder percorrer a cópia enquanto removemos
A classe JogoDeCartas toma conta de algumas tarefas básicas cartas do original. Uma vez que self.cartas é modificada no
comuns a todos os jogos, como, criar o baralho e embaralhá- laço, não queremos usá-la para controlar o percurso. Python
lo:                                                         pode ficar bem confuso se estiver percorrendo uma lista que
 class JogoDeCartas:
                                                            está mudando!


                                                Capitulo 16: Herança #77
Como pensar como um cientista da Computação usando Python

              Para cada carta na mão, verificamos qual é a          # distribuir as cartas
carta que faz par com ela e vamos procurá-la. O par da carta        self.baralho.distribuir(self.maos)
tem o mesmo valor (número ou figura) e naipe da mesma cor.          print "---------- As cartas foram dadas"
A expressão 3 - carta.naipe transforma um paus (naipe 0)
numa espadas (naipe 3) e um ouros (naipe 1) numa copas              self.exibirMaos()
(naipe 2). Você deve analisar a fórmula até se convencer de
que as operações opostas também funcionam. Se o par da             # remover casais iniciais
carta tambem estiver na mão, ambas as cartas são removidas.        casais = self.removerTodosOsCasais()
             O exemplo a seguir demonstra como usar                print "---------- Os pares foram descartados, o
descartarCasais:                                               jogo começa"
                                                                   self.exibirMaos()
>>> jogo = JogoDeCartas()
>>> mao = MaoDeMico("fabio")                                        # jogar até que 25 casais se formem
>>> jogo.baralho.distribuir([mao], 13)                              vez = 0
>>> print mao                                                       numMaos = len(self.maos)
mão fabio contém                                                    while casais < 25:
Ás de espadas                                                         casais = casais + self.jogarVez(vez)
 2 de ouros                                                           vez = (vez + 1) % numMaos
  7 de espadas
   8 de paus                                                        print "---------- Fim do jogo"
    6 de copas                                                      self.exibirMaos()
     8 de espadas                                                            Algumas etapas do jogo foram separadas em
      7 de paus                                                métodos. removerTodosOsCasais percorre a lista de mãos e
       Rainha de paus                                          invoca descartarCasais em cada uma:
        7 de ouros
                                                               class Mico(JogoDeCartas):
         5 de paus
                                                                 #...
          Valete de ouros
                                                                 def removerTodosOsCasais(self):
           10 de ouros
                                                                   conta = 0
            10 de copas
                                                                   for mao in self.maos:
                                                                     conta = conta + mao.descartarCasais()
>>> mao.descartarCasais()
                                                                   return conta
Mão fabio: 7 de espadas faz par com 7 de paus
Mão fabio: 8 de espadas faz par com 8 de paus
                                                                             Como exercício, escreva ``exibirMaos`` que
                                                                percorre ``self.maos`` e exibe cada mão.
Mão fabio: 10 de ouros faz par com 10 de copas
>>> print mao                                                               conta é uma acumulador que soma o número de
Mão fabio contém                                               pares em cada mão e retorna o total.
Ás de espadas                                                                Quando o número total de pares alcança 25, 50
 2 de ouros                                                    cartas foram removidas das mãos, o que significa que sobrou
  6 de copas                                                   só uma carta e o jogo chegou ao fim.
   Rainha de paus                                                           A variável vez mantém controle sobre de quem
    7 de ouros                                                 é a vez de jogar. Começa em 0 e incrementa de um em um;
     5 de paus                                                 quando atinge numMaos, o operador módulo faz ela retornar
      Valete de ouros                                          para 0.
              Observe que não existe um método __init__              O método jogarVez recebe um argumento que
para a classe MaoDeMico. Ele é herdado de Mao.          indica de quem é a vez de jogar. O valor de retorno é o
                                                        número de pares feitos durante essa rodada:
                                                               class Mico(JogoDeCartas):
16.7 Classe Mico                                                 #...
                                                                 def jogarVez(self, i):
Agora podemos focar nossa atenção no jogo em si. Mico é            if self.maos[i].estahVazia():
uma subclasse de JogoDeCartas com um novo método
chamado jogar que recebe uma lista de jogadores como                 return 0
argumento.                                                         vizinho = self.buscarVizinho(i)
                                                                   novaCarta = self.maos[vizinho].pegarCarta()
             Já que __init__ é herdado de JogoDeCartas, um         self.maos[i].adicionarCarta(novaCarta)
novo objeto Mico contém um novo baralho embaralhado:
                                                                   print "Mao", self.maos[i].nome, "pegou",
class Mico(JogoDeCartas):                                      novaCarta
  def jogar(self, nomes):                                          conta = self.maos[i].descartarCasais()
    # remover a Dama de paus                                       self.maos[i].embaralhar()
    self.baralho.removerCarta(Carta(0,12))                         return conta
                                                                              Se a mão de um jogador estiver vazia, ele está
    # fazer uma mão para cada jogador                          fora do jogo, então, ele não faz nada e retorna 0.
    self.maos = []                                                            Do contrário, uma jogada consiste em achar o
    for nome in nomes :                                        primeiro jogador à esquerda que tenha cartas, pegar uma carta
      self.maos.append(MaoDeMico(nome))                        dele, e tentar fazer pares. Antes de retornar, as cartas na mão
                                                               são embaralhadas, para que a escolha do próximo jogador seja
                                                               aleatória.

                                               Capitulo 16: Herança #78
Como pensar como um cientista da Computação usando Python

             O método buscarVizinho começa com o jogador      Rei de espadas
imediatamente à esquerda e continua ao redor da mesa até       10 de ouros
encontrar um jogador que ainda tenha cartas:
class Mico(JogoDeCartas):                                  Mão Jair contém
  #...                                                     Valete de espadas
  def buscarVizinho(self, i):                               Valete de copas
    numMaos = len(self.maos)                                 Rei de ouros
    for next in range(1,numMaos):
      vizinho = (i + next) % numMaos                       Mão Clara contém
      if not self.maos[vizinho].estahVazia():              Valete de ouros
        return vizinho                                      Rei de paus
              Se buscarVizinho alguma vez circulasse pela    10 de copas
mesa sem encontrar cartas, retornaria None e causaria um erro
em outra parte do programa. Felizmente, podemos provar que Mão Alice pegou o Rei de ouros
isso nunca vai acontecer (desde que o fim do jogo seja Mão Alice: Rei de copas faz par com Rei de ouros
detectado corretamente).                                      Mão Jair pegou 10 de copas
             Não mencionamos o método exibirBaralhos. Mão Clara pegou Valete de paus
Esse você mesmo pode escrever.                              Mão Alice pegou Valete de copas
             A saída a seguir é produto de uma forma Mão Jair pegou Valete de ouros
reduzida do jogo, onde apenas as 15 cartas mais altas do Mão Clara pegou Dama de espadas
baralho (do 10 para cima) foram dadas, para três jogadores. Mão Alice pegou Valete de ouros
Com esse baralho reduzido, a jogada pára depois que 7 Mão Alice: Valete de copas faz par com Valete de
combinações foram feitas, ao invés de 25:                   ouros
                                                           Mão Jair pegou Rei de paus
>>> import cartas
                                                           Mão Clara pegou Rei de espadas
>>> jogo = cartas.Mico()
                                                           Mão Alice pegou 10 de copas
>>> jogo.jogar(["Alice","Jair","Clara"])
                                                           Mão Alice: 10 de ouros faz par com 10 de copas
---------- As cartas foram dadas
                                                           Mão Jair pegou Dama de espadas
Mão Alice contém
                                                           Mão Clara pegou Valete de espadas
Rei de copas
                                                           Mão Clara: Valete de paus faz par com Valete de
 Valete de paus
                                                           espadas
  Rainha de espadas
                                                           Mão Jair pegou Rei de espadas
   Rei de espadas
                                                           Mão Jeff: Rei de paus faz par com Rei de espadas
    10 de ouros
                                                           ---------- Fim do jogo
                                                           Mão Alice está vazia
Mão Jair contém
Rainha de copas
                                                           Mão Jair contém
 Valete de espadas
                                                           Rainha de espadas
  Valete de copas
   Rei de ouros
                                                           Mão Clara está vazia
    Rainha de ouros
                                                           Então, o Jair perdeu.
Mão Clara contém
Valete of ouros
 Rei de paus
  10 de espadas                                            16.8 Glossário
   10 de copas
    10 de paus                                             herança (inheritance) Habilidade de definir uma nova
                                                                                 classe que é a versão modificada de
Mão Jair: Dama de copas faz par com Dama de ouros                                uma classe definida anteriormente.
Mão Clara: 10 de espadas faz par com 10 de paus               classe mãe (parent A classe de quem a classe filha
---------- Os pares foram descartados, o jogo começa                       class) herda.
Mão Alice contém
                                                               classe filho (child Um nova classe criada herdando de
Rei de copas                                                                class) uma classe existente; também
 Valete de paus                                                                    chamada de "subclasse".
  Rainha de espadas




                                            Capitulo 16: Herança #79
Como pensar como um cientista da Computação usando Python


                                        Capítulo 17: Listas encadeadas




17.1 Referências Embutidas
Nós temos visto exemplos de atributos que referenciam outros
objetos, que são chamados referências embutidas (veja a
Seção 12.8). Uma estrutura de dados comum, a lista ligada,
tira vantagem desta característica.
             Listas ligadas são constituídas de nós (nodos),
onde cada nó contém uma referência para o próximo nó na                Para ligar os nós, temos que fazer o primeiro nó
lista. Além disto, cada nó contém uma unidade de dados da lista referir ao segundo e o segundo nó referir ao terceiro:
chamada a carga.
                                                               >>> no1.proximo = no2
            Uma lista ligada é considerada uma estrutura
de dados recorrente porque ela tem uma definição >>> no2.proximo = no3
recorrente.                                                          A referência do terceiro nó é None, que indica
                                                         que ele é o final da lista. Agora o diagrama de estado se
            Uma lista ligada é:                          parece com:
    ●   Uma lista vazia, representada por None, ou
    ●   Um nó que contém um objeto carga e uma referência
        para uma lista ligada.
             Estruturas de dados recorrentes são adequadas
para métodos recorrentes.
                                                                             Agora você sabe como criar nós e ligá-los em
                                                               uma lista. O que pode estar menos claro neste ponto é por quê.
17.2 A classe No (Node)
Como é usual quando se escreve uma nova classe, nós 17.3 Listas como Coleções
começaremos com os métodos de inicialização e __str__ de
modo que podemos testar o mecanismo básico de se criar e
mostrar o novo tipo:                                     Listas são úteis porque elas provêm um modo de montar
                                                         múltiplos objetos em uma única entidade, algumas vezes
class No:                                                chamada uma coleção. No exemplo, o primeiro nó da lista
  def __init__(self, carga=None, proximo=None):          serve como uma referência para toda a lista.
    self.carga = carga                                                       Para passar uma lista como um parâmetro, você
    self.proximo = proximo                                     apenas tem que passar uma referência ao primeiro nó. Por
                                                               exemplo, a função imprimeLista toma um único nó como um
  def __str__(self):                                           argumento. Iniciando com o cabeça da lista, ela imprime cada
    return str(self.carga)                                     nó até que chegue ao fim:
               Como de costume, os parâmetros para o método def imprimeLista(no):
de inicialização são opcionais. Por omissão (default), ambos, a    while no:
carga e a ligação, proximo, são definidas como None.                 print no,
               A representação string de um nó é simplesmente        no = no.proximo
a representação string da carga. Como qualquer valor pode ser      print
passado para a função str, nós podemos armazenar qualquer                     Para chamar este método, nós passamos uma
valor em uma lista.                                             referência ao primeiro no:
               Para testar a implementação até agora, nós >>> imprimeLista(no1)
criamos um No e o imprimimos:
                                                               1 2 3
>>> no = No("teste")                                                         Dentro de imprimeLista nós temos uma
>>> print no                                                   referência para o primeiro nó da lista, mas não há variáveis
teste                                                          que refiram aos outros nós. Nós temos que usar o valor
             Para ficar interessante, nós precisamos uma lista proximo de cada nó para alcançar o próximo nó.
com mais do que um nó:                                                       Para percorrer uma lista ligada, é comum usar
                                                               uma variável laço como no para referir a cada um dos nós
>>> no1 = No(1)                                                sucessivamente.
>>> no2 = No(2)
>>> no3 = No(3)                                                         Este diagrama mostra o valor de lista e os
             Este código cria três nós, mas nós ainda não valores que no assume:
temos uma lista ainda porque os nós não estão ligados. O
diagrama de estado é parecido com este:

                                          Capítulo 17: Listas encadeadas #80
Como pensar como um cientista da Computação usando Python

                                                              figura mostra uma lista com dois nós, um dos quais refere-se a
                                                              si mesmo:




             Por convenção, listas são freqüentemente
impressas em braquetes com vírgulas entre os elementos,
como em [1, 2, 3]. Como um exercício, modifique
imprimeLista para que ela gere uma saída neste formato.


17.4 Listas e Recorrência                                                  Se nós invocarmos imprimeLista nesta lista, ele
                                                            ficará em laço para sempre. Se nós invocarmos
É natural expressar muitas operações de listas utilizando imprimeDeTrasParaFrente, ele recorrerá infinitamente. Este
métodos recorrentes. Por exemplo, o seguinte é um algoritmo tipo de comportamento torna as listas infinitas difíceis de se
recorrente para imprimir uma lista de trás para frente.     lidar.
    1. Separe a lista em dois pedaços: o primeiro nó                       A despeito disto, elas ocasionalmente são úteis.
         (chamado a cabeça); e o resto (chamado o rabo).    Por exemplo, podemos representar um número como uma lista
                                                            de dígitos e usar uma lista infinita para representar uma fração
    2. Imprima o rabo de trás para frente.                  repetente.
    3.   Imprima a cabeça.                                                   Mesmo assim, é problemático que não
                                                              possamos          provar        que       imprimeLista      e
              Logicamente, o Passo 2, a chamada recorrente,   imprimeDeTrasParaFrente terminem. O melhor que podemos
assume que nós temos um modo de imprimir a lista de trás      fazer é a afirmação hipotética, "Se a lista não contém laços,
para frente. Mas se nós assumimos que a chamada recorrente    então este método terminará." Este tipo de hipótese é chamado
funciona -- o passo de fé -- então podemos nos convencer de   uma pré-condição. Ele impõe uma limitação sobre um dos
que o algoritmo funciona.                                     parâmetros e descreve o comportamento do método se a
             Tudo o que precisamos são um caso base e um      limitação é satisfeita. Você verá mais exemplos em breve.
modo de provar que para qualquer lista, nós iremos, ao final,
chegar no caso base. Dada a definição recorrente de uma lista,
um caso base natural é a lista vazia, representada por None:   17.6 O Teorema da Ambigüidade Fundamental
def imprimeDeTrasParaFrente(lista):
  if lista == None : return                                   Uma parte de imprimeDeTrasParaFrente pode ter gerado
  cabeca = lista
                                                              surpresa:
  rabo = lista.proximo                                         cabeca = lista
  imprimeDeTrasParaFrente(rabo)                                rabo = lista.proximo
  print cabeca,                                                             Após a primeira atribuição, cabeca e lista têm o
              A primeira linha trata o caso base fazendo nada. mesmo tipo e o mesmo valor. Então por que nós criamos uma
As próximas duas linhas dividem a lista em cabeca e rabo. As nova variável?
duas últimas linhas imprimem a lista. A vírgula no final da
última linha impede o Python de imprimir uma nova linha                     A razão é que as duas variáveis têm diferentes
após cada nó.                                                  papéis. Quando pensamos em cabeca, pensamos como uma
                                                               referência a um único nó, e quando pensamos em lista o
              Nós invocamos este método como invocamos o fazemos como uma referência ao primeiro nó da lista. Estes
imprimeLista:                                                  "papéis" não são parte do programa; eles estão na mente do
                                                               programador.
>>> imprimeDeTrasParaFrente(no1)
3 2 1                                                                       Em geral não podemos dizer olhando para o
                                                              programa qual o papel que uma variável tem. Esta
             O resultado é a lista de trás para frente.       ambigüidade pode ser útil, mas também pode tornar os
               Você pode se perguntar por quê imprimeLista e  programas difíceis de serem lidos. Usamos freqüentemente
imprimeDeTrasParaFrente são funções e não métodos da          nomes de variáveis como no e lista para documentar como
classe No. A razão é que nós queremos usar None para          pretendemos usar uma variável e algumas vezes criamos
representa a lista vazia e não é legal invocar um método sobrevariáveis adicionais para remover a ambigüidade.
None. Esta limitação torna complicado escrever código de                    Poderíamos            ter             escrito
manipulação de lista em estilo orientado a objeto limpo.        imprimeDeTrasParaFrente sem cabeca e rabo, que a tornaria
               Podemos provar que imprimeDeTrasParaFrente mais concisa mas possivelmente menos clara:
sempre termina? Em outras palavras, irá ela sempre atingir o def imprimeDeTrasParaFrente(lista):
caso base? De fato, a resposta é não. Algumas listas farão este
método falhar.                                                    if lista == None : return
                                                                 imprimeDeTrasParaFrente(lista.proximo)
                                                                 print lista,
17.5 Listas Infinitas                                                  Olhando para as duas chamadas de função,
                                                           temos que lembrar que imprimeDeTrasParaFrente trata seu
                                                           argumento como uma coleção e print trata seu argumento
Não há nada que impeça um nó de referenciar de volta um nó como um objeto único.
anterior na lista, incluindo ele mesmo. Por exemplo, esta

                                             Capítulo 17: Listas encadeadas #81
Como pensar como um cientista da Computação usando Python

             O teorema da ambigüidade fundamental                  cabeca = lista
descreve a ambigüidade que é inerente à referência a um nó:        rabo = lista.proximo
           Uma variável que refere a um nó pode tratar o           imprimeDeTrasParaFrente(rabo)
 nó como um objeto único ou como o primeiro em uma lista           print cabeca,
 de nós.                                                         print "]",
                                                                            Novamente, é uma boa idéia verificar métodos
                                                               como este para ver se eles funcionam com casos especiais
17.7 Modificando Listas                                        como uma lista vazia ou um singleton.
                                                                             Quando usamos este método em algum lugar no
Existem duas maneiras de se modificar uma lista ligada.        programa,      invocamos     imprimeDeTrasParaFrenteLegal
Obviamente, podemos modificar a carga dos nós, mas as          diretamente, e ele invoca imprimeDeTrasParaFrente por nós.
operações mais interessantes são aquelas que adicionam,        Neste sentido, imprimeDeTrasParaFrenteLegal atua como um
removem ou reordenam os nós.                                   envoltório, e usa imprimeDeTrasParaFrente como um
                                                               ajudador.
             Como um exemplo, vamos escrever um método
que remove o segundo nó na lista e retorna uma referência ao
nó removido:
                                                               17.9 A Classe ListaLigada
def removeSegundo(lista):
  if lista == None : return                                    Existem alguns problemas sutis com o modo que
  primeiro = lista                                             implementamos listas. Em um inverso de causa e efeito,
  segundo = lista.proximo                                      proporemos uma implementação alternativa primeiro e então
  # faz o primeiro no referir ao terceiro                      explicaremos qual problema ela resolve.
  primeiro.proximo = segundo.proximo                                        Primeiro, criaremos uma nova classe chamada
  # separa o segundo no do resto da lista                      ListaLigada. Seus atributos são um inteiro que contém o
  segundo.proximo = None                                       comprimento da lista e uma referência para o primeiro nó.
  return segundo                                               Objetos do tipo ListaLigada servem como cabos (handles)
             Novamente,      estamos    usando       variáveis para se manipular listas de objetos No:
temporárias para tornar o código mais fácil de ser lido. Aqui class ListaLigada:
está como usar este método:                                       def __init__(self):
>>>   imprimeLista(no1)                                            self.comprimento = 0
1 2   3                                                            self.cabeca = None
>>>   removido = removeSegundo(no1)                                         Uma coisa legal acerca da classe ListaLigada é
>>>   imprimeLista(removido)                                   que ela provê um lugar natural para se colocar funções
2                                                              envoltórias como imprimeDeTrasParaFrenteLegal, que
                                                               podemos transformar em um método da classe ListaLigada:
>>>   imprimeLista(no1)
1 3                                                            class ListaLigada:
             Este diagrama de estado mostra o efeito da          ...
operação:                                                        def imprimeDeTrasParaFrente(self):
                                                                   print "[",
                                                                   if self.cabeca != None :
                                                                     self.cabeca.imprimeDeTrasParaFrente()
                                                                   print "]",



                                                               class No:
                                                                 ...
              O que acontece se você invocar este método e       def imprimeDeTrasParaFrente(self):
passar uma lista com somente um elemento (um singleton)? O         if self.proximo != None:
que acontece se você passar a lista vazia como um argumento?         rabo = self.proximo
Existe uma pré-condição para este método? Se houver, corrija
o método para tratar uma violação da pré-condição de modo            rabo.imprimeDeTrasParaFrente()
razoável.                                                          print self.carga,
                                                                           Apenas para tornar as coisas confusas,
                                                            mudamos o nome de imprimeDeTrasParaFrenteLegal. Agora
                                                            existem dois métodos chamados imprimeDeTrasParaFrente:
17.8 Envoltórios e Ajudadores                               um na classe No (o ajudador); e um na classe ListaLigada``(o
                                                            envoltório).       Quano        o      envoltório       invoca
Freqüentemente é útil dividir uma operação de lista em dois ``self.cabeca.imprimeDeTrasParaFrente, ele está invocando o
métodos. Por exemplo, para imprimir uma lista de trás para ajudador, porque self.cabeca é um objeto No.
frente no formato convencional de lista [3, 2, 1], podemos
usar o método imprimeDeTrasParaFrente para imprimir 3, 2,                  Outro benefício da classe ListaLigada é que ela
mas queremos um metodo separado para imprimir os torna mais fácil adicionar e remover o primeiro elemento de
braquetes e o primeiro nó. Vamos chamá-lo de uma lista. Por exemplo, adicionaPrimeiro é um método para
imprimeDeTrasParaFrenteLegal:                               ListaLigada; ele toma um item de carga como argumento e o
                                                            coloca no início da lista:
def imprimeDeTrasParaFrenteLegal(lista):
                                                               class ListaLigada:
  print "[",
                                                                 ...
  if lista != None :


                                          Capítulo 17: Listas encadeadas #82
Como pensar como um cientista da Computação usando Python

  def adicionaPrimeiro(self, carga):                                      (embedded armazenada/associada       em/a   um
    no = No(carga)                                                         reference) atributo de um objeto.
    no.proximo = self.cabeca
                                                                   lista ligada (linked Uma estrutura de dados que
    self.cabeca = no                                                                list) implementa uma coleção usando
    self.comprimento = self.comprimento + 1                                               uma sequência de nós ligados.
             Como de costume, você deve conferir códigos
como este para ver se eles tratam os casos especiais. Por          nó ou nodo (node) Um elemento de uma lista,
exemplo, o que acontece se a lista está inicialmente vazia?                          usualmente implementado como um
                                                                                     objeto que contém uma referência
                                                                                     para outro objeto do mesmo tipo.
17.10 Invariantes                                                       carga (cargo) Um item de dado contido em um nó.
                                                                        ligação (link) Uma referência embutida usada para
Algumas listas são "bem formadas"; outras não o são. Por                               ligar/conectar um objeto a outro.
exemplo, se uma lista contém um laço, ela fará muitos de
nossos métodos falharem, de modo que podemos querer                     pré-condição Uma asserção que precisa/deve ser
requerer que listas não contenham laços. Outro requerimento é          (precondition) verdadeira para que um método
que o valor de comprimento no objeto ListaLigada seja igual                           trabalhe corretamante.
ao número real de nós da lista.                                           teorema da   Uma referência para um nó de uma
               Requerimentos como estes são chamados de                 ambigüidade    lista pode ser tratada como um
invariantes porque, idealmente, eles deveriam ser verdade para          fundamental    objeto único ou como o primeiro em
cada objeto o tempo todo. Especificar invariantes para objetos         (fundamental    uma lista de nós.
é um prática de programação útil porque torna mais fácil          ambiguity theorem)
provar a correção do código, verificar a integridade das         singleton (singleton) Uma lista ligada com somente um
estruturas de dados e detectar erros.                                                  nó.
               Uma coisa que algumas vezes é confusa acerca      envoltório (wrapper) Um método que atua como um
de invariantes é que existem momentos em que eles são                                 intermediário (middleman) entre um
violados. Por exemplo, no meio de adicionaPrimeiro, após                              chamador e um método ajudador
termos adicionado o nó mas antes de termos incrementado                               (helper), freqüentemente tornando a
comprimento, o invariante é violado. Este tipo de violação é                          invocação do método mais fácil ou
aceitável; de fato, é freqüentemente impossível modificar um                          menos propensa a erros.
objeto sem violar um invariante por, no mínimo, um pequeno
instante. Normalmente, requeremos que cada método que               ajudador (helper) Um método que não é invocado
viola um invariante deve restaurar este invariante.                                   diretamente pelo chamador (caller)
                                                                                      mas é usado por outro método para
             Se há qualquer aumento significativo de código                           realizar parte de uma operação.
no qual o invariante é violado, é importante tornar isto claro
nos comentários, de modo que nenhuma operação seja feita                   invariante Uma asserção que deveria ser
que dependa daquele invariante.                                           (invariant) verdadeira sempre para um objeto
                                                                                      (exceto talvez enquanto o objeto
                                                                                      estiver sendo modificado).
17.11 Glossário

 referência embutida Uma                           referência




                                           Capítulo 17: Listas encadeadas #83
Como pensar como um cientista da Computação usando Python


                                                 Capítulo 18: Pilhas




18.1 Tipos abstratos de dados                                   18.3 Implementando pilhas com listas de Python
Os tipos de dados que você viu até agora são todos concretos,   As operações de lista que Python oferecem são similares às
no sentido que nós especificamos completamente como eles        operações que definem uma pilha. A interface não é
são implementados. Por exemplo, a classe Card(XXX ver           exatamente o que se supõe ser, mas podemos escrever um
como foi traduzido) representa uma carta utilizando dois        código para traduzir do TAD Pilha para as operações nativas.
inteiros. Como discutimos no momento, esta não é a única
maneira de representar uma carta; existem muitas                       Este código é chamado uma implementação do
implementações alternativas.                              TAD Pilha. Geralmente, uma implementaçõa é um conjunto
                                                          de métodos que satisfazem os requisitos sintáticos e
            Um tipo abstrato de dado, ou TAD, especifica semânticos de uma interface.
um conjunto de operações (ou métodos) e a semântica das
operações (o que elas fazem), mas não especifica a                     Aqui está uma implementação do TAD Pilha
implementação das operações. Isto é o que o faz abstrato. que usa uma lista do Python:
             Por que isto é útil?                               class Stack :
                                                                  def __init__(self) :
    ●   Simplifica a tarefa dde especificar um algoritmo se         self.items = []
        você pode XXXdenotar(denote) as operações que
        você precisa sem ter que pensar, ao mesmo tempo,
        como as operações são executadas.                         def push(self, item) :
                                                                    self.items.apend(item)
    ●   Uma vez que existem geralmente muitas maneiras de
        implementar um TAD, pode ser útil escrever um
        algritmo que pode ser usado com qualquer das              def pop(self) :
        possíveis implementações.                                   return self.items.pop()

    ●   TADs bastante conhecidos, como o TAD Pilha deste          def isEmpty(self) :
        capítulo, já estão implementados em bibliotecas
        padrão, então eles podem ser escritos uma vez e             return (self.items == [])
        usado por muitos programadores.                                 Um objeto Stack contém um atributo chamado
                                                         items que é uma lista de ítens na pilha. O método de
     ● As operações em TADs provêm uma linguagem de inicialização define items como uma lista vazia.
         alto nível comum para especificar e falar sobre
         algoritmos.                                                    Para adicionar um novo ítem na pilha, push o
                                                         coloca em items. Para remover um ítem da pilha, pop usa o
              Quando falamos sobre TADs, geralmente método de lista homônimo¹ para remover e retornar um último
distinguimos o código que usa o TAD, chamado cliente, do ítem da lista.
código que implementa o TAD, chamado código fornecedor.
                                                                        Finalmente, para verificar se a pilha está vazia,
                                                         isEmpty comprara items a uma lista vazia.
18.2 O TAD Pilha                                                              Uma implementação como esta, na qual os
                                                                métodos consistem de simples invocações de métodos
Neste capítulo, iremos olhar um TAD comum, a pilha. Uma         existentes, é chamado revestimento. Na vida real,
pilha é uma coleção, ou seja, é uma estrutura de dados que      revestimento é uma fina camada de madeira de boa qualidade
contei múltiplos elementos. Outras coleções que vimos           usado em XXX*furniture-making* para esconder madeira de
incluem dicionários e listas.                                   menor qualidade embaixo. Cientistas da Computação usam
                                                                esta metáfora para descrever um pequeno trecho de código
               Um TAd é definido pelo conjunto de operações     que esconde os detalhes de uma implementação e fornece uma
que podem ser executadas nele, que é chamado interface. A       interface mais simples, ou mais padronizada.
interface para uma pilha consiste nestas operações:
             __init__ : Inicializa uma nova pilha vazia.
                                                                18.4 Empilhando e desempilhando
             push : Adiciona um novo item na pilha
              pop : Remove um ítem da pilha e o retorna, O Uma pilha é uma estrutura de dados genérica, o que
ítem que é retornadao é sempre o último adicionado.        significa que podemos adicionar qualquer tipo de ítem a ela. O
                                                           exemplo a seguir empilha dois inteiros e uma XXXstring na
              isEmpty : Verifica se a pilha está vazia.    pilha:
              Uma às vezes é chamada uma estrutura de               >>>   s = Stack()
dados "last in, first out" ou LIFO, porque o último ítem            >>>   s.push(54)
adicionad é o primeiro a ser removido.                              >>>   s.push(45)
                                                                    >>>   s.push("+")
                                                                             Podemos usar isEmpty e pop para remover e
                                                                imprimir todos os ítens da pilha:


                                                  Capítulo 18: Pilhas #84
Como pensar como um cientista da Computação usando Python

     while not s.isEmpty() :                                                  A função re.split é mais poderosa, permitindo-
         priint s.pop()                                         nos fornecer uma expresão regular ao invés de um
              A saída é + 45 54. Em outras palavras, usamos a   delimitador. Uma expressão regular é uma maneira de
pilha para imprimir os ítens ao contrário! Sabidamente, este    especificar um conjunto de strings. Por exemplo, [A-z] é o
não é o formato padrão de imprimir uma lista, mas, usando       conjunto de todas as letras e [0-9] é o conjunto de todos os
uma pilha, foi notavelmente fácil de fazer.                     dígitos. O operador ^nega um conunto, então [^0-9] é o
                                                                conjunto de tudo o que não é número, que é exatamente o que
              Você deve comparar este trecho de código com      queremos para dividir expressões pós-fixas.
a implementação de printBackward na seção 17.4. Existe um           >>> import re
paralelo natura entre a versão recursiva de printBackward e o
algoritmo de pilha aqui. A diferenã é que printBackward usa a       >>> re.split ("[^0-9]", "123+456*/")
pilha de execução para XXXmanter a trilha(keep track) dos            ['123', '+', '456', '*', '', '/', ' ']
nós enquanto percorre a lista, e então imprime-a no retorno da                Note que a ordem dos argumentos é diferente de
recursão. o algoritmo de pilha faz a mesma coisa, exceto que string.split, o delimitador vem antes da string.
usa o objeto Stack ao invés da pilha de execução.
                                                                              A lista resultante inclui os operandos 123 e 456,
                                                               e os operadores * e /. Também inclui duas strings vazias que
                                                               são inseridas depois dos operadores.
18.5 Usando uma pilha para avaliar expressões pós-
fixas
                                                                18.7 Avaliando em pós-fixo.
Em muitas linguagens de programação, expressões
matemáticas são executadas com o poerador entre os roid         Para avaliar uma expressão pós-fixa, usaremos o parser da
operandos, como em 1 + 2. Este formato é chamado notação        seção anterior e o algoritmo da seção anterior a ela. Para
infixa. Uma alternativa usada por algumas calculadoras é        manter as coisas simples, começaremos com um avaliador que
chamada notação pós-fixa. Em notação pós-fixa, o operador       implementa somente os operadores + e .
segue os operandos, como em 1 2 +.
                                                                def evalPostfix (expr) :
            A razão pela qual a notação pós-fixa é útil           import re
algumas vezes é que existe uma maneira natural de avaliar         tokenList = re.split ("([^0-9])", expr)
uma expressão pós-fixa usando uma pilha:
                                                                  stack = Stack()
    ●   começando no início da expressão, peque um termo          for token in tokenList
        (operador ou operando) de cada vez.                         if token == '' or token = ' ' :
    ●   Se o termo é um operando, empilhe-o                           continue
                                                                    if token == '+' :
    ●   Se o termo é um operador, desempilhe dois                     sum = stack.pop() + stack.pop()
        operandos, execute a operação neles, e empilhe o              stack.push(sum)
        resultado.
                                                                    if token == '*' :
    ●   Quando chegar ao fim da expressão, deve existir               product = stack.pop() * stack.pop()
        exatamente um operando sobrando na pilha. Este                stack.push(product)
        operando é o resultado.                                     else:
    ●   Como um exercício, aplique este algoritmo à                   stack.push(int(token))
        expressão 1 2 + 3 * .                                     return stack.pop()
              Este exemplo demonstra uma das vantagens da                 A primeira condição cuida de espaços e strings
notação pós-fixa - não é necessário usar parênteses para vazias. As duas próximas condições manipulam os
controlar a ordem das operações. Para ter o mesmo resultado operadores. Nós assumimos, agora que qualquer coisa é um
em notação infixa, deveríamos escrever (1 + 2) * 3.         operador. É claro, seria melhor chegar por entrada errônea e
                                                            enviar uma mensagem de erro, mas faremos isto depois.
              Como um exercício, escreva a expressão pós-
  fixa que é equivalente a 1 + 2 * 3.                                     Vamos testá-lo avaliando a forma pós-fixa de
                                                            (56 + 47) * 2
                                                                     >>> print evalPostfix("56 47 + 2 *")
18.6 Análise sintática                                                206

Para implementar o algoritmo anterior, necessitamos estar
prontos para percorrer uma string e quebrá-la em operandos e 18.8 Clientes de fornecedores.
operadores. Este processó é um exemplo de parsing, e o
resultado - os pedaços da string - são chamados tokens. Você
deve lembrar estas palavras do capítulo 1.                       Um dos objetivos de um TAD é separar os interesses do
                                                                 fornecedor, quem escreve o código que implementa o TAD, e
               Python fornece um método split nos módulos o cliente, que usa o TAD. O fornecedor tem que se preocupar
string e re (expressões regulares). A função string.split separa apenas se a implementação está correta - de acordo com a
uma string numa lista usando um único caracter como especificação do TAD - e não como ele será usado.
delimitador. Por exemplo:
                                                                               Inversamente, o cliente assume que a
     >>> import string                                           implementação do TAD está correta e não se preocupa com os
     >>> string.split ("Now is the time", " ")                   detalhes. Quando você está usando um tipo nativo do Python,
       ['Now', 'is', 'the', 'time']                              tem o luxo de pensar exclusivamente como um cliente.
               Neste caso, o delimitador é o caracter de espaço,               É claro, quanto você implementa um TAD, você
então a string é dividida a cada espaço.                         também tem que escrever código cliente para testá-lo. Neste
                                                                 caso, você faz os dois papéis, o que pode ser confuso. Você

                                                  Capítulo 18: Pilhas #85
Como pensar como um cientista da Computação usando Python

deve fazer algum esforçõ para manter a trilha do papel que
está fazendo a cada momento.


18.9 Glossário

 tipo abstrato   Um tipo de dado(geralmente uma coleção
     de dados    de objetos) que é definidopor um conjunto
        (TAD)    de operações, que podem ser implementadas
(abstract data   de várias maneiras.
  type(ADT)):
      interface É o conjunto de operações que definem um
   (interface): TDA.
implementaçã Código que satisfaz(preenche?) os requisitos
           o sintáticos e semânticos de uma interface.
(implementati
        on):
        cliente Um programa (ou o programador que o
       (client): escreveu) que faz utilização de um TDA.
   fornecedor Código (ou o programador que o escreveu)
   (provider): que implementa um TDA.
  revestimento Definição de classe que implementa um
      (veneer): TDA com definições de métodos que são
                chamadas a outros métodos, às vezes com
                pequenas modificações. A lâmina faz um
                trabalho insignificante, mas melhora ou
                padroniza a interface dada ao cliente.
  estrutura de Tipo de estrutura de dados que contem data
         dados de um tipo qualquer(tipo genérico).
       genérica
 (generic data
    structure):
 infixa (infix): Notação matemática em que os operadores
                 se situam entre os operandos.
      pós-fixa Notação matemática em que os operadores
     (postfix): se situam após os operandos.
 parse (parse): Ler um conjunto de caracteres(string de
                caracteres) ou tokens(símbolos atômicos) e
                analisar sua estrutura gramatical.
token (token): Conjunto de caracteres que são tratados
               como uma unidade atômica para fins de
               análise gramatical, como as palavras na
               linguagem natural.
   delimitador Um caracter que é utilizado para separar os
   (delimiter): símbolos    atômicos(tokens),   como     a
                pontuação na linguagem natural.




                                               Capítulo 18: Pilhas #86
Como pensar como um cientista da Computação usando Python


                                                   Capítulo 19: Filas


Este capítulo apresenta dois TDAs: Fila e Fila por Prioridade.       else:
Na nossa vida diária, fila é um alinhamento de consumidores            # find the last node in the list
aguardando algum tipo de serviço. Na maioria dos casos, o              last = self.head
primeiro da fila é o primeiro a ser atendido. Mas há exceções.
No aeroporto, passageiros cujo vôo vai decolar logo, às vezes          while last.next: last = last.next
são chamados primeiro ao balcão do check-in, mesmo que                 # append the new node
estejam no meio da fila. No supermercado, é comum na fila do           last.next = node
caixa alguém deixar passar na frente uma pessoa que chega à          self.length = self.length + 1
fila só com um ou dois produtos na mão.
               A regra que determina quem é o próximo da fila      def remove(self):
chama-se política de enfileiramento. A política de                   cargo = self.head.cargo
enfileiramento mais simples chama-se FIFO, sigla de first-in-        self.head = self.head.next
first-out: primeiro a entrar, primeiro a sair. A política de         self.length = self.length - 1
enfileiramento mais geral é o enfileiramento por prioridade,
em que se atribui uma prioridade a cada pessoa da fila e a que       return cargo
tiver maior prioridade vai primeiro, independente da sua                        Os métodos isEmpty e remove são idênticos aos
ordem de chegada. Dizemos que essa é a política mais geral       métodos isEmpty e removeFirst de LinkedList. O método
de todas, porque a prioridade pode ser baseada em qualquer       insert é novo e um pouco mais complicado.
coisa: hora de partida do vôo; quantos produtos a pessoa vai
passar pelo caixa; o grau de prestígio da pessoa. É claro que             Queremos inserir novos itens no fim da lista. Se
                                                           a fila estiver vazia, basta fazer head apontar ao novo nó. Se
nem todas as políticas de enfileiramento são "justas", mas o
que é justo depende do ponto de vista.                     não, percorremos a lista até o último nó e lá penduramos o
                                                           novo nó. É possível identificar o último nó porque o seu
              O TDA Fila e o TDA Fila por Prioridade têm o atributo next é None.
mesmo conjunto de operações. A diferença está na semântica
das operações: a fila usa a política FIFO; e a fila por                   Existem duas invariantes para um objeto Fila
prioridade (como o próprio nome sugere) usa a política de  bem formado: o atributo length deve ser o número de nós na
enfileiramento por prioridade.                             fila, e o último nó deve ter seu atributo next igual a None.
                                                           Estude o método até ficar convencido de que ele preserva
                                                           ambas invariantes.
19.1 Um TDA Fila
                                                                 19.3 Características de performance
O TDA Fila é definido pelas seguintes operações:
 __init__ Inicializar uma nova fila vazia.            Quando invocamos um método, normalmente não estamos
                                                      preocupados com os detalhes da sua implementação. Porém,
    insert Adicionar um novo item à fila.             há um certo "detalhe" que pode ser bom conhecer: as
                                                      características de performance do método. Quanto tempo leva,
  remove Remover e retornar um item da fila. O item e como o tempo de execução muda à medida em que aumenta
           retornado é o que foi adicionado primeiro. o número de itens da coleção?
 isEmpty Checar se a fila está vazia.                                Primeiro, olhe para remove. Não há laços ou
                                                      chamadas de função aqui, o que sugere que o tempo de
                                                      execução desse método é sempre o mesmo, toda vez. Um
                                                      método assim é chamado de operação de tempo constante.
19.2 Fila encadeada                                   Na verdade, o método pode ser ligeiramente mais rápido
                                                      quando a lista está vazia, uma vez que ele pula o corpo da
A primeira implementação que vamos ver de um TDA Fila condicional, mas essa diferença não é significativa. XXX: o
chama-se fila encadeada porque é feita de objetos Nós condicional só aparece na re-implementação do método na
encadeados. A definição da classe é a seguinte:       classe ImprovedQueue, p.200; essa inconsistência pode ser
                                                      conferida também nas páginas 198-199 do livro original (PDF
class Queue:                                          e impresso).
  def __init__(self):                                                          A performance de insert é muito diferente. No
    self.length = 0                                              caso geral, temos de percorrer a lista para achar o último
    self.head = None                                             elemento.
                                                                               Este percurso leva um tempo proporcional à
  def isEmpty(self):                                             extensão da lista. Uma vez que o tempo de execução é uma
    return (self.length == 0)                                    função linear da extensão, dizemos que este método opera em
                                                                 tempo linear. Isso é bem ruim, se comparado com o tempo
  def insert(self, cargo):                                       constante.
    node = Node(cargo)
    node.next = None
    if self.head == None:                                        19.4 Fila encadeada aprimorada
      # if list is empty the new node goes first
      self.head = node                                           Queremos uma implementação do TDA Fila que possa


                                                   Capítulo 19: Filas #87
Como pensar como um cientista da Computação usando Python

realizar todas as operações em tempo constante. Uma maneira
de fazer isso é modificar a classe Fila, de modo que ela 19.5 Fila por prioridade
mantenha a referência tanto ao primeiro quanto ao último nó,
como mostra a figura:                                        O TDA Fila por Prioridade tem a mesma interface que o TDA
                                                             Fila, mas semântica diferente. Mais uma vez, a interface é a
                                                             seguinte:
                                                                __init__ Inicializar uma nova fila vazia.
                                                                   insert Adicionar um novo item à fila.
                                                                 remove Remover e retornar um item da fila. O item
                                                                        retornado é aquele que tiver maior prioridade.
             A implementação de ImprovedQueue tem essa           isEmpty Checar se a fila está vazia.
cara:
                                                                              A diferença semântica é que o item removido da
class ImprovedQueue:                                           fila não é necessariamente o que foi incluído primeiro e, sim,
  def __init__(self):                                          o que tem maior prioridade. Que prioridades são essas e como
    self.length = 0
                                                               elas se comparam umas com as outras não é especificado pela
                                                               implementação Fila por Prioridade. Isso depende de quais
    self.head   = None                                         itens estão na fila.
    self.last   = None
                                                                             Por exemplo, se os itens da fila tiverem nome,
   def isEmpty(self):
                                                               podemos escolhê-los por ordem alfabética. Se for a pontuação
                                                               de um jogo de boliche, podemos ir da maior para a menor,
     return (self.length == 0)                                 mas se for pontuação de golfe, teríamos que ir da menor para
             Até agora, a única mudança é o atributo last. Ele a maior. Se é possível comparar os itens da fila, é possível
é usado nos métodos insert e remove:                           achar e remover o que tem maior prioridade. Essa
                                                               implementação da Fila por Prioridade tem como atributo uma
class ImprovedQueue:                                           lista Python chamada items, que contém os itens da fila.
  # ...
  def insert(self, cargo):                                     class PriorityQueue:
    node = Node(cargo)                                           def __init__(self):
    node.next = None                                               self.items = []
    if self.length == 0:
      #if list is empty, the new node is head & last             def isEmpty(self):
      self.head = self.last = node                                 return self.items == []
    else:
      # find the last node                                       def insert(self, item):
      last = self.last                                             self.items.append(item)
      # append the new node                                                 O método de inicialização, isEmpty, e insert são
      last.next = node                                         apenas uma fachada para operações básicas de lista. O único
                                                               método interessante é remove:
      self.last = node
    self.length = self.length + 1                              class PriorityQueue:
             Uma vez que last não perde de vista o ultimo        # ...
nó, não é necessário buscá-lo. Como resultado, esse método       def remove(self):
tem tempo constante.                                               maxi = 0
             Mas essa rapidez tem preço. É preciso adicionar       for i in range(1,len(self.items)):
um caso especial a remove, para configurar last para None            if self.items[i] > self.items[maxi]:
quando o ultimo nó é removido:                                         maxi = i
                                                                   item = self.items[maxi]
class ImprovedQueue:
                                                                   self.items[maxi:maxi+1] = []
  #...
                                                                   return item
  def remove(self):
    cargo     = self.head.cargo                                              No início de cada iteração, maxi armazena o
                                                               índice do maior item (a prioridade mais alta de todas) que
    self.head = self.head.next                                 vimos até agora. A cada volta do laço, o programa compara o
    self.length = self.length - 1                              i-ésimo item ao campeão. Se o novo item for maior, maxi
    if self.length == 0:                                       recebe o valor de i.
      self.last = None
                                                                         Quando o comando for se completa, maxi é o
    return cargo                                              índice do maior item. Esse item é removido da lista e
             Essa implementação é mais complicada que a retornado.
primeira, e mais difícil de se demonstrar que está correta. A
vantagem é que o objetivo foi atingido -- tanto insert quanto            Vamos testar a implementação:
remove` são operações de tempo constante.
                                                               >>> q = PriorityQueue()
                  Como exercício, escreva uma implementação >>> q.insert(11)
do TDA Fila usando uma lista nativa do Python. Compare a >>> q.insert(12)
performance dessa implementação com a de ImprovedQueue, >>> q.insert(14)
para filas de diversos comprimentos.
                                                               >>> q.insert(13)
                                                               >>> while not q.isEmpty(): print q.remove()
                                                               14


                                                 Capítulo 19: Filas #88
Como pensar como um cientista da Computação usando Python

13                                                            >>> pq = PriorityQueue()
12                                                            >>> pq.insert(tiger)
11                                                            >>> pq.insert(phil)
              Se a fila contém números ou strings simples,    >>> pq.insert(hal)
eles são removidas em ordem numérica decrescente ou           >>> while not pq.isEmpty(): print pq.remove()
alfabética invertida (de Z até A). Pyhton consegue achar o    Tiger Woods    : 61
maior inteiro ou string porque consegue compará-los usando    Hal Sutton     : 69
os operadores de comparação nativos.
                                                              Phil Mickelson : 72
               Se a fila contém objetos de outro tipo, os                  Como exercício, escreva uma implementação do
objetos têm que prover um método __cmp__. Quando remove        TDA Fila por Prioridade usando uma lista encadeada.
usa o operador > para comparar dois itens, o método __cmp__    Mantenha a lista em ordem para que a remoção seja uma
de um dos itens é invocado, recebendo o segundo item como      operação de tempo constante. Compare a performance dessa
argumento. Desde que o método __cmp__ funcione de forma        implementação com a implementação usando uma lista
consistente, a Fila por Prioridade vai funcionar.              nativa do Python.


19.6 A classe Golfer                                          19.7 Glossário
Como exemplo de um objeto com uma definição não-usual de           fila (queue) Conjunto de objetos ordenados esperando
prioridade, vamos implementar uma classe chamada Golfer                         algum tipo de serviço.
(golfista), que mantém registro dos nomes e da pontuação de
golfistas. Como sempre, começamos definindo __init__ e           Fila (Queue) TAD (Tipo Abstrato de Dado) que realiza
__str__:                                                                      operações comuns de acontecerem em
                                                                              uma fila.
class Golfer:
  def __init__(self, name, score):                                   política de As regras que determinam qual membro
    self.name = name                                            enfileiramento de uma fila é o próximo a ser removido.
    self.score= score                                                (queueing
                                                                         policy)
  def __str__(self):                                                     FIFO "First In, First Out," (primeiro a entrar,
    return "%-16s: %d" % (self.name, self.score)                              primeiro a sair) política de enfileiramento
              O método __str__ usa o operador de formato                      em que o primeiro membro a chegar é o
para colocar nomes e pontuações em colunas arrumadas.                         primeiro a ser removido.

               Em seguida, definimos uma versão de __cmp__,            fila por Política de enfileiramento em que cada
ma qual a pontuação mais baixa fica com prioridade máxima.         prioridade membro tem uma prioridade, determinada
Como sempre, __cmp__ retorna 1 se self é "maior que" other,   (priority queue) por fatores externos. O membro com a
-1 se self é "menor que" other, e 0 se eles são iguais.                         maior prioridade é o primeiro a ser
                                                                                removido.
class Golfer:                                                         Fila por TAD que define as operações comuns de
  #...                                                             Prioridade acontecerem em uma fila por prioridade.
  def __cmp__(self, other):                                          (Priority
    if self.score < other.score: return 1        # less               Queue)
is more
                                                                fila encadeada Implementação de uma fila usando uma
    if self.score > other.score: return -1                      (linked queue) lista encadeada.
    return 0
             Agora estamos prontos para testar a fila por tempo constante Operação cujo tempo de execução não
prioridade com a classe Golfer:                            (constant time) depende do tamanho da estrutura de
                                                                           dados.
>>> tiger = Golfer("Tiger Woods",   61)
                                                                 tempo linear Operação cujo tempo de execução é uma
>>> phil = Golfer("Phil Mickelson", 72)                          (linear time) função linear do tamanho da estrutura de
>>> hal   = Golfer("Hal Sutton",    69)                                        dados.
>>>




                                                Capítulo 19: Filas #89
Como pensar como um cientista da Computação usando Python


                                                  Capítulo 20: Árvores


Como listas ligadas, árvores são constituídas de células. Uma
espécie comum de árvores é a árvore binária, em que cada              20.1 Construindo árvores
célula contém referências a duas outras células (possivelmente
nulas). Tais referências são chamadas de subárvore esquerda e         O processo de montar uma árvore é similar ao processo de
direita. Como as células de listas ligadas, as células de árvores     montar uma lista ligada. cada invocação do construtor cria
também contém uma carga. Um diagrama de estados para uma              uma célula.
árvore pode aparecer assim:
                                                                      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
                                                                      parâmetros left e right devem ser células. left e right são
                                                                      opcionais; o valor default é None.
                                                                                   Para imprimir uma célula, imprimimos apenas a
                                                                      sua carga.
                                                                                   Uma forma de construir uma árvore é de baixo
                                                                      para cima. Aloque os filhos primeiro:
                                                                      left = Tree(2)
                                                                      right = Tree(3)
                                                                                   Em seguida crie a célula pai e ligue ela a seus
                                                                      filhos:
              Para evitar a sobrecarga da figura, nós
frequentemente omitimos os Nones.                              tree = Tree(1, left, right);
                                                                           Podemos     escrever   este    código    mais
              O topo da árvore (a célula à qual o apontador concisamente encaixando as invocações do construtor:
tree se refere) é chamada de raiz. Seguindo a metáfora das
árvores, as outras células são chamadas de galhos e as células >>> tree = Tree(1, Tree(2), Tree(3))
nas pontas contendo as referências vazia são chamadas de                   De qualquer forma, o resultado é a árvore que
folhas. Pode parecer estranho que desenhamos a figura com a apareceu no início do capítulo.
raiz em cima e as folhas em baixo, mas isto nem será a coisa
mais estranha.
               Para piorar as coisas, cientistas da computação 20.2 Percorrendo árvores
misturam outra metáfora além da metáfora biológica - a árvore
genealógica. Uma célula superior pode ser chamada de pai e
as células a que ela se refere são chamadas de seus filhos. Cada vez que Você vê uma nova estrutura de dados, sua
Células com o mesmo pai são chamadas de irmãos.                 primeira pergunta deveria ser "Como eu percorro esta
                                                                estrutura?" A forma mais natural de percorrer uma árvore é
               Finalmente, existe também o vocabulário fazer o percurso recursivamente. Por exemplo, se a árvore
geométrico para falar de árvores. Já mencionamos esquerda e contém inteiros na carga, a função abaixo retorna a soma das
direita, mas existem também as direções "para cima" (na cargas:
direção da raiz) e "para baixo" (na direção dos filhos/folhas).
Ainda nesta terminologia, todas as células situadas à mesma def total(tree) :
distância da raiz constituem um nível da árvore.                   if tree == None : return 0
                                                                        return total(tree.left) + total(tree.right) +
             Provavelmente não precisamos                 de   três
metáforas para falar de árvores, mas aí elas estão.                   tree.cargo
                                                                          O caso base é a árvore vazia, que não contém
              Como listas ligadas, árvores são estruturas de nenhuma carga, logo a soma das cargas é 0. O passo recursivo
dados recursivas já que elas são definidas recursivamente:   faz duas chamadas recursivas para achar a soma das cargas
                                                             das subárvores dos filhos. Ao finalizar a chamada recursiva,
              Uma árvore é                                   adicionamos a carga do pai e devolvemos o valor total.
      ● a árvore vazia, representada por None, ou
      ● uma célula que contém uma referência a um objeto (a
         carga da célula) e duas referências a árvores.               20.3 Árvores de expressões
                                                                      Uma árvore é uma forma natural para representar a estrutura
                                                                      de uma expressão. Ao contrário de outras notações, a árvore
                                                                      pode representar a computação de forma não ambígua. Por
                                                                      exemplo, a expressão infixa 1 + 2 * 3 é ambígua, a menos que

                                                   Capítulo 20: Árvores #90
Como pensar como um cientista da Computação usando Python

saibamos que a multiplicação é feita antes da adição.  árvore numa ordem diferente, Você produzirá expressões
                                                       numa notação diferente. Por exemplo, se Você imprime
           A árvore de expressão seguinte representa a subárvores primeiro e depois a raiz, Você terá:
mesma computação:
                                                               def printTreePostorder(tree) :
                                                                 if tree == None : return
                                                                 printTreePostorder(tree.left)
                                                                 printTreePostorder(tree.right)
                                                                 print tree.cargo,
                                                                            O resultado, 1 2 3 * +, está na notação pósfixa!
                                                               Esta ordem de percurso é chamada de pósordem.
                                                                             Finalmente, para percorrer uma árvore em
                                                               inordem, Você imprime a subárvore esquerda, depois a raiz e
                                                               depois a subárvore direita:
                                                               def printTreeInorder(tree) :
                                                                 if tree == None : return
                                                                 printTreeInorder(tree.left)
                                                                 print tree.cargo,
                                                                 printTreeInorder(tree.right)
                                                                             O resultado é 1 + 2 * 3, que é a expressão na
                                                               notação infixa.
                                                                             Para sermos justos, devemos lembrar que
             As células de uma árvore de expressão podem       acabamos de omitir uma complicação importante. algumas
ser operandos como 1 e 2 ou operações como + e *. As células   vezes quando escrevemos expressões na notação infixa
contendo operandos são folhas; aquelas contendo operações      devemos usar parêntesis para prescrever a ordem das
devem ter referências aos seus operandos. (Todos os nossos     operações. Ou seja, um percurso em inordem não é suficiente
operandos são binários, significando que eles tem exatamente   para gerar a expressão infixa.
dois operandos.)
                                                                             Ainda assim, com alguns aperfeiçoamentos, a
              Podemos construir árvores assim:                 árvore de expressão e os três modos recursivos de percurso
                                                               resultam em algoritmos para transformar expressões de uma
>>> tree = Tree('+', Tree(1), Tree('*', Tree(2),               notação para outra.
Tree(3)))
              Examinando a figura, não há dúvida quanto à                   Como        um        exercício,     modifique
ordem das operações; a multiplicação é feita primeiro para      `printTreeInorder` de modo que ele coloque parêntesis em
calcular o segundo operando da adição.                          volta de cada operador e par de operandos. A saída é correta
                                                                e não ambígua? Os parêntesis são sempre necessários?
               Árvores de expressão tem muitos usos. O
exemplo neste capítulo usa árvores para traduzir expressões              Se percorrermos uma árvore em inordem e
para as notações pósfixa, prefixa e infixa. Árvores similares acompanharmos em qual nível na árvore estamos, podemos
são usadas em compiladores para analisar sintaticamente, gerar uma representação gráfica da árvore:
otimizar e traduzir programas.
                                                               def printTreeIndented(tree, level=0) :
                                                                 if tree == None : return
                                                                 printTreeIndented(tree.right, level+1)
20.4 Percurso de árvores                                         print ' '*level + str(tree.cargo)
                                                                 printTreeIndented(tree.left, level+1)
Podemos percorrer uma árvore de expressão e imprimir o seu              O parâmetro level registra aonde estamos na
conteúdo como segue:                                       árvore. Por 'default', o nível inicialmente é zero. A cada
def printTree(tree) :                                      chamada recursiva repassamos level+1 porque o nível do filho
  if tree == None : return
                                                           é sempre um a mais do que o nível do pai. Cada item é
                                                           indentado dois espaços por nível. Para o nosso exemplo
  print tree.cargo,                                        obtemos:
  printTree(tree.left)
  printTree(tree.right)                                        >>> printTreeIndented(tree)
             Em outras palavras, para imprimir uma árvore,      3
imprima primeiro o conteúdo da raiz, em seguida imprima      *
toda a subárvore esquerda e finalmente imprima toda a           2
subárvore direita. Esta forma de percorrer uma árvore é +
chamada de préordem, porque o conteúdo da raiz aparece       1
antes dos conteúdos dos filhos. Para o exemplo anterior, a
saída é:                                                                 Se Você deitar a saída acima Você enxerga uma
                                                           versão simplificada da figura original.
>>> tree = Tree('+', Tree(1), Tree('*', Tree(2),
Tree(3)))
>>> printTree(tree)                                            20.5 Construindo uma árvore de expressão
+ 1 * 2 3
             Esta notação é diferente tanto da notação Nesta seção analisamos expressões infixas e construímos as
pósfixa quanto da infixa; é uma notação chamada de prefixa, árvores de expressã correspondentes. Por exemplo, para a
em que os operadores aparecem antes dos seus operandos.     expressão (3+7)*9 resultará a seguinte árvore:
             Você pode suspeitar que se Você percorre a

                                                  Capítulo 20: Árvores #91
Como pensar como um cientista da Computação usando Python

                                                                                 Em seguida precisaremos da função getProduct,
                                                                   que constrói uma árvore de expressão para produtos. Os dois
                                                                   operandos de um produto simples são números, como em 3 *
                                                                   7.
                                                                                Segue uma versão 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ó célula atribuímos o
                                                          primeiro operando a à`. Se o próximo caractere for *, vamos
            Note que simplificamos o diagrama omitindo os buscar o segundo número e construir a árvore com a, b e o
nomes dos campos.                                         operador.
             O analisador que escreveremos aceitará                      Se o caractere seguinte for qualquer outra coisa,
expressões que incluam números, parêntesis e as operações + então simplesmente devolvemos uma célula folha com a.
e *. Vamos supor que a cadeia de entrada já foi tokenizada Seguem dois exemplos:
numa lista do Python. A lista de tokens para a expressão
(3+7)*9 é:                                                  >>> tokenList = [9, '*', 11, 'end']
                                                                   >>> tree = getProduct(tokenList)
['(', 3, '+', 7, ')', '*', 9, 'end']                               >>> printTreePostorder(tree)
              O token final end é prático para prevenir que o      9 11 *
analisador tente buscar mais dados após o término da lista.
               A título de um exercício, escreva uma função
  que recebe uma expressão na forma de uma cadeia e >>> tokenList = [9, '+', 11, 'end']
  devolve a lista de tokens.                                     >>> tree = getProduct(tokenList)
               A primeira função que escreveremos é getToken >>> printTreePostorder(tree)
que recebe como parâmetros uma lista de tokens e um token 9
esperado. Ela compara o token esperado com o o primeiro                        O segundo exemplo sugere que nós
token da lista: se eles batem a função remove o token da lista e consideramos um operando unitário como uma espécie de
devolve um valor verdadeiro, caso contrário a função devolve produto. Esta definição de "produto" talvez não seja intuitiva,
um valor falso:                                                  mas ela será útil.
def getToken(tokenList, expected) :                                              Agora tratamos produtos compostos, como 3 * 5
  if tokenList[0] == expected :                                    * 13. Encaramos esta expressão como um produto de
    tokenList[0:1] = []   # remove the token                       produtos, mais precisamente como 3 * (5 * 13). A árvore
                                                                   resultante é:
    return 1
  else :
    return 0
               Já que tokenList refere a um objeto mutável, as
alterações feitas aqui são visíveis para qualquer outra variável
que se refira ao mesmo objeto.
             A próxima função, getNumber, trata de
operandos. Se o primeiro token na tokenList for um número
então getNumber o remove da lista e devolve uma célula folha
contendo o número; caso contrário 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, convém testar getNumber
isoladamente. Atribuímos uma lista de números a tokenList,
extraímos o primeiro, imprimimos o resultado e imprimimos o             Com uma pequena alteração em getProduct,
que resta na lista de tokens:                               podemos acomodar produtos arbitrariamente longos:
>>> tokenList = [9, 11, 'end']                                     def getProduct(tokenList) :
>>> x = getNumber(tokenList)                                         a = getNumber(tokenList)
>>> printTreePostorder(x)                                            if getToken(tokenList, '*') :
9                                                                      b = getProduct(tokenList)       # this line changed
>>> print tokenList                                                    return Tree('*', a, b)
[11, 'end']                                                          else :



                                                  Capítulo 20: Árvores #92
Como pensar como um cientista da Computação usando Python

      return a                                              idéia dar a getNumber um nome mais descritivo do seu novo
               Em outras palavras, um produto pode ser um papel.
singleton ou uma árvore com * na raiz, que tem um número
como filho esquerdo e um produto como filho direito. Este
tipo de definição recursiva devia começar a ficar familiar. 20.6 Manipulando erros
              Testemos a nova versão com um produto
composto:                                                          Ao longo do analisador sintático tínhamos suposto que as
                                                                   expressões (de entrada) são bem formadas. Por exemplo,
>>>   tokenList = [2, '*', 3, '*', 5 , '*', 7, 'end']              quando atingimos o fim de uma subexpressão, supomos que o
>>>   tree = getProduct(tokenList)                                 próximo caractere é um facha parêntesis. Caso haja um erro e
>>>   printTreePostorder(tree)                                     o próximo caractere seja algo diferente, devemos tratar disto.
2 3   5 7 * * *
                                                                   def getNumber(tokenList) :
               A seguir adicionamos o tratamento de somas.           if getToken(tokenList, '(') :
De novo, usamos uma definição de "soma" que é ligeiramente
não intuitiva. Para nós, uma soma pode ser uma árvore com +            x = getSum(tokenList)
na raiz, que tem um produto como filho esquerdo e uma soma             if not getToken(tokenList, ')'):
como filho direito. Ou, uma soma pode ser simplesmente um                raise 'BadExpressionError',
produto.                                                                 'missing parenthesis'
              Se Você está disposto a brincar com esta                 return x
definição, ela tem uma propriedade interessante: podemos             else :
representar qualquer expressão (sem parêntesis) como uma               # the rest of the function omitted
soma de produtos. Esta propriedade é a base do nosso                       O comando raise cria uma exceção; neste caso
algoritmo de análise sintática.                               criamos um novo tipo de exceção, chamada de
              getSum tenta construir a árvore com um produto BadExpressionError. Se ano traceback, manipular a exceção,
                                                              uma das outras funções
                                                                                      função que chamou getNumber, ou
à esquerda e uma soma à direita. Mas, se ele não encontra uma então o programa pode continuar. caso contrário Python vai
+, ele simplesmente constrói um produto.                      imprimir uma mensagem de erro e terminará o processamento
def getSum(tokenList) :                                       em seguida.
  a = getProduct(tokenList)                                                    A título de exercício, encontre outros locais nas
  if getToken(tokenList, '+') :                                     funções criadas onde erros possam ocorrer e adiciona
    b = getSum(tokenList)                                           comandos ``raise`` apropriados. Teste seu código com
    return Tree('+', a, b)                                          expressões mal formadas.
  else :
    return a
              Vamos testar o algoritmo com 9 * 11 + 5 * 7:         20.7 A árvore dos animais
>>> tokenList = [9, '*', 11, '+', 5, '*', 7, 'end']                Nesta seção, desenvolvemos um pequeno programa que usa
>>> tree = getSum(tokenList)                                       uma árvore para representar uma base de conhecimento.
>>> printTreePostorder(tree)
9 11 * 5 7 * +                                                         O programa interage com o usuário para criar
                                                          uma árvore de perguntas e de nomes de animais. Segue uma
             Quase terminamos, mas ainda temos que tratar amostra da funcionalidade:
dos parêntesis. Em qualquer lugar numa expressão onde
podemos ter um número, podemos também ter uma soma Are you thinking of an animal? y
inteira envolvida entre parêntesis. Precisamos, apenas, Is it a bird? n
modificar getNumber para que ela possa tratar de What is the animals name? dog
subexpressões:
                                                                   What question would distinguish a dog from a bird?
def getNumber(tokenList) :                                         Can it fly
  if getToken(tokenList, '(') :                                    If the animal were dog the answer would be? n
    x = getSum(tokenList)      # get subexpression
    getToken(tokenList, ')') #eat closing parenthesis              Are you thinking of an animal? y
    return x                                                       Can it fly? n
  else :                                                           Is it a dog? n
    x = tokenList[0]                                               What is the animals name? cat
    if type(x) != type(0) : return None                            What question would distinguish a cat from a dog?
    tokenList[0:1] = []   # remove the token                       Does it bark
    return Tree(x, None, None)    # return a leaf                  If the animal were cat the answer would be? n
with the number
              Testemos este código com 9 * (11 + 5) * 7:           Are you thinking of an animal? y
                                                                   Can it fly? n
>>> tokenList = [9, '*', '(', 11, '+', 5, ')', '*',                Does it bark? y
7, 'end']
                                                                   Is it a dog? y
>>> tree = getSum(tokenList)
                                                                   I rule!
>>> printTreePostorder(tree)
9 11 5 + 7 * *
                                                                   Are you thinking of an animal? n
               O analisador tratou os parêntesis corretamente; a                Aqui está a árvore que este diálogo constrói:
adição é feita antes da multiplicação.
              Na versão final do programa, seria uma boa

                                                  Capítulo 20: Árvores #93
Como pensar como um cientista da Computação usando Python

                                                                     tree.setLeft(Tree(animal))
                                                                     tree.setRight(Tree(guess))
                                                                         A função yes é um auxiliar; ele imprime um
                                                             prompt e em seguida solicita do usuário uma entrada. Se a
                                                             resposta começar com y ou Y, a função devolve um valor
                                                             verdadeiro:
                                                              def yes(ques) :
                                                                from string import lower
                                                                ans = lower(raw_input(ques))
                                                                return (ans[0:1] == 'y')
                                                                           A condição do laço externo é 1`, que significa
                                                             que ele continuará até a execução de um comando break, caso
                                                             o usuário não pense num animal.
               No começo de cada rodada, o programa parte do
topo da árvore e faz a primeira pergunta. Dependendo da                     O laço while interno caminha na árvore de cima
resposta, ele segue pelo filho esquerdo ou direito e continua para baixo, guiado pelas respostas do usuário.
até chegar numa folha. Neste ponto ele arrisca um palpite. Se
o palpite não for correto, ele pergunta ao usuário o nome de                Quando uma nova célula é adicionada à árvore,
um novo animal e uma pergunta que distingue o palpite errado a nova pergunta substitui a carga e os dois filhos são o novo
do novo animal. A seguir, adiciona uma célula à árvore animal e a carga original.
contendo a nova pergunta e o novo animal.                                   Uma falha do programa é que ao sair ele
               Aqui está o código:                            esquece tudo que lhe foi cuidadosamente ensinado!
def animal() :                                                             A título de exercício, pense de várias jeitos para
                                                               salvar a árvore do conhecimento acumulado num arquivo.
   # start with a singleton                                    Implemente aquele que Você pensa ser o mais fácil.
   root = Tree("bird")

   # loop until the user quits
   while 1 :
                                                             20.8 Glossário
     print
     if not yes("Are you thinking of an animal? ") :          árvore binária Uma árvore em que cada célula tem zero,
       break                                                    (binary tree) um ou dois descendentes.
                                                                  raiz (root) A célula mais alta de uma árvore, a (única)
     # walk the tree                                                          célula de uma árvore que não tem pai.
     tree = root
                                                                  folha (leaf) Uma célula mais baixa numa árvore; uma
     while tree.getLeft() != None :                                            célula que não tem descendentes.
       prompt = tree.getCargo() + "? "
       if yes(prompt):                                           pai (parent) A célula que aponta para uma célula dada.
         tree = tree.getRight()                                  filho (child) Uma célula apontada por uma célula dada.
       else:
         tree = tree.getLeft()
                                                                      irmãos Células que tem o mesmo pai.
                                                                   (siebling)
     # make a guess                                              nível (level) Um conjunto de células equidistantes da
     guess = tree.getCargo()                                                   raiz.
     prompt = "Is it a " + guess + "? "                            operador Um operador sobre dois operandos.
     if yes(prompt) :                                                binário
       print "I rule!"                                               (binary
       continue                                                    operator)
                                                               subexpressão Uma expressão entre parêntesis que se
     # get new information                                     (subexpressio comporta como um operando simples numa
     prompt = "What is the animal's name? "                              n) expressão maior.
     animal = raw_input(prompt)                                   pré-ordem Uma forma de percorrer uma árvore
     prompt = "What question would distinguish a %s               (preorder) visitando cada célula antes dos seus filhos.
from a %s? "
     question = raw_input(prompt % (animal,guess))                   notação Uma forma de escrever uma expressão
                                                              prefixa (prefix matemática em que cada operador aparece
                                                                   notation) antes dos seus operandos.
     # add new information to the tree
     tree.setCargo(question)                                      pós-ordem Uma forma de percorrer uma árvore
     prompt = "If the animal were %s the answer would            (postorder) visitando os filhos de cada célula antes da
be? "                                                                        própria célula.
     if yes(prompt % animal) :                                     in-ordem Uma forma de percorrer uma árvore
       tree.setLeft(Tree(guess))                                   (inorder) visitando a subárvore esquerda, seguida da
       tree.setRight(Tree(animal))                                           raiz e finalmente da subárvore direita.
     else :




                                                Capítulo 20: Árvores #94
Como pensar como um cientista da Computação usando Python


                                       GNU Free Documentation License


              Version 1.2, November 2002                        matters, or of legal, commercial, philosophical, ethical or
                                                                political position regarding them.
  Copyright (C) 2000,2001,2002 Free Software
Foundation, Inc.                                                               The "Invariant Sections" are certain Secondary
  51 Franklin St, Fifth Floor, Boston, MA 02110-1301            Sections whose titles are designated, as being those of
USA                                                             Invariant Sections, in the notice that says that the Document is
  Everyone is permitted to copy and distribute
                                                                released under this License. If a section does not fit the above
                                                                definition of Secondary then it is not allowed to be designated
verbatim copies                                                 as Invariant. The Document may contain zero Invariant
  of this license document, but changing it is not              Sections. If the Document does not identify any Invariant
allowed.                                                        Sections then there are none.
                                                                                The "Cover Texts" are certain short passages of
                                                                text that are listed, as Front-Cover Texts or Back-Cover Texts,
0. PREAMBLE                                                     in the notice that says that the Document is released under this
                                                                License. A Front-Cover Text may be at most 5 words, and a
The purpose of this License is to make a manual, textbook, or Back-Cover Text may be at most 25 words.
other functional and useful document "free" in the sense of                     A "Transparent" copy of the Document means a
freedom: to assure everyone the effective freedom to copy and machine-readable copy, represented in a format whose
redistribute it, with or without modifying it, either specification is available to the general public, that is suitable
commercially or noncommercially. Secondarily, this License for revising the document straightforwardly with generic text
preserves for the author and publisher a way to get credit for editors or (for images composed of pixels) generic paint
their work, while not being considered responsible for programs or (for drawings) some widely available drawing
modifications made by others.                                   editor, and that is suitable for input to text formatters or for
              This License is a kind of "copyleft", which automatic translation to a variety of formats suitable for input
means that derivative works of the document must themselves to text formatters. A copy made in an otherwise Transparent
be free in the same sense. It complements the GNU General file format whose markup, or absence of markup, has been
Public License, which is a copyleft license designed for free arranged to thwart or discourage subsequent modification by
software.                                                       readers is not Transparent. An image format is not
                                                                Transparent if used for any substantial amount of text. A copy
              We have designed this License in order to use it that is not "Transparent" is called "Opaque".
for manuals for free software, because free software needs
free documentation: a free program should come with manuals                     Examples of suitable formats for Transparent
providing the same freedoms that the software does. But this copies include plain ASCII without markup, Texinfo input
License is not limited to software manuals; it can be used for format, LaTeX input format, SGML or XML using a publicly
any textual work, regardless of subject matter or whether it is available DTD, and standard-conforming simple HTML,
published as a printed book. We recommend this License PostScript or PDF designed for human modification.
principally for works whose purpose is instruction or Examples of transparent image formats include PNG, XCF
reference.                                                      and JPG. Opaque formats include proprietary formats that can
                                                                be read and edited only by proprietary word processors,
                                                                SGML or XML for which the DTD and/or processing tools
                                                                are not generally available, and the machine-generated
1. APPLICABILITY AND DEFINITIONS                                HTML, PostScript or PDF produced by some word processors
                                                                for output purposes only.
This License applies to any manual or other work, in any                        The "Title Page" means, for a printed book, the
medium, that contains a notice placed by the copyright holder title page itself, plus such following pages as are needed to
saying it can be distributed under the terms of this License. hold, legibly, the material this License requires to appear in
Such a notice grants a world-wide, royalty-free license, the title page. For works in formats which do not have any
unlimited in duration, to use that work under the conditions title page as such, "Title Page" means the text near the most
stated herein. The "Document", below, refers to any such prominent appearance of the work's title, preceding the
manual or work. Any member of the public is a licensee, and beginning of the body of the text.
is addressed as "you". You accept the license if you copy,
modify or distribute the work in a way requiring permission                     A section "Entitled XYZ" means a named
under copyright law.                                            subunit of the Document whose title either is precisely XYZ
                                                                or contains XYZ in parentheses following text that translates
              A "Modified Version" of the Document means XYZ in another language. (Here XYZ stands for a specific
any work containing the Document or a portion of it, either section name mentioned below, such as "Acknowledgements",
copied verbatim, or with modifications and/or translated into "Dedications", "Endorsements", or "History".) To "Preserve
another language.                                               the Title" of such a section when you modify the Document
              A "Secondary Section" is a named appendix or a means that it remains a section "Entitled XYZ" according to
front-matter section of the Document that deals exclusively this definition.
with the relationship of the publishers or authors of the                       The Document may include Warranty
Document to the Document's overall subject (or to related Disclaimers next to the notice which states that this License
matters) and contains nothing that could fall directly within applies to the Document. These Warranty Disclaimers are
that overall subject. (Thus, if the Document is in part a considered to be included by reference in this License, but
textbook of mathematics, a Secondary Section may not only as regards disclaiming warranties: any other implication
explain any mathematics.) The relationship could be a matter that these Warranty Disclaimers may have is void and has no
of historical connection with the subject or with related effect on the meaning of this License.

                                          GNU Free Documentation License #95
Como pensar como um cientista da Computação usando Python


                                                                       those of previous versions (which should, if there
2. VERBATIM COPYING                                                    were any, be listed in the History section of the
                                                                       Document). You may use the same title as a previous
You may copy and distribute the Document in any medium,                version if the original publisher of that version gives
either commercially or noncommercially, provided that this             permission.
License, the copyright notices, and the license notice saying      •   B. List on the Title Page, as authors, one or more
this License applies to the Document are reproduced in all             persons or entities responsible for authorship of the
copies, and that you add no other conditions whatsoever to             modifications in the Modified Version, together with
those of this License. You may not use technical measures to           at least five of the principal authors of the Document
obstruct or control the reading or further copying of the copies       (all of its principal authors, if it has fewer than five),
you make or distribute. However, you may accept                        unless they release you from this requirement.
compensation in exchange for copies. If you distribute a large     •   C. State on the Title page the name of the publisher
enough number of copies you must also follow the conditions            of the Modified Version, as the publisher.
in section 3.                                                      •   D. Preserve all the copyright notices of the
                                                                       Document.
              You may also lend copies, under the same             •   E. Add an appropriate copyright notice for your
conditions stated above, and you may publicly display copies.          modifications adjacent to the other copyright notices.
                                                                   •   F. Include, immediately after the copyright notices, a
                                                                       license notice giving the public permission to use the
3. COPYING IN QUANTITY                                                 Modified Version under the terms of this License, in
                                                                       the form shown in the Addendum below.
If you publish printed copies (or copies in media that             •   G. Preserve in that license notice the full lists of
commonly have printed covers) of the Document, numbering               Invariant Sections and required Cover Texts given in
more than 100, and the Document's license notice requires              the Document's license notice.
Cover Texts, you must enclose the copies in covers that carry,     •   H. Include an unaltered copy of this License.
clearly and legibly, all these Cover Texts: Front-Cover Texts      •   I. Preserve the section Entitled "History", Preserve its
on the front cover, and Back-Cover Texts on the back cover.            Title, and add to it an item stating at least the title,
Both covers must also clearly and legibly identify you as the          year, new authors, and publisher of the Modified
publisher of these copies. The front cover must present the full       Version as given on the Title Page. If there is no
title with all words of the title equally prominent and visible.       section Entitled "History" in the Document, create
You may add other material on the covers in addition.                  one stating the title, year, authors, and publisher of
Copying with changes limited to the covers, as long as they            the Document as given on its Title Page, then add an
preserve the title of the Document and satisfy these                   item describing the Modified Version as stated in the
conditions, can be treated as verbatim copying in other                previous sentence.
respects.                                                          •   J. Preserve the network location, if any, given in the
                                                                       Document for public access to a Transparent copy of
               If the required texts for either cover are too          the Document, and likewise the network locations
voluminous to fit legibly, you should put the first ones listed        given in the Document for previous versions it was
(as many as fit reasonably) on the actual cover, and continue          based on. These may be placed in the "History"
the rest onto adjacent pages.                                          section. You may omit a network location for a work
               If you publish or distribute Opaque copies of the       that was published at least four years before the
Document numbering more than 100, you must either include              Document itself, or if the original publisher of the
a machine-readable Transparent copy along with each Opaque             version it refers to gives permission.
copy, or state in or with each Opaque copy a computer-             •   K. For any section Entitled "Acknowledgements" or
network location from which the general network-using public           "Dedications", Preserve the Title of the section, and
has access to download using public-standard network                   preserve in the section all the substance and tone of
protocols a complete Transparent copy of the Document, free            each of the contributor acknowledgements and/or
of added material. If you use the latter option, you must take         dedications given therein.
reasonably prudent steps, when you begin distribution of           •   L. Preserve all the Invariant Sections of the
Opaque copies in quantity, to ensure that this Transparent             Document, unaltered in their text and in their titles.
copy will remain thus accessible at the stated location until at       Section numbers or the equivalent are not considered
least one year after the last time you distribute an Opaque            part of the section titles.
copy (directly or through your agents or retailers) of that        •   M. Delete any section Entitled "Endorsements". Such
edition to the public.                                                 a section may not be included in the Modified
                                                                       Version.
             It is requested, but not required, that you contact   •   N. Do not retitle any existing section to be Entitled
the authors of the Document well before redistributing any             "Endorsements" or to conflict in title with any
large number of copies, to give them a chance to provide you           Invariant Section.
with an updated version of the Document.                           •   O. Preserve any Warranty Disclaimers.
                                                                             If the Modified Version includes new front-
                                                              matter sections or appendices that qualify as Secondary
4. MODIFICATIONS                                              Sections and contain no material copied from the Document,
                                                              you may at your option designate some or all of these sections
You may copy and distribute a Modified Version of the as invariant. To do this, add their titles to the list of Invariant
Document under the conditions of sections 2 and 3 above, Sections in the Modified Version's license notice. These titles
provided that you release the Modified Version under must be distinct from any other section titles.
precisely this License, with the Modified Version filling the
role of the Document, thus licensing distribution and                        You     may    add    a    section     Entitled
modification of the Modified Version to whoever possesses a "Endorsements", provided it contains nothing but
copy of it. In addition, you must do these things in the endorsements of your Modified Version by various parties--
Modified Version:                                             for example, statements of peer review or that the text has
                                                              been approved by an organization as the authoritative
     • A. Use in the Title Page (and on the covers, if any) a definition of a standard.
         title distinct from that of the Document, and from

                                           GNU Free Documentation License #96
Como pensar como um cientista da Computação usando Python

               You may add a passage of up to five words as a
Front-Cover Text, and a passage of up to 25 words as a Back-      7. AGGREGATION WITH INDEPENDENT
Cover Text, to the end of the list of Cover Texts in the
Modified Version. Only one passage of Front-Cover Text and        WORKS
one of Back-Cover Text may be added by (or through
arrangements made by) any one entity. If the Document              A compilation of the Document or its derivatives with other
already includes a cover text for the same cover, previously       separate and independent documents or works, in or on a
added by you or by arrangement made by the same entity you         volume of a storage or distribution medium, is called an
are acting on behalf of, you may not add another; but you may      "aggregate" if the copyright resulting from the compilation is
replace the old one, on explicit permission from the previous      not used to limit the legal rights of the compilation's users
publisher that added the old one.                                  beyond what the individual works permit. When the
                                                                   Document is included in an aggregate, this License does not
                The author(s) and publisher(s) of the Document apply to the other works in the aggregate which are not
do not by this License give permission to use their names for themselves derivative works of the Document.
publicity for or to assert or imply endorsement of any
Modified Version.                                                                If the Cover Text requirement of section 3 is
                                                                   applicable to these copies of the Document, then if the
                                                                   Document is less than one half of the entire aggregate, the
                                                                   Document's Cover Texts may be placed on covers that bracket
5. COMBINING DOCUMENTS                                             the Document within the aggregate, or the electronic
                                                                   equivalent of covers if the Document is in electronic form.
You may combine the Document with other documents Otherwise they must appear on printed covers that bracket the
released under this License, under the terms defined in section whole aggregate.
4 above for modified versions, provided that you include in
the combination all of the Invariant Sections of all of the
original documents, unmodified, and list them all as Invariant
Sections of your combined work in its license notice, and that 8. TRANSLATION
you preserve all their Warranty Disclaimers.
                                                                   Translation is considered a kind of modification, so you may
                The combined work need only contain one copy distribute translations of the Document under the terms of
of this License, and multiple identical Invariant Sections may section 4. Replacing Invariant Sections with translations
be replaced with a single copy. If there are multiple Invariant requires special permission from their copyright holders, but
Sections with the same name but different contents, make the you may include translations of some or all Invariant Sections
title of each such section unique by adding at the end of it, in in addition to the original versions of these Invariant Sections.
parentheses, the name of the original author or publisher of You may include a translation of this License, and all the
that section if known, or else a unique number. Make the same license notices in the Document, and any Warranty
adjustment to the section titles in the list of Invariant Sections Disclaimers, provided that you also include the original
in the license notice of the combined work.                        English version of this License and the original versions of
                In the combination, you must combine any those notices and disclaimers. In case of a disagreement
sections Entitled "History" in the various original documents, between the translation and the original version of this License
forming one section Entitled "History"; likewise combine any or a notice or disclaimer, the original version will prevail.
sections Entitled "Acknowledgements", and any sections                           If a section in the Document is Entitled
Entitled "Dedications". You must delete all sections Entitled "Acknowledgements", "Dedications", or "History", the
"Endorsements."                                                    requirement (section 4) to Preserve its Title (section 1) will
                                                                   typically require changing the actual title.

6. COLLECTIONS OF DOCUMENTS
                                                                  9. TERMINATION
You may make a collection consisting of the Document and
other documents released under this License, and replace the   You may not copy, modify, sublicense, or distribute the
individual copies of this License in the various documents     Document except as expressly provided for under this
with a single copy that is included in the collection, providedLicense. Any other attempt to copy, modify, sublicense or
that you follow the rules of this License for verbatim copying distribute the Document is void, and will automatically
of each of the documents in all other respects.                terminate your rights under this License. However, parties
              You may extract a single document from such a who have received copies, or rights, from you under this
collection, and distribute it individually under this License, License will not have their licenses terminated so long as such
provided you insert a copy of this License into the extracted parties remain in full compliance.
document, and follow this License in all other respects
regarding verbatim copying of that document.
                                                                  10. FUTURE REVISIONS OF THIS LICENSE
                                                                  The Free Software Foundation may publish new, revised
                                                                  versions of the GNU Free Documentation License from time
                                                                  to time. Such new versions will be similar in spirit to the
                                                                  present version, but may differ in detail to address new
                                                                  problems or concerns. See http://www.gnu.org/copyleft/.
                                                                                Each version of the License is given a
                                                                  distinguishing version number. If the Document specifies that
                                                                  a particular numbered version of this License "or any later
                                                                  version" applies to it, you have the option of following the
                                                                  terms and conditions either of that specified version or of any
                                                                  later version that has been published (not as a draft) by the
                                                                  Free Software Foundation. If the Document does not specify a

                                           GNU Free Documentation License #97
Como pensar como um cientista da Computação usando Python

version number of this License, you may choose any version
ever published (not as a draft) by the Free Software
Foundation.

How to use this License for your documents
               To use this License in a document you have
written, include a copy of the License in the document and put
the following copyright and license notices just after the title
page:
  Copyright (c) YEAR YOUR NAME.
  Permission is granted to copy, distribute and/or
modify this document
  under the terms of the GNU Free Documentation
License, Version 1.2
  or any later version published by the Free Software
Foundation;
  with no Invariant Sections, no Front-Cover Texts,
and no Back-Cover
  Texts. A copy of the license is included in the
section entitled "GNU
  Free Documentation License".


            If you have Invariant Sections, Front-Cover
Texts and Back-Cover Texts, replace the "with...Texts." line
with this:
  with the Invariant Sections being LIST THEIR
TITLES, with the
  Front-Cover Texts being LIST, and with the Back-
Cover Texts being LIST.


              If you have Invariant Sections without Cover
Texts, or some other combination of the three, merge those
two alternatives to suit the situation.
              If your document contains nontrivial examples
of program code, we recommend releasing these examples in
parallel under your choice of free software license, such as the
GNU General Public License, to permit their use in free
software.




                                           GNU Free Documentation License #98

Mais conteúdo relacionado

Mais procurados (18)

PDF
Material LINUX
Fernando Palma
 
PDF
Manual sobre a ferramenta Kate - Linux
Rafael de Brito Marques
 
PDF
Manual Moodle
Professor Emerson
 
PDF
Apostila adobe-flash-cs5
aulaemvideo
 
PDF
Apostila bnb.2014 informatica_marcio_hunecke
Eliene Meira
 
PDF
Apostila dreamweaver-cs5
aulaemvideo
 
PDF
Manual epson
Biela_123456
 
PDF
Manual word 2009
Alexandre Figueiredo
 
PDF
Php
Tiago
 
PDF
Apostila ata informatica_julio_alves
Yara Grasielle
 
PDF
Indesign cs5 help
Matheus Araujo
 
PDF
Foca linux 1
pelincha
 
PDF
BrOffice - Apostila completa
Vera Mln Silva
 
PDF
Apostila r gen melhor
Gino Andrade
 
PDF
Apostila bnb2014 cef_informatica_sergio_spolador
Eliene Meira
 
DOCX
Apostila linux educacional karen lowhany
Karen Costa
 
PDF
Ap apostila arduino-rev4
ariferreira3
 
PDF
Guia pro tools_basico
Edson Magalhaes
 
Material LINUX
Fernando Palma
 
Manual sobre a ferramenta Kate - Linux
Rafael de Brito Marques
 
Manual Moodle
Professor Emerson
 
Apostila adobe-flash-cs5
aulaemvideo
 
Apostila bnb.2014 informatica_marcio_hunecke
Eliene Meira
 
Apostila dreamweaver-cs5
aulaemvideo
 
Manual epson
Biela_123456
 
Manual word 2009
Alexandre Figueiredo
 
Php
Tiago
 
Apostila ata informatica_julio_alves
Yara Grasielle
 
Indesign cs5 help
Matheus Araujo
 
Foca linux 1
pelincha
 
BrOffice - Apostila completa
Vera Mln Silva
 
Apostila r gen melhor
Gino Andrade
 
Apostila bnb2014 cef_informatica_sergio_spolador
Eliene Meira
 
Apostila linux educacional karen lowhany
Karen Costa
 
Ap apostila arduino-rev4
ariferreira3
 
Guia pro tools_basico
Edson Magalhaes
 

Destaque (17)

PPTX
Api facebook
Felipe Costa
 
PDF
Pepe Legal Python e Babalu MongoDB, uma dupla dinâmica
FATEC São José dos Campos
 
PDF
Lista funcões e recursividade
Anielli Lemes
 
PDF
Como Python está mudando a forma de aprendizagem à distância no Brasil
Marcel Caraciolo
 
PDF
Redes Neurais e Python
pugpe
 
PDF
Estrutura de Dados Aula 08 - Recursão (conceito, utilização, exemplos)
Leinylson Fontinele
 
PPTX
Conhecendo API do Facebook
Virtualize Interatividade Digital
 
PPTX
Estrutura de dados em Java - Recursividade
Adriano Teixeira de Souza
 
PDF
import pybr12: experiencias de inclusión en la última PyCon Brazil
FATEC São José dos Campos
 
PDF
Python e Aprendizagem de Máquina (Inteligência Artificial)
Marcel Caraciolo
 
PDF
Palestra: Cientista de Dados – Dominando o Big Data com Software Livre
Ambiente Livre
 
PDF
Orientação a Objetos em Python
Luciano Ramalho
 
PDF
Python para iniciantes
richardsonlima
 
PDF
Construindo Soluções Científicas com Big Data & MapReduce
Marcel Caraciolo
 
PDF
Big Data com Python
Marcel Caraciolo
 
PDF
Computação Científica com Python, Numpy e Scipy
Marcel Caraciolo
 
PDF
Aprendendo python
Victor Marcelino
 
Api facebook
Felipe Costa
 
Pepe Legal Python e Babalu MongoDB, uma dupla dinâmica
FATEC São José dos Campos
 
Lista funcões e recursividade
Anielli Lemes
 
Como Python está mudando a forma de aprendizagem à distância no Brasil
Marcel Caraciolo
 
Redes Neurais e Python
pugpe
 
Estrutura de Dados Aula 08 - Recursão (conceito, utilização, exemplos)
Leinylson Fontinele
 
Conhecendo API do Facebook
Virtualize Interatividade Digital
 
Estrutura de dados em Java - Recursividade
Adriano Teixeira de Souza
 
import pybr12: experiencias de inclusión en la última PyCon Brazil
FATEC São José dos Campos
 
Python e Aprendizagem de Máquina (Inteligência Artificial)
Marcel Caraciolo
 
Palestra: Cientista de Dados – Dominando o Big Data com Software Livre
Ambiente Livre
 
Orientação a Objetos em Python
Luciano Ramalho
 
Python para iniciantes
richardsonlima
 
Construindo Soluções Científicas com Big Data & MapReduce
Marcel Caraciolo
 
Big Data com Python
Marcel Caraciolo
 
Computação Científica com Python, Numpy e Scipy
Marcel Caraciolo
 
Aprendendo python
Victor Marcelino
 
Anúncio

Semelhante a Cientista da computacao usando python (20)

PDF
Aula 01 python
Tiago
 
PDF
Curso python
Tiago
 
PDF
Curso python
Tiago
 
KEY
Introdução ao Python & Web Services
Dorneles Treméa
 
PDF
Livro para desenvolvedores de Python
Alberto Jorge Sardo Monteiro
 
PDF
Automacao com Python.pdf
Secont
 
PDF
O_Fantastico_Mundo_da_Linguagem_C em pdf.pdf
ArgemiroAlbuquerque
 
PDF
O mundo-da-linguagem-c
wagnerprates77
 
PDF
O fantc3a1stico-mundo-da-linguagem-c
Valdinho Pereira
 
PPTX
Python.pptx
IvairLima3
 
PDF
Pe algoritmos manhã sem1 2012
FATEC São José dos Campos
 
PDF
Python: a primeira mordida
Luciano Ramalho
 
PDF
Aprenda a programar python
Lourenço Junior
 
PDF
Curso básico de Algoritmos com Python
Giancarlo Silva
 
PDF
Palestra python
Rony Cruch
 
PDF
Iniciando em Python
Rober Guerra
 
PDF
Py sintaxe
Flapenta
 
PDF
Trabalho sobre a linguagem Python
Ricardo Zalla
 
Aula 01 python
Tiago
 
Curso python
Tiago
 
Curso python
Tiago
 
Introdução ao Python & Web Services
Dorneles Treméa
 
Livro para desenvolvedores de Python
Alberto Jorge Sardo Monteiro
 
Automacao com Python.pdf
Secont
 
O_Fantastico_Mundo_da_Linguagem_C em pdf.pdf
ArgemiroAlbuquerque
 
O mundo-da-linguagem-c
wagnerprates77
 
O fantc3a1stico-mundo-da-linguagem-c
Valdinho Pereira
 
Python.pptx
IvairLima3
 
Pe algoritmos manhã sem1 2012
FATEC São José dos Campos
 
Python: a primeira mordida
Luciano Ramalho
 
Aprenda a programar python
Lourenço Junior
 
Curso básico de Algoritmos com Python
Giancarlo Silva
 
Palestra python
Rony Cruch
 
Iniciando em Python
Rober Guerra
 
Py sintaxe
Flapenta
 
Trabalho sobre a linguagem Python
Ricardo Zalla
 
Anúncio

Mais de Jean Lopes (9)

PDF
Python - Guia de bolso
Jean Lopes
 
PDF
K19 k03-sql-e-modelo-relacional
Jean Lopes
 
PDF
Computacao grafica python v2
Jean Lopes
 
PDF
Aprendendo python
Jean Lopes
 
PDF
What is node_js
Jean Lopes
 
PDF
Guia de referência rápida css
Jean Lopes
 
PDF
Brnosql luciano ramalho-modelosricos
Jean Lopes
 
PDF
Jquery 38book-pt-br
Jean Lopes
 
PDF
Flask docs
Jean Lopes
 
Python - Guia de bolso
Jean Lopes
 
K19 k03-sql-e-modelo-relacional
Jean Lopes
 
Computacao grafica python v2
Jean Lopes
 
Aprendendo python
Jean Lopes
 
What is node_js
Jean Lopes
 
Guia de referência rápida css
Jean Lopes
 
Brnosql luciano ramalho-modelosricos
Jean Lopes
 
Jquery 38book-pt-br
Jean Lopes
 
Flask docs
Jean Lopes
 

Cientista da computacao usando python

  • 1. Como pensar como um cientista da Computação usando Python Allen Downey Jeffrey Elkner Chris Meyers Tradução: Equipe do projeto http://pensarpython.incubadora.fapesp.br Edição: Cárlisson Galdino
  • 2. Como pensar como um cientista da Computação usando Python Copyright (c) 2002 Allen Downey, Jeffrey Elkner, and Chris Meyers. Edited by Shannon Turlington and Lisa Cutler. Cover design by Rebecca Gimenez. Printing history: April 2002: First edition. Green Tea Press 1 Grove St. P.O. Box 812901 Wellesley, MA 02482 Permission is granted to copy, distribute, and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being “Foreword,” “Preface,” and “Contributor List,” with no Front-Cover Texts, and with no BackCover Texts. A copy of the license is included in the appendix entitled “GNU Free Documentation License.” The GNU Free Documentation License is available from www.gnu.org or by writing to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. Como pensar como um cientista da Computação usando Python Versão atual: 1.0 Data de Publicação: 16 de janeiro de 2008 Classificação: Tutorial Autor: Jeffrey Elkner (traduzido pelo projeto Pensar Python - http://pensarpython.incubadora.fapesp.br) Publicação: Cyaneus.net #2
  • 3. Como pensar como um cientista da Computação usando Python Índice Foreword...................................................................5 . 4.8 A instrução return....................................................28 Apresentação.................................................. ............6 4.9 Recursividade..........................................................29 Preface......................................................... ...............7 4.10 Diagramas de pilha para funções recursivas........29 How and why I came to use Python...............................7 4.11 Recursividade infinita...........................................30 Finding a textb o ok........................................................7 4.12 Entrada pelo teclado..............................................30 Intro ducing programming with Python.........................7 4.13 Glossário...............................................................30 Building a community.....................................................8 Capítulo 5: Funções frutíferas.......................... ......31 . Prefácio................................................................... ....9 5.1 Valores de retorno...................................................31 Como e porque eu vim a usar Python.............................9 5.2 Desenvolvimento de programas.............................31 Encontrando um livro texto............................................9 5.3 Composição.............................................................32 Introduzindo programação com Python.......................10 5.4 Funções booleanas..................................................32 Construindo uma comunidade......................................10 5.5 Mais recursividade..................................................33 5.6 Voto de confiança (Leap of faith)...........................33 Contributor List...................................................... ..11 5.7 Mais um exemplo....................................................34 Versão Brasileira.....................................................13 5.8 Checagem de tipos..................................................34 Capítulo 1: O caminho do programa......................14 5.9 Glossário.................................................................34 1.1 A linguagem de programação Python....................14 Capítulo 6: Iteração.................................................36 1.2 O que é um programa?............................................15 6.1 Reatribuições...........................................................36 1.3 O que é depuração (debugging)?............................15 6.2 O comando while....................................................36 1.3.1 Erros de sintaxe.........................................15 6.3 Tabelas....................................................................37 1.3.2 Erros em tempo de execução (runtime errors) 6.4 Tabelas de duas dimensões (ou bi-dimensionais). .38 ........................................................................ ....15 6.5 Encapsulamento e generalização............................38 1.3.3 Erros de semântica.....................................15 6.6 Mais encapsulamento..............................................38 1.3.4 Depuração experimental (Debugging)........16 1.4 Linguagens naturais e linguagens formais.............16 6.7 Variáveis locais.......................................................38 1.5 O primeiro programa...............................................17 6.8 Mais generalização..................................................39 1.6 Glossário.................................................................17 6.9 Funções...................................................................39 6.10 Glossário...............................................................40 Capítulo 2: Variáveis, expressões e comandos......18 Capítulo 7: Strings..................................................41 . 2.1 Valores e tipos.........................................................18 2.2 Variáveis.................................................................18 7.1 Um tipo de dado composto.....................................41 2.3 Nomes de variáveis e palavras reservadas..............18 7.2 Comprimento..........................................................41 2.4 Comandos................................................................19 7.3 Travessia e o loop for..............................................41 2.5 Avaliando expressões..............................................19 7.4 Fatias de strings.......................................................42 2.6 Operadores e operandos..........................................19 7.5 Comparação de strings............................................42 2.7 Ordem dos operadores............................................20 7.6 Strings são imutáveis..............................................42 2.8 Operações com strings............................................20 7.7 Uma função find (encontrar)...................................42 2.9 Composição.............................................................20 7.8 Iterando e contando.................................................43 2.11 Glossário...............................................................20 7.9 O módulo string......................................................43 7.10 Classificação de caracteres...................................43 Capítulo 3: Funções................................................22 . 7.11 Glossário...............................................................43 3.1 Chamadas de funções..............................................22 7.11 Glossário2.............................................................44 3.2 Conversão entre tipos..............................................22 Capítulo 8: Listas....................................................45 . 3.3 Coerção entre tipos.................................................22 3.4 Funções matemáticas..............................................22 8.1 Valores da lista........................................................45 3.5 Composição.............................................................23 8.2 Acessado elementos................................................45 3.6 Adicionando novas funções....................................23 8.3 Comprimento da lista..............................................45 3.7 Definições e uso......................................................24 8.4 Membros de uma lista.............................................46 3.8 Fluxo de execução...................................................24 8.5 Listas e laços for.....................................................46 3.9 Parâmetros e argumentos........................................24 8.6 Operações em listas.................................................46 3.10 Variáveis e parâmetros são locais.........................25 8.7 Fatiamento de listas.................................................46 3.11 Diagramas da pilha...............................................25 8.8 Listas são mutáveis.................................................46 3.12 Funções com resultados........................................25 8.9 Remoção em lista....................................................47 3.13 Glossário...............................................................26 8.10 Ojetos e valores.....................................................47 8.11 Apelidos................................................................47 Capítulo 4: Condicionais e recursividade...............27 8.12 Clonando listas......................................................48 4.1 O operador módulo.................................................27 8.13 Lista como parâmetro...........................................48 4.2 Expressões booleanas..............................................27 8.14 Lista aninhadas......................................................48 4.3 Operadores lógicos..................................................27 8.15 Matrizes.................................................................48 4.4 Execução condicional.............................................27 8.16 Strings e listas.......................................................49 4.5 Execução alternativa...............................................28 8.17 Glossário...............................................................49 4.6 Condicionais encadeados........................................28 Capítulo 9: Tuplas................................................... .50 4.7 Condicionais aninhados..........................................28 #3
  • 4. Como pensar como um cientista da Computação usando Python 9.1 Mutabilidade e tuplas..............................................50 16.1 Herança.................................................................75 9.2 Atribuições de tupla................................................50 16.2 Uma mão de cartas................................................75 9.3 Tuplas como valores de retorno..............................50 16.3 Dando as cartas.....................................................75 9.4 Números aleatórios.................................................50 16.4 Exibindo a mao.....................................................76 9.5 Lista de números aleatórios....................................51 16.5 A classe JogoDeCartas..........................................76 9.6 Contando.................................................................51 16.6 Classe MaoDeMico...............................................76 9.7 Vários intervalos.....................................................51 16.7 Classe Mico...........................................................77 9.8 Uma solução em um só passo.................................52 16.8 Glossário...............................................................78 9.9 Glossário.................................................................52 Capítulo 17: Listas encadeadas...............................79 Capítulo 10: Dicionários.........................................54 17.1 Referências Embutidas.........................................79 10.1 Operações dos Dicionários...................................54 17.2 A classe No (Node)...............................................79 10.2 Métodos dos Dicionários......................................54 17.3 Listas como Coleções...........................................79 10.3 Aliasing (XXX) e Copiar......................................55 17.4 Listas e Recorrência..............................................80 10.4 Matrizes Esparsas..................................................55 17.5 Listas Infinitas.......................................................80 10.5 Hint........................................................................55 17.6 O Teorema da Ambigüidade Fundamental...........80 10.6 Inteiros Longos.....................................................56 17.7 Modificando Listas...............................................81 10.7 Contando Letras....................................................56 17.8 Envoltórios e Ajudadores......................................81 10.8 Glossário...............................................................56 17.9 A Classe ListaLigada............................................81 Capítulo 11: Arquivos e exceções..........................58 17.10 Invariantes...........................................................82 Arquivos e exceções......................................................58 17.11 Glossário.............................................................82 11.1 Arquivos texto.......................................................58 Capítulo 18: Pilhas................................................ ...83 11.2 Gravando variáveis...............................................59 18.1 Tipos abstratos de dados.......................................83 11.3 Diretórios...............................................................60 18.2 O TAD Pilha.........................................................83 11.4 Pickling.................................................................60 18.3 Implementando pilhas com listas de Python........83 11.5 Exceções................................................................60 18.4 Empilhando e desempilhando...............................83 11.6 Glossário...............................................................61 18.5 Usando uma pilha para avaliar expressões pós- Capítulo 12: Classes e objetos................................62 fixas...............................................................................84 12.1 Tipos compostos definidos pelo usuário..............62 18.6 Análise sintática....................................................84 12.2 Atributos................................................................62 18.7 Avaliando em pós-fixo..........................................84 12.3 Instâncias como parâmetros..................................63 18.8 Clientes de fornecedores.......................................84 12.4 O significado de "mesmo"....................................63 18.9 Glossário...............................................................85 12.5 Retângulos.............................................................63 Capítulo 19: Filas................................................ .....86 12.6 Instancias como valores retornados......................64 19.1 Um TDA Fila........................................................86 12.7 Objetos são mutáveis............................................64 19.2 Fila encadeada.......................................................86 12.8 Copiando...............................................................64 19.3 Características de performance.............................86 12.9 Glossário...............................................................65 19.4 Fila encadeada aprimorada...................................86 Capítulo 13: Classes e funções................................ 6 6 19.5 Fila por prioridade.................................................87 13.1 Horario..................................................................66 19.6 A classe Golfer......................................................88 13.2 Funções Puras.......................................................66 19.7 Glossário...............................................................88 13.3 Modificadores.......................................................66 Capítulo 20: Árvores...............................................89 13.4 O que é melhor ?...................................................67 20.1 Construindo árvores..............................................89 13.5 Desenvolvimento Prototipado versus 20.2 Percorrendo árvores..............................................89 Desenvolvimento Planejado.........................................67 20.3 Árvores de expressões...........................................89 13.6 Generalização........................................................67 20.4 Percurso de árvores...............................................90 13.7 Algoritmos.............................................................68 20.5 Construindo uma árvore de expressão..................90 13.8 Glossário...............................................................68 20.6 Manipulando erros................................................92 Capítulo 14: Classes e métodos..............................69 20.7 A árvore dos animais............................................92 14.1 Características da orientação a objetos.................69 20.8 Glossário...............................................................93 14.2 exibeHora (printTime)..........................................69 GNU Free Documentation License........................ 94 . 14.3 Um outro exemplo................................................70 0. PREAMBLE ............................................................94 14.10 Glossário.............................................................70 1. APPLICABILITY AND DEFINITIONS ................94 Capítulo 15: Conjuntos de objetos..........................71 2. VERBATIM COPYING ..........................................95 15.1 Composição...........................................................71 3. COPYING IN QUANTITY .....................................95 15.2 Objetos Carta.........................................................71 4. MODIFICATIONS ..................................................95 15.3 Atributos de classe e o método __str__................71 5. COMBINING DOCUMENTS .................................96 15.4 Comparando cartas................................................72 6. COLLECTIONS OF DOCUMENTS ......................96 15.5 Baralhos.................................................................72 7. AGGREGATION WITH INDEPENDENT WORKS . 15.6 Imprimindo o baralho...........................................72 96 15.7 Embaralhando.......................................................73 8. TRANSLATION ......................................................96 15.8 Removendo e distribuindo cartas.........................73 9. TERMINATION ......................................................96 15.9 Glossário...............................................................74 10. FUTURE REVISIONS OF THIS LICENSE.........96 Capitulo 16: Herança..............................................75 . How to use this License for your documents.......96 #4
  • 5. Como pensar como um cientista da Computação usando Python Foreword By David Beazley One of the reasons why I like Python is that it provides a really nice balance between the practical and the As an educator, researcher, and book author, I conceptual. Since Python is interpreted, beginners can pick up am delighted to see the completion of this book. Python is a the language and start doing neat things almost immediately fun and extremely easy-to-use programming language that has without getting lost in the problems of compilation and steadily gained in popularity over the last few years. linking. Furthermore, Python comes with a large library of Developed over ten years ago by Guido van Rossum, modules that can be used to do all sorts of tasks ranging from Python’s simple syntax and overall feel is largely derived web-programming to graphics. Having such a practical focus from ABC, a teaching language that was developed in the is a great way to engage students and it allows them to 1980’s. However, Python was also created to solve real complete significant pro jects. However, Python can also problems and it borrows a wide variety of features from serve as an excellent foundation for introducing important programming languages such as C++, Java, Modula-3, and computer science concepts. Since Python fully supports Scheme. Because of this, one of Python’s most remarkable procedures and classes, students can be gradually introduced features is its broad appeal to professional software to topics such as procedural abstraction, data structures, and developers, scientists, researchers, artists, and educators. ob ject-oriented programming—all of which are applicable to Despite Python’s appeal to many different later courses on Java or C++. Python even borrows a number communities, you may still wonder “why Python?” or “why of features from functional programming languages and can teach programming with Python?” Answering these questions be used to introduce concepts that would be covered in more is no simple task—especially when popular opinion is on the detail in courses on Scheme and Lisp. side of more masochistic alternatives such as C++ and Java. In reading Jeffrey’s preface, I am struck by his However, I think the most direct answer is that programming comments that Python allowed him to see a “higher level of in Python is simply a lot of fun and more productive. success and a lower level of frustration” and that he was able When I teach computer science courses, I want to “move faster with better results.”sometimes thesePython for Although comments to cover important concepts in addition to making the material refer to his same reasons in advanced graduate use computer introductory course, I interesting and engaging to students. Unfortunately, there is a these exact level tendency for introductory programming courses to focus far science courses at the University of Chicago. of covering a lot In these courses, too much attention on mathematical abstraction and for I am constantly faced with the a blistering nine week quarter. daunting task students to be come frustrated with annoying problems related of difficult course material in for me to inflict a lot of pain to low-level details of syntax, compilation, and the Although it is certainly possible enforcement of seemingly arcane rules. Although such and suffering by using a be counterproductive— have often language like C++, I abstraction and formalism is important to professional found this approachisto about a topic unrelatedespecially software engineers and students who plan to continue their when the coursefind that using Python allows me to better “programming.” I to just study of computer science, taking such an approach in an focus on the actual topic at hand while allowing students to introductory course mostly succeeds in making computer complete substantial class pro jects. science boring. When I teach a course, I don’t want to have a room of uninspired students. I would much rather see them Although Python is still a young and evolving trying to solve interesting problems by exploring different language, I believe that it has a bright future in education. This ideas, taking unconventional approaches, breaking the rules, book is an important step in that direction. and learning from their mistakes. In doing so, I don’t want to waste half of the semester trying to sort out obscure syntax David Beazley problems, unintelligible compiler error messages, or the several hundred ways that a program might generate a general University of Chicago protection fault. Author of the Python Essential Reference Foreword #5
  • 6. Como pensar como um cientista da Computação usando Python Apresentação Tradução do capítulo anterior, que foi mantido por ter sido marcado como seção invariante pelo autor original. Como educador, pesquisador e autor de livros, regozija-me é que ele oferece um equilíbrio realmente bom entre o lado ver completo este trabalho. Python é uma linguagem de prático e o lado conceitual. Sendo Python interpretado, os programação divertida e extremamente fácil de usar que tem iniciantes podem pegar a linguagem e começar a fazer coisas ganho forte popularidade nestes últimos poucos anos. legais quase imediatamente sem se perderem em problemas de Desenvolvida dez anos atrás por Guido van Rossun, a sintaxe compilação e ligação. Além disso, Python vem com uma simples do Python e seu sentido geral são grandemente grande biblioteca de módulos que podem ser utilizados para derivados do ABC, uma linguagem didática que foi fazer todo tipo de tarefa, desde a programação para a web até desenvolvida nos anos 80. Entretanto, Python também foi gráficos. Com tal enfoque prático temos uma bela maneira de criado para solucionar problemas reais e tomou emprestado alcançar o engajamento dos alunos e permitir que eles uma grande quantidade de características de linguagens de finalizem projetos significativos. Entretanto, Python também programação como C++, Java, Modula-3 e Scheme. Por causa pode servir de excelente embasamento para a introdução de disso, uma das mais notáveis características do Python é o conceitos importantes em ciência da computação. Já que grande apelo que tem junto a desenvolvedores profissionais de Python suporta plenamente procedimentos (procedures) e software, cientistas, pesquisadores, artistas e educadores. classes, os alunos podem ser gradualmente introduzidos a tópicos como abstração procedural, estruturas de dados, e A Despeito deste apelo do Python junto às mais programação orientada a objetos ? todos aplicáveis em cursos variadas comunidades, você pode ainda estar pensando ?por posteriores de Java ou C++. Python ainda toma emprestado que Python?? ou ?por que ensinar programação com certas características de linguagens de programação funcionais Python??. Responder à estas perguntas não é uma tarefa fácil ? e pode ser usado para introduzir conceitos cujos detalhes especialmente se a opinião pública está do lado de alternativas poderiam ser aprofundados em cursos de Scheme e Lisp. mais masoquistas como C++ e Java. Entretanto, eu acho que a resposta mais direta é que programar com Python é um Lendo o prefácio de Jeffrey, fiquei bocado divertido e mais produtivo. impressionado com seu comentário de que Python o fez ver um ?maior nível de sucesso e um menor nível de frustração? o Quando ministro cursos de ciências da que lhe permitiu ?progredir mais depressa com resultados computação, o que desejo é cobrir conceitos importantes além melhores?. Embora estes comentários refiram-se aos seus de tornar a matéria interessante e os alunos participativos. cursos introdutórios, eu às vezes uso Python exatamente pelas Infelizmente, existe uma tendência entre os cursos mesmas razões em cursos avançados de pós-graduação em introdutórios de programação a focar atenção demais em ciência da computação na Universidade de Chicago. Nestes abstrações matemáticas, e de frustração entre os alunos com cursos, enfrento constantemente a assustadora tarefa de cobrir problemas enfadonhos e inoportunos relacionados a detalhes muitos tópicos difíceis em um rapidíssimo trimestre de nove de sintaxe em baixo nível, compilação e a imposição de regras semanas. Embora me seja possível inflingir um bocado de dor que aparentemente só um expert pode compreender. Embora e sofrimento pelo uso de uma linguagem como C++, tenho alguma abstração e formalismo sejam importantes para percebido muitas vezes que este enfoque é contraproducente ? engenheiros profissionais de software e estudantes que especialmente quando o curso é sobre um tópico não planejam continuar seus estudos em ciências da computação, relacionado apenas com ?programar?. Acho que usar Python escolher tal abordagem em um curso introdutório faz da me permite um melhor foco no tópico em questão, enquanto ciência da computação algo entediante. Quando ministro um permite que os alunos completem projetos substanciais em curso, não desejo uma sala cheia de alunos sem inspiração. classe. Em vez disso, preferiria muito mais vê-los tentando solucionar problemas interessantes explorando idéias diferentes, Embora Python seja ainda uma linguagem trilhando caminhos não convencionais, quebrando regras, e jovem e em evolução, acredito que tem um futuro brilhante aprendendo a partir de seus erros. Fazendo assim, não em educação. Este livro é um passo importante nessa direção. pretendo desperdiçar metade de um semestre tentando explicar problemas obscuros de sintaxe, mensagens ininteligíveis de David Beazley compiladores ou as várias centenas de maneiras pelas quais Universidade de Chicago um programa pode gerar uma falha geral de proteção. Uma das razões pelas quais eu gosto de Python Autor de Python Essencial Reference Apresentação #6
  • 7. Como pensar como um cientista da Computação usando Python Preface By Jeff Elkner computer science classes the following year, the most pressing problem was the lack of an available textbook. This book owes its existence to the collaboration made possible by the Internet and the free Free content came to the rescue. Earlier in the software movement. Its three authors—a college professor, a year, Richard Stallman had introduced me to Allen Downey. high school teacher, and a professional programmer—have yet Both of us had written to Richard expressing an interest in to meet face to face, but we have been able to work closely developing free educational content. Allen had already written together and have been aided by many wonderful folks who a first-year computer science textbook, How to Think Like a have donated their time and energy to helping make this book Computer Scientist. When I read this book, I knew better. immediately that I wanted to use it in my class. It was the clearest and most helpful computer science text I had seen. It We think this book is a testament to the benefits emphasized the processes of thought involved in and future possibilities of this kind of collaboration, the programming rather than the features of a particular language. framework for which has been put in place by Richard Reading it immediately made me a better teacher. Stallman and the Free Software Foundation. How to Think Like a Computer Scientist was not just an excellent book, but it had been released under a GNU public license, which meant it could be used freely and How and why I came to use Python modified to meet the needs of its user. Once I decided to use Python, it occurred to me that I could translate Allen’s original In 1999, the College Board’s Advanced Placement (AP) Java version of the book into the new language. While I would Computer Science exam was given in C++ for the first time. not have been able to write a textbook on my own, having As in many high schools throughout the country, the decision Allen’s book to work from made it possible for me to do so, at to change languages had a direct impact on the computer the same time demonstrating that the cooperative development science curriculum at Yorktown High School in Arlington, model used so well in software could also work for Virginia, where I teach. Up to this point, Pascal was the educational content. language of instruction in both our first-year and AP courses. In keeping with past practice of giving students two years of Working on this book for the last two years has exposure to the same language, we made the decision to been rewarding for both my students and me, and my students switch to C++ in the first-year course for the 1997-98 school played a big part in the process. Since I could make instant year so that we would be in step with the College Board’s changes whenever someone found a spelling error or difficult change for the AP course the following year. passage, I encouraged them to look for mistakes in the book by giving them a bonus point each time they made a Two years later, I was convinced that C++ was suggestion that resulted in a change in the text. This had the a poor choice to use for introducing students to computer double benefit of encouraging them to read the text more science. While it is certainly a very powerful programming carefully and of getting the text thoroughly reviewed by its language, it is also an extremely difficult language to learn and most important critics, students using it to learn computer teach. I found myself constantly fighting with C++’s difficult science. syntax and multiple ways of doing things, and I was losing many students unnecessarily as a result. Convinced there had For the second half of the book on ob ject- to be a better language choice for our first-year class, I went oriented programming, I knew that someone with more real looking for an alternative to C++. programming experience than I had would be needed to do it right. The book sat in an unfinished state for the better part of I needed a language that would run on the a year until the free software community once again provided machines in our Linux lab as well as on the Windows and the needed means for its completion. Macintosh platforms most students have at home. I wanted it to be free and available electronically, so that students could I received an email from Chris Meyers use it at home regardless of their income. I wanted a language expressing interest in the book. Chris is a professional that was used by professional programmers, and one that had programmer who started teaching a programming course last an active developer community around it. It had to support year using Python at Lane Community College in Eugene, both procedural and ob ject-oriented programming. And most Oregon. The prospect of teaching the course had led Chris to importantly, it had to be easy to learn and teach. When I the book, and he started helping out with it immediately. By investigated the choices with these goals in mind, Python the end of the school year he had created a companion project stood out as the best candidate for the job. on our Website at http://www.ibiblio.org/obp called Python for Fun and was working with some of my most advanced I asked one of Yorktown’s talented students, students as a master teacher, guiding them beyond where I Matt Ahrens, to give Python a try. In two months he not only could take them. learned the language but wrote an application called pyTicket that enabled our staff to report technology problems via the Web. I knew that Matt could not have finished an application of that scale in so short a time in C++, and this Intro ducing programming with Python accomplishment, combined with Matt’s positive assessment of Python, suggested that Python was the solution I was looking The process of translating and using How to Think Like a for. Computer Scientist for the past two years has confirmed Python’s suitability for teaching beginning students. Python greatly simplifies programming examples and makes important programming ideas easier to teach. Finding a textb o ok The first example from the text illustrates this Having decided to use Python in both of my introductory point. It is the traditional “hello, world” program, which in the Preface #7
  • 8. Como pensar como um cientista da Computação usando Python C++ version of the book looks like this: they learned in their math courses. I had much less difficulty teaching variables this year than I did in the past, and I spent #include <iostream.h> less time helping students with problems using them. void main() { Another example of how Python aids in the teaching and learning of programming is in its syntax for cout << "Hello, world." << endl; functions. My students have always had a great deal of } difficulty understanding functions. The main problem centers in the Python version it becomes: around the difference between a function definition and a function call, and the related distinction between a parameter print "Hello, World!" and an argument. Python comes to the rescue with syntax that Even though this is a trivial example, the is nothing short of beautiful. Function definitions begin with advantages of Python stand out. Yorktown’s Computer the keyword def, so I simply tell my students, “When you Science I course has no prerequisites, so many of the students define a function, begin with def, followed by the name of the seeing this example are looking at their first program. Some of function that you are defining; when you call a function, them are undoubtedly a little nervous, having heard that simply call (type) out its name.” Parameters go with computer programming is difficult to learn. The C++ version definitions; arguments go with calls. There are no return types, has always forced me to choose between two unsatisfying parameter types, or reference and value parameters to get in options: either to explain the #include, void main(), {, and } the way, so I am now able to teach functions in less than half statements and risk confusing or intimidating some of the the time that it previously took me, with better students right at the start, or to tell them, “Just don’t worry comprehension. about all of that stuff now; we will talk about it later,” and risk Using Python has improved the effectiveness of the same thing. The educational ob jectives at this point in theour computer science program for all students. I see a higher course are to introduce students to the idea of a programming general level of success and a lower level of frustration than I statement and to get them to write their first program, thereby experienced during the two years I taught C++. I move faster introducing them to the programming environment. The with better results. More students leave the course with the Python program has exactly what is needed to do these things, ability to create meaningful programs and with the positive and nothing more. attitude toward the experience of programming that this Comparing the explanatory text of the program engenders. in each version of the book further illustrates what this means to the beginning student. There are thirteen paragraphs of explanation of “Hello, world!” in the C++ version; in the Building a community Python version, there are only two. More importantly, the missing eleven paragraphs do not deal with the “big ideas” in computer programming but with the minutia of C++ syntax. I I have received email from all over the globe from people found this same thing happening throughout the book. Whole using this book to learn or to teach programming. A user paragraphs simply disappear from the Python version of the community has begun to emerge, and many people have been text because Python’s much clearer syntax renders them contributing to the pro ject by sending in materials for the unnecessary. companion Website at http://www.thinkpython.com. Using a very high-level language like Python With the publication of the book in print form, I allows a teacher to postpone talking about low-level details of expect the growth in the user community to continue and the machine until students have the background that they need accelerate. The emergence of this user community and the to better make sense of the details. It thus creates the ability to possibility it suggests for similar collaboration among put “first things first” pedagogically. One of the best examples educators have been the most exciting parts of working on this of this is the way in which Python handles variables. In C++ a pro ject for me. By working together, we can increase the variable is a name for a place that holds a thing. Variables quality of materials available for our use and save valuable have to be declared with types at least in part because the size time. I invite you to join our community and look forward to of the place to which they refer needs to be predetermined. hearing from you. Please write to the authors at Thus, the idea of a variable is bound up with the hardware of [email protected]. the machine. The powerful and fundamental concept of a variable is already difficult enough for beginning students (in Jeffrey Elkner both computer science and algebra). Bytes and addresses do Yorktown High School not help the matter. In Python a variable is a name that refers to a thing. This is a far more intuitive concept for beginning Arlington, Virginia students and is much closer to the meaning of “variable” that Preface #8
  • 9. Como pensar como um cientista da Computação usando Python Prefácio Tradução do capítulo anterior, que foi mantido por ter sido marcado como seção invariante pelo autor original. Este livro deve sua existência à colaboração tornada possível pela Internet e pelo movimento do software livre. Seus três Encontrando um livro texto autores ? um professor universitário, um secundário e um programador profissional ? ainda não se encontraram Tendo decidido usar Python em ambas as minhas classes pessoalmente, mas temos podido trabalhar bem de perto e introdutórias de ciência da computação do ano seguinte, o temos sido ajudados por muitos colegas maravilhosos que têm problema mais urgente era a falta de um livro texto disponível. dedicado seu tempo e energia a ajudar a fazer deste um livro cada vez melhor. O conteúdo livre veio em socorro. Anteriormente naquele ano, Richard Stallman tinha me Achamos que este livro é um testemunho dos apresentado a Allen Downey. Ambos havíamos escrito a benefícios e possibilidades futuras deste tipo de colaboração, Richard expressando interesse em desenvolver conteúdo cujo modelo tem sido colocado por Richard Stallman e pela educacional livre. Allen já tinha escrito um livro texto para o Free Software Foundation. primeiro ano de ciência da computação, How to Think Like a Computer Scientist. Quando li este livro, soube imediatamente que queria utilizá-lo nas minhas aulas. Era o texto mais claro e proveitoso em ciência da computação que eu tinha visto. Ele Como e porque eu vim a usar Python enfatizava o processo de reflexão envolvido em programação em vez de características de uma linguagem em particular. Lê- Em 1999, o Exame de Colocação Avançada em Ciência da lo fez de mim imediatamente um melhor professor. Computação da Comissão de Faculdades (College Board?s Advanced Placement (AP) Computer Science) foi aplicado em O How to Think Like a Computer Scientist era C++ pela primeira vez. Como em muitas escolas secundárias não só um excelente livro, como também fora lançado sob através do país, a decisão de mudar linguagens teve um uma licença pública GNU, o que significava que ele poderia impacto direto no currículo de ciência da computação na ser usado livremente e modificado para atender as Yorktown High School em Arlington, Virginia, onde leciono. necessidades de seu usuário. Uma vez que eu havia decidido Até então, Pascal era a linguagem didática para nossos cursos usar Python, me ocorreu que eu poderia traduzir a versão de primeiro ano e avançado. Mantendo a prática corrente de original do livro de Allen do Java para a nova linguagem. dar aos estudantes dois anos de exposição à mesma Apesar de não estar capacitado para escrever eu mesmo um linguagem, tomamos a decisão de mudar para C++ no curso livro texto, tendo o livro de Allen a partir do qual trabalhar de primeiro ano para o ano letivo de 1997-98 de modo que tornou possível para mim fazê-lo, ao mesmo tempo estaríamos em sincronismo com a mudança da Comissão de demonstrando que o modelo de desenvolvimento cooperativo Faculdades (College Board?s) em relação ao curso avançado tão bem utilizado em software poderia também funcionar para para o ano seguinte. conteúdo educacional. Dois anos depois, eu estava convencido que Trabalhar neste livro pelos últimos dois anos C++ foi uma escolha infeliz para introduzir os alunos em tem sido recompensador para mim e meus alunos, e eles ciência da computação. Ao mesmo tempo em que é tiveram um grande papel neste processo. A partir do momento certamente uma linguagem de programação muito poderosa, em que eu podia fazer mudanças instantâneas assim que também é uma linguagem extremamente difícil de aprender e alguém encontrasse um erro ortográfico ou um trecho difícil, de ensinar. Eu me encontrava constantemente lutando com a eu os encorajei a procurar por erros no livro, dando a eles sintaxe difícil do C++ e as múltiplas maneiras de fazer a pontos de bonificação cada vez que eles fizessem uma mesma coisa, e estava, como resultado, perdendo muitos sugestão que resultasse em uma mudança no texto. Isto teve o alunos desnecessariamente. Convencido de que deveria existir duplo benefício de encorajá-los a ler o texto mais uma linguagem melhor para a nossa classe de primeiro ano, cuidadosamente e de ter o texto totalmente revisado por seus fui procurar por uma alternativa ao C++. críticos mais importantes: alunos utilizando-o para aprender ciência da computação. Eu precisava de uma linguagem que pudesse rodar nas máquinas em nosso laboratório Linux bem como nas Para a segunda metade do livro, sobre plataformas Windows e Macintosh que a maioria dos alunos programação orientada a objetos, eu sabia que seria preciso tinha em casa. Eu precisava que ela fosse gratuita e disponível alguém com uma maior experiência do que a minha em eletronicamente, assim os alunos poderiam utilizá-la em casa programação real para fazê-lo corretamente. O livro esteve em independentemente de suas rendas. Eu queria uma linguagem estado inacabado por quase um ano até que a comunidade de que fosse utilizada por programadores profissionais, e que software livre providenciasse mais uma vez os meios tivesse uma comunidade de desenvolvimento ativa em torno necessários para sua conclusão. dela. Ela teria que suportar ambas, programação procedural e Eu recebi um e-mail de Chris Meyers mostrando orientada a objetos. E, mais importante, deveria ser fácil de interesse no livro. Chris é um programador profissional que aprender e de ensinar. Quando considerei as alternativas tendo começou a dar um curso de programação no ano anterior em mente aquelas metas, Python sobressaiu-se como a melhor usando Python no Lane Community College em Eugene, candidata para a tarefa. Oregon. A perspectiva de dar aquele curso ligou Chris ao Pedi para um dos talentosos estudantes de livro, e ele começou a ajudar o trabalho imediatamente. Pelo Yorktown, Matt Ahrens, que experimentasse Python. Em dois final do ano letivo ele tinha criado um projeto colaborativo em meses ele não só aprendeu a linguagem como também nosso Website em http://www.ibiblio.org/obp chamado escreveu uma aplicação chamada pyTicket que possibilitou à Python for Fun e estava trabalhando com alguns dos meus nossa equipe reportar problemas de tecnologia pela Web. Eu alunos mais avançados como um guru, guiando-os além de sabia que Matt não poderia ter finalizado uma aplicação onde eu poderia levá-los. daquele porte em período tão curto em C++, esta realização, combinada com a avaliação positiva do Python dada por Matt, sugeriam que Python era a solução que eu estava procurando. Prefácio #9
  • 10. Como pensar como um cientista da Computação usando Python poderoso e fundamental de variável já é bastante difícil para o Introduzindo programação com Python aluno iniciante (em ambas, ciência da computação e álgebra). Bytes e endereços não ajudam neste caso. Em Python uma O processo de traduzir e utilizar How to Think Like a variável é um nome que se refere a uma coisa. Este é um Computer Scientist pelos últimos dois anos tem confirmado a conceito muito mais intuitivo para alunos iniciantes e está conveniência de Python no ensino de alunos iniciantes. muito mais próximo do significado de ?variável? que eles Python simplifica tremendamente os programas exemplo e aprenderam em seus cursos de matemática. Eu tive muito torna idéias importantes de programação mais fáceis de menos dificuldade em ensinar variáveis este ano do que tive ensinar. no passado, e gastei menos tempo ajudando aos alunos com problemas no uso delas. O primeiro exemplo do texto ilustra este ponto. É o tradicional programa ?Alô mundo?, do qual na versão Um outro exemplo de como Python ajuda no C++ do livro se parece com isto: ensino e aprendizagem de programação é em sua sintaxe para função. Meus alunos têm sempre tido grande dificuldade na #include <iostream.h> compreensão de funções. O problema principal gira em torno da diferença entre a definição de uma função e a chamada de void main() uma função, e a distinção relacionada entre um parâmetro e um argumento. Python vem em auxílio com uma sintaxe não { apenas curta quanto bela. As definições de função começam cout << "Alô, mundo." << endl; com def, então eu simplesmente digo aos meus alunos ? } Quando você define uma função, comece com def, seguido do Na versão Python, ele se transforma em: nome da função que você está definindo; quando você chama uma função, simplesmente chame-a digitando o nome dela?. print "Alô, Mundo!" Parâmetros ficam nas definições; argumentos vão com as Mesmo sendo um exemplo trivial, as vantagens chamadas. Não existem tipos de retorno, tipos de parâmetro do Python saltam aos olhos. O curso de Ciência da ou passagem de parâmetros por valor ou por referência no Computação I que ministro em Yorktown não tem pré- meio do caminho, permitindo-me ensinar funções em menos requisitos, assim, muitos dos alunos que veem esse exemplo da metade do tempo que isto me tomava anteriormente, com estão olhando para o seu primeiro programa. Alguns deles uma melhor compreensão. estão indubitavelmente nervosos, por já terem ouvido falar que programação de computadores é difícil de aprender. A efetividade deA utilização do Python da computação para nosso programa em ciência tem melhorado a versão C++ tem sempre me forçado a escolher entre duas todos os estudantes. Eu vejo um nível geral de sucesso muito opções insatisfatórias: ou explicar os comandos #include, void mais alto e um nível mais baixo de frustração do que main(), {, e } e arriscar confundir ou intimidar alguns dos experimentei durante os dois anos em que ensinei C++. Eu alunos logo assim que iniciam, ou dizer a eles ?Não se avanço mais rápido com melhores resultados. Mais alunos preocupem com todas estas coisas agora; falaremos sobre elas deixam o curso com a habilidade de criar programas mais tarde?, e correr o mesmo risco. O objetivo educacional significativos e com uma atitude positiva em relação a neste ponto do curso é introduzir os alunos à idéia de experiência de programação que isso traz. comando em programação e vê-los escrever seu primeiro programa, deste modo introduzindo-os ao ambiente de programação. O programa em Python tem exatamente o que é necessário para conseguir isto, e nada mais. Construindo uma comunidade Comparar o texto explicativo do programa em cada versão do livro ilustra ainda mais o que significa para o Tenho recebido e-mails de todo o planeta de pessoas aluno iniciante. Existem treze parágrafos de explicação do ? utilizando este livro para aprender ou ensinar programação. Alô, mundo!? na versão C++; na versão Python existem Uma comunidade de usuários tem começado a emergir e apenas dois. Mais importante, os onze parágrafos perdidos não muitas pessoas têm contribuído com o projeto enviando seus se ocupam das ?idéias chave? da programação de materiais para o Website cooperativo em: computadores, mas com a minúcia da sintaxe C++. Vejo a mesma coisa acontecendo através de todo o livro. Parágrafos http://www.thinkpython.com inteiros simplesmente desaparecem da versão do texto para Com a publicação do livro em formato Python porque a sintaxe muito mais clara do Python os torna impresso, minha expectativa quanto ao crescimento da desnecessários. comunidade de usuários é que ela seja contínua e acelerada. O Utilizar uma linguagem de tão alto nível como surgimento desta comunidade de usuários e a possibilidade Python, permite ao professor deixar para falar mais tarde que sugere de colaboração semelhante entre educadores tem sobre os níveis mais baixos, próximos à máquina, quando os sido para mim a parte mais excitante do trabalho neste projeto. alunos já terão a experiência necessária para ver com mais Trabalhando juntos, podemos aumentar a qualidade do sentido os detalhes. Desta maneira podemos ?por em primeiro material disponível para o nosso uso e poupar tempo valioso. lugar as primeiras coisas?, pedagogicamente. Um dos Eu convido você a se juntar a nossa comunidade e espero melhores exemplos disto é a maneira com que Python lida ouvir algo de você. Por favor, escreva para os autores em com variáveis. Em C++ uma variável é um nome para um [email protected]. lugar que guarda uma coisa. Variáveis têm de ser declaradas Jeffrey Elkner com seu tipo pelo menos em parte por que o tamanho do lugar a que se referem precisa ser predeterminado. Assim, a idéia de Yorktown High School variável fica amarrada ao hardware da máquina. O conceito Arlington, Virginia Prefácio #10
  • 11. Como pensar como um cientista da Computação usando Python Contributor List To paraphrase the philosophy of the Free Software “unconsciously” in Chapter 1 needed to be changed Foundation, this book is free like free speech, but not to “subconsciously”. necessarily free like free pizza. It came about because of a collaboration that would not have been possible without the ● Chris McAloon sent in several corrections to GNU Free Documentation License. So we thank the Free Sections 3.9 and 3.10. Software Foundation for developing this license and, of ● Matthew J. Moelter has been a long-time contributor course, making it available to us. who sent in numerous corrections and suggestions to We also thank the more than 100 sharp-eyed the book. and thoughtful readers who have sent us suggestions and ● Simon Dicon Montford reported a missing function corrections over the past few years. In the spirit of free definition and several typos in Chapter 3. He also software, we decided to express our gratitude in the form of a found errors in the increment function in Chapter 13. contributor list. Unfortunately, this list is not complete, but we are doing our best to keep it up to date. ● John Ouzts corrected the definition of “return value” If you have a chance to look through the list, in Chapter 3. you should realize that each person here has spared you and ● Kevin Parks sent in valuable comments and all subsequent readers from the confusion of a technical error suggestions as to how to improve the distribution of or a less-than-transparent explanation, just by sending us a the book. note. ● David Pool sent in a typo in the glossary of Chapter Impossible as it may seem after so many 1, as well as kind words of encouragement. corrections, there may still be errors in this book. If you should stumble across one, please check the online version of ● Michael Schmitt sent in a correction to the chapter on the book at http://thinkpython.com, which is the most up-to- files and exceptions. date version. If the error has not been corrected, please take a minute to send us email at [email protected]. If we ● Robin Shaw pointed out an error in Section 13.1, make a change due to your suggestion, you will appear in the where the printTime function was used in an example next version of the contributor list (unless you ask to be without being defined. omitted). Thank you! ● Paul Sleigh found an error in Chapter 7 and a bug in ● Lloyd Hugh Allen sent in a correction to Section 8.4. Jonah Cohen’s Perlscript that generates HTML from LaTeX. ● Yvon Boulianne sent in a correction of a semantic error in Chapter 5. ● Craig T. Snydal is testing the text in a course at Drew University. He has contributed several valuable ● Fred Bremmer submitted a correction in Section 2.1. suggestions and corrections. ● Jonah Cohen wrote the Perl scripts to convert the ● Ian Thomas and his students are using the text in a LaTeX source for this book into beautiful HTML. programming course. They are the first ones to test the chapters in the latter half of the book, and they ● Michael Conlon sent in a grammar correction in have made numerous corrections and suggestions. Chapter 2 and an improvement in style in Chapter 1, and he initiated discussion on the technical aspects of ● Keith Verheyden sent in a correction in Chapter 3. interpreters. ● Peter Winstanley let us know about a longstanding ● Benoit Girard sent in a correction to a humorous error in our Latin in Chapter 3. mistake in Section 5.6. ● Chris Wrobel made corrections to the code in the ● Courtney Gleason and Katherine Smith wrote chapter on file I/O and exceptions. horsebet.py, which was used as a case study in an earlier version of the book. Their program can now ● Moshe Zadka has made invaluable contributions to be found on the website. this pro ject. In addition to writing the first draft of the chapter on Dictionaries, he provided continual ● Lee Harr submitted more corrections than we have guidance in the early stages of the book. room to list here, and indeed he should be listed as one of the principal editors of the text. ● Christoph Zwerschke sent several corrections and pedagogic suggestions, and explained the difference ● James Kaylin is a student using the text. He has between gleich and selbe. submitted numerous corrections. ● James Mayer sent us a whole slew of spelling and ● David Kershaw fixed the broken catTwice function in typographical errors, including two in the contributor Section 3.10. list. ● Eddie Lam has sent in numerous corrections to ● Hayden McAfee caught a potentially confusing Chapters 1, 2, and 3. He also fixed the Makefile so inconsistency between two examples. that it creates an index the first time it is run and helped us set up a versioning scheme. ● Angel Arnal is part of an international team of translators working on the Spanish version of the ● Man-Yong Lee sent in a correction to the example text. He has also found several errors in the English code in Section 2.4. version. ● David Mayo pointed out that the word ● Tauhidul Hoque and Lex Berezhny created the Contributor List #11
  • 12. Como pensar como um cientista da Computação usando Python illustrations in Chapter 1 and improved many of the ● Ben Logan sent in a number of typos and problems other illustrations. with translating the book into HTML. ● Dr. Michele Alzetta caught an error in Chapter 8 and ● Jason Armstrong saw the missing word in Chapter 2. sent some interesting pedagogic comments and suggestions about Fibonacci and Old Maid. ● Louis Cordier noticed a spot in Chapter 16 where the code didn’t match the text. ● Andy Mitchell caught a typo in Chapter 1 and a broken example in Chapter 2. ● Brian Cain suggested several clarifications in Chapters 2 and 3. ● Kalin Harvey suggested a clarification in Chapter 7 and caught some typos. ● Rob Black sent in a passel of corrections, including some changes for Python 2.2. ● Christopher P. Smith caught several typos and is helping us prepare to update the book for Python 2.2. ● Jean-Philippe Rey at Ecole Centrale Paris sent a number of patches, including some updates for ● David Hutchins caught a typo in the Foreword. Python 2.2 and other thoughtful improvements. ● Gregor Lingl is teaching Python at a high school in ● Jason Mader at George Washington University made Vienna, Austria. He is working on a German a number of useful suggestions and corrections. translation of the book, and he caught a couple of bad errors in Chapter 5. ● Jan Gundtofte-Bruun reminded us that “a error” is an error. ● Julie Peters caught a typo in the Preface. ● Abel David and Alexis Dinno reminded us that the ● Florin Oprina sent in an improvement in makeTime, plural of “matrix” is “matrices”, not “matrixes”. This a correction in printTime, and a nice typo. error was in the book for years, but two readers with the same initials reported it on the same day. Weird. ● D. J. Webre suggested a clarification in Chapter 3. ● Charles Thayer encouraged us to get rid of the semi- ● Ken found a fistful of errors in Chapters 8, 9 and 11. colons we had put at the ends of some statements and ● Ivo Wever caught a typo in Chapter 5 and suggested to clean up our use of “argument” and “parameter”. a clarification in Chapter 3. ● Roger Sperberg pointed out a twisted piece of logic ● Curtis Yanko suggested a clarification in Chapter 2. in Chapter 3. Contributor List #12
  • 13. Como pensar como um cientista da Computação usando Python Versão Brasileira A versão traduzida para Português Brasileiro foi feita pela equipe do site http://pensarpython.incubadora.fapesp.br, abaixo relacionada: • Adrovane Kade (adrovane) • Alex Augusto da Luz dos Santos (nowayx) • Claudio Fernando Berrondo Soares (cl-audio) • Daniel Rosa Franzini (danielt3) • Douglas Soares de Andrade (dsa) • Fabio Rizzo Matos (fabrizmat) • Imre Simon (imres) • Joao Paulo Liberato (jpliberato) • João Paulo Gomes Vanzuita (taken) • Julio Monteiro (jmonteiro) • Luciano Ramalho (luciano) • Marcus Pereira (mvmendes) • Mario O. de Menezes (modemene) • Paulo J. S. Silva (pjssilva) • Victor Rafael da Paixão Lopes (reije) • marta mello (martamello) • vitor gurgel (vitor_gurgel) Esta diagramação foi feita por Cárlisson Galdino <[email protected]>, com algumas modificações visando a facilitar a publicação em duas colunas. Alguns comentários de depuração feitos pela equipe de tradução foram omitidos. Versão Brasileira #13
  • 14. Como pensar como um cientista da Computação usando Python Capítulo 1: O caminho do programa O objetivo deste livro é ensinar o leitor a pensar como um cientista da computação. Essa maneira de pensar combina algumas das melhores características da matemática, da engenharia e das ciências naturais. Como os matemáticos, os cientistas da computação usam linguagens formais para representar idéias (especificamente, computações). Como os engenheiros, eles projetam coisas, montando sistemas a partir de componentes e avaliando as vantagens e desvantagens de diferentes alternativas. Como os cientistas naturais, eles O compilador lê o programa e o traduz observam o comportamento de sistemas complexos, completamente antes que o programa comece a rodar. Neste formulam hipóteses e testam previsões. caso, o programa escrito em linguagem de alto nível é A habilidade mais importante de um cientista da chamado de código fonte, e o programa traduzido programa é é chamado de código objeto ou executável. Uma vez que um computação é a solução de problemas. Solução de problemas compilado, você pode executá-lo repetidamente, sem que é a habilidade de formular questões, pensar criativamente precise de nova tradução. sobre soluções possíveis e expressar uma solução de forma clara e precisa. Ocorre que aprender a programar é uma excelente oportunidade de praticar a habilidade da solução de problemas. É por isso que este capítulo se chama "O caminho do programa". Em certo nível, você estará aprendendo a programar, habilidade que é útil em si mesma. Em outro nível, você usará a programação como um meio para atingir Python é considerada uma linguagem um objetivo. À medida que você for avançando na leitura, interpretada, porque os programas em Python são executados esse objetivo ficará mais claro. por um interpretador. Existem duas maneiras de usar o interpretador: no modo de linha de comando e no modo de script. No modo de linha de comando, você digita programas em Python e o interpretador mostra o resultado: 1.1 A linguagem de programação Python $ python Python 2.4.3 (#2, Oct 6 2006, 07:49:22) Python é a linguagem de programação que você vai estudar neste livro. Python é um exemplo de linguagem de [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2 programação de alto nível; outras linguagens de alto nível Type "help", "copyright", "credits" or "license" for de que você já pode ter ouvido falar são C, C++, Perl e Java. more information. >>> print 1 + 1 Como você pode deduzir a partir da expressão 2 "linguagem de alto nível", também existem as "linguagens de baixo nível", às vezes chamadas de "linguagens de máquina" A primeira linha deste exemplo é o comando ou "linguagem assembly" ("linguagens de montagem"). Dito que inicia o interpretador Python. As três linhas seguintes são de maneira simples, o computador só consegue executar mensagens do interpretador. A quarta linha começa com >>>, programas escritos em linguagens de baixo nível. Deste que é o sinal usado pelo interpretador para indicar que ele está modo, programas escritos em linguagens de alto nível pronto. No exemplo anterior, digitamos print 1 + 1 e o precisam ser processados antes que possam rodar. Esse interpretador respondeu 2. processamento extra toma algum tempo, o que é uma pequena Você também pode escrever um programa em desvantagem em relação às linguagens de alto nível. um arquivo e usar o interpretador para executar o conteúdo Mas as vantagens são enormes. Primeiro, é desse arquivo. Um arquivo como este é chamado de script. muito mais fácil programar em uma linguagem de alto nível. Por exemplo, usamos um editor de texto para criar um É mais rápido escrever programas em uma linguagem de alto arquivo chamado leticia.py com o seguinte conteúdo: nível; eles são mais curtos e mais fáceis de ler e é mais provável que estejam corretos. Segundo, as linguagens de alto print 1 + 1 nível são portáveis, o que significa que podem rodar em Por convenção, arquivos que contenham diferentes tipos de computador, com pouca ou nenhuma programas em Python têm nomes que terminam com .py. modificação. Programas em baixo nível só podem rodar em um único tipo de computador e precisam ser re-escritos para Para executar o programa, temos de dizer ao rodar em outro tipo. interpretador o nome do script: Devido a essas vantagens, quase todos os $ python leticia.py programas são escritos em linguagens de alto nível. As de 2 baixo nível são utilizadas somente para umas poucas Em outros ambientes de desenvolvimento, os aplicações especializadas. detalhes da execução de programas podem ser diferentes. Além disso, a maioria dos programas são mais interessantes Dois tipos de programas processam linguagens do que esse... de alto nível, traduzindo-as em linguagens de baixo nível: interpretadores e compiladores. O interpretador lê um A maioria dos exemplos neste livro são programa escrito em linguagem de alto nível e o executa, ou executados a partir da linha de comando. Trabalhar com a seja, faz o que o programa diz. Ele processa o programa um linha de comando é conveniente no desenvolvimento e pouco de cada vez, alternadamente: ora lendo algumas linhas, testagem de programas, porque você pode digitar os ora realizando computações. programas e executá-los imediatamente. Uma vez que você Capítulo 1: O caminho do programa #14
  • 15. Como pensar como um cientista da Computação usando Python tem um programa que funciona, deve guardá-lo em um script, lugar, o interpretador Python vai exibir uma mensagem de de forma a poder executá-lo ou modificá-lo no futuro. erro e vai terminar - e o programa não 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 experiência, entretanto, 1.2 O que é um programa? cometerá menos erros e os localizará mais rápido. Um programa é uma seqüência de instruções que 1.3.2 Erros em tempo de execução (runtime errors) especificam como executar uma computação. A computação pode ser algo matemático, como solucionar um sistema de O segundo tipo de erro é o erro de runtime, ou erro em tempo equações ou encontrar as raízes de um polinômio, mas de execução, assim chamado porque só aparece quando você também pode ser uma computação simbólica, como buscar e roda o programa. Esses erros são também conhecidos como substituir uma palavra em um documento ou (estranhamente) exceções, porque normalmente indicam que alguma coisa compilar um programa. excepcional (e ruim) aconteceu. Os detalhes são diferentes em diferentes Erros de runtime são raros nos programas linguagens, mas algumas instruções básicas aparecem em simples que você vai ver nos primeiros capítulos - então, vai praticamente todas as linguagens: demorar um pouco até você se deparar com um erro desse tipo. entrar Pegar dados do teclado, de um arquivo ou de algum outro dispositivo. 1.3.3 Erros de semântica sair Mostrar dados na tela ou enviar dados para um arquivo ou outro dispositivo. O terceiro tipo de erro é o erro de semântica (mais comumente chamado erro de lógica). Mesmo que o seu calcular Executar operações matemáticas programa tenha um erro de semântica, ele vai rodar com básicas, como adição e multiplicação. sucesso, no sentido de que o computador não vai gerar nenhuma mensagem de erro. Só que o programa não vai fazer executar Checar certas condições e executar a a coisa certa, vai fazer alguma outra coisa. Especificamente, condicionalmente seqüência apropriada de instruções. aquilo que você tiver dito para ele fazer. repetir Executar alguma ação repetidamente, O problema é que o programa que você normalmente com alguma variação. escreveu não é aquele que você queria escrever. O significado Acredite se quiser: isso é praticamente tudo. do programa (sua semântica ou lógica) está errado. Identificar Todos os programas que você já usou, não importa quão erros semânticos pode ser complicado, porque requer que complicados, são feitos de instruções mais ou menos você trabalhe de trás para frente, olhando a saída do programa parecidas com essas. Assim, poderíamos definir programação e tentando imaginar o que ele está fazendo. como o processo de dividir uma tarefa grande e complexa em sub-tarefas cada vez menores, até que as sub-tarefas sejam 1.3.4 Depuração experimental (Debugging) simples o suficiente para serem executadas com uma dessas instruções básicas. Uma das habilidades mais importantes que você vai adquirir é Isso pode parecer um pouco vago, mas vamos a de depurar. Embora possa ser frustrante, depurar é uma das voltar a esse tópico mais adiante, quando falarmos sobre partes intelectualmente mais ricas, desafiadoras e algoritmos. interessantes da programação. De certa maneira, a depuração é como um trabalho de detetive. Você se depara com pistas, e tem que 1.3 O que é depuração (debugging)? deduzir os processos e eventos que levaram aos resultados que aparecem. Programar é um processo complicado e, como é feito por Depurar também é como uma ciência seres humanos, freqüentemente conduz a erros. Por mero experimental. Uma vez que você tem uma idéia do que está capricho, erros em programas são chamados de bugs e o errado, você modifica o seu programa e tenta de novo. Se a processo de encontrá-los e corrigi-los é chamado de sua hipótese estava correta, então você consegue prever o depuração (debugging). resultado da modificação e fica um passo mais perto de um programa que funciona. Se a sua hipótese estava errada, você Três tipos de erro podem acontecer em um tem que tentar uma nova. Como Sherlock Holmes mostrou, programa: erros de sintaxe, erros em tempo de execução "Quando você tiver eliminado o impossível, aquilo que (runtime errors) e erros de semântica. Distinguir os três tipos restou, ainda que improvável, deve ser a verdade." (Arthur ajuda a localizá-los mais rápido: Conan Doyle, O signo dos quatro). 1.3.1 Erros de sintaxe Para algumas pessoas, programação e depuração são a mesma coisa. Ou seja, programar é o Python só executa um programa se ele estiver sintaticamente processo de gradualmente depurar um programa, até que ele correto; caso contrário, o processo falha e retorna uma faça o que você quer. A idéia é começar com um programa mensagem de erro. Sintaxe se refere à estrutura de um que faça alguma coisa e ir fazendo pequenas modificações, programa e às regras sobre esta estrutura. Por exemplo, em depurando-as conforme avança, de modo que você tenha português, uma frase deve começar com uma letra maiúscula sempre um programa que funciona. e terminar com um ponto. Por exemplo, o Linux é um sistema operacional esta frase contém um erro de sintaxe. Assim que programa simples, que Linus Torvaldsmas começou como um contém milhares de linhas de código, usou para explorar como esta o chip Intel 80386. De acordo com Larry Greenfield, "Um dos Para a maioria dos leitores, uns errinhos de primeiros projetos de Linus Torvalds foi um programa que sintaxe não chegam a ser um problema significativo e é por deveria alternar entre imprimir AAAA e BBBB. Isso depois isso que conseguimos ler a poesia moderna de e. e. cummings evoluiu até o Linux". (The Linux User's Guide Versão Beta 1) sem cuspir mensagens de erro. Python não é tão indulgente. Capítulos posteriores farão mais sugestões Se o seu programa tiver um único erro de sintaxe em algum Capítulo 1: O caminho do programa #15
  • 16. Como pensar como um cientista da Computação usando Python sobre depuração e outras práticas de programação. redundância nas linguagens naturais, o que freqüentemente as torna prolixas. As linguagens formais são menos redundantes e 1.4 Linguagens naturais e linguagens formais mais concisas. literalidade As linguagens naturais estão cheias de Linguagens naturais são as linguagens que as pessoas falam, expressões idiomáticas e metáforas. Se eu como o português, o inglês e o espanhol. Elas não foram digo "Caiu a ficha", é possível que não exista projetadas pelas pessoas (muito embora as pessoas tentem ficha nenhuma, nem nada que tenha caído. colocar alguma ordem nelas); elas evoluíram naturalmente. Nas linguagens formais, não há sentido ambíguo. Linguagens formais são linguagens que foram Pessoas que crescem falando uma linguagem projetadas por pessoas, para aplicações específicas. Por natural -- ou seja, todo mundo - muitas vezes têm dificuldade exemplo, a notação que os matemáticos usam é uma de se acostumar com uma linguagem formal. De certa linguagem formal, que é particularmente boa em denotar maneira, a diferença entre linguagens formais e naturais é relações entre números e símbolos. Os químicos usam uma como a diferença entre poesia e prosa, porém mais acentuada: linguagem formal para representar a estrutura química das moléculas. E, mais importante: poesia As palavras são usadas pela sua sonoridade, Linguagens de programação são linguagens além de seus sentidos, e o poema como um formais que foram desenvolvidas para expressar todo cria um efeito ou uma reação emocional. computações. A ambigüidade não é apenas freqüente, mas na maioria das vezes, proposital. As linguagens formais tendem a ter regras estritas quanto à sintaxe. Por exemplo, 3 + 3 = 6 é uma prosa O sentido literal das palavras é mais expressão matemática sintaticamente correta, mas 3=+6$ não importante, e a estrutura contribui mais para o é. H2O é um nome químico sintaticamente correto, mas 2Zz significado. A prosa é mais fácil de analisar do não é. que a poesia, mas ainda é muitas vezes ambígua. As regras de sintaxe são de dois tipos, um relacionado aos tokens, outro à estrutura. "Tokens" são os programas O significado de um programa de computador elementos básicos da linguagem, como as palavras, números, é exato e literal, e pode ser inteiramente e elementos químicos. Um dos problemas com 3=+6$ é que $ entendido pela análise de seus tokens e de sua não é um token válido em linguagem matemática (pelo menos estrutura. até onde sabemos). Do mesmo modo, 2Zz é inválida porque Aqui vão algumas sugestões para a leitura de não existe nenhum elemento cuja abreviatura seja Zz. programas (e de outras linguagens formais). Primeiro, lembre- O segundo tipo de erro de sintaxe está se de que linguagens por formais são muito mais densas do que relacionado à estrutura de uma expressão -- quer dizer, ao linguagens naturais, muito isso, é mais demorado lê-las. A modo como os tokens estão arrumados. A expressão 3=+6$ é estrutura, também, éde cima para baixo, da geralmente não a importante, logo, é estruturalmente inválida, porque você não pode colocar um uma boa idéia ler esquerda para sinal de "mais" imediatamente após um sinal de "igual". Do direita. Em vez disso, os tokens e interpretando a estrutura. aprenda a analisar o programa na sua mesmo modo, fórmulas moleculares devem ter índices cabeça, identificando subscritos colocados depois do nome do elemento, não antes. Finalmente, ortográficos e má importantes.com as quais você como, erros os detalhes são pontuação, Pequenas coisas, Faça este exercício: crie o que pareça ser uma pode se safar nas linguagens naturais, podem fazer uma frase bem estruturada em português com "tokens" grande diferença em uma linguagem formal. irreconhecíveis dentro dela. Depois escreva outra frase com todos os "tokens" válidos, mas com uma estrutura inválida. Quando você lê uma frase em português ou 1.5 O primeiro programa uma expressão em uma linguagem formal, você tem de imaginar como é a estrutura da frase (embora, em uma Tradicionalmente, o primeiro programa escrito linguagem natural, você faça isso inconscientemente). Este em uma nova linguagem de programação é chamado de "Alô, processo é chamado parsing (análise sintática). Mundo!" porque tudo que ele faz é apresentar as palavras Por exemplo, quando você ouve a frase, "Caiu a "Alô, Mundo!". Em Python, ele é assim: ficha", entende que "a ficha" é o sujeito e "caiu" é o verbo. print "Alô, Mundo!" Uma vez que você analisou a frase, consegue entender o seu significado, ou a semântica da frase. Assumindo que você Isso é um exemplo de um comando print, que, saiba o que é uma ficha e o que significa cair, você entenderá na realidade, não "imprime" nada em papel. Ele apresenta o o sentido geral dessa frase. valor na tela. Neste caso, o resultado são as palavras: Muito embora as linguagens formais e as Alô, Mundo! naturais tenham muitas características em comum -- tokens, As aspas no programa marcam o começo e o estrutura, sintaxe e semântica -- existem muitas diferenças: fim do valor; elas não aparecem no resultado final. ambigüidade As linguagens naturais estão cheias de Algumas pessoas julgam a qualidade de uma ambigüidades, que as pessoas contornam linguagem de programação pela simplicidade do programa usando pistas contextuais e outras "Alô, Mundo!". Por esse padrão, Python se sai tão bem informações. Já as linguagens formais são quanto possível. desenvolvidas para serem quase ou totalmente desprovidas de ambigüidade, o que significa que qualquer expressão tem 1.6 Glossário precisamente só um sentido, independentemente do contexto. solução de O processo de formular um problema, redundância Para compensar a ambigüidade e reduzir problemas encontrar uma solução e expressar esta mal-entendidos, emprega-se muita Capítulo 1: O caminho do programa #16
  • 17. Como pensar como um cientista da Computação usando Python (problem solução. (algorithm) categoria de problemas. solving) bug Erro em um programa. linguagem de Uma linguagem de programação como alto nível (high- Python: projetada para ser fácil para os depuração O processo de encontrar e remover level language) seres humanos a utilizarem. (debugging) qualquer um dos três tipos de erros de programação. linguagem de Uma linguagem de programação que é baixo nível (low- concebida para ser fácil para um sintaxe (syntax) A estrutura de um programa. level language) computador, tal como a linguagem de erro de sintaxe Erro em um programa, que torna máquina ou a linguagem montagem (syntax error) impossível a análise sintática (logo, (assembly language) também impossível a interpretação). portabilidade Propriedade que um programa tem, de erro em tempo Erro que não ocorre até que o programa (portability) rodar em mais de um tipo de de execução seja executado, mas que impede que o computador. (runtime error) programa continue. interpretar Executar um programa escrito em uma exceção Um outro nome para um erro em tempo (interpret) linguagem de alto nível, traduzindo-o (exception) de execução ou erro de runtime. uma linha de cada vez. erro de Erro em um programa, que o leva a fazer compilar Traduzir todo um programa escrito em semântica algo diferente do que pretendia o (compile) uma linguagem de alto nível para uma de (semantic error) programador. baixo nível de um só vez, em preparação para uma execução posterior. semântica O significado de um programa. código fonte Um programa em uma linguagem de alto (semantics) (source code) nível, antes de ter sido compilado. linguagem Qualquer língua falada pelos seres natural (natural humanos que tenha evoluído código objeto A saída do compilador, depois que ele language) naturalmente. (object code) traduziu o programa. executável Um outro nome para código objeto que linguagem Qualquer linguagem desenvolvida pelas (executable) está pronto para ser executado. formal (formal pessoas para propósitos específicos, tais language) como, a representação de idéias script Um programa guardado em um arquivo matemáticas ou programas de (normalmente um que será interpretado). computadores; todas as linguagens de programação são linguagens formais. programa Conjunto de instruções que especifica (program) uma computação. átomo (token) Um elemento básico da estrutura sintática de um programa, análogo a uma palavra em uma linguagem natural. análise sintática Examinar um programa e analisar sua (parse) estrutura sintática. comando print Instrução que leva o interpretador Capítulo 1: O caminho do programa #17
  • 18. Como pensar como um cientista da Computação usando Python (`print` Python a apresentar um valor na tela. statement) Capítulo 1: O caminho do programa #18
  • 19. Como pensar como um cientista da Computação usando Python Capítulo 2: Variáveis, expressões e comandos variável é um nome que se refere a um valor. 2.1 Valores e tipos O comando de atribuição cria novas variáveis e O valor (por exemplo, letras e números) é uma das coisas dá a elas valores: fundamentais que um programa manipula. Os valores que já >>> mensagem = "E aí, Doutor?" vimos até agora foram o 2 (como resultado, quando adicionamos 1 + 1) e "Alô, Mundo!". >>> n = 17 >>> pi = 3.14159 Esses valores pertencem a tipos diferentes: 2 é Este exemplo faz três atribuições. A primeira um inteiro, e "Alô, Mundo!" é uma string, assim chamada atribui a string "E aí, Doutor?" a uma nova variável chamada porque "string", em inglês, quer dizer seqüência, série, cadeia mensagem. A segunda dá o valor inteiro 17 a n, e a terceira (de caracteres), ou neste caso, "série de letras". Você (e o atribui o número de ponto flutuante 3.14159 à variável interpretador) consegue identificar strings porque elas chamada pi. aparecem entre aspas. Uma maneira comum de representar variáveis O comando print também funciona com no papel é escrever o nome delas com uma seta apontando inteiros: para o valor da variável. Esse tipo de figura é chamado de >>> print 4 diagrama de estado porque mostra em que estado cada variável está (pense nisso como o estado de espírito da 4 variável). O diagrama a seguir mostra o resultado das Se você estiver em dúvida sobre qual é o tipo de instruções de atribuição: um determinado valor, o interpretador pode revelar: >>> type("Alô, Mundo!") <type 'string'> >>> type(17) <type 'int'> Nenhuma surpresa: strings pertencem ao tipo string e inteiros pertencem ao tipo int. Menos obviamente, números com um ponto decimal pertencem a um tipo chamado float, porque estes números são representados em um formato chamado ponto flutuante1: >>> type(3.2) O comando print também funciona com <type 'float'> variáveis: O que dizer de valores como "17" e "3.2"? Eles >>> print mensagem parecem números, mas estão entre aspas, como strings: E aí, Doutor? >>> type("17") >>> print n <type 'string'> 17 >>> type("3.2") >>> print pi <type 'string'> 3.14159 Eles são strings. Em cada um dos casos, o resultado é o valor da variável. Variáveis também têm tipo; novamente, podemos Ao digitar um número grande, é tentador usar perguntar ao interpretador quais são eles: pontos entre grupos de três dígitos, assim: 1.000.000. Isso não funciona porque Python usa o ponto como separador decimal. >>> type(mensagem) Usar a vírgula, como se faz em inglês, resulta numa expressão <type 'string'> válida, mas não no número que queríamos representar: >>> type(n) >>> print 1,000,000 <type 'int'> 1 0 0 >>> type(pi) <type 'float'> Não é nada do que se esperava! Python interpreta 1,000,000 como uma tupla, algo que veremos no O tipo de uma variável é o tipo do valor ao qual Capítulo 9. Por hora, lembre-se apenas de não colocar vírgulas ela se refere. nos números. 2.3 Nomes de variáveis e palavras reservadas 2.2 Variáveis Os programadores geralmente escolhem nomes significativos Uma das características mais poderosas de uma linguagem de para suas variáveis -- eles documentam para o quê a variável é programação é a habilidade de manipular variáveis. Uma usada. Nomes de variáveis podem ser arbitrariamente 1 N.T.: Observe o uso de ponto no lugar da vírgula para longos. Eles podem conter tanto letras quanto números, mas separar a parte inteira da parte fracionária. Capítulo 2: Variáveis, expressões e comandos #19
  • 20. Como pensar como um cientista da Computação usando Python têm de começar com uma letra. Embora seja válida a comando, o interpretador avalia e exibe o resultado: utilização de letras maiúsculas, por convenção, não usamos. Se você o fizer, lembre-se de que maiúsculas e minúsculas são >>> 1 + 1 diferentes. Bruno e bruno são variáveis diferentes. 2 O caractere para sublinhado ( _ ) pode aparecer Embora expressões contenham valores, em um nome. Ele é muito utilizado em nomes com múltiplas variáveis e operadores, nem toda expressão contém todos estes palavras, tal como em meu_nome ou preco_do_cha_na_china. elementos. modovalor uma variável: do mesmo Um que por si só é considerado uma expressão, Se você der a uma variável um nome inválido, causará um erro de sintaxe: >>> 17 17 >>> 76trombones = "grande parada" >>> x SyntaxError: invalid syntax 2 >>> muito$ = 1000000 Avaliar uma expressão não é exatamente a SyntaxError: invalid syntax mesma coisa que imprimir um valor: >>> class = "Ciencias da Computacao 101" >>> mensagem = "E aí, Doutor?" SyntaxError: invalid syntax >>> mensagem 76trombones é inválida porque não começa com uma letra. muito$ é inválida porque contém um caractere 'E aí, Doutor?' ilegal, o cifrão. Mas o que está errado com class? >>> print mensagem E aí, Doutor? Ocorre que class é uma das palavras reservadas em Python. Palavras reservadas definem as regras usa o mesmo formato que você usaria valorentrar com o valor. Quando Python exibe o para de uma expressão, e a estrutura da linguagem e não podem ser usadas como No caso de strings, isso significa que as aspas são incluídas nomes de variáveis. [#]_. Mas o comando print imprime o valor da expressão, que, Python tem 29 palavras reservadas: neste caso, é o conteúdo da string. and def exec if not return Num script, uma expressão sozinha é um assert del finally import or try comando válido, porém sem efeito. O script: break elif for in pass while 17 class else from is print yield 3.2 continue except global lambda raise "Alô, Mundo!" Pode ser útil ter essa lista à mão. Se o 1 + 1 interpretador acusar erro sobre um de seus nomes de variável não produz qualquer saída. Como você mudaria e você não souber porquê, veja se o nome está na lista. o "script" para exibir os valores destas quatro expressões? 2.4 Comandos 2.6 Operadores e operandos Um comando é uma instrução que o interpretador Python Operadores são símbolos especiais que representam pode executar. Vimos até agora dois tipos de comandos: de computações como adição e multiplicação. Os valores que o exibição (print) e de atribuição. operador usa são chamados operandos. Quando você digita um comando na linha de Todas as expressões seguintes são válidas em comando, o Python o executa e mostra o resultado, se houver Python e seus significados são mais ou menos claros: um. O resultado de um comando print é a exibição de um valor. Comandos de atribuição não produzem um resultado 20+32 hora-1 hora*60+minuto minuto/60 5**2 visível. (5+9)*(15-7) Um script normalmente contém uma seqüência Em Python, os símbolos +, -, / e o uso de de comandos. Se houver mais de um comando, os resultados parênteses para agrupamento têm o mesmo significado que em aparecerão um de cada vez, conforme cada comando seja matemática. O asterisco (*) é o símbolo para multiplicação e executado. ** é o símbolo para potenciação. Por exemplo, o "script": Quando um nome de variável aparece no lugar de um operando, ele é substituído pelo valor da variável, antes print 1 da operação ser executada. x = 2 Adição, subtração, multiplicação e potenciação print 2 fazem o que se espera, mas você pode ficar surpreso com a produz a saída: divisão. A operação seguinte tem um resultado inesperado: 1 >>> minuto = 59 2 >>> minuto/60 Novamente, o comando de atribuição não 0 produz saída. O valor de minuto é 59 e, em aritmética convencional, 59 dividido por 60 é 0,98333, não 0. A razão para a discrepância é que Python está realizando uma divisão 2.5 Avaliando expressões inteira. Quando ambos os operandos são inteiros, o Uma expressão é uma combinação de valores, variáveis e resultado tem de ser também um inteiro e, por convenção, a operadores. Se você digitar uma expressão na linha de divisão inteira sempre arredonda para baixo, mesmo em casos Capítulo 2: Variáveis, expressões e comandos #20
  • 21. Como pensar como um cientista da Computação usando Python como este, em que o inteiro seguinte está muito próximo: como 4*3 equivale a 4+4+4, não é de estranhar que "Legal"*3 seja o mesmo que "Legal"+"Legal"+"Legal". Por outro lado, >>> minuto*100/60 uma diferença significativa separa concatenação e repetição de 98 adição e multiplicação. Você saberia mencionar uma De novo, o resultado é arredondado para baixo, propriedade da adição e da multiplicação que não ocorre na mas agora pelo menos a resposta é aproximadamente correta. concatenação e na repetição? A alternativa é usar a divisão em ponto flutuante, o que veremos no capítulo 3. 2.9 Composição 2.7 Ordem dos operadores Até agora, vimos os elementos de um programa -- variáveis, expressões, e instruções ou comandos -- isoladamente, sem Quando mais de um operador aparece em uma expressão, a mencionar como combiná-los. ordem de avaliação depende das regras de precedência. Uma das características mais práticas das Python segue as mesmas regras de precedência para seus linguagens de programação é a possibilidade de pegar operadores matemáticos que a matemática. O acrônimo pequenos blocos e combiná-los numa composição. Por PEMDAS é uma maneira prática de lembrar a ordem das exemplo, nós sabemos como somar números e sabemos como operações: exibi-los; acontece que podemos fazer as duas coisas ao ● Parênteses têm a mais alta precedência e podem ser mesmo tempo: usados para forçar uma expressão a ser avaliada na >>> print 17 + 3 ordem que você quiser. Já que expressões entre parênteses são avaliadas primeiro, 2 * (3-1) é 4, e 20 (1+1)**(5-2) é 8. Você também pode usar parênteses Na realidade, a soma tem que acontecer antes da para tornar uma expressão mais fácil de ler, como em impressão, assim, as ações não estão na realidade acontecendo (minuto * 100) / 60, ainda que isso não altere o ao mesmo tempo. O ponto é que qualquer expressão resultado. envolvendo números, strings, e variáveis pode ser usada dentro de um comando print. Você já tinha visto um exemplo ● Exponenciação ou potenciação tem a próxima disto: precedência mais alta, assim 2**1+1 é 3 e não 4, e 3*1**3 é 3 e não 27. print "Número de minutos desde a meia-noite: ", hora*60+minuto ● Multiplicação e Divisão têm a mesma precedência, que é mais alta do que a da Adição e da Subtração, Esta possibilidade pode não parecer muito que também têm a mesma precedência. Assim 2*3-1 impressionante agora, mas você verá outros exemplos em que dá 5 em vez de 4, e 2/3-1 é -1, não 1 (lembre-se de a composição torna possível expressar computações que na divisão inteira, 2/3=0). complexas de modo limpo e conciso. ● Operadores com a mesma precedência são avaliados Atenção: Existem limites quanto ao lugar onde da esquerda para a direita. Assim, na expressão você pode usar certos tipos de expressão. Por exemplo, o lado minuto*100/60, a multiplicação acontece primeiro, esquerdo de um comando de atribuição tem que ser um nome resultando em 5900/60, o que se transforma de variável, e não uma expressão. Assim, o seguinte não é produzindo 98. Se as operações tivessem sido válido: minuto+1 = hora. avaliadas da direita para a esquerda, o resultado poderia ter sido 59*1, que é 59, que está errado. 2.11 Glossário 2.8 Operações com strings valor (value) Um número ou string (ou outra coisa que ainda vamos conhecer) que pode ser De maneira geral, você não pode executar operações atribuída a uma variável ou computada em matemáticas em strings, ainda que as strings se pareçam com uma expressão. números. O que segue é inválido (assumindo que mensagem é do tipo string): tipo (type) Um conjunto de valores. O tipo de um valor determina como ele pode ser usado mensagem-1 "Alô"/123 mensagem*"Alô" "15"+2 em expressões. Até agora, os tipos vistos Interessante é o operador +, que funciona com são: inteiros (tipo int), números em ponto- strings, embora ele não faça exatamente o que você poderia flutuante (tipo float) e strings (tipo string). esperar. Para strings, o operador + representa concatenação, ponto- Formato para representar números que que significa juntar os dois operandos ligando-os pelos flutuante possuem partes fracionárias. extremos. Por exemplo: (floating-point) fruta = "banana" variável Nome que se refere a um valor. assada = " com canela" (variable) print fruta + assada A saída deste programa é banana com canela. O comando Trecho de código que representa uma espaço antes da palavra com é parte da string e é necessário (statement) instrução ou ação. Até agora, os comandos para produzir o espaço entre as strings concatenadas. vistos foram de atribuição e exibição. O operador * também funciona com strings; ele atribuição Comando que atribui um valor a uma realiza repetição. Por exemplo, "Legal"*3 é (assignment) variável. "LegalLegaLegal". Um dos operadores tem que ser uma diagrama de Representação gráfica de um conjunto de string; o outro tem que ser um inteiro. estado (state variáveis e os valores aos quais elas se diagram) referem. Por um lado, esta interpretação de + e * faz sentido pela analogia entre adição e multiplicação. Assim Capítulo 2: Variáveis, expressões e comandos #21
  • 22. Como pensar como um cientista da Computação usando Python palavra-chave Palavra reservada usada pelo compilador (keyword) para analisar o programa; você não pode usar palavras-chave como if, def, e while como nomes de variáveis. operador Símbolo especial que representa uma (operator) computação simples, como adição, multiplicação ou concatenação de strings. operando Um dos valores sobre o qual o operador (operand) opera. expressão Combinação de variáveis, operadores e (expression) valores, que representa um resultado único. avaliar Simplificar uma expressão através da (evaluate) realização de operações, para produzir um valor único. divisão inteira Operação que divide um inteiro por outro e (integer resulta em um inteiro. A divisão inteira division) resulta no número de vezes que o numerador é divisível pelo denominador e descarta qualquer resto. regras de O conjunto de regras que governa a ordem precedência em que expressões envolvendo múltiplos (rules of operadores e operandos são avaliadas. precedence) concatenar Juntar dois operandos lado a lado. (concatenate) composição Habilidade de combinar expressões e (composition) comandos simples em expressões e comandos compostos, de forma a representar computações complexas de forma concisa. comentário Informação em um programa dirigida a (comment) outros programadores (ou qualquer pessoa que esteja lendo o código fonte) e que não tem efeito na execução do programa. Capítulo 2: Variáveis, expressões e comandos #22
  • 23. Como pensar como um cientista da Computação usando Python Capítulo 3: Funções '32' 3.1 Chamadas de funções >>> str(3.14149) '3.14149' Você já viu um exemplo de uma chamada de função: Pode parecer curioso que Python faça distinção >>> type('32') entre o valor inteiro 1 e o valor em ponto flutuante 1.0. Eles <type 'str'> podem representar o mesmo número, mas pertencem a tipos diferentes. A razão é que eles são representados de modo O nome da função é type e ela exibe o tipo de diferente dentro do computador. um valor ou variável. O valor ou variável, que é chamado de argumento da função, tem que vir entre parênteses. É comum se dizer que uma função 'recebe' um valor e 'retorna' um resultado. O resultado é chamado de valor de retorno. 3.3 Coerção entre tipos Em vez de imprimir um valor de retorno, podemos atribui-lo a uma variável: Agora que podemos converter entre tipos, temos outra maneira de lidar com a divisão inteira. Voltando ao exemplo >>> bia = type('32') do capítulo anterior, suponha que queiramos calcular a fração >>> print bia de hora que já passou. A expressão mais óbvia, minuto / 60, <type 'str'> faz aritmética inteira, assim, o resultado é sempre 0, mesmo aos 59 minutos passados da hora. Como outro exemplo, a função id recebe um valor ou uma variável e retorna um inteiro, que atua como um Uma solução é converter minuto para ponto identificador único para aquele valor: flutuante e fazer a divisão em ponto flutuante: >>> id(3) >>> minuto = 59 134882108 >>> float(minuto) / 60 >>> bia = 3 0.983333333333 >>> bia(beth) Opcionalmente, podemos tirar vantagem das 134882108 regras de conversão automática entre tipos, chamada de Todo valor tem um id, que é um número único coerção de tipos. Para os operadores matemáticos, se relacionado ao local onde ele está guardado na memória do qualquer operando for um float, o outro é automaticamente computador. O id de uma variável é o id do valor a qual ela se convertido para float: refere. >>> minuto = 59 >>> minuto / 60.0 0.983333333333 3.2 Conversão entre tipos Fazendo o denominador um float, forçamos o Python a fazer a divisão em ponto flutuante. Python provê uma coleção de funções nativas que convertem valores de um tipo em outro. A função int recebe um valor e o converte para inteiro, se possível, ou, se não, reclama: 3.4 Funções matemáticas >>> int('32') 32 Em matemática, você provavelmente já viu funções como >>> int('Alô') seno (sin) e log, e aprendeu a resolver expressões como ValueError: invalid literal for int() : Alô sin(pi/2) e log(1/x). Primeiro você resolve e expressão entre parênteses (o argumento). Por exemplo, pi/2 é int também pode converter valores em ponto aproximadamente 1,571, e 1/x é 0.1 (se x for 10,0). flutuante para inteiro, mas lembre que isso trunca a parte fracionária: Aí você avalia a função propriamente dita, seja procurando numa tabela ou realizando vários cálculos. O sin >>> int(3.99999) de 1,571 é 1 e o log de 0,1 é -1 (assumindo que log indica o 3 logaritmo na base 10). >>> int(-2.3) -2 Este processo pode ser aplicado repetidamente para avaliar expressões mais complicadas, como A função float converte inteiros e strings em log(1/sin(pi/2)). Primeiro você avalia o argumento na função números em ponto flutuante: mais interna, depois avalia a função e assim por diante. >>> float(32) Python tem um módulo matemático que provê a 32.0 maioria das funções matemáticas mais familiares. Um módulo >>> float('3.14159') é um arquivo que contém uma coleção de funções 3.14159 relacionadas agrupadas juntas. Finalmente, a função str converte para o tipo Antes de podermos usar as funções contidas em string: um módulo, temos de importá-lo: >>> str(32) >>> import math Capítulo 3: Funções #23
  • 24. Como pensar como um cientista da Computação usando Python Para chamar uma das funções, temos que lista de parâmetros especifica que informação, se houver especificar o nome do módulo e o nome da função, separados alguma, você tem que fornecer para poder usar a nova função. por um ponto. Esse formato é chamado de notação de ponto: Uma função pode ter quantos comandos forem >>> decibel = math.log10(17.0) necessários, mas eles precisam ser endentados a partir da >>> angulo = 1.5 margem esquerda. Nos exemplos deste livro, usaremos uma >>> altura = math.sin(angulo) endentação de dois espaços. A primeira instrução atribui a decibel o As primeiras funções que vamos mostrar não logaritmo de 17 na base 10. Existe também uma função terão parâmetros, então, a sintaxe terá esta aparência: chamada log, que pega o logaritmo na base e. def novaLinha(): A terceira instrução encontra o seno do valor da print variável angulo. sin e as outras funções trigonométricas (cós, tan, etc.) recebem argumentos em radianos. Para converter de Esta função é chamada de novaLinha. Os graus em radianos, divida por 360 e multiplique por 2*pi. Por parênteses vazios indicam que ela não tem parâmetros. exemplo, para encontrar o seno de 45 graus, primeiro calcule Contém apenas um único comando, que gera como saída um o ângulo em radianos e depois ache o seno: caractere de nova linha (isso é o que acontece quando você usa um comando print sem qualquer argumento). >>> graus = 45 A sintaxe para a chamada desta nova função é a >>> angulo = graus * 2 * math.pi / 360.0 mesma sintaxe para as funções nativas: >>> math.sin(angulo) 0.707106781187 print 'Primeira Linha.' A constante pi também é parte do módulo math. novaLinha() Se você sabe geometria, pode checar o resultado anterior print 'Segunda Linha.' comparando-o com a raiz quadrada de dois dividido por dois: A saída deste programa é: >>> math.sqrt(2) / 2.0 Primeira Linha. 0.707106781187 Segunda Linha. Observe o espaço extra entre as duas linhas. E 3.5 Composição se quiséssemos mais espaço entre as linhas? Poderíamos chamar a mesma função repetidamente: Do mesmo modo como nas funções matemáticas, as funções print 'Primeira Linha.' do Python podem ser compostas, o que significa que você novaLinha() pode usar uma expressão como parte de outra. Por exemplo, novaLinha() você pode usar qualquer expressão como um argumento para uma função: novaLinha() print 'Segunda Linha.' >>> x = math.cos(angulo + pi/2) Ou poderíamos escrever uma nova função Esta instrução toma o valor de pi, divide-o por chamada tresLinhas, que produzisse três novas linhas: 2, e soma o resultado ao valor de angulo. A soma é então passada como um argumento para a função cos. def tresLinhas() : novaLinha() Você também pode pegar o resultado de uma novaLinha() função e passá-lo como um argumento para outra: novaLinha() >>> x = math.exp(math.log(10.0)) Esta instrução encontra o logaritmo base e de 10 print 'Primeira Linha.' e então eleva e àquela potência. O resultado é atribuído a x. tresLinhas() print 'Segunda Linha.' Esta função contém três comandos, todos com 3.6 Adicionando novas funções recuo de dois espaços a partir da margem esquerda. Já que o próximo comando não está endentado, Python reconhece que ele não faz parte da função. Até aqui, temos utilizado somente as funções que vêm com Python, mas também é possível adicionar novas funções. Criar Algumas coisas que devem ser observadas sobre novas funções para resolver seus próprios problemas é uma este programa: das coisas mais úteis de uma linguagem de programação de propósito geral. 1. Você pode chamar o mesmo procedimento repetidamente. Isso é muito comum, além de útil. No contexto de programação, função é uma seqüência nomeada de instruções ou comandos, que realizam 2. Você pode ter uma função chamando outra função; uma operação desejada. Esta operação é especificada numa neste caso tresLinhas chama novaLinha. definição de função. Até agora, as funções que usamos neste livro são pré-definidas e suas definições não foram Pode não estar claro, até agora, de que vale o apresentadas. Isso demonstra que podemos usar funções sem esforço de criar novas funções - existem várias razões, mas ter que nos preocupar com os detalhes de suas definições. este exemplo demonstra duas delas: A sintaxe para uma definição de função é: ● Criar uma nova função permite que você coloque nome em um grupo de comandos. As funções podem def NOME( LISTA DE PARAMETROS ) : simplificar um programa ao ocultar uma computação COMANDOS complexa por trás de um simples comando cujo nome pode ser uma palavra em português, em vez de Você pode usar o nome que quiser para as algum código misterioso. funções que criar, exceto as palavras reservadas do Python. A Capítulo 3: Funções #24
  • 25. Como pensar como um cientista da Computação usando Python ● Criar uma nova função pode tornar o programa lembra que uma função pode chamar outra. Enquanto estiver menor, por eliminar código repetido. Por exemplo, no meio de uma função, o programa poderia ter de executar os um atalho para 'imprimir' nove novas linhas comandos em uma outra função. Mas enquanto estivesse consecutivas é chamar tresLinhas três vezes. executando esta nova função, o programa poderia ter de executar ainda outra função! Como exercício, escreva uma função chamada noveLinhas que use tresLinhas para imprimir nove linhas Felizmente, Python é adepto de monitorar a em branco. Como você poderia imprimir vinte e sete novas posição onde está, assim, cada vez que uma função se linhas? completa, o programa retoma de onde tinha parado na função que a chamou. Quando chega ao fim do programa, ele termina. 3.7 Definições e uso Qual a moral dessa história sórdida? Quando você ler um programa, não o leia de cima para baixo. Em vez Reunindo os fragmentos de código da Seção 3.6, o programa disso, siga o fluxo de execução. completo fica assim: def novaLinha() : 3.9 Parâmetros e argumentos print Algumas das funções nativas que você já usou requerem def tresLinhas() : argumentos, aqueles valores que controlam como a função faz novaLinha() seu trabalho. Por exemplo, se você quer achar o seno de um novaLinha() número, você tem que indicar qual número é. Deste modo, sin novaLinha() recebe um valor numérico como um argumento. Algumas funções recebem mais de um print 'Primeira Linha.' argumento. Por exemplo, pow recebe dois argumentos, a base tresLinhas() e o expoente. Dentro da função, os valores que lhe são print 'Segunda Linha.' passados são atribuídos a variáveis chamadas parâmetros. Esse programa contém duas definições de Veja um exemplo de uma função definida pelo funções: novaLinha e tresLinhas. Definições de funções são usuário, que recebe um parâmetro: executadas como quaisquer outros comandos, mas o efeito é criar a nova função. Os comandos dentro da definição da def imprimeDobrado(bruno): função não são executados até que a função seja chamada, print bruno, bruno logo, a definição da função não gera nenhuma saída. Esta função recebe um único argumento e o Como você já deve ter imaginado, é preciso atribui a um parâmetro chamado bruno. O valor do parâmetro criar uma função antes de poder executá-la. Em outras (a essa altura, não sabemos qual será) é impresso duas vezes, palavras, a definição da função tem que ser executada antes seguido de uma nova linha. Estamos usando bruno para que ela seja chamada pela primeira vez. mostrar que o nome do parâmetro é decisão sua, mas claro que é melhor escolher um nome que seja mais ilustrativo. Como exercício, mova as últimas três linhas deste programa para o topo, de modo que a chamada da A função imprimeDobrado funciona para função apareça antes das definições. Rode o programa e veja qualquer tipo que possa ser impresso: que mensagem de erro você terá. >>> imprimeDoobrado('Spam') Também a título de exercício, comece com a Spam Spam versão que funciona do programa e mova a definição de >>> imprimeDobrado(5) novaLinha para depois da definição de tresLinhas. O que 5 5 acontece quando você roda este programa? >>> imprimeDobrado(3.14159) 3.14159 3.14159 Na primeira chamada da função, o argumento é 3.8 Fluxo de execução uma string. Na segunda, é um inteiro. Na terceira é um float. Para assegurar que uma função esteja definida antes do seu As mesmas regras de composição que se primeiro uso, é preciso saber em que ordem os comandos são aplicam a funções nativas também se aplicam às funções executados, o que é chamado de fluxo de execução. definidas pelo usuário, assim, podemos usar qualquer tipo de expressão como um argumento para imprimeDobrado: A execução sempre começa com o primeiro comando do programa. Os comandos são executados um de >>> imprimeDobrado('Spam'*4) cada vez, pela ordem, de cima para baixo. SpamSpamSpamSpam SpamSpamSpamSpam >>> imprimeDobrado(math.cos(math.pi)) As definições de função não alteram o fluxo de execução do programa, mas lembre-se que comandos dentro -1.0 -1.0 da função não são executados até a função ser chamada. Como acontece normalmente, a expressão é Embora não seja comum, você pode definir uma função avaliada antes da execução da função, assim imprimeDobrado dentro de outra. Neste caso, a definição mais interna não é imprime SpamSpamSpamSpam SpamSpamSpamSpam em executada até que a função mais externa seja chamada. vez de 'Spam'*4 'Spam'*4. Chamadas de função são como um desvio no Como exercício, escreva um chamada a fluxo de execução. Em vez de ir para o próximo comando, o imprimeDobrado que imprima 'Spam'*4 'Spam'*4. Dica: fluxo salta para a primeira linha da função chamada, executa strings podem ser colocadas tanto entre aspas simples todos os comandos lá e então volta atrás para retomar de onde quanto duplas e o tipo de aspas que não for usado para havia deixado. envolver a string pode ser usado dentro da string, como parte dela. Parece muito simples, até a hora em que você Capítulo 3: Funções #25
  • 26. Como pensar como um cientista da Computação usando Python Também podemos usar uma variável 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 música do grupo humorístico britânico 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 variável que passamos como um argumento (miguel) não tem nada a ver com o nome do parâmetro (bruno). Não importa de que modo o valor foi chamado de onde veio (do 'chamador'); A ordem da pilha mostra o fluxo de execução. aqui, em imprimeDobrado, chamamos a todo mundo de imprimeDobrado foi chamado por concatDupla, e bruno. concatDupla foi chamado por __main__ (principal), que é um nome especial para a função mais no topo. Quando você cria uma variável fora de qualquer função, ela pertence à __main__. 3.10 Variáveis e parâmetros são locais Cada parâmetro se refere ao mesmo valor que o Quando você cria uma variável local dentro de uma função, seu argumento correspondente. Assim, parte1 tem o mesmo ela só existe dentro da função e você não pode usá-la fora de valor de canto1, parte2 tem o mesmo valor de canto2 e bruno lá. Por exemplo: tem o mesmo valor de concat. def concatDupla(parte1, parte2) Se um erro acontece durante uma chamada de função, Python imprime o nome da função, e o nome da concat = parte1 + parte2 função que a chamou, e o nome da função que chamou a que imprimeDobrado(concat) chamou, percorrendo todo o caminho de volta a __main__. Esta função recebe dois argumentos, concatena- os, e então imprime o resultado duas vezes. Podemos chamar Por exemplo, se tentássemos acessar concat de a função com duas strings: dentro de imprimeDobrado, teríamos um NameError: >>> canto1 = 'Pie Jesu domine, ' Traceback (innermost last): >>> canto2 = 'dona eis requiem. ' File "teste.py", line 13, in __main__ >>> concatDupla(canto1, canto2) concatDupla(canto1, canto2) Pie Jesu domine, Dona eis requiem. Pie Jesu domine, File "teste.py", line 5, in concatDupla Dona eis requiem. imprimeDobrado(concat) File "teste.py", line 9, in imprimeDobrado Quando a função concatDupla termina, a variável concat é destruída. Se tentarmos imprimi-la, teremos print concat um erro: NameError: concat Esta lista de funções é chamada de traceback. >>> print concat Ela mostra em qual arquivo de programa o erro ocorreu, em NameError: concat que linha, e quais funções estavam sendo executadas naquele Parâmetros são sempre locais. Por exemplo, momento. Mostra também a linha de código que causou o fora da função imprimeDobrado, não existe alguma coisa erro. chamada bruno. Se você tentar utilizá-la, Python vai reclamar. Note a similaridade entre o traceback e o diagrama da pilha; não é coincidência. 3.11 Diagramas da pilha 3.12 Funções com resultados Para entender que variáveis 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 variável, A essa altura, você deve ter percebido que algumas das mas também a função à qual cada variável pertence. funções que estamos usando, tais como as funções matemáticas, produzem resultados. Outras funções, como Cada função é representada por um frame novaLinha, executam uma ação, mas não retornam um valor. (quadro). Um frame é uma caixa com o nome de uma função O que levanta algumas questões: ao lado dela e os parâmetros e variáveis da função dentro dela. O diagrama de pilha para o exemplo anterior tem a seguinte 1. O que acontece se você chama uma função e não faz aparência: nada com o resultado (por exemplo, não atribui o resultado a uma variável ou o usa como parte de uma expressão maior)? 2. O que acontece se você usa uma função que não produz resultado em uma expressão tal como novaLinha() + 7? 3. Você pode escrever funções que produzem resultados, ou está preso a funções como novaLinha e imprimeDobrado? A resposta para a terceira questão é afirmativa e nós vamos fazer isso no Capítulo 5. Capítulo 3: Funções #26
  • 27. Como pensar como um cientista da Computação usando Python A título de exercício, responda as outras duas questões testando-as. Se tiver dúvida sobre o que é válido ou inválido em Python, tente buscar a resposta perguntando ao interpretador. 3.13 Glossário chamada de Comando que executa uma função. função Consiste do nome da função seguido de (function call) uma lista de argumentos entre parênteses. argumento Valor fornecido a uma função quando ela (argument) é chamada. Este valor é atribuído ao parâmetro correspondente na função. valor de O resultado da função. Se uma chamada retorno (return de função é usada como expressão, o value) valor de retorno é o valor da expressão. conversão de Comando explícito que pega um valor de tipo (type um tipo e devolve o valor correspondente conversion) em outro tipo. coercividade de Uma conversão de tipo que ocorre tipo (type automaticamente, de acordo com as regras coercion) de coercividade do Python. módulo Arquivo que contém uma coleção de (module) funções e classes relacionadas entre si. notação de A sintaxe para chamar uma função que ponto (dot está em outro módulo, especificando o notation) nome do módulo, seguido por um ponto (.) e o nome da função. função Seqüência de comandos nomeada, que (function) realiza alguma tarefa útil. As funções podem ou não receber parâmetros e podem ou não retornar valores. definição de Comando que cria uma nova função, função especificando seu nome, parâmetros e (function comandos que ela executa. definition) fluxo de A ordem na qual os comandos são execução (flow executados durante a execução do of execution) programa. parâmetro Nome usado numa função para referir-se (parameter) ao valor passado como argumento. variável local Variável definida dentro da função. Uma (local variable) variável local só pode ser usada dentro da função onde foi definida. diagrama da Representação gráfica da pilha de funções, pilha (stack suas variáveis e os valores aos quais elas diagram) se referem. frame Retângulo no diagrama da pilha que representa uma chamada de função. Contém as variáveis locais e os parâmetros da função. traceback Lista de funções que estão em execução, impressa quando um erro de execução ocorre. Capítulo 3: Funções #27
  • 28. Como pensar como um cientista da Computação usando Python Capítulo 4: Condicionais e recursividade 4.1 O operador módulo 4.3 Operadores lógicos O operador módulo trabalha com inteiros (e expressões que Existem três operadores lógicos: and, or, not (e, ou, não). A têm inteiros como resultado) e produz o resto da divisão do semântica (significado) destes operadores é similar aos seus primeiro pelo segundo. Em Python, o operador módulo é um significados em inglês (ou português). Por exemplo, x > 0 and símbolo de porcentagem (%). A sintaxe é a mesma que a de x < 10 é verdadeiro somente se x for maior que 0 e menor que outros operadores: 10. >>> quociente = 7 / 3 n%2 == 0 or n%3 == 0 é verdadeiro se qualquer >>> print quociente das condições for verdadeira, quer dizer, se o número n for 2 divisível por 2 ou por 3. >>> resto = 7 % 3 Finalmente, o operador lógico not nega uma >>> print resto expressão booleana, assim, not(x > y) é verdadeiro se (x > y) 1 for falso, quer dizer, se x for menor ou igual a y. Então, 7 dividido por 3 é 2 e o resto é 1. A rigor, os operandos de operadores lógicos deveriam ser expressões booleanas, mas Python não é muito O operador módulo se revela surpreendentemente útil. Por exemplo, você pode checar se rigoroso. Qualquer número diferente de zero é interpretado um número é divisível por outro - se x % y dá zero, então x é como verdadeiro (True): divisível por y. >>> x = 5 Você também pode extrair o algarismo ou >>> x and 1 algarismos mais à direita de um número. Por exemplo, x % 10 1 resulta o algarismo mais à direita de x (na base 10). >>> y = 0 Similarmente, x % 100 resulta nos dois dígitos mais à direita. >>> y and 1 0 Em geral, esse tipo de coisa não é considerado 4.2 Expressões booleanas de bom estilo. Se você precisa comparar um valor com zero, deve fazê-lo explicitamente. Uma expressão booleana é uma expressão que é verdadeira (true) ou é falsa (false). Em Python, uma expressão que é verdadeira tem o valor 1, e uma expressão que é falsa tem o 4.4 Execução condicional valor 0. O operador == compara dois valores e produz Para poder escrever programas úteis, quase sempre uma expressão booleana: precisamos da habilidade de checar condições e mudar o comportamento do programa de acordo com elas. As >>> 5 == 5 instruções condicionais nos dão essa habilidade. A forma True mais simples é a instrução if (se): >>> 5 == 6 if x > 0 False print "x é positivo" No primeiro comando, os dois operadores são iguais, então a expressão avalia como True (verdadeiro); no A expressão booleana depois da instrução if é segundo comando, 5 não é igual a 6, então temos False (falso). chamada de condição. Se ela é verdadeira (true), então a instrução endentada é executada. Se não, nada acontece. O operador == é um dos operadores de comparação; os outros são: Assim como outras instruções compostas, a instrução if é constituída de um cabeçalho e de um bloco de x != y # x é diferente de y instruções: x > y # x é maior que y CABECALHO: x < y # x é menor que y PRIMEIRO COMANDO x >= y # x é maior ou igual a y ... x <= y # x é menor ou igual a y ULTIMO COMANDO Embora esses operadores provavelmente sejam O cabeçalho começa com uma nova linha e familiares a você, os símbolos em Python são diferentes dos termina com dois pontos (:). Os comandos ou instruções símbolos da matemática. Um erro comum é usar um sinal de endentados que seguem são chamados de bloco. A primeira igual sozinho (=) em vez de um duplo (==). Lembre-se de que instrução não endentada marca o fim do bloco. Um bloco de = é um operador de atribuição e == é um operador de comandos dentro de um comando composto ou instrução comparação. Também não existem coisas como =< ou =>. composta é chamado de corpo do comando. Não existe limite para o número de instruções que podem aparecer no corpo de uma instrução if, mas tem que haver pelo menos uma. Ocasionalmente, é útil ter um Capítulo 4: Condicionais e recursividade #28
  • 29. Como pensar como um cientista da Computação usando Python corpo sem nenhuma instrução (usualmente, como um uma delas é verdadeira, o ramo correspondente é executado, e delimitador de espaço para código que você ainda não a instrução termina. Mesmo que mais de uma condição seja escreveu). Nesse caso, você pode usar o comando pass, que verdadeira, apenas o primeiro ramo verdadeiro executa. não faz nada. Como exercício, coloque os exemplos acima em funções chamadas comparar(x, y) e executar(escolha). 4.5 Execução alternativa 4.7 Condicionais aninhados Um segundo formato da instrução if é a execução alternativa, na qual existem duas possibilidades e a condição determina qual delas será executada. A sintaxe se parece com: Um condicional também pode ser aninhado dentro de outra. Poderíamos ter escrito o exemplo tricotômico (dividido em if x % 2 == 0: três) como segue: print x, "é par" if x == y: else: print x, "e", y, "são iguais" print x, "é impar" else: Se o resto da divisão de x por 2 for 0, então if x < y: sabemos que x é par, e o programa exibe a mensagem para esta condição. Se a condição é falsa, o segundo grupo de print x, "é menor que", y instruções é executado. Desde que a condição deva ser else: verdadeira (true) ou falsa (false), precisamente uma das print x, "é maior que", y alternativas vai ser executada. As alternativas são chamadas O condicional mais externo tem dois ramos. O ramos (branches), porque existem ramificações no fluxo de primeiro ramo contém uma única instrução de saída. O execução. segundo ramo contém outra instrução if, que por sua vez tem Por sinal, se você precisa checar a paridade de dois ramos. Os dois ramos são ambos instruções de saída, números com freqüência, pode colocar este código dentro de embora pudessem conter instruções condicionais também. uma função: Embora a endentação das instruções torne a def imprimeParidade(x): estrutura aparente, condicionais aninhados tornam-se difíceis de ler rapidamente. Em geral, é uma boa idéia evitar o if x % 2 == 0: aninhamento quando for possível. print x, "é par" else: Operadores lógicos freqüentemente fornecem print x, "é impar" uma maneira de simplificar instruções condicionais aninhadas. Por exemplo, podemos reescrever o código a seguir usando Para qualquer valor de x, imprimeParidade uma única condicional: exibe uma mensagem apropriada. Quando você a chama, pode fornecer uma expressão de resultado inteiro como um if 0 < x: argumento: if x < 10: >>> imprimeParidade(17) print "x é um número positivo de um só algarismo." >>> imprimeParidade(y+1) A instrução print é executada somente se a fizermos passar por ambos os condicionais, então, podemos usar um operador and: 4.6 Condicionais encadeados if 0 < x and x < 10: print "x é um número positivo de um só algarismo." Às vezes existem mais de duas possibilidades e precisamos de mais que dois ramos. Uma condicional encadeada é uma Esses tipos de condição são comuns, assim, maneira de expressar uma computação dessas: Phython provê uma sintaxe alternativa que é similar à notação matemática: if x < y: if 0 < x < 10: print x, "é menor que", y print "x é um número positivo de um só algarismo." elif x > y: print x, "é maior que", y else: print x, "e", y, "são iguais" 4.8 A instrução return elif é uma abreviação de "else if" ("então se"). De novo, precisamente um ramo será executado. Não existe O comando return permite terminar a execução de uma função limite para o número de instruções elif, mas se existir uma antes que ela alcance seu fim. Uma razão para usá-lo é se você instrução else ela tem que vir por último: detectar uma condição de erro: if escolha == 'A': import math funcaoA() elif escolha == 'B': def imprimeLogaritmo(x): funcaoB() if x <= 0: elif escolha == 'C': print "Somente números positivos, por favor." funcaoC() return else: print "Escolha inválida." resultado = math.log(x) Cada condição é checada na ordem. Se a print "O log de x é ", resultado primeira é falsa, a próxima é checada, e assim por diante. Se A função imprimeLogaritmo recebe um Capítulo 4: Condicionais e recursividade #29
  • 30. Como pensar como um cientista da Computação usando Python parâmetro de nome x. A primeira coisa que ela faz é checar se novaLinha() x é menor ou igual a 0, neste caso ela exibe uma mensagem de Muito embora isso funcione, não seria muito erro e então usa return para sair da função. O fluxo de útil se precisássemos gerar como saída 2 novas linhas, ou 106. execução imediatamente retorna ao ponto chamador, quer Uma alternativa melhor seria esta: dizer, de onde a função foi chamada, e as linhas restantes da função não são executadas. def nLinhas(n): Lembre-se que para usar uma função do módulo if n > 0: de matemática, math, você tem de importá-lo. print nLinhas(n-1) Esse programa é similar a contagemRegressiva; 4.9 Recursividade sempre que n for maior que 0, ele gera como saída uma nova linha e então chama a si mesmo para gerar como saída n-1 linhas adicionais. Deste modo, o número total de novas linhas Já mencionamos que é válido uma função chamar outra é 1 + (n-1) que, se você estudou álgebra direitinho, vem a ser função, e você viu vários exemplos disso. Mas ainda não o próprio n. tínhamos dito que também é válido uma função chamar a si mesma. Talvez não seja óbvio porque isso é bom, mas trata-se O processo de uma função chamando a si de uma das coisas mais mágicas e interessantes que um mesma é chamado de recursividade, e tais funções são ditas programa pode fazer. Por exemplo, dê uma olhada na seguinte recursivas. função: def contagemRegressiva(n): if n == 0: 4.10 Diagramas de pilha para funções recursivas print "Fogo!" else: Na Seção 3.11, usamos um diagrama de pilha para representar o estado de um programa durante uma chamada de função. O print n mesmo tipo de diagrama pode ajudar a interpretar uma função contagemRegressiva(n-1) recursiva. contagemRegressiva espera que o parâmetro, n, seja um inteiro positivo. Se n for 0, ela produz como saída a Toda vez que uma função é chamada, Python palavra "Fogo!". De outro modo, ela produz como saída n e cria um novo quadro (frame) para a função, que contém as então chama uma função de nome contagemRegressiva -- ela variáveis locais e parâmetros da função. Para uma função mesma -- passando n-1 como argumento. recursiva, terá que existir mais de um quadro na pilha ao mesmo tempo. O que acontece se chamarmos essa função da seguinte maneira: Esta figura mostra um diagrama de pilha para contagemRegressiva, chamada com n = 3: >>> contagemRegressiva(3) A execução de contagemRegressiva começa com n=3, e desde que n não é 0, produz como saída o valor 3, e então chama a si mesma... A execução de contagemRegressiva começa com n=2, e desde que n não é 0, produz como saída o valor 2, e então chama a si mesma... A execução de contagemRegressiva começa com n=1, e desde que n não é 0, produz como saída o valor 1, e então chama a si mesma... A execução de contagemRegressiva começa com n=0, e desde que n é 0, produz como saída a palavra "Fogo!" e então retorna. A contagemRegressiva que tem n=1 retorna. A contagemRegressiva que tem n=2 retorna. A contagemRegressiva que tem n=1 retorna. E então estamos de volta em __main__ (que viagem!). Assim, a saída completa se parece com: Como de costume, no topo da pilha está o quadro para __main__. Ele está vazio porque nem criamos 3 qualquer variável em __main__ nem passamos qualquer valor 2 para ele. 1 Os quatro quadros contagemRegressiva têm Fogo! valores diferentes para o parâmetro n. A parte mais em baixo Como um segundo exemplo, dê uma olhada na pilha, onde n=0, é chamada de caso base. Ele não faz uma novamente nas funções novaLinha e tresLinhas: chamada recursiva, então não há mais quadros. def novaLinha(): Como exercício, desenhe um diagrama de pilha print para nLinhas chamada com n=4. def tresLinhas(): novaLinha() novaLinha() Capítulo 4: Condicionais e recursividade #30
  • 31. Como pensar como um cientista da Computação usando Python >>> velocidade = input(deixa) 4.11 Recursividade infinita Qual... é a velocidade de vôo de uma andorinha? De qual você fala, uma andorinha Africana ou uma Se uma recursividade nunca chega ao caso base, ela prossegue Européia? fazendo chamadas recursivas para sempre, e o programa nunca termina. Isto é conhecido como recursividade infinita, e SyntaxError: invalid syntax geralmente não é considerada uma boa idéia. Aqui está um Para evitar esse tipo de erro, geralmente é bom programa mínimo com uma recursividade infinita: usar raw_input para pegar uma string e, então, usar funções de conversão para converter para outros tipos. def recursiva(): recursiva() Na maioria dos ambientes de programação, um 4.13 Glossário programa com recursividade infinita na verdade não roda para sempre. Python reporta uma mensagem de erro quando a profundidade máxima de recursividade é alcançada: operador Operador denotado por um símbolo de módulo porcentagem (%), que trabalha com File "<stdin>", line 2, in recursiva (modulus inteiros e retorna o resto da divisão de um (98 repetitions omitted) operator) número por outro. File "<stdin>", line 2, in recursiva RuntimeError: Maximum recursion depth exceeded expressão Uma expressão que é verdadeira ou falsa. booleana (boolean Este traceback é um pouco maior do que aquele expression) que vimos no capítulo anterior. Quando o erro ocorre, existem operador de Um dos operadores que compara dois 100 quadros recursiva na pilha! comparação valores: ==, !=, >, <, >=, e <=. Como exercício, escreva uma função com (comparison recursividade infinita e rode-a no interpretador Python. operator) operador lógico Um dos operadores que combina (logical expressões booleanas: and, or, e not. operator) 4.12 Entrada pelo teclado comando Comando que controla o fluxo de Os programas que temos escrito até agora são um pouco crus, condicional execução dependendo de alguma no sentido de não aceitarem dados entrados pelo usuário. Eles (conditional condição. simplesmente fazem a mesma coisa todas as vezes. statement) Python fornece funções nativas que pegam condição A expressão booleana que determina qual entradas pelo teclado. A mais simples é chamada raw_input. (condition) bloco será executado num comando Quando esta função é chamada, o programa pára e espera que condicional. o usuário digite alguma coisa. Quando o usuário aperta a tecla comando Comando que consiste de um cabeçalho e Enter ou Return, o programa prossegue e a função raw_input composto um corpo. O cabeçalho termina com um retorna o que o usuário digitou como uma string: (compound dois-pontos (:). O corpo é endentado em >>> entrada = raw_input() statement) relação ao cabeçalho. O que você está esperando? bloco (block) Grupo de comandos consecutivos com a >>> print entrada mesma endentação. O que você está esperando? corpo (body) O bloco que se segue ao cabeçalho em Antes de chamar raw_input, é uma boa idéia um comando composto. exibir uma mensagem dizendo ao usuário o que ele deve entrar. Esta mensagem é uma deixa (prompt). Podemos suprir aninhamento Estrutura de programa dentro da outra, uma deixa como um argumento para raw_input: (nesting) como um comando condicional dentro de um bloco de outro comando condicional. >>> nome = raw_input("Qual... é o seu nome? ") Qual... é o seu nome? Arthur, Rei dos Bretões! recursividade O processo de chamar a própria função >>> print nome (recursion) que está sendo executada. Arthur, Rei dos Bretões! caso base (base Bloco de comando condicional numa Se esperamos que a entrada seja um inteiro, case) função recursiva que não resulta em uma podemos usar a função input: chamada recursiva. deixa = "Qual... é a velocidade de vôo de uma recursão infinita Função que chama a si mesma andorinha?n" (infinite recursivamente sem nunca chegar ao caso velocidade = input(deixa) recursion) base. Após algum tempo, uma recursão infinita causa um erro de execução. Se o usuário digita uma string de números, ela é convertida para um inteiro e atribuída a velocidade. deixa (prompt) Indicação visual que diz ao usuário que o Infelizmente, se o usuário digitar um caractere que não seja programa está esperando uma entrada de um número, o programa trava: dados. Capítulo 4: Condicionais e recursividade #31
  • 32. Como pensar como um cientista da Computação usando Python Capítulo 5: Funções frutíferas terminará sem encontrar um comando return. Neste caso, o 5.1 Valores de retorno valor de retorno será um valor especial chamado None: Algumas das funções nativas do Python que temos usado, >>> print valorAbsoluto(0) como as funções matemáticas, produziram resultados. Chamar None a função gerou um novo valor, o qual geralmente atribuímos à Como exercício, escreva uma função compare uma variável ou usamos como parte de uma expressão: que retorne 1 se x > y, 0 se x == y e -1 se x < y. e = math.exp(1.0) altura = raio * math.sin(angulo) Mas até agora, nenhuma das funções que nós 5.2 Desenvolvimento de programas mesmos escrevemos retornou um valor. Neste ponto, você deve estar apto a olhar para funções Neste capítulo, iremos escrever funções que completas e dizer o que elas fazem. Também, se você vem retornam valores, as quais chamaremos de funções frutíferas, fazendo os exercícios, você escreveu algumas pequenas ou funções que dão frutos, na falta de um nome melhor. O funções. Conforme escrever funções maiores, você pode primeiro exemplo é area, que retorna a área de um círculo começar a ter mais dificuldade, especialmente com erros em dado o seu raio: tempo de execução (erros de runtime) ou erros semânticos. import math Para lidar com programas de crescente complexidade, vamos sugerir uma técnica chamada def area(raio): desenvolvimento incremental. A meta do desenvolvimento temp = math.pi * raio**2 incremental é evitar seções de depuração (debugging) muito longas pela adição e teste de somente uma pequena quantidade return temp de código de cada vez. Já vimos a instrução return antes, mas em uma função frutífera a instrução return inclui um valor de retorno. Como exemplo, suponha que você queira Esta instrução significa: "Retorne imediatamente desta função encontrar a distância entre dois pontos, dados pelas e use a expressão em seguida como um valor de retorno". A coordenadas (x1,y1) e (x2,y2). Pelo teorema de Pitágoras, a expressão fornecida pode ser arbitrariamente complicada, de distância é: modo que poderíamos ter escrito esta função de maneira mais concisa:  distancia=  x2− x12  y2−y12 ¿ def area(raio): O primeiro passo é considerar como deveria ser return math.pi * raio**2 uma função distancia em Python. Em outras palavras, quais Por outro lado, variáveis temporárias como são as entradas (parâmetros) e qual é a saída (valor de temp muitas vezes tornam a depuração mais fácil. retorno)? Às vezes é útil ter múltiplos comandos return, Neste caso, os dois pontos são as entradas, os um em cada ramo de uma condicional: quais podemos representar usando quatro parâmetros. O valor de retorno é a distância, que é um valor em ponto flutuante. def valorAbsoluto(x): if x < 0: Já podemos escrever um esboço da função: return -x def distancia(x1, y1, x2, y2): else: return 0.0 return x Obviamente, esta versão da função não computa Já que estes comandos return estão em ramos distâncias; ela sempre retorna zero. Mas ela está alternativos da condicional, apenas um será executado. Tão sintaticamente correta, e vai rodar, o que significa que logo um seja executado, a função termina sem executar podemos testá-la antes de torná-la mais complicada. qualquer instrução ou comando subseqüente. Para testar a nova função, vamos chamá-la com O código que aparece depois de uma instrução valores hipotéticos: return, ou em qualquer outro lugar que o fluxo de execução jamais alcance, é chamado código morto (dead code). >>> distancia(1, 2, 4, 6) 0.0 Em uma função frutífera, é uma boa idéia assegurar que todo caminho possível dentro do programa Escolhemos estes valores de modo que a encontre uma instrução return. Por exemplo: distância horizontal seja igual a 3 e a distância vertical seja igual a 4; deste modo, o resultado é 5 (a hipotenusa de um def valorAbsoluto(x): triângulo 3-4-5). Quando testamos uma função, é útil if x < 0: sabermos qual o resultado correto. return -x Neste ponto, já confirmamos que a função está elif x > 0: sintaticamente correta, e podemos começar a adicionar linhas return x de código. Depois de cada mudança adicionada, testamos a Este programa não está correto porque se x for função de novo. Se um estar: nas linhas adicionadasponto, erro ocorre em qualquer 0, nenhuma das condições será verdadeira, e a função sabemos aonde ele deve mais recentemente. Capítulo 5: Funções frutíferas #32
  • 33. Como pensar como um cientista da Computação usando Python Um primeiro passo lógico nesta computação é parâmetros. Registre cada estágio do desenvolvimento encontrar as diferenças x2 - x1 e y2 - y1. Nós iremos guardar incremental conforme você avance. estes valores em variáveis temporárias chamadas dx e dy e imprimi-las: def distancia(x1, y1, x2, y2): 5.3 Composição dx = x2 - x1 dy = y2 - y1 Conforme você poderia esperar agora, você pode chamar uma print "dx vale", dx função de dentro de outra. Esta habilidade é chamada de print "dy vale", dy composição. return 0.0 Como um exemplo, vamos escrever uma função Se a função estiver funcionando, as saídas que recebe dois pontos, o centro de um círculo e um ponto em deverão ser 3 e 4. Se é assim, sabemos que a função está seu perímetro, e calcula a área do círculo. recebendo os parâmetros corretos e realizando a primeira computação corretamente. Se não, existem poucas linhas para Assuma que o ponto do centro está guardado checar. nas variáveis xc e yc, e que o ponto do perímetro está nas variáveis xp e yp. O primeiro passo é encontrar o raio do Em seguida, computaremos a soma dos círculo, o qual é a distância entre os dois pontos. Felizmente, quadrados de dx e dy: temos uma função, distancia, que faz isto: def distancia(x1, y1, x2, y2): Raio = distancia(xc, yc, xp, yp) dx = x2 - x1 O segundo passo é encontrar a área de um dy = y2 - y1 círculo com o raio dado e retorná-la: dquadrado = dx**2 + dy**2 resultado = area(raio) print "dquadrado vale: ", dquadrado return resultado return 0.0 Juntando tudo numa função, temos: Note que removemos os comandos print que havíamos escrito no passo anterior. Código como este é def area2(xc, yc, xp, yp): chamado de andaime porque ajuda a escrever o programa, raio = distancia(xc, yc, xp, yp) mas não é parte do produto final. resultado = area(raio) De novo, nós vamos rodar o programa neste return resultado estágio e checar a saída (que deveria ser 25). Chamamos à esta função de area2 para distinguir da função area, definida anteriormente. Só pode Finalmente, se nós tínhamos importado o existir uma única função com um determinado nome em um módulo matemático math, podemos usar a função sqrt para determinado módulo. computar e retornar o resultado: As variáveis temporárias raio e resultado são def distancia(x1, x2, y1, y2): úteis para o desenvolvimento e para depuração (debugging), dx = x2 - x1 mas uma vez que o programa esteja funcionando, podemos dy = y2 - y1 torná-lo mais conciso através da composição das chamadas de dquadrado = dx**2 + dy**2 função: resultado = math.sqrt(dquadrado) def area2(xc, yc, xp, yp): return resultado return area(distancia(xc, yc, xp, yp)) Se isto funcionar corretamente, você conseguiu. Como exercício, escreva uma função Caso contrário, talvez fosse preciso imprimir (exibir) o valor inclinacao(x1, y1, x2, y2) que retorne a inclinação (ou de resultado antes da instrução return. coeficienteAngular?) de uma linha dados os pontos (x1, y1) e Enquanto for iniciante, você deve acrescentar (x2, y2). Depois use esta função em uma função chamada apenas uma ou duas linhas de código de cada vez. Conforme cortaY(x1, y1, x2, y2) que retorne a interseção da linha com o ganhar mais experiência, você se verá escrevendo e depurando eixo y, dados os pontos (x1, y1) e (x2, y2). pedaços maiores. De qualquer modo, o processo de desenvolvimento incremental pode poupar um bocado de tempo de depuração. 5.4 Funções booleanas Os aspectos chave do processo são: Funções podem retornar valores booleanos, o que muitas 1. Comece com um programa que funciona e faça vezes é conveniente por ocultar testes complicados dentro de pequenas mudanças incrementais. Em qualquer ponto funções. Por exemplo: do processo, se houver um erro, você saberá exatamente onde ele está. def ehDivisivel(x, y): 2. Use variáveis temporárias para manter valores If x % y == 0: intermediários de modo que você possa exibi-los e return True # é verdadeiro (true), é divisível checá-los. else: return False # é falso (false), não é divisível 3. Uma vez que o programa funcione, você pode querer remover algum código muleta, ou andaime O nome desta função é ehDivisivel ("é (scaffolding) ou consolidar múltiplos comandos divisível"). É comum dar a uma função booleana nomes que dentro de expressões compostas, mas somente se isto soem como perguntas sim/não. ehDivisivel retorna ou True ou não tornar o programa difícil de ler. False para indicar se x é ou não é divisível por y. Como um exercício, use o desenvolvimento Podemos tornar a função mais concisa se incremental para escrever uma função chamada hipotenusa tirarmos vantagem do fato de a condição da instrução if ser ela que retorna a medida da hipotenusa de um triângulo mesma uma expressão booleana. Podemos retorná-la retângulo dadas as medidas dos dois catetos como diretamente, evitando totalmente o if: Capítulo 5: Funções frutíferas #33
  • 34. Como pensar como um cientista da Computação usando Python def ehDivisivel(x, y): def fatorial(n): return x % y == 0 Se acontece de o argumento ser 0, tudo o que Esta sessão mostra a nova função em ação: temos de fazer é retornar 1: >>> ehDivisivel(6, 4) def fatorial(n): False if n == 0: >>> ehDivisivel(6, 3) return 1 True Por outro lado, e esta é a parte interessante, Funções booleanas são freqüentemente usadas temos que fazer uma chamada recursiva para encontrar o em comandos condicionais: fatorial de n-1 e então multiplicá-lo por n: if ehDivisivel(x, y): def fatorial(n): print "x é divisível por y" if n == 0: else: return 1 print "x não é divisível por y" else: recursivo = fatorial(n-1) Mas a comparação extra é desnecessária. resultado = n * recursivo Como exercício, escreva uma função return resultado estaEntre(x, y, z) que retorne True se y < x < z ou False se O fluxo de execução para este programa é não. similar ao fluxo de contagemRegressiva na Seção 4.9. Se chamarmos fatorial com o valor 3: Já que 3 não é 0, tomamos o segundo ramo e 5.5 Mais recursividade calculamos o fatorial de n-1 ... Até aqui, você aprendeu apenas um pequeno subconjunto da Já que 2 não é 0, tomamos o segundo ramo e linguagem Python, mas pode ser que te interesse saber que calculamos o fatorial de n-1 ... este pequeno subconjunto é uma linguagem de programação Já que 1 não é 0, tomamos o segundo ramo e completa, o que significa que qualquer coisa que possa ser calculamos o fatorial de n-1 ... computada pode ser expressa nesta linguagem. Qualquer programa já escrito pode ser reescrito usando somente os Já que 0 é 0, tomamos o primeiro ramo e aspectos da linguagem que você aprendeu até agora retornamos 1 sem fazer mais qualquer chamada recursiva. (usualmente, você precisaria de uns poucos comandos para controlar dispositivos como o teclado, mouse, discos, etc., mas O valor retornado (1) é multiplicado por n, que isto é tudo). é 1, e o resultado é retornado. Provar esta afirmação é um exercício nada O valor retornado (1) é multiplicado por n, que trivial, que foi alcançado pela primeira vez por Alan Turing, é 2, e o resultado é retornado. um dos primeiros cientistas da computação (alguém poderia dizer que ele foi um matemático, mas muitos dos primeiros O valor retornado (2) é multiplicado por n, que cientistas da computação começaram como matemáticos). Por é 1, e o resultado, 6, se torna o valor de retorno da chamada de isso, ficou conhecido como Tese de Turing. Se você fizer um função que iniciou todo o processo. curso em Teoria da Computação, você terá chance de ver a Eis o diagrama de pilha para esta seqüência de prova. chamadas de função: Para te dar uma idéia do que você pode fazer com as ferramentas que aprendeu a usar até agora, vamos avaliar algumas funções matemáticas recursivamente definidas. Uma definição recursiva é similar à uma definição circular, no sentido de que a definição faz referência à coisa que está sendo definida. Uma verdadeira definição circular não é muito útil: vorpal: adjetivo usado para descrever algo que é vorpal. Se você visse esta definição em um dicionário, ficaria confuso. Por outro lado, se você procurasse pela definição da função matemática fatorial, você encontraria algo assim: Os valores de retorno são mostrados sendo 0! = 1 passados de volta para cima da pilha. Em cada quadro, o valor n! = n.(n-1)! de retorno é o valor de resultado, o qual é o produto de n por recursivo. Esta definição 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") é 5.6 Voto de confiança (Leap of faith) 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. Seguir o fluxo de execução é uma maneira de ler programas, mas que pode rapidamente se transformar em um labirinto. Se você pode escrever uma definição recursiva Uma alternativa é o que chamamos de "voto de confiança". de alguma coisa, você geralmente pode escrever um programa Quando você tem uma chamada de função, em vez de seguir o em Python para executá-la. O primeiro passo é decidir quais fluxo de execução, você assume que a função funciona são os parâmetros para esta função. Com pouco esforço, você corretamente e retorna o valor apropriado. deverá concluir que fatorial recebe um único parâmetro: Capítulo 5: Funções frutíferas #34
  • 35. Como pensar como um cientista da Computação usando Python De fato, você está agora mesmo praticando este RuntimeError: Maximum recursion depth exceeded voto de confiança ao usar as funções nativas. Quando você Parece um caso de recursividade infinita. Mas o chama math.cos ou math.exp, você não examina a que será que é de fato? Existe um caso base -- quando n == 0. implementação destas funções. Você apenas assume que elas O problema é que o valor de n nunca encontra o caso base. funcionam porque as pessoas que escreveram as bibliotecas nativas eram bons programadores. Na primeira chamada recursiva, o valor de n é 0.5. Na próxima, ele é igual a -0.5. Daí em diante, ele se torna O mesmo também é verdade quando você cada vez menor, mas jamais será 0. chama uma de suas próprias funções. Por exemplo, na Seção 5.4, escrevemos a função chamada ehDivisivel que determina Temos então duas alternativas. Podemos tentar se um número é divisível por outro. Uma vez que nos generalizar a função fatorial para que funcione com números convencemos que esta função está correta -- ao testar e em ponto flutuante, ou fazemos fatorial realizar a checagem examinar o código -- podemos usar a função sem examinar o de tipo de seus parâmetros. A primeira é chamada função código novamente. gamma e está um pouco além do escopo deste livro. Sendo assim, ficaremos com a segunda. O mesmo também é verdadeiro para programas recursivos. Quando você tem uma chamada recursiva, em vez Podemos usar type para comparar o tipo do de seguir o fluxo de execução, você poderia assumir que a parâmetro com o tipo de um valor inteiro conhecido (como 1). chamada recursiva funciona (produz o resultado correto) e Ao mesmo tempo em que fazemos isto, podemos nos então perguntar-se, "Assumindo que eu possa encontrar o certificar também de que o parâmetro seja positivo: fatorial de n-1, posso calcular o fatorial de n?" Neste caso, é claro que você pode, multiplicando por n. def fatorial (n): if type(n) != type(1): Naturalmente, é um pouco estranho que uma print "Fatorial somente é definido para função funcione corretamente se você ainda nem terminou de escrevê-la, mas é por isso que se chama voto de confiança! inteiros." return -1 elif n < 0: print "Fatorial somente é definido para inteiros 5.7 Mais um exemplo positivos." return -1 No exemplo anterior, usamos variáveis temporárias para elif n ==0: explicitar (spell out XXX) os passos e tornar o código mais fácil de depurar, mas poderíamos ter economizado algumas return 1 linhas: else: return n * fatorial(n-1) def fatorial(n): Agora temos três casos base. O primeiro pega os if n == 0: não-inteiros. O segundo pega os inteiros negativos. Em ambos return 1 os casos, o programa exibe uma mensagem de erro e retorna else: um valor especial, -1, para indicar que alguma coisa saiu return n * fatorial(n-1) errada: De agora em diante, tenderemos a utilizar um >>> fatorial ("Fred") formato mais conciso, mas recomendamos que você use a Fatorial somente é definido para inteiros. versão mais explícita enquanto estiver desenvolvendo código. Quando ele estiver funcionando, você pode enxugá-lo se -1 estiver se sentindo inspirado. >>> fatorial (-2) Fatorial somente é definido para inteiros positivos. Depois de fatorial, o exemplo mais comum de uma função matemática definida recursivamente é fibonacci, a -1 qual tem a seguinte definição: Se passarmos pelas duas checagens, então saberemos que n é um inteiro positivo, e poderemos provar fibonacci(0) = 1 que a recursividade encontra seu término. fibonacci(1) = 1 Este programa demonstra um padrão (pattern) fibonacci(n) = fibonacci(n-1) + fibonacci(n-2); chamado às vezes de guardião. As duas primeiras Traduzido em Python, parecerá assim: condicionais atuam como guardiãs, protegendo o código que vem em seguida de valores que poderiam causar um erro. Os def fibonacci(n): guardiões tornam possível garantir a correção do código. if n == 0 or n == 1: return 1 else: 5.9 Glossário return fibonacci(n-1) + fibonacci(n-2) Se você tentar seguir o fluxo de execução aqui, mesmo para valores bem pequenos de n, sua cabeça explodirá. função frutífera Uma função que produz um valor de Mas, de acordo com o voto de confiança, se você assume que (fruitful function) retorno. as duas chamadas recursivas funcionam corretamente, então é valor de retorno O valor entregue como resultado de uma claro que você terá o resultado correto ao juntá-las. (return value) chamada de função. variável Uma variável usada para guardar um temporária valor intermediário em um cálculo 5.8 Checagem de tipos (temporary complexo. variable) O que acontece se chamamos fatorial e damos a ela 1.5 como argumento?: código morto Parte de um programa que nunca pode (dead code) ser executada, muitas vezes por que ela >>> fatorial (1.5) aparece depois de uma instrução return. Capítulo 5: Funções frutíferas #35
  • 36. Como pensar como um cientista da Computação usando Python None Um valor especial em Python, retornado por funções que não possuem uma instrução return ou têm uma instrução return sem argumento. desenvolvimento Uma estratégia de desenvolvimento de incremental programas que evita a depuração ao (incremental adicionar e testar somente uma pequena development) quantidade de código de cada vez. andaime Código usado durante o (scaffolding) desenvolvimento do programa, mas que não faz parte do produto final. guardião Uma condição que checa e manipula (guardian) circunstâncias que poderiam causar um erro. Capítulo 5: Funções frutíferas #36
  • 37. Como pensar como um cientista da Computação usando Python Capítulo 6: Iteração Vimos dois programas, nLinhas e 6.1 Reatribuições contagemRegressiva, que usam recursividade (recursão) para fazer a repetição, que também é chamada iteração. Porque a Como você talvez já tenha descoberto, é permitido fazer mais iteração é muito comum, Python tem várias características de uma atribuição à mesma variável. Uma nova atribuição faz para torná-la mais fácil. A primeira delas em que vamos dar uma variável existente referir-se a um novo valor (sem se uma olhada é o comando while. referir mais ao antigo).: Aqui está como fica contagemRegressiva com bruno = 5 um comando while: print bruno, def contagemRegressiva(n): bruno = 7 while n > 0: print bruno print n A saída deste programa é 5 7, porque na n = n-1 primeira vez que bruno é impresso, seu valor é 5 e na segunda print "Fogo!" vez, seu valor é 7. A vírgula no final do primeiro comando print suprime a nova linha no final da saída, que é o motivo pelo qual as duas saídas aparecem na mesma linha. Desde que removemos a chamada recursiva, Veja uma reatribuição em um diagrama de esta função não é recursiva. estado: Você quase pode ler o comando while como se fosse Inglês. Ele significa, "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 fluxo de execução para um comando while: 1. Teste a condição, resultando 0 ou 1. Com a reatribuição torna-se ainda mais 2. Se a condição for falsa (0), saia do comando while e importante distinguir entre uma operação de atribuição e um continue a execução a partir do próximo comando. comando de igualdade. Como Python usa o sinal de igual ( = ) para atribuição, existe a tendência de lermos um comando 3. Se a condição for verdadeira (1), execute cada um como a = b como um comando de igualdade. Mas não é! dos comandos dentro do corpo e volte ao passo 1. Em primeiro lugar, igualdade é comutativa e O corpo consiste de todos os comandos abaixo atribuição não é. Por exemplo, em matemática, se a = 7 então do cabeçalho, com a mesma endentação. 7 = a. Mas em Python, o comando a = 7 é permitido e 7 = a Este tipo de fluxo é chamado de um loop (ou não é. laço) porque o terceiro passo cria um "loop" ou um laço de Além disso, em matemática, uma expressão de volta ao topo. Note que se a condição for falsa na primeira vez igualdade é sempre verdadeira. Se a = b agora, então, a será que entrarmos no loop, os comandos dentro do loop jamais sempre igual a b. Em Python, um comando de atribuição pode serão executados. tornar duas variáveis iguais, mas elas não têm que permanecer O corpo do loop poderia alterar o valor de uma assim: ou mais variáveis de modo que eventualmente a condição se a = 5 torne falsa e o loop termine. Se não for assim, o loop se repetirá para sempre, o que é chamado de um loop infinito. b = a # a e b agora são iguais Uma fonte de diversão sem fim para os cientistas da b = 3 # a e b não são mais iguais computação é a observação de que as instruções da A terceira linha muda o valor de a mas não embalagem de shampoo, "Lave, enxágüe, repita" é um loop muda o valor de b, então, elas não são mais iguais. (Em infinito. algumas linguagens de programação, um símbolo diferente é usado para atribuição, como <- ou :=, para evitar confusão.) No caso de contagemRegressiva, podemos provar que o loop terminará porque sabemos que o valor de n Embora a reatribuição seja freqüentemente útil, é finito, e podemos ver que o valor de n diminui dentro de você deve usá-la com cautela. Se o valor das variáveis muda cada repetição (iteração) do loop, então, eventualmente freqüentemente, isto pode fazer o código difícil de ler e de chegaremos ao 0. Em outros casos, isto não é tão simples de depurar. afirmar: def sequencia(n): while n != 1: 6.2 O comando while print n, if n%2 == 0: # n é par Os computadores são muito utilizados para automatizar tarefas n = n/2 repetitivas. Repetir tarefas idênticas ou similares sem cometer else: # n é impar erros é uma coisa que os computadores fazem bem e que as pessoas fazem poorly. n = n*3+1 Capítulo 6: Iteração #37
  • 38. Como pensar como um cientista da Computação usando Python A condição para este loop é n != 1, então o loop 1.0 0.0 vai continuar até que n seja 1, o que tornará a condição falsa. 2.0 0.69314718056 Dentro de cada repetição (iteração) do loop, o 3.0 1.09861228867 programa gera o valor de n e então checa se ele é par ou 4.0 1.38629436112 impar. Se ele for par, o valor de n é dividido por 2. Se ele for 5.0 1.60943791243 impar, o valor é substituído por n*3+1. Por exemplo, se o 6.0 1.79175946923 valor inicial (o argumento passado para seqüência) for 3, a 7.0 1.94591014906 seqüência resultante será 3, 10, 5, 16, 8, 4, 2, 1. 8.0 2.07944154168 Já que n às vezes aumenta e às vezes diminui, 9.0 2.19722457734 não existe uma prova óbvia de que n jamais venha a alcançar Se estes valores parecem odd, lembre-se que a 1, ou de que o programa termine. Para alguns valores função log usa a base e. Já que potências de dois são tão particulares de n, podemos provar o término. Por exemplo, se importantes em ciência da computação, nós freqüentemente o valor inicial for uma potência de dois, então o valor de n temos que achar logaritmos referentes à base 2. Para fazermos será par dentro de cada repetição (iteração) do loop até que isso, podemos usar a seguinte fórmula: alcance 1. O exemplo anterior termina com uma dessas seqüências começando em 16. Valores específicos à parte, A questão interessante é se há como provarmos que este programa termina para todos os valores de n. Até hoje, ninguém foi capaz de provar que sim ou que não! Alterando o comando de saída para: Como um exercício, reescreva a função nLinhas print x, 't', math.log(x)/math.log(2.0) da seção 4.9 usando iteração em vez de recursão. o que resultará em: 1.0 0.0 2.0 1.0 6.3 Tabelas 3.0 1.58496250072 4.0 2.0 Uma das coisas para qual os loops são bons é para gerar dados 5.0 2.32192809489 tabulares. Antes que os computadores estivessem readily disponíveis, as pessoas tinham que calcular logaritmos, senos, 6.0 2.58496250072 cossenos e outras funções matemáticas à mão. Para tornar isto 7.0 2.80735492206 mais fácil, os livros de matemática continham longas tabelas 8.0 3.0 listando os valores destas funções. Criar as tabelas era 9.0 3.16992500144 demorado e entediante, e elas tendiam a ser cheias de erros. Podemos ver que 1, 2, 4 e 8 são potências de Quando os computadores entraram em cena, dois porque seus logaritmos na base 2 são números redondos. uma das reações iniciais foi "Isto é ótimo! Podemos usar Se precisássemos encontrar os logaritmos de outras potências computadores para geras as tabelas, assim não haverá erros." de dois, poderíamos modificar o programa deste modo: Isto veio a se tornar verdade (na maioria das vezes) mas shortsighted. Rapidamente, porém, computadores e x = 1.0 calculadoras tornaram-se tão pervasivos que as tabelas ficaram while x < 100.0: obsoletas. print x, 't', math.log(x)/math.log(2.0) x = x * 2.0 Bem, quase. Para algumas operações, os computadores usam tabelas de valores para conseguir uma Agora, em vez de somar algo a x a cada iteração resposta aproximada e então realizar cálculos para melhorar a do loop, o que resulta numa seqüência aritmética, nós aproximação. Em alguns casos, têm havido erros nas tabelas multiplicamos x por algo, resultando numa seqüência underlying, o caso mais famoso sendo o da tabela usada pelo geométrica. O resultado é: processador Pentium da Intel para executar a divisão em ponto-flutuante. 1.0 0.0 2.0 1.0 Embora uma tabela de logaritmos não seja mais 4.0 2.0 tão útil quanto já foi um dia, ela ainda dá um bom exemplo de 8.0 3.0 iteração. O seguinte programa gera uma seqüência de valores na coluna da esquerda e seus respectivos logaritmos na coluna 16.0 4.0 da direita: 32.0 5.0 64.0 6.0 x = 1.0 Por causa do caractere de tabulação entre as while x < 10.0: colunas, a posição da segunda coluna não depende do número print x, 't', math.log(x) de dígitos na primeira coluna. x = x + 1.0 Tabelas de logaritmos podem não ser mais úteis, A string 't' representa um caracterde mas para cientistas da computação, conhecer as potências de tabulação. dois é! Conforme caracteres e strings vão sendo Como um exercício, modifique este programa mostrados na tela, um ponteiro invisível chamado cursor de modo que ele produza as potências de dois acima de 65.535 marca aonde aparecerá o próximo caractere. Depois de um (ou seja, 216). Imprima e memorize-as. comando print, o cursor normalmente vai para o início de uma nova linha. O caractere de barra invertida em 't' indica o início de uma seqüência de escape. Seqüências de escape são O caractere de tabulação desloca o cursor para a usadas para representar caracteres invisíveis como de direita até que ele encontre uma das marcas de tabulação. tabulação e de nova linha. A seqüência n representa uma nova Tabulação é útil para fazer colunas de texto line up, como na linha. saída do programa anterior: Capítulo 6: Iteração #38
  • 39. Como pensar como um cientista da Computação usando Python Uma seqüência de escape pode aparecer em Se chamarmos esta função com o argumento 2, qualquer lugar em uma string; no exemplo, a seqüência de teremos a mesma saída que antes. Com o argumento 3, a saída escape de tabulação é a única coisa dentro da string. é: Como você acha que se representa uma barra 3 6 9 12 15 18 invertida em uma string? Com o argumento 4, a saída é: Como um exercício, escreva um única string 4 8 12 16 20 24 que Agora você provavelmente pode adivinhar produza como imprimir uma tabela de multiplicação - chamando esta imprimeMultiplos repetidamente com argumentos diferentes. saída. De fato, podemos usar um outro loop: i = 1 while i <= 6: 6.4 Tabelas de duas dimensões (ou bi-dimensionais) imprimeMultiplos(i) i = i + 1 Uma tabela de duas dimensões é uma tabela em que você lê o Note o quanto este loop é parecido com aquele valor na interseção entre uma linha e uma coluna. Uma tabela dentro de imprimeMultiplos. Tudo o que fiz foi substituir o de multiplicação é um bom exemplo. Digamos que você comando print pela chamada à função. queira imprimir uma tabela de multiplicação de 1 a 6. A saída deste programa é uma tabela de Uma boa maneira de começar é escrever um multiplicação: loop que imprima os múltiplos de 2, todos em uma linha: 1 2 3 4 5 6 i = 1 2 4 6 8 10 12 while i <= 6: 3 6 9 12 15 18 print 2*i, ' ', 4 8 12 16 20 24 i = i + 1 5 10 15 20 25 30 print 6 12 18 24 30 36 A primeira linha inicializa a variável chamada i, a qual age como um contador ou variável de controle do loop. Conforme o loop é executado, o valor de i é incrementado de 1 a 6. Quando i for 7, o loop termina. A cada 6.6 Mais encapsulamento repetição (iteração) do loop, é mostrado o valor de 2*i, seguido de três espaços. Para demonstrar de novo o encapsulamento, vamos pegar o código do final da seção 6.5 e acondicioná-lo, envolvê-lo em De novo, a vírgula no comando print suprime a uma função: nova linha. Depois que o loop se completa, o segundo comando print inicia uma nova linha. def imprimeTabMult(): A saída do programa é: i = 1 while i <= 6: 2 4 6 8 10 12 imprimeMultiplos(i) Até aqui, tudo bem. O próximo passo é i = i + 1 encapsular e generalizar. Este processo é um plano de desenvolvimento comum. Nós desenvolvemos código escrevendo linhas de código fora de qualquer função, ou digitando-as no 6.5 Encapsulamento e generalização interpretador. Quando temos o código funcionando, extraímos ele e o embalamos em uma função. Encapsulamento é o processo de wrapping um pedaço de Este plano de desenvolvimento é código em uma função, permitindo que você tire vantagem de particularmente útil se você não sabe, quando você começa a todas as coisas para as quais as funções são boas. Você já viu escrever, como dividir o programa em funções. Esta técnica dois exemplos de encapsulamento: imprimeParidade na seção permite a você projetar enquanto desenvolve. 4.5; e eDivisivel na seção 5.4 Generalização significa tomar algo que é específico, tal como imprimir os múltiplos de 2, e torná-lo 6.7 Variáveis locais mais geral, tal como imprimir os múltiplos de qualquer inteiro. Esta função encapsula o loop anterior e Você pode estar pensando como podemos utilizar a mesma generaliza-o para imprimir múltiplos de n: variável, i, em ambos, imprimeMultiplos e imprimeTabMult. Isto não causaria problemas quando uma das funções mudasse def imprimeMultiplos(n): o valor da variável? i = 1 A resposta é não, porque o i em while i <= 6: imprimeMultiplos e o i em imprimeTabMult não são a mesma print n*i, 't ', variável. i = i + 1 Variáveis criadas dentro de uma definição de print função são locais; você não pode acessar uma variável local Para encapsular, tudo o que tivemos que fazer de fora da função em que ela "mora". Isto significa que você é foi adicionar a primeira linha, que declara o nome de uma livre para ter múltiplas variáveis com o mesmo nome, desde função e sua lista de parâmetros. Para generalizar, tudo o que que elas não estejam dentro da mesma função. tivemos que fazer foi substituir o valor 2 pelo parâmetro n. O diagrama de pilha para este programa mostra Capítulo 6: Iteração #39
  • 40. Como pensar como um cientista da Computação usando Python que duas variáveis chamadas i não são a mesma variável. Elas def imprimeMultiplos(n, altura): podem se referir a valores diferentes e alterar o valor de uma i = 1 não afeta à outra. while i <= altura: print n*i, 't', i = i + 1 print def imprimeTabMult(altura): i = 1 while i <= altura: imprimeMultiplos(i, altura) i = i + 1 Note que quando adicionamos um novo parâmetro, temos que mudar a primeira linha da função (o cabeçalho da função), e nós também temos que mudar o lugar de onde a função é chamada em imprimeTabMult. Como esperado, este programa gera uma tabela quadrada de sete por sete: O valor de i em imprimeTabMult vai de 1 a 6. 1 2 3 4 5 6 7 No diagrama, i agora é 3. Na próxima iteração do loop i será 2 4 6 8 10 12 14 4. A cada iteração do loop, imprimeTabMult chama 3 6 9 12 15 18 21 imprimeMultiplos com o valor corrente de i como argumento. O valor é atribuído ao parâmetro n. 4 8 12 16 20 24 28 5 10 15 20 25 30 35 Dentro de imprimeMultiplos, o valor de i vai de 6 12 18 24 30 36 42 1 a 6. No diagrama, i agora é 2. Mudar esta variável não tem 7 14 21 28 35 42 49 efeito sobre o valor de i em imprimeTabMult. Quando você generaliza uma função É comum e perfeitamente legal ter variáveis apropriadamente, você muitas vezes tem um programa com locais diferentes com o mesmo nome. Em particular, nomes capacidades que você não planejou. Por exemplo, você pode como i e j são muito usados para variáveis de controle de ter notado que, porque ab = ba, todas as entradas na tabela loop. Se você evitar utilizá-los em uma função só porque você aparecem duas vezes. Você poderia economizar tinta já os usou em outro lugar, você provavelmente tornará seu imprimindo somente a metade da tabela. Para fazer isso, você programa mais difícil de ler. tem que mudar apenas uma linha em imprimeTabMult. Mude: imprimeTabMult(i, altura) para: 6.8 Mais generalização imprimeTabMult(i, i) Como um outro exemplo de generalização, imagine que você e você terá: precise de um programa que possa imprimir uma tabela de multiplicação de qualquer tamanho, não apenas uma tabela de 1 seis por seis. Você poderia adicionar um parâmetro a 2 4 imprimeTabMult: 3 6 9 def imprimeTabMult(altura): 4 8 12 16 i = 1 5 10 15 20 25 while i <= altura: 6 12 18 24 30 36 imprimeMultiplos(i) 7 14 21 28 35 42 49 i = i + 1 Como um exercício, trace a execução desta Nós substituímos o valor 6 pelo parâmetro versão de imprimeTabMult e explique como ela funciona. altura. Se chamarmos imprimeTabMult com o argumento 7, ela mostra: 1 2 3 4 5 6 6.9 Funções 2 4 6 8 10 12 ● Há pouco tempo mencionamos "todas as coisas para 3 6 9 12 15 18 as quais as funções são boas." Agora, você pode estar 4 8 12 16 20 24 pensando que coisas exatamente são estas. Aqui estão 5 10 15 20 25 30 algumas delas: 6 12 18 24 30 36 ● Dar um nome para uma seqüência de comandos torna 7 14 21 28 35 42 seu programa mais fácil de ler e de depurar. Isto é bom, exceto que nós provavelmente quereríamos que a tabela fosse quadrada - com o mesmo ● Dividir um programa longo em funções permite que número de linhas e colunas. Para fazer isso, adicionamos você separe partes do programa, depure-as outro parâmetro a imprimeMultiplos para especificar quantas isoladamente, e então as componha em um todo. colunas a tabela deveria ter. ● Funções facilitam tanto recursão quanto iteração. Só para confundir, chamamos este novo parâmetro de altura, demonstrando que diferentes funções ● Funções bem projetadas são freqüentemente úteis podem ter parâmetros com o mesmo nome (como acontece para muitos programas. Uma vez que você escreva e com as variáveis locais). Aqui está o programa completo: depure uma, você pode reutilizá-la. Capítulo 6: Iteração #40
  • 41. Como pensar como um cientista da Computação usando Python 6.10 Glossário reatribuição quando mais de um valor é atribuído a (multiple mesma variável durante a execução do assignment 1) programa. iteração execução repetida de um conjunto de (iteration) comandos/instruções (statements) usando uma chamada recursiva de função ou um laço (loop). laço (loop) um comando/instrução ou conjunto de comandos/instruções que executam repetidamente até que uma condição de interrupção seja atingida. laço infinito um laço em que a condição de interrupção (infinite loop) nunca será atingida. corpo (body) o conjunto de comandos/instruções que pertencem a um laço. variável de laço uma variável usada como parte da (loop variable) condição de interrupção do laço. tabulação (tab) um carácter especial que faz com que o cursor mova-se para a próxima parada estabelecida de tabulação na linha atual. nova-linha um carácter especial que faz com que o (newline) cursor mova-se para o início da próxima linha. cursor (cursor) um marcador invisível que determina onde o próximo carácter var ser impresso. sequência de um carácter de escape () seguido por um escape (escape ou mais caracteres imprimíveis, usados sequence) para definir um carácter não imprimível. encapsular quando um programa grande e complexo (encapsulate) é dividido em componentes (como funções) e estes são isolados um do outro (pelo uso de variáveis locais, por exemplo). generalizar quando algo que é desnecessariamente (generalize) específico (como um valor constante) é substituído por algo apropriadamente geral (como uma variável ou um parâmetro). Generalizações dão maior versatilidade ao código, maior possibilidade de reuso, e em algumas situações até mesmo maior facilidade para escrevê-lo. plano de um processo definido para desenvolviment desenvolvimento de um programa. Neste o (development capítulo, nós demonstramos um estilo de plan) desenvolvimento baseado em escrever código para executar tarefas simples e específicas, usando encapsulamento e generalização. 1 N.T.: O termo multiple assignment (ou atribuição múltipla) é usado com mais frequência para descrever a sintaxe a = b = c. Por este motivo optamos pelo termo reatribuição no contexto da seção 6.1 desse capítulo. Capítulo 6: Iteração #41
  • 42. Como pensar como um cientista da Computação usando Python Capítulo 7: Strings de comprimento: 7.1 Um tipo de dado composto comprimento = len(fruta) Até aqui, vimos três diferentes tipos de dado: int, float e ultima = fruta[comprimento-1] string. Strings são qualitativamente diferentes dos outros dois Como alternativa, podemos usar índices tipos porque são feitas de pedaços menores - caracteres. negativos, os quais contam de trás pra frente os elementos da string. A expressão fruta[-1] resulta a última letra, fruta[-2] Tipos que consistem de pedaços menores são resulta a penúltima (a segunda de trás para frente), e assim por chamados tipos de dados compostos. Dependendo do que diante. estejamos fazendo, pode ser que precisemos tratar um tipo de dado composto como uma coisa única, ou pode ser que queiramos acessar suas partes. Esta ambigüidade é útil. O operador colchete seleciona um único 7.3 Travessia e o loop for caractere de uma string.: Várias computações envolvem o processamento de uma string >>> fruta = "banana" um caractere de cada vez. Muitas vezes elas começam com o >>> letra = fruta[1] primeiro, selecionam um de cada vez, fazem alguma coisa >>> print letra com ele, e continuam até o fim. Este padrão de processamento é chamado uma travessia (traversal, com a idéia de A expressão fruta[1] seleciona o caractere "percorrimento"). Uma maneira de codificar uma travessia é número 1 de fruta. A variável letra referencia ou refere-se ao com um comando while: resultado da expressão. Quando exibimos letra, temos uma surpresa: indice = 0 while indice < len(fruta): a letra = fruta[indice] A primeira letra de "banana" não é a. A menos que você seja um cientista da computação. Neste caso, você print letra deve entender a expressão dentro dos colchetes como um indice = indice + 1 deslocamento (offset,) a partir do começo da string, e o Este loop percorre a string e exibe cada letra em deslocamento da primeira letra é zero. Assim, b é a 0ª ("zero- sua própria linha. A condição do loop é indice < len(fruta), ésima") letra de "banana", a é a 1ª ("um-ésima", diferente de assim, quando índice é igual ao comprimento da string, a primeira), e n é a 2ª ("dois-ésima", diferente de segunda) letra. condição se torna falsa, e o corpo do loop não é executado. O último caractere acessado é aquele com o índice len(fruta)-1, Para pegar a primeira letra de uma string, você que vem a ser o último caractere da string. simplesmente põe 0, ou qualquer expressão que resulte o valor 0, dentro dos colchetes: Como um exercício, escreva uma função que tome uma string como argumento e devolva suas letras de >>> letra = fruta[0] trás para frente, uma por linha. >>> print letra b Usar um índice para percorrer um conjunto de valores é tão comum que Python oferece uma sintaxe A expressão entre colchetes é chamada de alternativa simplificada - o loop for: índice. Um índice especifica um membro de um conjunto ordenado, neste caso o conjunto de caracteres da string. O for char in fruta: índice indica aquele membro que você quer, daí seu nome. Ele print char pode ser qualquer expressão inteira. A cada vez através do loop, o próximo caractere da string é atribuído à variável char. O loop continua até que não reste mais caracteres. 7.2 Comprimento O exemplo seguinte mostra como usar concatenação e um loop for para gerar uma série abecedário. A função len retorna o número de caracteres de uma string: "Abecedário" se refere a uma série ou lista na qual os elementos aparecem em ordem alfabética. Por exemplo, no >>> fruta = "banana" livro de Robert McCloskey's Make Way for Ducklings, os >>> len(fruta) nomes dos "ducklings" são Jack, Kack, Lack, Mack, Nack, 6 Ouack, Pack e Quack. O loop seguinte, produz como saída Para pegar a última letra de uma string, você aqueles nomes, em ordem: pode ficar tentado a fazer alguma coisa assim: prefixos = "JKLMNOPQ" comprimento = len(fruta) sufixo = "ack" ultima = fruta[comprimento] # ERRO! Não vai funcionar. Isto vai causar o seguinte for letra in prefixos: erro em tempo de execução (runtime error): IndexError: string print letra + sufixo index out of range. (ErroDeIndice: índice da string fora do A saída deste programa é: intervalo). A razão é que não existe 6ª letra em "banana". Já que começamos a contar do zero, as seis letras são numeradas Jack de 0 a 5. Para pegar o último caractere, temos que subtrair 1 Kack Capítulo 7: Strings #42
  • 43. Como pensar como um cientista da Computação usando Python Lack else: Mack print "Sim, nós não temos bananas!" Nack Entretanto, você deve atentar para o fato de que Oack Pyhton não manipula letras maiúsculas e minúsculas da Pack mesma maneira que as pessoas o fazem. Todas as letras Qack maiúsculas vêm antes das minúsculas. Como resultado: Naturalmente, esta saída não está cem por cento Sua palavra, Zebra, vem antes de banana. certa porque "Ouack" e "Quack" estão escritos de maneira Uma maneira comum de resolver este problema errada. é converter as strings para um formato padrão, seja todas Como um exercício, modifique o programa para minúsculas, ou todas maiúsculas, antes de realizar a corrigir este erro. comparação. Um problema mais difícil é fazer o programa perceber que zebras não são frutas. 7.4 Fatias de strings 7.6 Strings são imutáveis Um segmento de uma string é chamado de uma fatia. Selecionar uma fatia é similar a selecionar um caractere: É tentador usar o operador [] no lado esquerdo de uma expressão de atribuição, com a intenção de alterar um >>> s = "Pedro, Paulo e Maria" caractere em uma string. Por exemplo: >>> print s[0:5] saudacao = "Alô, mundo!" Pedro saudacao[0] = 'E' # ERRO! >>> print s[7:12] print saudacao Paulo Em vez de produzir a saída Elô, Mundo!, este >>> print s[16:21] código produz o erro em tempo de execução (runtime error): Maria TypeError: object doesn't support item assignment O operador [n:m] retorna a parte da string do (ErroDeTipo: objeto não dá suporte à atribuição de item.) "n-ésimo" caractere ao "m-ésimo" caractere, incluindo o primeiro mas excluindo o último. Este comportamento não é Strings são imutáveis, o que significa que você intuitivo; ele faz mais sentido se você imaginar os índices não pode mudar uma string que já existe. O melhor que você apontando para os intervalos entre os caracteres, como no pode fazer é criar uma nova string que seja uma variação da seguinte diagrama: original: saudacao = "Alô, mundo!" novaSaudacao = 'E' + saudação[1:] print novaSaudacao A solução aqui é concatenar uma nova primeira letra com uma fatia de saudação. Esta operação não tem nenhum efeito sobre a string original. Se você omitir o primeiro índice (antes dos dois 7.7 Uma função find (encontrar) pontos ":"), a fatia começa do início da string. Se você omitir o segundo índice, a fatia vai até o final da string. Assim: O que faz a seguinte função?: >>> fruta = "banana" def find(str, ch): >>> fruta[:3] indice = 0 'ban' while indice < len(str): >>> fruta[3:] if str[indice] == ch: 'ana' return indice O que você acha de s[:] significa? indice = indice + 1 return -1 Num certo sentido, find (encontrar) é o oposto 7.5 Comparação de strings do operador []. Em vez de pegar um índice e extrair o caractere correspondente, ela pega um caractere e encontra (finds) em qual índice aquele caractere aparece. Se o caractere O operador de comparação funciona com strings. Para ver se não é encontrado, a função retorna -1. duas strings são iguais: Este é o primeiro exemplo que vemos de uma if palavra == "banana": instrução return dentro de um loop. Se str[indice] == ch, a print "Sim, nós não temos bananas!" função retorna imediatamente, abandonando o loop Outras operações de comparação são úteis para prematuramente. colocar palavras em ordem alfabética: Se o caractere não aparece na string, então o if palavra < "banana": programa sai do loop normalmente e retorna -1. print "Sua palavra," + palavra + ", vem antes de Este padrão de computação é às vezes chamado banana." de travessia "eureka", porque tão logo ele encontra (find) o elif palavra > "banana": que está procurando, ele pode gritar "Eureka!" e parar de print "Sua palavra," + palavra + ", vem depois procurar. de banana." Como um exercício, modifique a função find Capítulo 7: Strings #43
  • 44. Como pensar como um cientista da Computação usando Python (encontrar) de modo que ela receba um terceiro parâmetro, o Neste exemplo, a busca falha porque a letra b índice da string por onde ela deve começar sua procura. não aparece no intervalo entre 1 e 2 (não incluindo o 2) do índice. 7.8 Iterando e contando 7.10 Classificação de caracteres O programa seguinte conta o número e vezes que a letra a aparece em uma string: Muitas vezes é útil examinar um caractere e testar se ele é maiúsculo ou minúsculo, ou se ele é um caractere ou um fruta = "banana" dígito. O módulo string oferece várias constantes que são úteis contador = 0 para esses propósitos. for letra in fruta: A string string.lowercase contém todas as letras if letra == 'a' que o sistema considera como sendo minúsculas. contador = contador + 1 Similarmente, string.uppercase contém todas as letras print contador maiúsculas. Tente o seguinte e veja o que você obtém: Este programa demonstra um outro padrão de >>> print string.lowercase computação chamado de contador. A variável contador é inicializada em 0 e então incrementada cada vez que um a é >>> print string.uppercase encontrado. (Incrementar é o mesmo que aumentar em um; é >>> print string.digits o oposto de decrementar, e não tem relação com excremento, Nós podemos usar essas constantes e find que é um substantivo.) Quando se sai do loop, contador (encontrar) para classificar caracteres. Por exemplo, se guarda o resultado - o número total de a's. find(lowercase, ch) retorna um valor outro que não -1, então ch deve ser minúsculo: Como um exercício, encapsule este código em uma função chamada contaLetras, e generalize-a de modo def eMinusculo(ch): que possa aceitar uma string e uma letra como parâmetros. return string.find(string.lowercase, ch) != -1 Como um segundo exercício, reescreva esta Como uma alternativa, podemos tirar vantagem função de modo que em vez de percorrer a string, ela use a do operador in, que determina se um caractere aparece em versão com três parâmetros de find (encontrar) da seção uma string: anterior. def eMinusculo(ch): return ch in string.lowercase 7.9 O módulo string Ainda, como uma outra alternativa, podemos usar o operador de comparação: O módulo string contém funções úteis que manipulam strings. def eMinusculo(ch): Conforme é usual, nós temos que importar o módulo antes que return 'a' <= ch <= 'z' possamos utilizá-lo: Se ch estiver entre a e z, ele deve ser uma letra >>> import string minúscula. O módulo string inclui uma função chamada Como um exercício, discuta que versão de find (encontrar) que faz a mesma coisa que a função que eMinusculo você acha que será a mais rápida. Você pode escrevemos. Para chamá-la, temos que especificar o nome do pensar em outras razões além da velocidade para preferir módulo e o nome da função usando a notação de ponto.: uma em vez de outra? >>> fruta = "banana" Outra constante definida no módulo string pode >>> indice = string.find(fruta, "a") te surpreender quando você executar um print sobre ela: >>> print indice >>> print string.whitespace 1 Caracteres de espaçamento (ou espaços em Este exemplo demonstra um dos benefícios dos branco) movem o cursor sem "imprimir" qualquer coisa. Eles módulos - eles ajudam a evitar colisões entre nomes de criam os espaços em branco entre os caracteres visíveis (pelo funções nativas e nomes de funções definidas pelo usuário. menos numa folha de papel branco). A string constante Usando a notação de ponto podemos especificar que versão de string.whitespace contém todos os caracteres de espaçamento, find (encontrar) nós queremos. incluindo espaço, tabulação (t) e nova linha (n). De fato, string.find é mais generalizada que a Existem outras funções úteis no módulo string, nossa versão. Primeiramente, ela pode encontrar substrings, mas este livro não pretende ser um manual de referência. Por não apenas caracteres: outro lado, Python Library Reference é exatamente isto. Em meio a uma abundante documentação, ele está disponível no >>> string.find("banana", "na") site da web do Python, www.python.org. 2 Além disso, ela recebe um argumento adicional que especifica o índice pelo qual ela deve começar sua procura: 7.11 Glossário >>> string.find("banana", "na", 3) tipo de dado Um tipo de dado no qual o valor consiste de 4 composto componentes, ou elementos, que são eles Ou ela pode receber dois argumentos adicionais (compound mesmos valores. que especificam o intervalo de índices: data type) >>> string.find("bob", "b", 1, 2) travessia Iterar através dos elementos de um conjunto, -1 (traverse) realizando uma operação similar em cada um Capítulo 7: Strings #44
  • 45. Como pensar como um cientista da Computação usando Python deles. índice (index) Uma variável ou valor usados para selecionar um membro de um conjunto ordenado, como um caractere em uma string. fatia (slice) Uma parte de uma string especificada por um intervalo de índices. mutável Um tipo de dado composto a cujos (mutable) elementos podem ser atribuídos novos valores. contador Uma variável utilizada para contar alguma (counter) coisa, usualmente inicializada em zero e então incrementada. incrementar aumentar o valor de uma variável em 1. (increment) decrementar diminuir o valor de uma variável em 1. (decrement) espaçamento Qualquer um dos caracteres que move o (whitespace) cursor sem imprimir caracteres visíveis. A constante string.whitespace contém todos os caracteres de espaçamento. 7.11 Glossário2 tipo de dado Um tipo de dado em que os valores são composto compostos de componentes, ou elementos, (compound que podem ser tratados como valores data type) separados. atravessar Iterar através dos elementos definidos, (traverse) executando uma operação similar em cada. índice (index) Uma variável ou valor usado para selecionar um membro de uma definição ordenada, como um caractere de uma string. fatia (slice) Uma parte da string especificada por um intervalo de índice. mutável Um tipo de dado composto do qual (mutable) elementos podem atribuir novos valores. contador Uma variável usada para contar alguma (counter) coisa, geralmente iniciada em zero e incrementada. incremento Para aumentar o valor da variável. (increment) decremento Para dimiuir o valor da variável. (decrement) espaço em Qualquer caractere que move o cursor sem branco imprimir caracteres visíveis. A constante (whitespace) string.whitespace contém todos os caracteres de espaço em branco. Capítulo 7: Strings #45
  • 46. Como pensar como um cientista da Computação usando Python Capítulo 8: Listas Uma lista é um conjunto ordenado de valores, onde cada valor é identificado por um índice. Os valores que compõem uma 8.2 Acessado elementos lista são chamados elementos. Listas são similares a strings, que são conjuntos ordenados de caracteres, com a diferença A sintaxe para acessar os elementos de uma lista é a mesma que os elementos de uma lista podem possuir qualquer tipo. que a sintaxe para acessar os caracteres de uma string XXX o Listas e strings XXX e outras coisas que se comportam como operator colchete ([]). A expressão dentro dos colchetes conjuntos ordenados XXX são chamados seqüências. especifica o índice. Lembre-se que os índices iniciam em 0: >>> print numeros[0] >>> numeros[1]= 5 8.1 Valores da lista O operador colchete pode aparecer em qualquer lugar em uma expressão. Quando ele aparece no lado Existem várias maneiras de criar uma nova lista; a mais esquerdo de uma atribuição, ele modifica um dos elementos simples é envolver os elementos em colchetes ([ e ]): em uma lista, de forma que o um-ésimo elemento de numeros, >>> [10, 20, 30, 40] que era 123, é agora 5. >>> ['spam', 'bungee', 'swallow'] Qualquer expressão inteira pode ser utilizada O primeiro exemplo á uma lista de quatro como um índice: inteiros. O segundo é uma lista de três strings. Os elementos de uma lista não necessitam ser do mesmo tipo. A lista a >>> numeros[3-2] seguir contém uma string, um valor float, um valor inteiro, e 5 mirabile dictu uma outra lista: >>> numeros[1.0] TypeError: sequence index must be integer >>> ['alo', 2.0, 5, [10,20]] Se você tentar ler ou escrever um elemento que Uma lista dentro de outra lista é dita estar não existe, você recebe um erro de tempo de execução aninhada. (runtime error): Listas que contém inteiros consecutivos são >>> numeros[2]=5 comuns, então Python fornece uma maneira simples de criá- los: IndexError: list assignment index out of range Se um índice possui um valor negativo, ele >>> range(1,5) conta ao contrário a partir do final da lista: [1, 2, 3, 4] >>> numeros[-1] A função range pega dois argumentos e devolve uma lista que contém todos os inteiros do primeiro até o 5 segundo, incluindo o primeiro mas não incluindo o segundo! >>> numeros[-2] 17 Existem outras formas de range. Com um >>> numeros[-3] argumento simples, ela cria uma lista que inicia em 0: IndexError: list index out of range >>> range(10) numeros[-1] é o último elemento da lista, [0,1, 2, 3, 4, 5, 6, 7, 8, 9] numeros[-2] é o penúltimo e numeros[-3] não existe. Se existe um terceiro argumento, ele especifica É comum utilizar uma variável de laço como um o espaço entre os valores sucessivos, que é chamado de índice da lista: tamanho do passo. Este exemplo conta de 1 até 10 em passos de 2: >>> cavaleiros = ['guerra', 'fome', 'peste', 'morte'] i = 0 >>> range(1, 10, 2) while i < 4: [1, 3, 5, 7, 9] print cavaleiros[i] Finalmente, existe uma lista especial que não contém elementos. Ela é chamada lista vazia, e sua notação é i = i + 1 []. Este laço while conta de 0 até 4. Quando a variável do laço i é 4, a condição falha e o laço se encerra. Com todas estas formas de criar listas, seria Desta forma o corpo do laço é executado somente quando i é decepcionante se não pudéssemos atribuir valores de listas a 0, 1, 2 e 3. variáveis ou passar listas como parâmetros a funções. Felizmente, podemos. Em cada vez dentro do laço, a variável i é utilizada como um índice para a lista, exibindo o i-ésimo >>> vocabulario = ['melhorar', 'castigar', elemento. Este padrão de computação é chamado de percurso 'defenestrar'] na lista. >>> numeros = [17, 123] >>> vazio = [] >>> print vocabulario, numeros, vazio 8.3 Comprimento da lista ['melhorar', 'castigar', 'defenestrar'] [17, 123] [] A função len devolve o comprimento de uma lista. É uma boa idéia utilizar este valor como o limite superior de um laço ao invés de uma constante. Desta forma, se o tamanho da lista Capítulo 8: Listas #46
  • 47. Como pensar como um cientista da Computação usando Python mudar, você não precisará ir através de todo o programa Quase se lê como Português: "For (para cada) modificando todos os laços; eles funcionarão corretamente cavaleiro in (na lista de) cavaleiros, print (imprima o nome do) para qualquer tamanho de lista: cavaleiro." >>> cavaleiros = ['guerra', 'fome', 'peste', 'morte'] Qualquer expressão de lista pode ser utilizada i = 0 num laço for: while i < len(cavaleiros): >>> for numero in range(20): print cavaleiros[i] if numero % 2 == 0: i = i + 1 print numero A última vez que o corpo do laço é executado, i é len(cavaleiros) - 1, que é o índice do último elemento. >>> for fruta in ["banana", "abacaxi", "laranja"]: Quando i é igual a len(cavaleiros), a condição falha e o corpo não é executado, o que é uma boa coisa, porque print "Eu gosto de comer " + fruta + "s!" len(cavaleiros) não é um índice legal. O primeiro exemplo exibe todos os números pares entre zero e dezenove. O segundo exemplo expressa o Embora uma lista possa conter uma outra lista, a entusiasmo por várias frutas. lista aninhada ainda conta como um elemento simples. O comprimento desta lista é quatro: >>> [`spam!', 1, ['Brie', 'Roquefort', 8.6 Operações em listas 'Pol lê Veq'], [1, 2 3]] Como um exercício, escreva um laço que O operador + concatena listas: percorra a lista anterior e exiba o comprimento de cada elemento. O que acontece se você manda um inteiro para >>> a = [1, 2, 3] len? >>> b = [4, 5, 6] >>> c = a + b >>> print c 8.4 Membros de uma lista [1, 2, 3, 4, 5, 6] Similarmente, o operador * repete uma lista um in é um operador lógico que testa se um elemento é membro número dado de vezes: de uma seqüência. Nós o utilizamos na Seção 7.10 com strings, mas ele também funciona com listas e outras >>> [0] * 4 seqüências: [0, 0, 0, 0] >>> [1, 2, 3] * 3 >>> cavaleiros = ['guerra', 'fome', 'peste', 'morte'] [1, 2, 3, 1, 2, 3, 1, 2, 3] >>> 'peste' in cavaleiros O primeiro exemplo repete [0] quatro vezes. O True segundo exemplo repete a lista [1, 2, 3] três vezes. >>> 'depravação' in cavaleiros False Uma vez que 'peste' é um membro da lista 8.7 Fatiamento de listas cavaleiros, o operador in devolve verdadeiro. Uma vez que depravação não está na lista, in devolve falso. A operação de fatiamento que vimos na Seção 7.4 também Podemos utilizar também o not em combinação funciona sobre listas: com o in para testar se um elemento não é um membro de uma lista: >>> lista = ['a', 'b', 'c', 'd', 'e', 'f'] >>> lista[1:3] >>> ``depravação`` not in cavaleiros ['b', 'c'] True >>> lista[:4] ['a', 'b', 'c', 'd'] >>> lista[3:] 8.5 Listas e laços for ['d', 'e', 'f'] >>> lista[:] O laço for que vimos na Seção 7.3 também funciona com ['a', 'b', 'c', 'd', 'e', 'f'] listas. A sintaxe generalizada de um laço for é: for VARIÁVEL in LISTA: CORPO 8.8 Listas são mutáveis Esta declaração é equivalente a: Diferente das strings, as listas são mutáveis, o que significa >>> i = 0 que podemos modificar seus elementos. Utilizando o operador while i < len(LIST): colchete no lado esquerdo de uma atribuição, podemos VARIABLE = LIST[i] atualizar um de seus elementos: XXX BODY >>> fruta = ["banana", "abacaxi", "laranja"] i = i + 1 >>> fruta[0] = "abacate" O laço for é mais conciso porque podemos >>> fruta[-1] = "tangerina" eliminar a variável do laço, i. Aqui está o laço anterior escrito com um`laço for: >>> print fruta ['abacate', 'abacaxi', 'tangerina'] >>> for cavaleiro in cavaleiros: Com o operador de fatiamento podemos print cavaleiro atualizar vários elementos de uma vez: Capítulo 8: Listas #47
  • 48. Como pensar como um cientista da Computação usando Python >>> lista = ['a', 'b', 'c', 'd', 'e', 'f'] se referem à mesma coisa. Estas "coisas" possume nomes - >>> lista[1:3] = ['x', 'y'] elas são chamadas objetos. Um objeto é algo ao qual uma >>> print lista variável pode se referenciar. ['a', 'x', 'y', 'd', 'e', 'f'] Todo objeto possui um identificador único, que Também podemos remover elementos de uma podemos obter com a função id. Exibindo o identificador de a lista atribuindo a lista vazia a eles: e b, podemos dizer se elas se referem ao mesmo objeto. >>> lista = ['a', 'b', 'c', 'd', 'e', 'f'] >>> id(a) >>> lista[1:3] = [] 135044008 >>> print lista >>> id(b) ['a', 'd', 'e', 'f'] 135044008 E podemos adicionar elementos a uma lista De fato, obtivemos o mesmo identificador duas enfiando-os numa fatia vazia na posição desejada: vezes, o que significa que Python criou apenas uma string, e tanto a quanto b se referem a ela. >>> lista = ['a', 'd', 'f'] >>> lista[1:1] = ['b', 'c'] Interessantemente, listas se comportam de forma diferente. Quando criamos duas listas, obtemos dois objetos: >>> print lista ['a', 'b', 'c', 'd', 'f'] >>> a = [1, 2, 3] >>> lista[4:4] = ['e'] >>> b = [1, 2, 3] >>> print lista >>> id(a) ['a', 'b', 'c', 'd', 'e', 'f'] 135045528 >>> id(b) 135041704 8.9 Remoção em lista Então o diagrama de estado fica assim: Utilizando fatias para remover elementos pode ser complicado, e desta forma propenso a erro. Python fornece uma alternativa que é mais legível. del remove um elemento de uma lista: >>> a = ['um', 'dois', 'tres'] >>> del a[1] a e b possuem o mesmo valor mas não se >>> a referem ao mesmo objeto. ['um', 'tres'] Como você deveria esperar, del trata valores negativos e causa erros de tempo de execução se o índice 8.11 Apelidos estiver fora da faixa. Você também pode utilizar uma faixa como um Uma vez que variáveis se referem a objetos, se atribuimos índice para del: uma variável a uma outra, ambas as variáveis se referem ao mesmo objeto: >>> lista = ['a', 'b', 'c', 'd', 'e', 'f'] >>> del lista[1:5] >>> a = [1, 2, 3] >>> print lista >>> b = a ['a', 'f'] Neste caso, o diagrama de estado se parece com Como de costume, fatias selecionam todos os isto: elementos até, mas não incluindo, o segundo índice. 8.10 Ojetos e valores Se executamos estas declarações de atribuição: >>> a = "banana" >>> b = "banana" Uma vez que a lista possui dois nomes diferentes, a e b, dizemos que ela está "apelidada" (aliased). sabemos que a e b se referem a uma string com Mudanças feitas em um apelido afetam o outro nome: as letras banana. Mas não podemos dizer se elas apontam para a mesma string. >>> b[0] = 5 >>> print a Existem dois possíveis estados: [5, 2, 3] 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 mutáveis. É claro, para objetos imutáveis, não há problema. É por isto que Python é livre para apelidar cadeias de caracteres quando vê uma oportunidade de economizar. Em um caso, a e b se referem a duas coisas diferentes que possuem o mesmo valor. No segundo caso, elas Capítulo 8: Listas #48
  • 49. Como pensar como um cientista da Computação usando Python que contém todos menos o primeiro elemento de uma 8.12 Clonando listas determinada lista: Se queremos modificar uma lista e também manter uma cópia >>> def cauda(lista): da original, preciamos ter condições de fazer uma cópia da return lista[1:] própria lista, não apenas uma referência. Este processo é Aqui está a maneira como ela é utilizada: algumas vezes chamado clonagem, para evitar a ambigüidade da palavra "cópia". >>> numeros = [1, 2, 3] >>> resto = cauda(numeros) A maneira mas fácil de clonar uma lista é utilizar o operador de fatia: >>> print resto [2, 3] >>> a = [1, 2, 3] Uma vez que o valor de retorno foi criado com >>> b = a[:] o operador de fatia, ele é uma nova lista. A criação de resto, e >>> print b qualquer alteração subseqüente a resto, não tem efeito sobre [1, 2, 3] numeros. Pegar qualquer fatia de a cria uma nova lista. Neste caso acontece da fatia consistir da lista inteira. Agora estamos livres para fazer alterações a b 8.14 Lista aninhadas sem nos preocuparmos com``a``: Uma lista aninhada é uma lista que aparece como um >>> b[0] = 5 elemento de uma outra lista. Nesta lista, o terceiro elemento é >>> print a uma lista aninhada: [1, 2, 3] >>> lista = ["alo", 2.0, 5, [10, 20]] Como exercício, desenhe um diagrama de Se exibimos lista[3], obtemos [10, 20]. Para estado para``a`` e b antes e depois desta mudança. extrairmos um elemento de uma lista aninhada, podemos agir em duas etapas: >>> elem = lista[3] 8.13 Lista como parâmetro >>> elem[0] 10 Passar uma lista como um argumento passa realmente uma referência à lista, não uma cópia da lista. Por exemplo, a Ou podemos combiná-las: função cabeca pega uma lista como parâmetro e devolve a >>> lista[3][1] cabeça da lista, ou seja, seu primeiro elemento: 20 >>> def cabeca(lista): Os operadores colchete avaliam da esquerda return lista[0] para a direita, então a expressão pega o terceiro elemento de Eis como ela é utilizada: lista e extrai o primeiro elemento dela. >>> numeros = [1, 2, 3] >>> cabeca(numeros) 8.15 Matrizes 1 O parâmetro lista e a variável numeros são Listas aninhadas são freqüentemente utilizadas para apelidos para o mesmo objeto. O diagrama de estado se parece representar matrizes. Por exemplo, a matriz: com isto: poderia ser representada como: >>> matriz = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] Uma vez que o objeto é compartilhado pelos matriz é uma lista com três elementos, onde dois quadros, o desenhamos entre eles. cada elemento é uma linha da matriz. Podemos selecionar uma Se a função modifica um parâmetro da lista, a linha inteira da matriz da maneira habitual: função chamadora vê a mudança. Por exemplo, >>> matriz[1] removeCabeca remove o primeiro elemento da lista: [4, 5, 6] >>> def removecabeca(lista): Ou podemos extrair um único elemento da del lista[0] matriz utilinado a forma de duplo índice: Aqui está a maneira como ela é utilizada: >>> matriz[1][1] >>> numeros = [1, 2, 3] 5 >>> removeCabeca(numeros) O primeiro índice seleciona a linha, e o segundo >>> print numeros índice seleciona a coluna. Embora esta maneira de representar [2, 3] matrizes seja comum, ela não é a única possibilidade. Uma pequena variação é utilizar uma lista de colunas ao invés de Se uma função devolve uma lista, ela devolve uma lista de linhas. uma referência à lista. Por exemplo, cauda devolve uma lista Capítulo 8: Listas #49
  • 50. Como pensar como um cientista da Computação usando Python Mais adiante veremos uma alternativa mais mesmo para qualquer string? Quando eles seriam diferentes? radical utilizando um dicionário. 8.17 Glossário 8.16 Strings e listas lista (list) Uma coleção denominada de objetos, onde Duas das mais úteis funções no módulo string envolvem listas cada objeto é identificado por um índice. de strings. A função split (separar) quebra uma string em uma lista de palavras. Por padrão, qualquer número de caracteres índice (index) Uma variável inteira ou valor que indica um espaço em branco é considerado um limite de uma palavra: elemento de uma lista. >>> import string elemento Um dos valores em uma lista(ou outra >>> poesia = "O orvalho no carvalho..." (element) seqüência). O operador colchete seleciona >>> string.split(poesia) elementos de uma lista. ['O', 'orvalho', 'no', 'carvalho...'] seqüência Qualquer um dos tipos de dados que Um argumento opcional chamado um (sequence) consiste de um conjunto ordenado de delimitador pode ser utilizado para especificar qual caracter elementos, com cada elemento identificado utilizar como limites da palavra. O exemplo a seguir utiliza a por um índice. string va: lista aninhada Uma lista que é um elemento de uma outra >>> string.split(poesia, 'va') (nested list) lista. ['O or', 'lho no car', 'lho...'] percurso na O acesso seqüencial de cada elemento em Perceba que o delimitador não aparece na lista. lista (list uma lista. traversal) A função join (juntar) é o inverso de split. Ela pega uma lista de strings e concatena os elementos com um objeto Um coisa a qual uma variável pode se espaço entre cada par: (object) referir. >>> lista = ['O', 'orvalho', 'no', 'carvalho...'] apelidos Múltiplas variáveis que contém referências >>> string.join(lista) (aliases) ao mesmo objeto. 'O orvalho no carvalho...' clonar (clone) Criar um novo objeto que possui o mesmo Como split, join recebe um delimitador que é valor de um objeto existente. Copiar a inserido entre os elementos: referência a um objeto cria um apelido (alias) mas não clona o objeto. >>> string.join(lista, '_') 'O_orvalho_no_carvalho...' delimitador Um caracter uma string utilizados para (delimiter) indicar onde uma string deveria ser Como um execício, descreva o relacionamento dividida(split). entre string.join(string.split(poesia)) e poesia. Eles são o Capítulo 8: Listas #50
  • 51. Como pensar como um cientista da Computação usando Python Capítulo 9: Tuplas >>> temp = a 9.1 Mutabilidade e tuplas >>> a = b >>> b = temp Até agora, você tem visto dois tipos compostos: strings, que são compostos de caracteres; e listas, que são compostas de Se você tiver que fazer isso com frequência, esta elementos de qualquer tipo. Uma das diferenças que notamos abordagem se torna incômoda. Python fornece uma forma de é que os elementos de uma lista podem ser modificados, mas atribuição de tupla que resolve esse problema os caracteres em uma string não. Em outras palavras, strings elegantemente: são imutáveis e listas são mutáveis. >>> a, b = b, a Há um outro tipo em Python chamado tupla O lado esquedo é uma tupla de variáveis; o lado (tuple) que é similar a uma lista exceto por ele ser imutável. direito é uma tupla de valores. Cada valor é atribuído à sua Sintaticamente, uma tupla é uma lista de valores separados por respectiva variável. Todas as expressões do lado direito são vírgulas: avaliadas antes de qualquer das atribuições. Esta característica torna as atribuições de tupla bastante versáteis. >>> tupla = 'a', 'b', 'c', 'd', 'e' Embora não seja necessário, é convencional esquerda e o número de valores nanúmero deve ser igual: na Naturalmente, o de variáveis colocar tuplas entre parênteses: direita >>> tupla = ('a', 'b', 'c', 'd', 'e') >>> a, b, c, d = 1, 2, 3 ValueError: unpack tuple of wrong size Para criar uma tupla com um único elemento, temos que incluir uma vírgula final: >>> t1 = ('a',) >>> type(t1) 9.3 Tuplas como valores de retorno <type 'tuple'> Funções podem retornar tuplas como valor de retorno. Por Sem a vírgula, Python entende ('a') como uma Exemplo, nós poderíamos escrever uma função que troca dois string entre parênteses: parâmetros entre si: >>> t2 = ('a') def troca(x, y): >>> type(t2) return y, x <type 'string'> Então nós poderíamos atribuir o valor de retorno Questões de sintaxe de lado, as operações em para uma tupla com duas variáveis: tuplas são as mesmas operações das listas. O operador índice seleciona um elemento da tupla. a, b = troca(a, b) Neste caso, não existe uma grande vantagem em >>> tupla = ('a', 'b', 'c', 'd', 'e') fazer de troca (swap) uma função. De fato, existe um perigo >>> tupla[0] em tentar encapsular troca, o qual é a tentação de cometer o 'a' seguinte erro: E o operador slice (fatia) seleciona uma "faixa" (range) de elementos. def troca(x, y): # versao incorreta x, y = y, x >>> tupla[1:3] Se nós chamarmos esta função desta forma: ('b', 'c') Mas se tentarmos modificar um dos elementos troca(a, b) de uma tupla, teremos um erro: então a e x são apelidos para um mesmo valor. Mudar x dentro da função troca, faz com que x se referencie a >>> tupla[0] = 'A' um valor diferente, mas sem efeito sobre a dentro de TypeError: object doesn't support item assignment __main__. Do mesmo modo, a mudança em y não tem efeito Naturalmente, mesmo que não possamos sobre b. modificar os elementos de uma tupla, podemos substituí-la por Esta função roda sem produzir uma mensagem uma tupla diferente: de erro, mas ela não faz o que pretendemos. Este é um >>> tupla = ('A',) + tupla[1:] exemplo de um erro semântico. >>> tupla Como exercício, desenhe um diagrama de ('A', 'b', 'c', 'd', 'e') estado pra esta função de modo que você possa ver porque ela não funciona. 9.2 Atribuições de tupla 9.4 Números aleatórios De vez em quando, é necessário trocar entre si os valores de duas variáveis. Com operações de atribuição convencionais, A maioria dos programas de computador fazem a mesma coisa temos que utilizar uma variável temporária. Por exemplo, para sempre que são executados, então, podemos dizer que eles são fazer a troca entre a e b: determinísticos. Determinismo em geral é uma coisa boa, se Capítulo 9: Tuplas #51
  • 52. Como pensar como um cientista da Computação usando Python nós esperamos que um cálculo dê sempre o mesmo resultado. programa que divida a faixa de valores em intervalos e conte o Entretanto, para algumas aplicações queremos que o número de valores de cada intervalo. computador se torne imprevisível. Jogos são um exemplo óbvio, mas existem outros. Fazer um programa realmente não- 9.6 Contando determinístico se mostra não ser tão fácil, mas existem maneiras de fazê-lo ao menos parecer não-determinístico. Uma boa maneira de abordar problemas como esse é dividir o Uma dessas maneiras é gerar números aleatórios e usá-los problema em subproblemas, e encontrar um subproblema que para determinar o resultado de um programa. Python tem uma se enquadre em um padrão de solução computacional que função nativa que gera números pseudo aleatórios, os quais você já tenha visto antes. não são verdadeiramente aleatórios no sentido matemático, mas para os nossos propósitos eles são. Neste caso, queremos percorrer uma lista de números e contar o número de vezes que valor se encaixa em O módulo random contém uma função chamada um determinado intervalo. Isso soa familiar. Na Seção 7.8, random que retorna um número em ponto flutuante (floating- nós escrevemos um programa que percorria uma string e point number) entre 0.0 e 1.0. Cada vez que você chama contava o número de vezes que uma determinada letra random, você recebe o próximo número de uma longa série. aparecia. Para ver uma amostra, execute este loop: Assim, podemos prosseguir copiando o import random programa original e adaptando-o para o problema atual. O programa original era: for i in range(10): x = random.random() contador = 0 print x for letra in fruta: if letra == 'a': Para gerar um número aleatório ente 0.0 e um limite superior, digamos superior, multiplique x por superior. contador = contador + 1 print contador Como exercício, gere um número aleatório entre O primeiro passo é substituir fruta por lista e 'inferior' e 'superior'. letra por numero. Isso não muda o programa, apenas o ajusta Como exercício adicional, gere um número para que ele se torne mais fácil de ler e entender. inteiro aleatório entre 'inferior' e 'superior', inclusive os dois O segundo passo é mudar o teste. Nós não extremos. estamos interessados em procurar letras. Nós queremos ver se numero está entre inferior e superior.: contador = 0 9.5 Lista de números aleatórios for numero in lista if inferior < numero < superior: O primeiro passo é gerar uma lista aleatória de valores. listaAleatoria pega um parâmetro inteiro e retorna uma lista de contador = contador + 1 números aleatórios com o comprimento dado. Inicia-se com print contador uma lista de n zeros. A cada iteração do loop, ele substitui um O último passo é encapsular este código em dos elementos por um número aleatório. O valor retornado é uma função chamada noIntervalo. Os parâmetros são a lista e uma referência para a lista completa: os valores inferior e superior: def listaAleatoria(n): def noIntervalo(lista, inferior, superior): s = [0] * n contador = 0 for i in range(n): for numero in lista: s[i] = random.random() if inferior < numero < superior: return s contador = contador + 1 Vamos realizar um teste desta função com uma return contador lista de oito elementos. Para efeitos de depuração, é uma boa Através da cópia e da modificação de um idéia começar com uma lista pequena. programa existente, estamos aptos a escrever esta função >>> listaAleatoria(8) rapidamente e economizar um bocado de tempo de depuração. Este plano de desenvolvimento é chamado de casamento de 0.15156642489 padrões. Se você se encontrar trabalhando em um problema 0.498048560109 que você já solucionou antes, reuse a solução. 0.810894847068 0.360371157682 0.275119183077 9.7 Vários intervalos 0.328578797631 0.759199803101 Conforme o número de intervalos aumenta, noIntervalo torna- 0.800367163582 se intragável. Com dois intervalos, não é tão ruim: Os números gerados por random são supostamente uniformemente distribuídos, o que significa que inferior = noIntervalo(a, 0.0, 0.5) cada valor tem uma probabilidade igual de acontecer. superior = noIntervalo(a, 0.5, 1) Mas com quatro intervalos, começa a ficar Se nós dividirmos a faixa de valores possíveis desconfortável.: em intervalos do mesmo tamanho, e contarmos o número de vezes que um determinado valor aleatório caiu em seu intervalo1 = noIntervalo(a, 0.0, 0.25) respectivo intervalo, nós devemos obter o mesmo número intervalo2 = noIntervalo(a, 0.25, 0.5) aproximado de valores em cada um dos intervalos. intervalo3 = noIntervalo(a, 0.5, 0.75) Nós podemos testar esta teoria escrevendo um Capítulo 9: Tuplas #52
  • 53. Como pensar como um cientista da Computação usando Python intervalo4 = noIntervalo(a, 0.75, 1.0) Existem aqui dois problemas. Um é que temos 9.8 Uma solução em um só passo que criar novos nomes de variável para cada resultado. O outro é que temos que calcular os limites de cada intervalo. Embora este programa funcione, ele não é tão eficiente quanto poderia ser. Toda vez que ele chama noIntervalo, ele percorre Vamos resolver o segundo problema primeiro. a lista inteira. Conforme o número de intervalos aumenta, a Se o número de intervalos é numeroDeIntervalos, então a lista será percorrida um bocado de vezes. largura de cada intervalo é 1.0 / numeroDeIntervalos. Seria melhor fazer uma única passagem pela Vamos usar um laço (loop) para calcular a faixa, lista e calcular para cada valor o índice do intervalo ao qual o ou largura, de cada intervalo. A variável do loop, i, conta de 0 valor pertença. Então podemos incrementar o contador até numeroDeIntervalos-1: apropriado. larguraDoIntervalo = 1.0 / numeroDeIntervalos Na seção anterior, pegamos um índice, i, e o for i in range(numeroDeIntervalos): multiplicamos pela larguraDoIntervalo para encontrar o limite inferior = i * larguraDoIntervalo inferior daquele intervalo. Agora queremos pegar um valor entre 0.0 e 1.0 e encontrar o índice do intervalo ao qual ele se superior = inferior + larguraDoIntervalo encaixa. print "do" inferior, "ao", superior Para calcular o limite inferior (inferior) de cada Já que este problema é o inverso do problema intervalo, nós multiplicamos a variável do loop (i) pela largura anterior, podemos imaginar que deveríamos dividir por do intervalo (larguraDoIntervalo). O limite superior (superior) larguraDoIntervalo em vez de multiplicar. Esta suposição está está exatamente uma "largura de intervalo" acima. correta. Com numeroDeIntervalos = 8, o resultado é: Já que larguraDoIntervalo = 1.0 / numeroDeIntervalos, dividir por larguraDoIntervalo é o 0.0 to 0.125 mesmo que multiplicar por numeroDeIntervalos. Se 0.125 to 0.25 multiplicarmos um número na faixa entre 0.0 e 1.0 por 0.25 to 0.375 numeroDeIntervalos, obtemos um número na faixa entre 0.0 e numeroDeIntervalos. Se arredondarmos este número para 0.375 to 0.5 baixo, ou seja, para o menor inteiro mais próximo, obtemos 0.5 to 0.625 exatamente o que estamos procurando - o índice do intervalo: 0.625 to 0.75 0.75 to 0.875 numeroDeIntervalos = 8 0.875 to 1.0 intervalos = [0] * numeroDeIntervalos for i in lista: Você pode confirmar que cada intervalo tem a mesma largura, que eles não se sobrepõe, e que eles cobrem indice = int(i * numeroDeIntervalos) toda a faixa de valores de 0.0 a 1.0. intervalos[indice] = intervalos[indice] + 1 Usamos a função int para converter um número Agora, de volta ao primeiro problema. Nós em ponto flutuante (float) para um inteiro. precisamos de uma maneira de guardar oito inteiros, usando a váriavel do loop para indicar cada um destes inteiros. Você Existe a possibilidade deste cálculo produzir um deve estar pensando, "Lista!" índice que esteja fora dos limites (seja negativo ou maior que len(intervalos)-1)? Nós temos que criar a lista de intervalos fora do loop, porque queremos fazer isto apenas uma vez. Dentro do Uma lista como intervalos que contém uma loop, nós vamos chamar noIntervalo repetidamente e atualizar contagem do número de valores em cada intervalo é chamada o i-ésimo elemento da lista: de histograma. numeroDeIntervalos = 8 Como exercício, escreva uma função chamada intervalos = [0] * numeroDeIntervalos ``histograma`` que receba uma lista e um número de larguraDoIntervalo = 1.0 / numeroDeIntervalos intervalos como argumentos e retorne um histograma com o for i in range(numeroDeIntervalos): número de intervalos solicitado. inferior = i * larguraDoIntervalo superior = inferior + larguraDoIntervalo intervalos[i] = noIntervalo(lista, inferior, 9.9 Glossário superior) print intervalos tipo imutável Um tipo de elemento que não pode ser Com uma lista de 1000 valores, este código vai (immutable modificado. Atribuições a um elemento ou produzir esta lista de quantidades de valores em cada type) "fatiamento (slices)" XXX aos tipos intervalo: imutáveis causarão erro. [138, 124, 128, 118, 130, 117, 114, 131] tipo mutável Tipo de dados onde os elementos podem Esses números estão razoavelmente póximos de (mutable type) ser modificados. Todos os tipos mutáveis, 125, o que era o que esperávamos. Pelo menos eles estão são tipos compostos. Listas e dicionários próximos o bastante para nos fazer acreditar que o gerador de são exemplos de tipos de dados mutáveis. número aleatórios está funcionando. String e tuplas não são. Como exercício, teste esta função com algumas tupla (tuple) Tipo sequencial similar as listas com listas longas, e veja se o número de valores em cada um dos exceção de que ele é imutável. Podem ser intervalos tendem a uma distribuição nivelada. usadas Tuplas sempre que um tipo imutável for necessário, por exemplo uma "chave (key)" em um dicionário Atribuição a Atribuição a todos os elementos de uma tupla (tuple tupla feita num único comando de Capítulo 9: Tuplas #53
  • 54. Como pensar como um cientista da Computação usando Python assignment) atribução. A atribuição aos elementos ocorre em paralelo, e não em sequência, tornando esta operação útil para swap, ou troca recíproca de valores entre variáveis (ex: a,b=b,a). determinístico Um programa que realiza a mesma coisa (deterministic) sempre que é executado. pseudo Uma sequência de números que parecem aleatório ser aleatórios mas são na verdade o (pseudorando resultado de uma computação m) "determinística" histograma Uma lista de inteiros na qual cada elemento (histogram) conta o número de vezes que algo acontece. Capítulo 9: Tuplas #54
  • 55. Como pensar como um cientista da Computação usando Python Capítulo 10: Dicionários Os tipos compostos que voce aprendeu - strings, listas e tuplas >>> del inventario['peras'] - utilizam inteiros como indices. Se voce tentar utilizar >>> print inventario qualquer outro tipo como indice, voce receberá um erro. {'laranjas': 525, 'abacaxis': 430, 'bananas': 312} Dicionários sao similiares a outros tipos Ou se nós esperamos por mais peras em breve, compostos exceto por eles poderem user qualquer tipo nos podemos simplesmente trocar o valor associoado as peras: imutavel de dados como indice. Como exemplo, nos criaremos um dicionário para traduzir palavras em Inglês para >>> inventario['peras'] = 0 Espanhol. Para esse dicionário, os indices serão strings. >>> print inventario Uma maneira de criar um dicionario é {'laranjas': 525, 'abacaxis': 430, 'peras': 0, comecando com um dicionário vazio e depois adiconando 'bananas': 312} elementos. Um dicionário vazio é denotado assim {}: A função len também funciona com dicionários; retornando o número de pares chave-valor: >>> ing2esp = {} >>> ing2esp['one'] = 'uno' >>> len(inventario) >>> ing2esp['two'] = 'dos' 4 A primeira atribuição cria um dicionario chamado ing2esp; as outras atribuições adicionam novos elementos para o dicionário. Nos podemos imprimir o valor 10.2 Métodos dos Dicionários corrente de um dicionario da maneira usual: >>> print ing2esp Um método é parecido com uma função - possui parametros e {'one': 'uno', 'two': 'dos'} retorna valores - mas a sintaxe é diferente. Por exemplo, o Os elementos de um dicionário aparecem em metodo keys recebe um dicionário e retorna uma lista com as uma lista separada por vírgulas. Cada entrada contêm um chaves, mas em vez de usarmos a sintaxe de função indice e um valor separado por dois-pontos. Em um keys(ing2esp), nos usamos a sintaxe de método dicionário, os índices sao chamados de chaves, entao os ing2esp.keys(): elementos são chamados de pares chave-valor. >>> ing2esp.keys() Outra maneira de criar dicionários é fornecendo ['one', 'three', 'two'] uma lista de pares chaves-valor utilizando a mesma sintaxe da Dessa forma o ponto especifica o nome da última saída. função, keys, e o nome do objeto em que deve ser aplicada a função, ing2esp. Os parenteses indicam que esse método não >>> ing2esp = {'one': 'uno', 'two': 'dos', possui parameteros. 'three': 'tres'} Se nos imprimirmos o valor de ing2esp que ele é invocado, nesse caso, nós podemos dizer dizemos Ao invés de chamarmos um método, que nós novamente, nos teremos uma surpresa: estamos invocando keys do objeto ing2esp. >>> print ing2esp O método values é parecido; retorna a lista de {'one': 'uno', 'three': 'tres', 'two': 'dos'} valores de um dicionário: Os pares chave-valor não estão em ordem! Felizmente, não a motivos para se preocupar com a ordem, >>> ing2esp.values() desde que os elementos do dicionário nunca sejam indexados ['uno', 'tres', 'dos'] com indices inteiros. Podemos usar as chaves para buscar os O método items retorna os dois, na forma de valores correspondentes: uma lista de tuplas - cada tupla com um par chave-valor: >>> print ing2esp['two'] >>> ing2esp.items() 'dos' [('one','uno'), ('three','tres'), ('two','dos')] A chave 'two' retornou o valor 'dos' mesmo A sintaxe fornece uma informação util. Os pensando que retornaria o terceiro par chave-valor. colchetes indicam que isso é uma lista. Os parentêses indicam que os elementos da lista são tuplas. Se o método recebe de algum parâmetro, se 10.1 Operações dos Dicionários utiliza a mesma sintaxe das funções. Por exemplo, o método has_key recebe uma chave e retorna verdadeiro (1) se a chave O comando del remove um par chave-valor de um dicionário. existe no dicionário: Por exemplo, o dicionário abaixo contem os nomes de varias frutas e o numero de cada fruta em no estoque: >>> ing2esp.has_key('one') True >>> inventario = {'abacaxis': 430, 'bananas': 312, >>> ing2esp.has_key('deux') 'laranjas': 525, 'peras': 217} False >>> print inventario Se voce tentar chamar um método sem {'laranjas': 525, 'abacaxis': 430, 'peras': 217, especificar em qual objeto, voce obterá um erro. Nesse caso, a 'bananas': 312} mensagem de erro não é muito útil: Se alguem comprar todas as peras, podemos >>> has_key('one') remover a entrada do dicionário: Capítulo 10: Dicionários #55
  • 56. Como pensar como um cientista da Computação usando Python NameError: has_key utilizamos o operador []: >>> matriz[0,3] 1 10.3 Aliasing (XXX) e Copiar Note que a sintaxe da representação de um dicionário não é a mesma que a sintaxe usada pela Uma vez que os dicionários são mutáveis, voce precisa saber representação pelas listas. Em vez de usarmos dois índices sobre Aliasing. Sempre que duas variáveis referenciarem o inteiros, nós usamos apenas um índice, que é uma tupla de mesmo objeto, quando uma é alterada, afeta a outra. inteiros. Se você quer modificar um dicionário e Mas existe um problema. Se tentarmos buscar continuar com uma copia original, utilize o método copy. Por um elemento zero, obteremos um erro, pois não existe uma exemplo, opposites é um dicionário que contêm pares de entrada no dicionário para a chave especificada: antônimos: >>> matriz[1,3] >>> opposites = {'up': 'down', 'right': 'wrong', KeyError: (1,3) 'true': 'false'} O método get resolve esse problema: >>> alias = opposities >>> copy = opposities.copy() >>> matriz.get((0,3), 0) 1 alias e opposites se referem ao mesmo objeto; copy se refere a um novo objeto igual ao dicionário opposites. O primeiro parâmetro é a chave; o segundo é o Se você modificar o alias, opposites também será alterado. valor que get retornará caso não existe a chave no dicionário: >>> alias['right'] = 'left' >>> matriz.get((1,3), 0) >>> opossites['right'] 0 'left' get definitivamente melhora a semântica e a Se modificarmos copy, opposites não será sintaxe do acesso a matrizes esparsas. modificado: >>> copy['right'] = 'privilege' 10.5 Hint >>> opposites['right'] 'left' Se você brincou com a função fibonacci da seção 5.7, é provável que você notou que quanto maior o número passado para a função, mais tempo a função demora para executar. 10.4 Matrizes Esparsas Além disso, o tempo da execução aumenta rapidamente. Em uma das nossas máquinas, fibonacci(20) executa instantaneamente, fibonacci(30) demora cerca de um Na seção 8.14, nós usamos uma lista de listas para representar segundo, e fibonacci(40) demora uma eternidade. uma matriz. Essa é uma boa escolha se a matriz for principalmente de valores diferentes de zero, mas Para entender o porque, considere o gráfico de considerando uma matriz esparsa como essa: chamadas para fibonacci com n=4: Uma representação 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 dicionário. Para as chaves, nós podemos usar tuplas que contêm os números da O gráfico mostra a estrutura da função, com linha e a coluna. Abaixo uma representação em um diciónario linhas conectando cada execução com a execução que a da mesma matriz: chamou. No topo do gráfico, fibonacci tem n=4, que chama >>> matriz = {(0,3): 1, (2, 1): 2, (4, 3): 3} fibonacci com n=3 e n=2. Em seguida, fibonacci com n=3 chama fibonacci com n=2 e n=1. E assim por diante. Nós precisamos apenas de três pares chave- valor, cada um sendo um elemento diferente de zero da Conte quantas vezes fibonacci(0) e fibonacci(1) matriz. Cada chave é uma tupla, e cada valor é um número são chamadas. Essa é uma solução ineficiente para o inteiro. problema, e torna-se pior quando o parâmetro recebido é um número maior. Para acessarmos um elemento da matriz, nos Capítulo 10: Dicionários #56
  • 57. Como pensar como um cientista da Computação usando Python Uma boa solução é guardar os valores que já Os dois primeiros numeros da sequência são long ints, então foram calculados armazenando-os em um dicionário. Um todos os números subsequentes da sequência também serão. valor previamente calculado que é guardado para ser utilizado mais tarde é chamado de hint. Abaixo uma implementação de Como exercício, converta fatorial para produzir fibonacci usando hints: um inteiro longo como resultado. >>> previous = {0:1, 1:1} >>> def fibonacci(n): 10.7 Contando Letras if previous.has_key(n): return previous[n] No capítulo 7, escrevemos uma função que contava o número else: de ocorrências de uma letra em uma string. A versão mais newValue = fibonacci(n-1) + comum desse problema é fazer um histograma das letras da fibonacci(n-2) string, ou seja, quantas vezes cada letra aparece na string. previous[n] = newValue Um histograma pode ser util para comprimir um return newValue arquivo de texto. Pois diferentes letras aparecem com O dicionário chamado previous guarda os diferentes frequências, podemos comprimir um arquivo números de Fibonacci que nós ja conhecemos. Ele começa usando pequenos códigos para letras comuns e longos códigos com apenas dois pares: 0 possui 1; e 1 possui 1. para letras que aparecem em menor frequência. Sempre que fibonacci é chamada, ela verifica o Dicionários fornecem uma maneira elegante de dicionário para determinar se ele já possui o resultado. Se o gerar um histograma: resultado estiver ali, a função pode retornar imediatamente sempre precisar fazer mais chamadas recursivas. Se o >>> letterCounts = {} resultado não estiver ali, ele é calculado no newValue. O >>> for letter in "Mississippi": valor de newValue é adicionado no dicionário antes da função ... letterCounts[letter] = retornar. letterCounts.get(letter,0) + 1 Usando essa versão de fibonacci, nossa ... máquina consegue calcular fibonacci(40) em um piscar de >>> letterCounts olhos. Mas quando tentamos calcular fibonacci(50), nós {'M': 1, 's': 4, 'p': 2, 'i': 4} veremos um problema diferente: Começamos com um dicionário vazio. Para >>> fibonacci(50) cada letra da string, achamos o contador (possivelmente zero) e o incrementamos. No final, o dicionário contem pares de OverflowError: integer addition letras e as suas frequências. A resposta, que você verá em um minuto, é 20.365.011.074. O problema é que esse número é muito É mais atraente mostrarmos o histograma na grande para guardarmos como um inteiro do Python 1. Isso é ordem alfabética. Podemos fazer isso com os métodos items e overflow. Felizmente, esse problema tem uma solução sort: simples. >>> letterItems = letterCounts.items() >>> letterItems.sort() >>> print letterItems 10.6 Inteiros Longos [('M', 1), ('i', 4), ('p', 2), ('s', 4)] Você ja tinha visto o método items antes, mas Python possui um tipo chamado long int que permite sort é o primeiro método que você se depara para aplicar em trabalharmos com qualquer tamanho de inteiros. Existem duas listas. Existem muitos outros métodos de listas, incluindo maneiras de criarmos um valor long int. A primeira é escrever append, extend, e reverse. Consulte a documentação do um inteiro seguido de um L no final: Python para maiores detalhes. >>> type(1L) <type 'long int'> A outra maneira é usarmos a função long que 10.8 Glossário converte um valor para um long int. long pode receber qualquer valor númerico e até mesmo uma string de digitos: dicionário Uma coleção de pares de chaves-valores >>> long(1) (dictionary) que são mapeados pelas chaves, para se 1L obter os valores. As chaves podem ser qualquer tipo de dados imutavel, e os >>> long(3.9) valores podem ser de qualquer tipo. 3L >>> long('57') chave (key) Um valor que é usado para buscar uma 57L entrada em um dicionário. Todas as operações matemáticas funcionam par chave-valor Um dos itens de um dicionário. com long int s, então não precisamos modificar muito para (key-value pair) adaptar fibonacci: método Um tipo de função que é chamada com >>> previous = {0: 1L, 1:1L} (method) uma sintaxe diferente e invocada no >>> fibonacci(50) contexto de um objeto. 20365011074L invocar (invoke) Chamar um método. Somente trocando os valores iniciais de previous, conseguimos mudar o comportamento da fibonacci. hint O armazenamento temporário de um valor pré-computado para evitar a computação 1 N.T. A partir do Python 2. XXX este erro não ocorre mais, redundante. pois em caso de sobrecarga o valor inteiro é automaticamente promovido para o tipo long. overflow Um resultado numérico que é muito Capítulo 10: Dicionários #57
  • 58. Como pensar como um cientista da Computação usando Python grande para ser representado no formato numérico. Capítulo 10: Dicionários #58
  • 59. Como pensar como um cientista da Computação usando Python Capítulo 11: Arquivos e exceções temos um erro: Arquivos e exceções >>> f = open("teste.cat", "r") Durante a execução de um programa, seus dados ficam na IOError: [Errno 2] No such file or directory: memória. Quando o programa termina, ou o computador é 'teste.cat' desligado, os dados na memória desaparecem. Para armazenar Sem nenhuma surpresa, o método read lê dados os dados permanentemente, você tem que colocá-los em um do arquivo. Sem argumentos, ele lê todo o conteúdo do arquivo. Arquivos usualmente são guardados em um disco arquivo: rígido (HD), num disquete ou em um CD-ROM. >>> texto = f.read() Quando existe um número muito grande de arquivos, eles muitas vezes são organizados dentro de >>> print texto diretórios (também chamados de ?pastas? ou ainda ? Agora é horade fechar o arquivo *folders*?). Cada arquivo é identificado por um nome único, Não existe espaço entre ?hora? e ?de? porque ou uma combinação de um nome de arquivo com um nome de nós não gravamos um espaço entre as strings. diretório. read também pode receber um argumento que Lendo e escrevendo em arquivos, os programas indica quantos caracteres ler: podem trocar informações uns com os outros e gerar formatos imprimíveis como PDF. >>> f = open("teste.dat", "r") >>> print f.read(9) Trabalhar com arquivos é muito parecido com Agora é h trabalhar com livros. Para utilizar um livro, você tem que abrí- lo. Quando você termina, você tem que fechá-lo. Enquanto o Se não houver caracteres suficientes no arquivo, livro estiver aberto, você pode tanto lê-lo quanto escrever read retorna os caracteres restantes. Quando chegamos ao final nele. Em qualquer caso, você sabe onde você está situado no do arquivo, read retorna a string vazia: livro. Na maioria das vezes, você lê o livro inteiro em sua ordem natural, mas você também pode saltar através de alguns >>> print f.read(1000006) trechos (skip around). orade fechar o arquivo >>> print f.read() Tudo isso se aplica do mesmo modo a arquivos. Para abrir um arquivo, você especifica o nome dele e indica o que você quer, seja ler ou escrever (gravar). >>> A função seguinte, copia um arquivo, lendo e Abrir um arquivo cria um objeto arquivo. Neste gravando até cinqüenta caracteres de uma vez. O primeiro exemplo, a variável f se referencia ao novo objeto arquivo. argumento é o nome do arquivo original; o segundo é o nome do novo arquivo: >>> f = open("teste.dat", "w") >>> print f def copiaArquivo(velhoArquivo, novoArquivo): <open file "teste.dat", mode "w" at fe820> f1 = open(velhoArquivo, "r") A função open recebe dois argumentos. O f2 = open(novoArquivo, "w") primeiro é o nome do arquivo, e o segundo é o modo. Modo ? while 1: w? significa que estamos abrindo o arquivo para gravação texto = f1.read(50) (?*write*?, escrever). if texto == "": Se não existir nenhum arquivo de nome break teste.dat, ele será criado. Se já existir um, ele será substituído f2.write(texto) pelo arquivo que estamos gravando (ou escrevendo). f1.close() Quando executamos um comando print sobre o f2.close() objeto arquivo, visualizamos o nome do arquivo, o modo e a return localização do objeto na memória. A comando break é novo. O que ele faz é saltar a execução para fora do loop; o fluxo de execução passa para Para colocar dados dentro do arquivo, o primeiro comando depois do loop. invocamos o método write do objeto arquivo: Neste exemplo, o loop while é infinito porque o >>> f.write("Agora é hora") valor 1 é sempre verdadeiro. O único modo de sair do loop é >>> f.write("de fechar o arquivo") executando o break, o que ocorre quando texto é a string Fechar o arquivo diz ao sistema que terminamos vazia, o que ocorre quando alcançamos o fim do arquivo. de escrever (gravar) e que o arquivo está livre para ser lido: >>> f.close() Agora podemos abrir o arquivo de novo, desta 11.1 Arquivos texto vez para leitura, e ler o seu conteúdo para uma string. Desta vez, o argumento modo é ?r? para leitura (?reading?): Um arquivo texto é um arquivo que contém caracteres imprimíveis e espaços, organizados dentro de linhas separadas >>> f = open("teste.dat", "r") por caracteres de nova linha. Já que Pyhton é especialmente Se tentarmos abrir um arquivo que não existe, projetado para processar arquivos texto, ele oferece métodos que tornam esta tarefa mais fácil. Capítulo 11: Arquivos e exceções #59
  • 60. Como pensar como um cientista da Computação usando Python Para demonstrar, vamos criar um arquivo texto módulo. Mas quando o primeiro operador é uma string, % é o com três linhas de texto separadas por caracteres de nova operador de formatação. linha: O primeiro operando é a string de formatação, >>> f = open("teste.dat", "w") e o segundo operando é uma tupla de expressões. O resultado >>> f.write("linha umnlinha doisnlinha trêsn") é uma string que contém os valores das expressões, >>> f.close() formatadas de acordo com a string de formatação. O método readline lê todos os caracteres até, e Num exemplo simples, a seqüência de incluindo, o próximo caractere de nova linha: formatação "??%d??" significa que a primeira expressão na tupla deve ser formatada como um inteiro. Aqui a letra d >>> f = open("teste.dat", "r") representa ?decimal?. >>> print f.readline() linha um >>> carros = 52 >>> "%d" % carros >>> '52' readlines retorna todas as linhas restantes como O resultado é a string ?52?, que não deve ser uma lista de strings: confundida com o valor inteiro 52. >>> print f.readlines() Uma seqüência de formatação pode aparecer em qualquer lugar na string de formatação, assim, podemos ['linha dois012', 'linha três012'] embutir um valor em uma seqüência: Neste caso, a saída está em formado de lista, o que significa que as strings aparecem entre aspas e o caractere >>> carros = 52 de nova linha aparece como a seqüência de escape 012. >>> "Em julho vendemos %d carros." % carros No fim do arquivo, readline retorna a string 'Em julho vendemos 52 carros.' vazia e readlines retorna a lista vazia: A seqüência de formatação "%f" formata o próximo item da tupla como um número em ponto flutuante, e >>> print f.readline() "%s" formata o próximo como uma string: >>> "Em %d dias fizemos %f milhões %s." % >>> print f.readlines() (34,6.1,'reais') [] 'Em 34 dias fizemos 6.100000 milhões de reais.' A seguir temos um exemplo de um programa de processamento de linhas. filtraArquivo faz uma cópia de Por padrão, o formato de ponto flutuante exibe velhoArquivo, omitindo quaisquer linhas que comecem por #: seis casas decimais. def filtraArquivo(velhoArquivo, novoArquivo): O número de expressões na tupla tem que ser igual ao número de seqüências de formatação na string. Além f1 = open(velhoArquivo, "r") disso, os tipos das expressões têm que iguais aos da seqüência f2 = open(novoArquivo, "w") de formatação: while 1: texto = f1.readline() >>> "%d %d %d" % (1,2) if texto == "": TypeError: not enough arguments for format string break >>> "%d" % 'reais' if texto[0] == '#': TypeError: illegal argument type for built-in continue operation f2.write(texto) No primeiro exemplo, não existem expressões f1.close() suficientes; no segundo, a expressão é do tipo errado. f2.close() Para um controle maior na formatação de return números, podemos especificar o número de dígitos como parte O comando continue termina a iteração corrente da seqüência de formatação: do loop, mas continua iterando o loop. O fluxo de execução >>> "%6d" % 62 passa para o topo do loop, checa a condição e prossegue conforme o caso. ' 62' >>> "%12f" % 6.1 Assim, se texto for a string vazia, o loop ' 6,100000' termina. Se o primeiro caractere de texto for o jogo da velha (? O número depois do sinal de porcentagem é o # ?), o fluxo de execução passa para o topo do loop. Somente número mínimo de espaços que o valor ocupará. Se o valor se ambas as condições falharem é que texto será copiado para fornecido tiver um número menor de dígitos, espaços em dentro do novo arquivo. branco serão adicionados antes para preencher o restante. Se o número de espaços for negativo, os espaços serão adicionados depois: 11.2 Gravando variáveis >>> "%-6d" % 62 '62 ' O argumento de write tem que ser uma string, assim se quisermos colocar outros valores em um arquivo, temos de Para números em ponto-flutuante, também convertê-los para strings primeiro. A maneira mais fácil de podemos especificar o número de dígitos depois da vírgula: fazer isso é com a função str: >>> "%12.2f" % 6.1 >>> x = 52 ' 6.10' >>> f.write(str(x)) Neste exemplo, o resultado reserva 12 espaços e Uma alternativa é usar o operador de inclui dois dígitos depois da vírgula. Esta formatação é útil formatação %. Quando aplicado a inteiros, % é o operador para exibir valores monetários com os centavos alinhados. Capítulo 11: Arquivos e exceções #60
  • 61. Como pensar como um cientista da Computação usando Python Por exemplo, imagine um dicionário que comandos necessários. Para usá-lo, importe pickle e então contém nomes de estudantes como chaves e salários-hora abra o arquivo da maneira usual: como valores. Aqui está uma função que imprime o conteúdo do dicionário como um relatório formatado: >>> import pickle >>> f = open(?test.pck?, ?w?) def relatorio(salarios): Para armazenar uma estrutura de dados, use o estudantes = salarios.keys() método dump e então feche o arquivo do modo usual: estudantes.sort() for estudante in estudantes: >>> pickle.dump(12.3, f) print "%-20s %12.02f" % (estudante, >>> pickle.dump([1,2,3], f) salarios[estudante]) >>> f.close() Para testar esta função, criaremos um pequeno Então, podemos abrir o arquivo para leitura e dicionário e imprimiremos o conteúdo: carregar as estruturas de dados que foram descarregadas (dumped): >>> salarios = {'maria': 6.23, 'joão': 5.45, >>> f = open(?test.pck?, ?r?) 'josué': 4.25} >>> x = pickle.load(f) >>> relatorio(salarios) >>> x joão 5.45 12,3 josué 4.25 >>> type(x) maria 6.23 <type ?float?> Controlando a largura de cada valor, podemos garantir que as colunas ficarão alinhadas, desde que os nomes >>> y = pickle.load(f) contenham menos que vinte e um caracteres e os salários >>> y sejam menores do que um bilhão de reais por hora. [1, 2, 3] >>> type(y) <type ?list?> 11.3 Diretórios Cada vez que invocamos load, obtemos um único valor do arquivo, completo com seu tipo original. Quando você cria um novo arquivo abrindo-o e escrevendo nele, o novo arquivo fica no diretório corrente (seja lá onde for que você esteja quando rodar o programa). Do mesmo 11.5 Exceções modo, quando você abre um arquivo para leitura, Python procura por ele no diretório corrente. Whenever que um erro em tempo de execução acontece, ele Se você quiser abrir um arquivo que esteja em gera uma exceção. Usualmente, o programa pára e Python algum outro lugar, você tem que especificar o caminho (path) exibe uma mensagem de erro. para o arquivo, o qual é o nome do diretório (ou folder) onde o arquivo está localizado: Por exemplo, dividir por zero gera uma exceção: >>> f = open("/usr/share/dict/words", "r") >>> print 55/0 >>> print f.readline() ZeroDivisionError: integer division or modulo Aarhus Este exemplo abre um arquivo chamado words Do mesmo modo, acessar um item de lista que reside em um diretório de nome dict, o qual reside em inexistente: share, o qual reside em usr, o qual reside no diretório de mais >>> a = [] alto nível do sistema, chamado /. >>> print a[5] Você não pode usar / como parte do nome de IndexError: list index out of range um arquivo; ela é um caractere reservado como um Ou acessar uma chave que não está em um delimitador entre nomes de diretórios e nomes de arquivos. dicionário: O arquivo /usr/share/dict/words contém uma >>> b = {} lista de palavras em ordem alfabética, na qual a primeira >>> print b[?what?] palavra é o nome de uma universidade Dinamarquesa. KeyError: what Em cada caso, a mensagem de erro tem duas partes: o tipo do erro antes dos dois pontos, e especificidades 11.4 Pickling do erro depois dos dois pontos. Normalmente Python também exibe um ?*traceback*? de onde estava a execução do Para colocar valores em um arquivo, você tem que convertê- programa, mas nós temos omitido esta parte nos exemplos. los para strings. Você já viu como fazer isto com str: Às vezes queremos executar uma operação que >>> f.write (str(12.3)) pode causar uma exceção, mas não queremos que o programa >>> f.write (str([1,2,3])) pare. Nós podemos tratar a exceção usando as instruções try e except. O problema é que quando você lê de volta o valor, você tem uma string. O Tipo original da informação foi Por exemplo, podemos pedir ao usuário um perdido. De fato, você não pode sequer dizer onde começa um nome de arquivo e então tentar abrí-lo. Se o arquivo não valor e termina outro: existe, não queremos que o programa trave; queremos tratar a exceção: >>> f.readline() ?12.3[1, 2, 3]? nomedoarquivo = raw_input(?Entre com o nome do A solução é o pickling, assim chamado porque ? arquivo: ?) preserva? estruturas de dados. O módulo pickel contém os try: Capítulo 11: Arquivos e exceções #61
  • 62. Como pensar como um cientista da Computação usando Python f = open (nomedoarquivo, ?r?) Se a função que chamou entraNumero trata o except: erro, então o programa pode continuar; de outro modo, Pyhton print ?Não existe arquivo chamado?, nomedoarquivo exibe uma mensagem de erro e sai: A instrução try executa os comandos do >>> entraNumero() primeiro bloco. Se não ocorrerem exceções, ele ignora a Escolha um número: 17 instrução except. Se qualquer exceção acontece, ele executa os ErroNumeroRuim: 17 é um número ruim comandos do ramo except e continua. A mensagem de erro inclui o tipo da exceção e a Podemos encapsular esta habilidade numa informação adicional que você forneceu. função: existe toma um nome de arquivo e retorna verdadeiro se o arquivo existe e falso se não existe: Como um exercício, escreva uma função que use entraNumero para pegar um número do teclado e que def existe(nomedoarquivo) trate a exceção ErroNumeroRuim. try: f = open(nomedoarquivo) f.close() 11.6 Glossário return 1 except: arquivo (file) Uma entidade nomeada, usualmente return 0 armazenada em um disco rígido (HD), Você pode usar múltiplos blocos except para disquete ou CD-ROM, que contém uma tratar diferentes tipos de exceções. O Manual de Referência de seqüência de caracteres. Python (Python Reference Manual) tem os detalhes. diretório Uma coleção nomeada de arquivos, Se o seu programa detecta uma condição de (directory) também chamado de pasta ou folder. erro, você pode fazê-lo lançar uma exceção. Aqui está um exemplo que toma uma entrada do usuário e testa se o valor é caminho (path) Uma seqüência de nomes de diretórios que 17. Supondo que 17 não seja uma entrada válida por uma especifica a exata localização de um razão qualquer, nós lançamos uma exceção. arquivo. def entraNumero(): arquivo texto Um arquivo que contém caracteres (text file) organizados em linhas separadas por x = input (?Escolha um número: ?) caracteres de nova linha. if x == 17: raise ?ErroNumeroRuim?, ?17 é um número ruim? comando Um comando que força a atual iteração de return x break (break um loop a terminar. O fluxo de execução statement) vai para o topo do loop, testa a condição e O comando raise toma dois argumentos: o tipo prossegue conforme o caso. da exceção e informações específicas sobre o erro. ErroNumeroRuim é um novo tipo de exceção que nós inventamos para esta aplicação. Capítulo 11: Arquivos e exceções #62
  • 63. Como pensar como um cientista da Computação usando Python Capítulo 12: Classes e objetos >>> final.y = 4.0 12.1 Tipos compostos definidos pelo usuário Esta sintaxe é similar à sintaxe para acessar uma variável de um módulo, como math.pi ou string.uppercase. Depois de usarmos alguns tipos nativos do Python, estamos Neste caso, porém, estamos acessando um item de dado de prontos para criar um tipo de dados: o Ponto. uma instância. Estes itens são chamados atributos. Considere o conceito matemático de um ponto. O seguinte diagrama de estado mostra o Em duas dimensões, um ponto é um par de números resultado destas atribuições: (coordenadas) que são tratadas coletivamente como um objeto simples. Na notação matemática, pontos são freqüentemente escritos entre parênteses com vírgula 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 numéricos em ponto flutuante. A questão, então, é como agrupar estes dois valores em um A variável final refere a um objeto Ponto, que objeto composto. A maneira rápida e rasteira é usar uma lista contém dois atributos. Cada atributo faz referência a um ou uma tupla, e para algumas aplicações, esso pode ser a número em ponto flutuante. melhor escolha1. Podemos ler o valor de um atributo usando a Uma alternativa é definir um novo tipo mesma sintaxe: composto, também chamado uma classe. Esta abordagem envolve um pouco mais de esforço, mas ela tem vantagens >>> print final.y que logo ficarão evidentes. 4.0 Eis a definição de uma classe: >>> x = final.x >>> print x class Ponto: 3.0 pass A expressão final.x significa, "Vá ao objeto Definições de classes podem aparecer em final e pegue o valor de x". Neste caso, atribuímos este valor a qualquer parte de um programa, mas elas costuma ficar uma variável cujo nome é 'x'. Não há conflito entre a variável próximas do começo do programa (após os comandos import). x e o atributo x. O propósito da notação objeto.atributo é As regras de sintaxe para a definição de classes são as mesmas identificar a qual variável você está fazendo referência de de outros comandos compostos (veja Seção 4.4). forma que não é ambíguo. A definição acima cria uma nova classe Você pode usar a notação objeto.atributo como chamada Ponto. O comando pass não tem nenhum efeito; aqui parte de qualquer expressão; assim os seguintes comandos são ele é necessário porque um comando composto precisa ter válidos: algo no seu corpo. print '(' + str(final.x) + ', ' + str(final.y) + ')' Quando criamos a classe Ponto, criamos um distAoQuadrado = final.x * final.x + final.y * novo tipo de dado, também chamado Ponto. Os membros deste novo tipo são chamados instâncias deste tipo ou final.y objetos. Criar uma nova instância é instanciar. Para A primeira linha imprime (3.0, 4.0); a segunda instanciar o objeto Ponto, invocamos a função (adivinhou?) linha calcula o valor 25.0. Ponto: É tentador imprimir o valor do próprio objeto final = Ponto() final: A variável final agora contém uma referência a >>> print final um novo objeto da classe Ponto. Uma função como Ponto, que <__main__.Ponto instance at 80f8e70> cria novos objetos, é chamada construtor. O resultado indica que final é uma instância da classe Ponto e foi definida no prgrama principal: __main__. 80f8e70 é o identificador único deste objeto, escrito em 12.2 Atributos hexadecimal (base 16). Esta não é provavelmente a forma mais informativa para mostrar um objeto Ponto. Logo você irá Podemos adicionar novos dados em uma instância usando a ver como mudar isso. notação de ponto (dot notation): Como exercício, crie e imprima um objeto >>> final.x = 3.0 Ponto, e então use id para imprimir o identificador único do objeto. Traduza a forma hexadecimal para a forma decimal e 1 N.T.: A linguagem Python também incorpora um tipo nativo confirme se são compatíveis. complex que representa números complexos. Uma instância de complex, como a=3+5j possui dois valores de ponto flutuante em seus atributos a.real e a.imag, e pode ser utilizada para armazenar pontos em um espaço bi- dimensional. Capítulo 12: Classes e objetos #63
  • 64. Como pensar como um cientista da Computação usando Python Para comparar o conteúdo dos objetos -- 12.3 Instâncias como parâmetros igualdade profunda -- podemos escrever uma função chamada mesmoPonto: Você pode passar uma instância como um parâmetro da forma usual. Por exemplo: def mesmoPonto(p1, p2) : return (p1.x == p2.x) and (p1.y == p2.y) def mostrarPonto(p): Agora se criarmos dois diferentes objetos que print '(' + str(p.x) + ', ' + str(p.y) + ')' contém os mesmos dados, podemos usar mesmoPonto para A função mostrarPonto pega o ponto (p) como verificar se eles representam o mesmo ponto. um argumento e mostra-o no formato padrão. Se você chamar mostrarPonto(final), a saída será (3.0, 4.0). >>> p1 = Ponto() >>> p1.x = 3 Como um exercício, re-escreva a função >>> p1.y = 4 distância da Seção 5.2 para receber dois pontos como >>> p2 = Ponto() parâmetros, ao invés de quatro números. >>> p2.x = 3 >>> p2.y = 4 >>> mesmoPonto(p1, p2) 12.4 O significado de "mesmo" True É claro, se as duas variáveis referirem ao O significado da palavra "mesmo" parece perfeitamente claro mesmo objeto, elas têm igualdade rasa e igualdade profunda. até que você pense a respeito, e então você percebe que há mais nesta palavra do que você esperava. Por exemplo, se você diz "Cris e eu temos o 12.5 Retângulos mesmo carro", você está dizendo que o carro de Cris e o seu são do mesmo fabricante e modelo, mas são dois carros diferentes. Se você disser "Cris e eu temos a mesma mãe", Digamos que desejemos uma classe para representar um você está dizendo que a mãe de Cris e a sua, são a mesma retângulo. A questão é, qual informação temos de prover para pessoa1. Portanto a idéia de 'semelhança' é diferente especificar um retângulo? Para manter as coisas simples, dependendo do contexto. assuma que o retângulo é orientado verticalmente ou horizontalmente, nunca em um ângulo. Quando falamos de objetos, há uma ambigüidade similar. Por exemplo, se dois Pontos forem os Há algumas possibilidades: poderíamos mesmos, isto quer dizer que eles contêm os mesmos dados especificar o centro do retângulo (duas coordenadas) e seu (coordenadas) ou que são realmente o "mesmo" objeto? tamanho (largura e altura); ou poderíamos especificar um dos lados e o tamanho; ou poderíamos especificar dois lados Para verificar se duas referências se referem ao opostos. A escolha convencional é especificar o canto superior 'mesmo' objeto, use o operador '==' 2. Por exemplo: esquerdo do retângulo e o tamanho. >>> p1 = Ponto() Novamente, vamos definir uma nova classe: >>> p1.x = 3 class Rectangle: >>> p1.y = 4 pass >>> p2 = Ponto() >>> p2.x = 3 E instanciá-la: >>> p2.y = 4 box = Rectangle() >>> p1 == p2 box.width = 100.0 False box.height = 200.0 Mesmo que p1 e p2 contenham as mesmas Este código cria um novo objeto Retângulo com coordenadas, os dois não representam o mesmo objeto. Se dois atributos ponto-flutuante. Para especificar o canto atribuirmos p1 a p2, então as duas variáveis são pseudônimos superior esquerdo, podemos embutir um objeto dentro de um do mesmo objeto. objeto! >>> p2 = p1 box.corner = Ponto() >>> p1 == p2 box.corner.x = 0.0; True box.corner.y = 0.0; Este tipo de igualdade é chamado de igualdade A expressão box.corner.x significa, "vá ao rasa porque ela compara somente as referências e não o objeto referenciado por 'box' e selecione o atributo 'corner'; conteúdo dos objetos. então vá ao objeto 'corner' e deste, selecione o atributo de nome 'x'". 1 Nem todos os idiomas têm este problema. Por exemplo, em alemão há palavras diferentes para diferentes sentidos A figura mostra o estado deste objeto: de "mesmo". "Mesmo carro" nesse contexto seria "gleiche Auto", e "mesma mãe" seria "selbe Mutter". 2 LR: Eu não diria que devemos usar == para verificar se dois objetos são 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 referências, e portanto serve para determinar se duas variáveis apontam para o mesmo objeto. No entanto, a o código acima está correto porque em Python a implemetação default de == (método __eq__) é comparar o id das instâncias, porém 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). Capítulo 12: Classes e objetos #64
  • 65. Como pensar como um cientista da Computação usando Python >>> import copy 12.6 Instancias como valores retornados >>> p1 = Ponto() >>> p1.x = 3 Funções podem retornar instâncias. Por exemplo, findCenter >>> p1.y = 4 pega um Retângulo como um argumento e retorna um Ponto que contem as coordenadas do centro do retângulo: >>> p2 = copy.copy(p1) >>> p1 == p2 def findCenter(box): 0 p = Ponto() >>> mesmoPonto(p1, p2) p.x = box.corner.x + box.width/2.0 1 p.y = box.corner.y + box.height/2.0 Uma vez que importamos o modulo 'copy', Para chamar esta função, passe 'box' como um podemos usar o método 'copy' para criar um outro 'Ponto'. p1 argumento e coloque o resultado em uma variável. e p2 não representam o mesmo ponto, mas eles contem os mesmo dados. >>> center = findCenter(box) >>> print mostrarPonto(center) Para copiar um simples objeto como um 'Ponto', (50.0, 100.0) que não contem nenhum objeto embutido, 'copy' é suficiente. Isto eh chamado 'shallow' copia. Mas para um objeto como um 'Rectangle', que contem uma referencia para um 'Ponto', o método 'copy' não 12.7 Objetos são mutáveis irá executar corretamente a copia. Ele irá copiar a referencia para o objeto 'Ponto', portanto o que acontece aqui é que os Podemos mudar o estado de um objeto fazendo uma dois Rectangle (o novo e o antigo) irão fazer referencia a um atribuição a um dos seus atributos. Por exemplo, para mudar o simples 'Ponto'. tamanho de um retângulo sem mudar sua posição, podemos modificar os valores de sua largura e altura. Veja: Em outras palavras, se criarmos um 'box', c1, utilizando a forma usual, e depois fazer uma copia, c2, usando box.width = box.width + 50 o método 'copy', o diagrama de estado resultante ficará assim: box.height = box.height + 100 Poderíamos encapsular este código em um método e generaliza-lo para aumentar o tamanho deste retângulo em qualquer medida: def growRect(box, dwidth, dheight) : box.width = box.width + dwidth box.height = box.height + dheight As variáveis dwidth e dheight indicam em o resultado não será o que esperamos. Neste quanto vamos aumentar o tamanho do retângulo em cada caso, invocando 'growRect' em um dos retângulos (c1), isto direção. Chamando este método, teríamos o mesmo efeito. não irá afetar o outro retângulo (c2, neste exemplo). Mas se usarmos o método 'moveRect' em qualquer um deles, isto irá Por exemplo, poderíamos criar um novo inevitavelmente afetar o outro. Este comportamento é confuso Retângulo com o nome de 'bob' e passar este nome para o e propenso a erros! método growRect: Mas felizmente o modulo 'copy' contem um >>> bob = Rectangle() método chamado 'deepcopy' que copia não somente o objeto, >>> bob.width = 100.00 mas também copia todo e qualquer objeto 'embutido' neste >>> bob.height = 200.00 objeto. Por isto, você não ficará surpreso porque este método chama-se 'deepcopy' (copia profunda) não é? Veja como >>> bob.corner.x = 0.0; funciona: >>> bob.corner.y = 0.0; >>> growRect(bob, 50, 100) >>> c2 = copy.deepcopy(c1) Enquanto growRect está sendo executado, o Agora, c1 e c2 são objetos completamente parâmetro 'box' é um alias (apelido) para 'bob'. Qualquer separados. mudança feita em 'box', também irá afetar 'bob'. Podemos usar 'deepcopy' para re-escrever Como exercício, escreva uma function (método) 'growRect' sendo que ao invés de modificar um Rectangle com o nome de moveRect que pega um Rectangle e dois existente, ele cria um novo que tem a mesma localização do parâmetros com o nome de 'dx' e 'dy'. Esta função deverá outro, mas com novas dimensões: mudar a localização do retângulo através da adição de 'dx' à coordenada 'x' e da adição de 'dy' à coordenada 'y'. def growRect(box, dwidth, dheight): import copy newBox = copy.deepcopy(box) newBox.width = newBox.width + dwidth 12.8 Copiando newBox.height = newBox.height + dheight return newBox Ao usar 'alias' - como fizemos na seção anterior - podemos tornar o programa um pouco difícil de ler ou entender, pois as Como exercício, re-escreva o método mudanças feitas em um local, podem afetar inesperadamente 'moveRect' para ele criar e retornar um novo Rectangle ao um outro objeto. E pode se tornar difícil de encontrar todas as invés de apenas modificar o antigo. variáveis que podem afetar um dado objeto. Copiar um objeto é freqüentemente uma alternativa ao 'alias'. O modulo 'copy' contém uma função chamada 'copy' que duplica um qualquer objeto. Veja: Capítulo 12: Classes e objetos #65
  • 66. Como pensar como um cientista da Computação usando Python 12.9 Glossário classe (class) Um tipo composto (XXX compound type) definido pelo usuário. Uma classe também pode ser visualizada como um molde que define a forma dos objetos que serão suas instâncias. instanciar Criar uma instância de uma classe. (instantiate) instância Um objeto que pertence a uma classe. (instance) objeto (object) Um tipo de dado composto comumente utilizado para representar uma coisa ou um conceito do mundo real. construtor Um método utilizado para criar novos (constructor) objetos. atributo Um dos itens de dados nomeados que (attribute) compõem uma instância. igualdade rasa Igualdade de referências; ocorre quando (shallow duas referências apontam para o mesmo equality) objeto. igualdade Igualdade de valores; ocorre quando duas profunda (deep referências apontam para objetos que têm equality) o mesmo valor. cópia rasa Ato de copiar o conteúdo de um objeto, (shallow copy) incluindo as referências a objetos embutidos (XXX embedded); implementada pela função copy do módulo copy. cópia profunda Ato de copiar o conteúdo de um objeto, (deep copy) bem como dos objetos embutidos (XXX embedded), e dos objetos embutidos nestes, e assim por diante; implementada pela função deepcopy do módulo copy. Capítulo 12: Classes e objetos #66
  • 67. Como pensar como um cientista da Computação usando Python Capítulo 13: Classes e funções quantidade de tempo que a máquina de fazer pão gasta para 13.1 Horario fazer pão. Então vamos usar somaHorario para tentar saber quando o pão estará pronto. Se você não tiver terminado de Como exemplo de outro tipo definido pelo usuário, vamos escrever imprimirHorario ainda, de uma olhada na seção 14.2 definir uma classe chamada Horario que grava os registros de antes de você continuar isso: horário do dia. Eis a definição da classe: >>> horarioAtual = Horario() class Horario: >>> horarioAtual.horas = 9 pass >>> horarioAtual.minutos = 14 Podemos criar uma nova instância de Horario e >>> horarioAtual.segundos = 30 determinar atributos para horas, minutos e segundos: >>> horarioDoPao = Horario() horario = Horario() >>> horarioDoPao.horas = 3 horario.horas = 11 >>> horarioDoPao.minutos = 35 horario.minutos = 59 >>> horarioDoPao.segundos = 0 horario.segundos = 30 O diagrama de estado para o objeto Horario parece com isso: >>> horarioTermino = somaHorario(horarioAtual, horarioDoPao) >>> imprimirHorario(horarioTermino) A saída deste programa é 12:49:30, o que é correto. Por outro lado, existem casos onde o resultado não é correto. Você pode pensar em algum ? O problema é que esta função não lida com casos onde o número 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. Como exercício, escreva uma função Aqui está a segunda versão corrigida da função: 'imprimirHorario' que tenha como argumento um objeto Horario e imprima-o na forma horas:minutos:segundos. def somaHorario(t1, t2): soma = Horario() Como um segundo exercício, escreva uma função booleana que tenha como argumento dois objetos soma.horas = t1.horas + t2.horas Horario, h1 e h2, e retorne verdadeiro (1) se h1 vem depois soma.minutos = t1.minutos + t2.minutos de h2 cronologicamente, do contrário, retorne falso (0). soma.segundos = t1.segundos + t2.segundos if soma.segundos >= 60: 13.2 Funções Puras soma.segundos = soma.segundos - 60 soma.minutos = soma.minutos + 1 Nas próximas sessões, vamos escrever duas versões de uma função chamada adicionaHorario, que calcula a soma de dois if soma.minutos >= 60: horários. Elas vão demonstrar 2 tipos de funções: funções soma.minutos = soma.minutos - 60 puras e funções modificadoras. soma.horas = soma.horas + 1 Segue uma versão rudimentar de somaHorario: return soma def somaHorario(h1, h2): Apesar desta função estar correta, ela está soma = Horario() começando a ficar grande. Depois vamos sugerir uma soma.horas = h1.horas + h2.horas aproximação alternativa que rende um código menor. Clique soma.minutos = h1.minutos + h2.minutos aqui para feedback soma.segundos = h1.segundos + h2.segundos return soma A função cria um novo objeto Horario, 13.3 Modificadores inicializa os seus atributos, e retorna uma referência para o novo objeto. Isso é chamado de função pura pois ela não Existem momentos quando é útil para uma função modificar modifica nenhum dos objetos que são passados como um ou mais dos objetos que ela recebe como parâmetro. parâmetros e não tem nenhum efeito colateral, como imprimir Usualmente, quem está chamando a função mantém uma um valor ou pegar entrada do usuário. referência para os objetos que ele passa, de forma que Aqui está um exemplo de como usar esta quaisquer mudanças que a função faz são visíveis para quem função. Nós vamos criar dois objetos Horario: horarioAtual, está chamando. Funções que trabalham desta forma são que contém o horário atual; e horarioDoPao, que contém a chamadas modificadores. Capítulo 13: Classes e funções #67
  • 68. Como pensar como um cientista da Computação usando Python incrementar, que adiciona um número dado de segundos para um objeto Horario, poderia ser escrito quase 13.5 Desenvolvimento Prototipado versus naturalmente como um modificador. Um rascunho rudimentar da função seria algo parecido com isso: Desenvolvimento Planejado def incrementar(horario, segundos): Neste capítulo, demonstramos uma aproximação para o horario.segundos = desenvolvimento de programas que chamamos de horario.segundos + segundos desenvolvimento prototipado. Em cada caso, escrevemos um rascunho rudimentar (ou protótipo) que executou os cálculos if horario.segundos >= 60: básicos e então, o testamos em uns poucos casos, corrigindo as falhas que fomos encontrando. horario.segundos = horario.segundos - 60 Embora esta aproximação possa ser eficaz, ela horario.minutos = horario.minutos + 1 pode conduzir a código que é desnecessariamente complicado pois trata de muitos casos especiais e não é confiável, pois é difícil saber se você encontrou todos os erros. if horario.minutos >= 60: horario.minutos = Uma alternativa é o desenvolvimento planejado, horario.minutos - 60 onde uma visão de alto nível do problema pode tornar a horario.horas = horario.horas + 1 codificação muito mais fácil. Nesse caso, a abstração é que um objeto Horario é realmente um número de três digitos na A primeira linha executa a operação básica; o base 60! O componente segundo é a "coluna dos uns", o resto lida com os caso especiais que vimos antes. componente minuto é a "coluna do 60", e o componente hora Esta função está correta ? O que aconteceria se é a "coluna do 360". o parâmetro segundos for muito maior que sessenta ? Nesse Quando nós escrevemos somaHorario e caso, não é suficiente transportar apenas uma vez; teríamos de incrementar, nós estávamos efetivamente fazendo adições em continuar fazendo isso até que segundos seja menor que base 60, que é o motivo pelo qual tinhamos de transportar de sessenta. Uma solução seria substituir os comando if por uma coluna para a próxima. comandos while: Essa observação sugere uma outra aproximação def incrementar(horario, segundos): para todo o problema - nós podemos converter um objeto horario.segundos = Horario em um número simples e levar vantagem do fato de horario.segundos + segundos que um computador sabe como fazer aritmética com números. A seguinte função converte o objeto Horario em um inteiro: while horario.segundos >= 60: def converterParaSegundos(t): horario.segundos = horario.segundos - minutos = t.horas * 60 + t.minutos 60 segundos = minutos * 60 + t.segundos horario.minutos = horario.minutos + 1 return segundos Agora, tudo que precisamos é uma maneira de while horario.minutos >= 60: converter de um inteiro para um objeto Horario: horario.minutos = horario.minutos - 60 def criarHorario(segundos): horario.horas = horario.horas + 1 horario = Time() Esta função agora esta correta, mas não é a horario.horas = segundos/3600 solução mais eficiente. segundos = segundos - horario.horas * 3600 horario.minutos = segundos/60 Como um exercício, reescreva esta função de segundos = segundos - horario.minutos * 60 maneira que ela não contenha nenhum loop. Como um segundo exercício, reescreva incrementar como uma função horario.segundos = segundos pura, e escreva chamadas de funções para as duas funções. return horario Clique aqui para feedback Você deve ter que pensar um pouco para se convencer que esta técnica de converter de uma base para outra é correta. Assumindo que você está convencido, você pode usar essas funções para reescrever somaHorario: 13.4 O que é melhor ? def somaHorario(t1, t2): Qualquer coisa que pode ser feita com modificadores também segundos = podem ser feitas com funções puras. De fato, algumas converterParaSegundos(t1) + converterParaSegundos(t2) linguagens de programação permitem apenas funções puras. return criarHorario(segundos) Existe alguma evidência que programas que usam funções puras são desenvolvidos mais rapidamente e são menos Esta versão é muito mais curta que a original, e propensos a erros que programas que usam modificadores. No é muito mais fácil para demonstrar que está correta entanto, modificadores as vezes são convenientes, e em alguns (assumindo, como sempre, que as funções que são chamadas casos, programação funcional é menos eficiente. estão corretas). Em geral, recomendamos que você escreva Como um exercício, reescreva incrementar da funções puras sempre que for necessário e recorrer para mesma forma. Clique aqui para feedback modificadores somente se existir uma grande vantagem. Esta aproximação poderia ser chamada de um estilo de programação funcional. Clique aqui para feedback 13.6 Generalização Algumas vezes, converter de base 60 para base 10 e voltar é mais difícil do que simplesmente lidar com horários. Capítulo 13: Classes e funções #68
  • 69. Como pensar como um cientista da Computação usando Python Conversão de base é mais abstrata; nossa intuição para lidar Na nossa opinião, é preocupante que humanos com horários é melhor. gastem tanto tempo na escola aprendendo a executar algoritmos que, literalmente, não requerem inteligência. Mas se conseguirmos abstrair horários como números de base 60 e investirmos em criar as funções de Por outro lado, o processo de projetar conversão (converterParaSeguntos e criarHorario), nós algoritmos é interessante, intelectualmente desafiante, e uma conseguimos um programa que é menor, fácil de ler e depurar, parte central daquilo que chamamos programação. e mais confiável. Algumas das coisas que as pessoas fazem É também fácil para adicionar funcionalidades naturalmente, sem dificuldade ou consciência, são as mais depois. Por exemplo, imagine subtrair dois Horarios para difíceis de se expressar através de algoritmos. Entender a encontrar a duração entre eles. Uma aproximação ingênua linguagem natural é um bom exemplo. Todos nós fazemos seria implementar subtração com empréstimo. Usando as isso, mas até hoje ninguém conseguiu explicar como fazemos funções de conversão será mais fácil e provavelmente estará isso, pelo menos não na forma de algoritmo. Clique aqui para correto. feedback. Ironicamente, algumas vezes fazer um problema mais difícil (ou mais genérico) o torna mais simples (porque existem alguns poucos casos especiais e poucas oportunidades 13.8 Glossário para errar). Clique aqui para feedback função pura (pure Uma função que não modifica function) nenhum dos objetos que ela recebe 13.7 Algoritmos como parâmetro. A maioria das funções puras é frutífera. Quando você escreve uma solução genérica para uma classe modificador Uma função que muda um ou mais de problemas, ao contrário de uma solução específica para um (modifier) dos objetos que ela recebe como único problema, você escreveu um algoritmo. Nós parâmetros. A maioria dos mencionamos isso antes mas não definimos cuidadosamente. modificadores é nula. Isso não é fácil para definir, então vamos tentar definir demonstrando algumas situações. estilo de Um estilo de programação onde a programação maioria das funções são puras. Primeiramente, considere alguma coisa que não funcional (functional seja um algoritmo. Quando você aprendeu a multiplicar programming style) números de um dígito, você provavelmente memorizou a tabela de multiplicação. Como resultado, você memorizou 100 desenvolvimento Uma maneira de desenvolver soluções específicas. Esse tipo de conhecimento não é prototipado programas começando com um algoritmo. (prototype protótipo e gradualmente development) melhorando-o. Mas se você é "preguiçoso", você provavelmente trapaceou por ter aprendido alguns truques. desenvolvimento Uma maneira de desenvolver Por exemplo, para encontrar o produto de n e 9, você pode planejado (planned programas que envolvem uma escrever n-1 como o primeiro dígito e 10-n como o segundo development) percepção de alto nível do problema dígito. Este truque é um solução genérica para multiplicar e mais planejamento do que qualquer número de um dígito por 9. Isso é um algoritmo! desenvolvimento incremental ou desenvolvimento prototipado. De modo parecido, as técnicas que você aprendeu para somar com transporte, subtração com algoritmo (algorithm) Um conjunto de instruções para empréstimo, e divisão longa são todas algoritmos. Uma das resolver uma classe de problemas características dos algoritmos é que eles não requerem usando um processo mecânico, não nenhuma inteligência para serem executados. Eles são inteligente. processos mecânicos no qual cada passo segue o último de acordo com um conjunto simples de regras. Capítulo 13: Classes e funções #69
  • 70. Como pensar como um cientista da Computação usando Python Capítulo 14: Classes e métodos ATENÇÃO As referências cruzadas a nomes em códigos de outros capítulos (especialmente 13) ainda não foram 14.2 exibeHora (printTime) unificadas... No capítulo 13, definimos uma classe chamada Horário (Time) e você escreveu uma função chamada exibeHora (printTime), que deve ter ficado mais ou menos assim: 14.1 Características da orientação a objetos class Horario: Python é uma linguagem de programação orientada a pass objetos, o que significa que ela tem características que suportam a programação orientada a objetos. def exibeHora(time) Não é fácil definir programação orientada a print str(time.horas) + ?:? + objetos, mas temos visto already algumas de suas str(time.minutos) + ?:? + características: str(time.segundos) ● Programas são construídos sobre definições de Para chamar esta função, passamos um objeto objetos e definições de funções, e a maioria das Time como um parâmetro: computações é expressa em termos de operações sobre objetos. >>> horaCorrente = Hora() >>> horaCorrente.horas = 9 ● Cada definição de objeto corresponde a algum objeto >>> horaCorrente.minutos = 14 ou conceito do mundo real, e as funções que operam >>> horaCorrente.segundos = 30 com aqueles objetos correspondem à maneira como os objetos do mundo real interagem. >>> exibeHora(horaCorrente) Para fazer de exibeHora um método, tudo o que Por exemplo, a classe Tempo, definida no temos a fazer é mover a definição da função para dentro da capítulo 13 corresponde à maneira como as pessoas registram definição da classe. Note a mudança na endentação: as horas do dia, e as funções que definimos correspondem aos tipos de coisas que as pessoas fazem com times. Do mesmo class Horario: modo, as classes Ponto e Retângulo correspondem aos def exibeHora(time): conceitos matemáticos de um ponto e de um retângulo. print str(time.horas) + ?:? + Até aqui, não tiramos vantagem das str(time.minutos) + ?:? + características fornecidas por Python que suportam a str(time.segundos) programação orientada a objetos. Estritamente falando, estas Agora podemos chamar exibeHora usando a características não são necessárias. Na maior parte das vezes, natação de ponto: elas fornecem uma sintaxe alternativa para as coisas que já fizemos, mas em muitos casos, a alternativa é mais concisa e >>> horaCorrente.exibeHora() convém mais acuradamente à estrutura do programa. Como é usual, o objeto no qual o método é Por exemplo, no programa Time, não existe invocado aparece antes do ponto e o nome do método aparece uma conexão óbvia entre a definição da classe e a definição da depois do ponto. função que segue. Com alguma investigação, fica aparente O objeto no qual o método é invocado é que toda função toma pelo menos um objeto Time como um atribuído ao primeiro parâmetro, então, neste caso, parâmetro. horaCorrente é atribuído ao parâmetro time. Esta observação é a motivação por trás dos Por convenção, o primeiro parâmetro de um métodos. Já temos visto alguns métodos, tais como keys método é chamado self. A razão para isto é um pouco (chaves) e values (valores), os quais foram invocados em convoluted, mas é baseada numa metáfora útil. dicionários. Cada método é associado com uma classe e é intended para ser invocado em instâncias daquela classe. A sintaxe para uma chamada de função, exibeHora(horaCorrente), sugere que a função é um agente Métodos são simplesmente como funções, com ativo. Diz algo como, ?Ei, exibeHora! Aqui está um objeto duas diferenças: para você exibir.? ● Métodos são definidos dentro da definição de uma Na programação orientada a objetos, os objetos classe para tornar explícita a relação entre a classe e o são agentes ativos. Uma chamado do tipo método. horaCorrente.exibeHora() diz ?Ei, horaCorrente! Por favor ● A sintaxe para a chamada do método é diferente da exiba-se a si mesmo!? sintaxe para a chamada de uma função. Esta mudança de perspectiva pode ser mais Nas próximas seções, vamos pegar as funções polida, mas não fica óbvio que seja útil. Nos exemplos que dos dois capítulos anteriores e transformá-las em métodos. temos visto até aqui, pode ser que não seja. Mas às vezes, Esta transformação é puramente mecânica: você pode deslocar a responsabilidade das funções para cima dos objetos conseguí-la simplesmente seguindo uma seqüência de passos. torna possível escrever funções mais versáteis, e torna mais Se você se sentir confortável convertendo de uma forma para fácil manter e reutilizar o código. a outra, você estará apto para escolher a melhor forma para seja o lá o que for que você estiver fazendo. Capítulo 14: Classes e métodos #70
  • 71. Como pensar como um cientista da Computação usando Python 14.3 Um outro exemplo orientada a tais como classes definidas pelo usuário e objetos herança, que facilitam a programação orientada a objetos. Vamos converter incremento (da Seção 13.3) em um método. Para poupar espaço, deixaremos de fora métodos definidos programação Um estilo de programação na qual os previamente(anteriormente?), mas você deve mantê-los em orientada a dados e as operações que os manipulam sua versão: objetos estão organizados em classes e métodos. class Time: método Uma função que é definida dentro de uma #previous method definitions here... definição de classe e é chamada em instâncias desta classe. def increment(self, segundos): override (sem Substituir uma definição já pronta. self.seconds = seconds + self.seconds traducao; Exemplos incluem substituir um termo parâmetro padrão por um argumento consagrado) particular e substituir um método padrão, while self.segundos >= 60: fornecendo um novo método com o self.seconds = self.segundos - 60 mesmo nome. self.minutes = self.minutos + 1 método de Um método especial que é invocado inicialização automaticamente quando um novo objeto while self.minutes >= 60: (tambem é criado e que inicializa os atributos deste self.minutes = self.minutos - 60 chamado de objeto. self.hours = self.horas + 1 construtor) A transformação é puramente mecânica ? sobrecarga de Estender a funcionalidade dos operadores movemos a definição do método para dentro da definição da operador nativos (+, -, *, >, <, etc.) de forma que classe e mudamos o nome do primeiro parâmetro. eles funcionem também com tipos Agora podemos chamar incremento como um definidos pelo usuário. método: produto escalar Operação definida na álgebra linear que horaCorrente.incremento(500) multiplica dois pontos (com coordenadas (x,y,z)) e retorna um valor numérico. De novo, o objeto no qual o método é chamado gets atribui ao primeiro parâmetro, self. O segundo parâmetro, multiplicação Operação definida na álgebra linear que segundo toma(gets) o valor 500. por escalar multiplica cada uma das coordenadas de um ponto por um valor numérico. Como um exercício, converta ? converteParaSegundos? (da Seção 13.5) para um método na polimórfica Uma função que pode operar com mais de classe ?Time?. um tipo. Se todas as operações de uma função pode ser aplicadas a um certo tipo, então a função pode ser aplicada a este tipo. 14.10 Glossário linguagem Uma linguagem que provê características Capítulo 14: Classes e métodos #71
  • 72. Como pensar como um cientista da Computação usando Python Capítulo 15: Conjuntos de objetos ● Rainha -> 12 15.1 Composição ● Rei -> 13 Até agora, você vio diversos exemplos de composição. Um O motivo pelo qual nós estamos usando notação dos primeiros exemplos foi o uso de uma invocação de matemática para estes mapeamentos é que eles não são parte método como parte de uma expressão. Outro exemplo é a do programa Python. Eles são parte do projeto do programa, estrutura aninhada dos comandos: você pode pôr um comando mas eles nunca aparecem explicitamente no código. A if dentro de um laço while, dentro de outro comando if, e definição de classe para o tipo Carta fica parecida com esta: assim por diante. class Carta: Tendo visto este padrão, e tendo aprendido a respeito de listas e objetos, você não deveria ficar surpreso em def __init__(self, naipe=0, posicao=0): aprender que você pode criar listas de objetos. Você também self.naipe = naipe pode criar obejtos que contêm listas (como atritos); você pode self.posicao = posicao criar listas que contêm listas; você pode criar objetos que Como sempre, nós fornecemos um método de contêm objetos; e assim por diante. inicialização que recebe um parâmetro opcional para cada Neste capítulo e no próximo, você irá ver alguns atributo. exemplos destas combinações, usando objetos Carta como Para criar um objeto que representa o 3 de Paus, exemplo. usa-se este comando: tresDePaus = Carta(0, 3) 15.2 Objetos Carta O primeiro argumento, 0, representa o naipe de Paus. Se você não estiver familiarizado com jogos de cartas, agora é um bom momento para conseguir um baralho, ou então esse capítulo pode não fazer muito sentido. Há 52 cartas em um 15.3 Atributos de classe e o método __str__ baralho, cada uma das quais pertence a um dos quatro naipes e a uma das treze posições. Os naipes são Espadas, Copas, Para imprimir objetos Carta de uma maneira que as pessoas Ouros e Paus (em ordem descendente no bridge). As posições possam facilmente ler, nós gostaríamos de mapear os códigos são Ás, 2, 3, 4, 5, 6, 7, 8, 9, 10, Valete, Rainha e Rei. inteiros para palavras. Uma forma natural de fazer isso é usar Dependendo do jogo, a posição do Ás pode ser maior do que a listas de strings. Nós atribuímos estas listas para atributos de do Rei ou menor do que a do 2. classe no topo da definição de classe: Se quisermos definir um novo objeto para class Carta: representar uma carta, é óbvio que os atributos devem ser posicao e naipe. Não tão óbvio são os tipos aos quais devem listaDeNaipes = ["Paus", "Ouros", "Copas", pertencer os atributos. Uma possibilidade é usar strings "Espadas"] contendo palavras como "Espada" para naipes e "Rainha" para listaDePosicoes = ["narf", "Ás", "2", "3", "4", posições. Um problema com esta implementação é que não "5", "6", "7", seria fácil comparar cartas para ver qual possui o maior naipe "8", "9", "10", "Valete", ou posição. "Rainha", "Rei"] Uma alternativa é usar inteiros para codificar as posições e naipes. "Codificar", neste caso, não significa o # método init omitido mesmo que as pessoas normalmente pensam, que é criptografar ou traduzir para um código secreto. O que um def __str__(self): cientista da computação quer dizer com "codificar" é "definir return (self.listaDePosicoes[self.posicao] + um mapeamento entre uma seqüência de números e os itens " de " + self.ListaDeNaipes[self.naipe]) que eu quero representar". Por exemplo: Um atributo de classe é definido fora de ● Espadas -> 3 qualquer método, e ele pode ser acessado por quaisquer métodos da classe. ● Copas -> 2 Dentro de __str__, nós podemos usar ● Ouros -> 1 listaDeNaipes e listaDePosicoes para mapear os valores ● Paus -> 0 numéricos de naipe e posicao para strings. Por exemplo, a expressão self.listaDeNaipes[self.naipe] significa "use o Uma característica óbvia deste mapeamento é atributo naipe do objeto self como um índice para o atributo que os naipes são mapeados para inteiros na ordem, de modo de classe chamado listaDeNaipes, e selecione a string que nós podemos comparar naipes pela comparação de apropriada". inteiros. O mapeamento de posições é bastante óbvio. Cada uma das posições numéricas mapeia para o inteiro em listaDePosicoes é preencher "narf" no primeiro elemento O motivo para o correspondente e, as cartas com figura são mapeadas da lista, que nunca será usado. As únicasdo 0-ésimo elemento o lugar posições válidas são conforme abaixo: de 1 a 13. Este item desperdiçado não é inteiramente ● Valete -> 11 necessário. Nós poderíamos ter iniciado com 0, como é normal. Porém, é menos confuso codificar 2 como 2, 3 como Capítulo 15: Conjuntos de objetos #72
  • 73. Como pensar como um cientista da Computação usando Python 3, e assim por diante. # verificar os naipes if self.naipe > other.naipe: return 1 Com os métodos que nós temos até agora, nós podemos criar e imprimir cartas: if self.naipe < other.naipe: return -1 # as cartas têm o mesmo naipe... verificar as >>> carta1 = Carta(1, 11) posições >>> print carta1 if self.posicao > other.posicao: return 1 Valete de Ouros if self.posicao < other.posicao> return -1 Atributos de classe como listaDeNaipes são # as posições são iguais... é um empate compartilhados por todos os objetos Carta. A vantagem disso return 0 é que nós podemos usar qualquer objeto Carta para acessar os Nesta ordenação, Ases são menores do que 2. atributos de classe: Como um exercício, modifique ``__cmp__``, de >>> carta2 = Carta(1, 3) modo que os Ases sejam maiores do que os Reis. >>> print carta2 3 de Ouros >>> print carta2.listaDeNaipes[1] Ouros 15.5 Baralhos A desvantagem é que se nós modificarmos um atributo de classe, isso afetará cada instância da classe. Por Agora que nós temos objetos para representar Cartas, o exemplo, se nós decidirmos que "Valete de Ouros" deveria próximo passo lógico é definir uma classe para representar um realmente se chamar "Valete de Baleias Rodopiantes", nós Baralho. É claro que um baralho é formado por cartas; poderíamos fazer isso: portanto, cada objeto Baralho irá conter uma lista de cartas como um atributo. >>> carta1.listaDeNaipes = "Baleias Rodopiantes" A seguir, damos uma definição para a classe >>> print carta1 Baralho. O método de inicialização cria o atributo cartas e 3 de Baleias Rodopiantes gera o conjunto padrão de 52 cartas: O problema é que todos os Ouros se tornam Baleias Rodopiantes: classe Baralho def __init__(self): >>> print carta2 self.cartas = [] 3 de Baleias Rodopiantes for naipe in range(4): Normalmente, não é uma boa idéia modificar for posicao in range(1, 14): atributos de classe. self.cartas.append(Carta(naipe, posicao)) A maneira mais fácil de popular o baralho é com um laço aninhado. O laço externo enumera os naipes de 0 15.4 Comparando cartas até 3. O laço interno enumera as posições de 1 até 13. Como o laço externo repete quatro vezes e o laço interno 13 vezes, o número total de vezes que o corpo é executado é 52 (13 vezes Para tipos primitivos, existem operadores condicionais (<, >, quatro). Cada iteração cria uma nova instância de Carta com o ==, etc.) que comparam valores e determinam quando um é naipe e posição atuais e a inclui na lista cartas. maior que, menor que ou igual a outro. Para tipos definidos pelo usuário, nós podemos sobrescrever o comportamento dos O método append trabalha sobre listas mas não, operadores pré-definidos fornecendo um método __cmp__. obviamente, sobre tuplas. Por convenção, __cmp__ recebe dois parâmetros, 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. 15.6 Imprimindo o baralho Alguns tipos são totalmente ordenados, o que significa que nós podemos comparar quaisquer dois elementos e dizer qual é o maior. Por exemplo, os inteiros e os números Como sempre, quando nós definimos um novo tipo de objeto, de ponto flutuante são totalmente ordenados. Alguns nós gostaríamos de ter um método para imprimir o conteúdo conjuntos são não-ordenados, o que significa que não existe de um objeto. Para imprimir um Baralho, nós percorremos a maneira significativa de dizer que um elemento é maior que o lista e imprimimos cada Carta: outro. Por exemplo, as frutas são não-ordenadas, e é por isso class Baralho: que não podemos comparar maçãs e laranjas. ... O conjunto de cartas de jogo é parcialmente def imprimirBaralho(self): ordenado, o que significa que às vezes você pode comparar for carta in self.cartas: cartas, e às vezes não. Por exemplo, você sabe que o 3 de Paus print carta é 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 Aqui, e a partir daqui, as reticências (...) Ouros? Um tem uma posição maior, mas o outro tem um indicam que nós omitimos os outros métodos da classe. naipe maior. Como uma alternativa a imprimirBaralho, nós Para tornar as cartas comparáveis, você tem que poderíamos escrever um método __str__ para a classe decidir o que é mais importante: posição ou naipe. Para ser Baralho. A vantagem de __str__ é que ela é mais flexível. Em honesto, a escolha é arbitrária. Por questão de escolha, nós vez de apenas imprimir o conteúdo de um objeto, ela gera uma iremos dizer que naipe é mais importante, porque um baralho representação em string que outras partes do programa podem de cartas novo vem ordenado com todas as cartas de Paus manipular antes de imprimir ou armazenar para uso posterior. juntas, seguidas pelas de Ouros, e assim por diante. Abaixo, uma versão de __str__ que devolve Com essa decisão, nós podemos escrever uma representação em string de um Baralho. Para adicionar __cmp__: um pouco de estilo, ela distribui as cartas em uma cascata, na qual cada carta é indentada um espaço a mais do que a carta def __cmp__(self, other): anterior: Capítulo 15: Conjuntos de objetos #73
  • 74. Como pensar como um cientista da Computação usando Python class Baralho: totalmente aleatória: ... class Baralho: def __str__(self): ... s = "" def embaralhar(self): for i in range(len(self.cartas)): import random s = s + " "*i + str(self.cartas[i]) + "n" nCartas = len(self.cartas) return s for i in range(nCartas): Este exemplo demonstra diversas j = random.randrange(i, nCartas) características. Primeiro, em vez de percorrer self.cartas e atribuir cada carta a uma variável, nós estamos usando i como self.cartas[i], self.cartas[j] = uma variável de laço e um índice para a lista de cartas. self.cartas[j], self.cartas[i] Em vez de assumir que existem 52 cartas no Segundo, nós estamos usando o operador de baralho, nós obtivemos o comprimento real da lista e o multiplicação de strings para indentar cada carta com um guardamos na variável nCartas. espaço adicional com relação à anterior. A expressão " "*i produz um número de espaços igual ao valor atual de i. Para cada carta no baralho, nós escolhemos uma carta aleatória dentre as cartas que ainda não foram Terceiro, em vez de usar o comando print para embaralhadas. Então, nós trocamos a carta atual (i) pela carta imprimir as cartas, nós usamos a função str. Passar um objeto selecionada (j). Para trocar as cartas, nós usamos uma como um argumento para str equivale a invocar o método atribuição de tupla, como visto na Seção 9.2: __str__ sobre o objeto. self.cartas[i], self.cartas[j] = self.cartas[j], Finalmente, nós estamos usando a variável s self.cartas[i] como um acumulador. Inicialmente, s é a string vazia. A cada repetição do laço, uma nova string é gerada e concatenada Como exercício, reescreva esta linha de código com o valor antigo de s para obter um novo valor. Quando o sem usar uma atribuição de seqüência. laço termina, s contém a representação em string completa do Baralho, que se parece com: >>> baralho = Baralho() 15.8 Removendo e distribuindo cartas >>> print Baralho Ás de Paus Outro método que pode ser útil para a classe Baralho é removerCarta. Ele recebe uma carta como parâmetro, remove- 2 de Paus a do baralho e retorna verdadeiro (1), se a carta estava no 3 de Paus baralho e falso (0), caso contrário: 4 de Paus 5 de Paus class Baralho: 6 de Paus ... 7 de Paus def removerCarta(self, carta): 8 de Paus if carta in self.cartas: 9 de Paus self.cartas.remove(carta) 10 de Paus return 1 Valete de Paus else Rainha de Paus return 0 Rei de Paus O operador in retorna verdadeiro se o primeiro Ás de Ouros operando estiver contido no segundo, que deve ser uma lista ou uma tupla. Se o primeiro operando for um objeto, Python E assim por diante. Mesmo que o resultado usa o método __cmp__ do objeto para determinar igualdade apareça em 52 linhas, é uma string longa que contém com os itens da lista. Como o método __cmp__ da classe newlines. Carta verifica por igualdade profunda, o método removerCarta também testa por igualdade profunda. Para distribuir as cartas, nós iremos remover e 15.7 Embaralhando devolver a carta do topo. O método de lista pop fornece uma maneira conveniente de fazer isso: Se um baralho estiver perfeitamente embaralhado, então cada carta tem a mesma probabilidade de aparecer em qualquer class Baralho: lugar no baralho, e qualquer localização no baralho tem a ... mesma probabilidade de conter qualquer carta. def distribuirCarta(self): return self.cards.pop() Para embaralhar as cartas, nós usaremos a função randrange do módulo random. Com dois argumentos Na verdade, pop remove a última carta da lista. inteiros, a e b, randrange escolhe um inteiro aleatório no Portanto, nós estamos realmente distribuindo as cartas do fim intervalo a <= x < b. Como o limite superior é estritamente para o início do baralho. menor que b, nós podemos usar o comprimento de uma lista Uma última operação que nós poderíamos como o segundo parâmetro, e nós garantimos que o índice querer é a função booleana estahVazio, que retorna verdadeiro sempre será válido. Por exemplo, esta expressão escolhe o se o baralho não contém cartas: índice de uma carta aleatória em um baralho: class Baralho: random.randrange(0, len(self.cartas)) ... Uma maneira fácil de embaralhar as cartas é percorrer a lista e trocar cada carta por outra escolhida def estahVazio(self): aleatoriamente. É possível que a carta seja trocada por ela return (len(self.cartas) == 0) mesma, mas isso não é problema. Na verdade, se nós excluíssemos essa possibilidade, a ordem das cartas não seria Capítulo 15: Conjuntos de objetos #74
  • 75. Como pensar como um cientista da Computação usando Python 15.9 Glossário codificar Representar um conjunto de valores (encode) usando outro conjunto de valores, construindo um mapeamento entre eles. atributo de Uma variável que é definida dentro de uma classe (class definição de classe, mas fora de qualquer atribute) método. Atributos de classe podem ser acessados a partir de qualquer método da classe e são compartilhados por todas as instâncias da classe. acumulador Uma variável usada em um laço para (accumulator) acumular uma série de valores, para, por exemplo, concatená-los em uma string ou somá-los a uma soma em andamento. Capítulo 15: Conjuntos de objetos #75
  • 76. Como pensar como um cientista da Computação usando Python Capitulo 16: Herança Esse comando indica que a nova classe Mao 16.1 Herança herda da classe existente Baralho. Uma das características mais marcantes das linguagens O construtor de Mao inicializa os atributos da orientadas a objetos é a herança. Herança é a habilidade de mão, que são nome e cartas. A string nome identifica essa definir uma nova classe que é uma versão modificada de uma mão, provavelmente pelo nome do jogador que está segurando classe existente. as cartas. O nome é um parâmetro opcional com a string vazia como valor default. cartas é a lista de cartas da mão, A principal vantagem dessa característica é que inicializada com uma lista vazia você pode adicionar novos métodos a uma classe sem ter que modificar a classe existente. Chama-se "herança" porque a class Mao(Baralho): nova classe herda todos os métodos da classe existente. def __init__(self, nome=""): Ampliando a metáfora, podemos dizer que a classe existente é self.cartas = [] às vezes chamada de classe mãe (parent). A nova classe pode self.nome = nome ser chamada de classe filha ou, simplesmente, "subclasse". Em praticamente todos os jogos de cartas, é A herança é uma característica poderosa. necessario adicionar e remover cartas do baralho. Remover Alguns programas que seriam complicados sem herança cartas já está resolvido, uma vez que Mao herda removerCarta podem ser escritos de forma simples e concisa graças a ela. E de Baralho. Mas precisamos escrever adicionarCarta: a herança também pode facilitar o reuso do código, uma vez que você pode adaptar o comportamento de classes existentes class Mao(Baralho): sem ter que modificá-las. Em alguns casos, a estrutura da #... herança reflete a natureza real do problema, tornando o def adicionarCarta(self,carta): programa mais fácil de entender. self.cartas.append(carta) Por outro lado, a herança pode tornar um De novo, a elipse indica que omitimos outros programa seja difícil de ler. Quando um método é invocado, métodos. O método de listas append adiciona a nova carta no às vezes não está claro onde procurar sua definição. A parte final da lista de cartas. relevante do código pode ser espalhada em vários módulos. E, também, muitas das coisas que podem ser feitas utilizando herança também podem ser feitas de forma igualmente 16.3 Dando as cartas elegante (ou até mais) sem ela. Se a estrutura natural do problema não se presta a utilizar herança, esse estilo de programação pode trazer mais problemas que vantagens. Agora que temos uma classe Mao, queremos distribuir cartas de Baralho para mãos de cartas. Não é imediatamente óbvio se Nesse capítulo, vamos demonstrar o uso de esse método deve ir na classe Mao ou na classe Baralho, mas herança como parte de um programa que joga uma variante de como ele opera num único baralho e (possivelmente) em Mico. Um dos nossos objetivos é escrever um código que várias mãos de cartas, é mais natural colocá-lo em Baralho. possa ser reutilizado para implementar outros jogos de cartas. O método distribuir deve ser bem geral, já que diferentes jogos terão diferentes requerimentos. Podemos querer distribuir o baralho inteiro de uma vez só ou adicionar 16.2 Uma mão de cartas uma carta a cada mão. distribuir recebe dois argumentos, uma lista (ou Para quase todos os jogos de baralho, é preciso representar tupla) de mãos e o numero total de cartas a serem dadas. Se uma mão de cartas. Uma mão de cartas é similar a um maço não houver cartas suficientes no baralho, o método dá todas as de baralho. Porque ambos são formados por uma série de cartas e pára: cartas e ambos requerem operações, como, adicionar e remover cartas. Fora isso, a habilidade de embaralhar a mão e class Baralho: o baralho também são úteis. #... Mas, ao mesmo tempo, a mão é também def distribuir(self, maos, nCartas=999): diferente do baralho. Dependendo do jogo que está sendo nMaos = len(maos) jogado, precisamos realizar algumas operações nas mãos de for i in range(nCartas): cartas que não fazem sentido para o baralho inteiro. Por if self.estahVazia(): break exemplo, no pôquer, podemos classificar uma mão (trinca, flush, etc.) ou compará-la com outra mão. No jogo de bridge, # interromper se acabaram as cartas podemos querer computar a quantidade de pontos que há carta = self.pegarCarta() numa mão, a fim de fazer um lance. # pegar a carta do topo mao = maos[i % nMaos] Essa situação sugere o uso de herança. Se Mao é uma subclasse de Baralho, terá todos os métodos de Baralho, e # quem deve receber agora? novos métodos podem ser adicionados. mao.adicionarCarta(carta) # adicionar a carta à mao Na definição de classe, o nome da classe pai O segundo parâmetro, nCartas, é opcional; o aparece entre parênteses: default é um número grande, o que na prática significa que class Mao(Baralho): todas as cartas do baralho serão dadas se este parâmetro for pass omitido. Capitulo 16: Herança #76
  • 77. Como pensar como um cientista da Computação usando Python A variável do laço i vai de 0 a nCartas-1. A cada def __init__(self): volta do laço, uma carta é removida do baralho, usando o self.baralho = Baralho() método de lista pop, que remove e retorna o último item na self.baralho.embaralhar() lista. Este é o primeiro dos casos que vimos até agora O operador módulo (%) permite dar cartas em em que o método de inicialização realiza uma computação ao redor da mesa (uma carta de cada vez para cada mão). significativa, para além de inicializar atributos. Quando i é igual ao numero de mãos na lista, a expressão i % nMaos volta para o começo da lista (índice 0). Para implementar jogos específicos, podemos herdar de JogoDeCartas e adicionar caracteristicas para o novo jogo. Como exemplo, vamos escrever uma simulação de Mico. 16.4 Exibindo a mao O objetivo do jogo é livrar-se das cartas que estiverem na mão. Para fazer isso, é preciso combinar cartas Para exibir o conteúdo de uma mão, podemos tirar vantagem formando pares ou casais que tenham a mesma cor e o mesmo dos métodos exibirBaralho e __str__ herdados de Baralho. Por número ou figura. Por exemplo, o 4 de paus casa com o 4 de exemplo: espadas porque os dois naipes são pretos. O Valete de copas >>> baralho = Baralho() combina com o Valete de ouros porque ambos são vermelhos. >>> baralho.embaralhar() Antes de mais nada, a Dama de paus é removida >>> mao = Mao("fabio") do baralho, para que a Dama de espadas fique sem par. A >>> baralho.distribuir([mao], 5) Dama de espadas então faz o papel do mico. As 51 cartas que >>> print mao sobram são distribuidas aos jogadores em ao redor da mesa (uma carta de cada vez para cada mão). Depois que as cartas Mão fabio contém foram dadas, os jogadores devem fazer todos os casais 2 de espadas possíveis que tiverem na mão, e em seguida descartá-los na 3 de espadas mesa. 4 de espadas Quando ninguém mais tiver nenhum par para Ás de copas descartar, o jogo começa. Na sua vez de jogar, o jogador pega 9 de paus uma carta (sem olhar) do vizinho mais proximo à esquerda, Nao é lá uma grande mão, mas tem potencial que ainda tiver cartas. Se a carta escolhida casar com uma para um straight flush. carta que ele tem na mão, ele descarta esse par. Quando todos os casais possíveis tiverem sido feitos, o jogador que tiver Embora seja conveniente herdar os métodos sobrado com a Dama de espadas na mão perde o jogo. existentes, há outras informacoes num objeto Mao que podemos querer incluir quando ao exibí-lo. Para fazer isso, Em nossa simulação computacional do jogo, o podemos fornecer um método __str__ para a classe Mao que computador joga todas as mãos. Infelizmente, algumas sobrescreva o da classe Baralho: nuances do jogo presencial se perdem. Num jogo presencial, o jogador que está com o mico na mão pode usar uns truques class Mao(Baralho) para induzir o vizinho a pegar a carta, por exemplo, #... segurando-a mais alto que as outras, ou mais baixo, ou se def __str__(self): esforçando para que ela não fique em destaque. Já o computador simplesmente pega a carta do vizinho s = "Mao " + self.nome aleatoriamente... if self.estahVazia(): return s + " está vazian" else: return s + " contémn" + Baralho.__str__(self) 16.6 Classe MaoDeMico Inicialmente, s é uma string que identifica a mão. Se a mão estiver vazia, o programa acrescenta as Uma das habilidades gerais de uma algumas habilidades uma mão para jogar Mico requer para palavras está vazia e retorna o resultado. alem Mao. Vamos definir nova classe, MaoDeMico, que herda de Mao e provê um Se não, o programa acrescenta a palavra contém método adicional chamado descartarCasais: e a representação de string do Baralho, computada pela invocação do método __str__ na classe Baralho em self. class MaoDeMico(Mao): def descartarCasais(self): Pode parecer estranho enviar self, que se refere conta = 0 à Mao corrente, para um método Baralho, mas isso só até voce cartasIniciais = self.cartas[:] se lembrar que um Mao é um tipo de Baralho. Objetos Mao podem fazer tudo que os objetos Baralho fazem, entao, é for carta in cartasIniciais: permitido passar uma instância de Mao para um método casal = Carta(3 - carta.naipe, carta.valor) Baralho. if casal in self.cartas: self.cartas.remove(carta) Em geral, sempre é permitido usar uma instância de uma subclasse no lugar de uma instância de uma self.cartas.remove(casal) classe mãe. print "Mao %s: %s casais %s" % (self.nome,carta,casal) conta = conta + 1 return conta 16.5 A classe JogoDeCartas Começamos fazendo uma cópia da lista de cartas, para poder percorrer a cópia enquanto removemos A classe JogoDeCartas toma conta de algumas tarefas básicas cartas do original. Uma vez que self.cartas é modificada no comuns a todos os jogos, como, criar o baralho e embaralhá- laço, não queremos usá-la para controlar o percurso. Python lo: pode ficar bem confuso se estiver percorrendo uma lista que class JogoDeCartas: está mudando! Capitulo 16: Herança #77
  • 78. Como pensar como um cientista da Computação usando Python Para cada carta na mão, verificamos qual é a # distribuir as cartas carta que faz par com ela e vamos procurá-la. O par da carta self.baralho.distribuir(self.maos) tem o mesmo valor (número ou figura) e naipe da mesma cor. print "---------- As cartas foram dadas" A expressão 3 - carta.naipe transforma um paus (naipe 0) numa espadas (naipe 3) e um ouros (naipe 1) numa copas self.exibirMaos() (naipe 2). Você deve analisar a fórmula até se convencer de que as operações opostas também funcionam. Se o par da # remover casais iniciais carta tambem estiver na mão, ambas as cartas são removidas. casais = self.removerTodosOsCasais() O exemplo a seguir demonstra como usar print "---------- Os pares foram descartados, o descartarCasais: jogo começa" self.exibirMaos() >>> jogo = JogoDeCartas() >>> mao = MaoDeMico("fabio") # jogar até que 25 casais se formem >>> jogo.baralho.distribuir([mao], 13) vez = 0 >>> print mao numMaos = len(self.maos) mão fabio contém while casais < 25: Ás de espadas casais = casais + self.jogarVez(vez) 2 de ouros vez = (vez + 1) % numMaos 7 de espadas 8 de paus print "---------- Fim do jogo" 6 de copas self.exibirMaos() 8 de espadas Algumas etapas do jogo foram separadas em 7 de paus métodos. removerTodosOsCasais percorre a lista de mãos e Rainha de paus invoca descartarCasais em cada uma: 7 de ouros class Mico(JogoDeCartas): 5 de paus #... Valete de ouros def removerTodosOsCasais(self): 10 de ouros conta = 0 10 de copas for mao in self.maos: conta = conta + mao.descartarCasais() >>> mao.descartarCasais() return conta Mão fabio: 7 de espadas faz par com 7 de paus Mão fabio: 8 de espadas faz par com 8 de paus Como exercício, escreva ``exibirMaos`` que percorre ``self.maos`` e exibe cada mão. Mão fabio: 10 de ouros faz par com 10 de copas >>> print mao conta é uma acumulador que soma o número de Mão fabio contém pares em cada mão e retorna o total. Ás de espadas Quando o número total de pares alcança 25, 50 2 de ouros cartas foram removidas das mãos, o que significa que sobrou 6 de copas só uma carta e o jogo chegou ao fim. Rainha de paus A variável vez mantém controle sobre de quem 7 de ouros é a vez de jogar. Começa em 0 e incrementa de um em um; 5 de paus quando atinge numMaos, o operador módulo faz ela retornar Valete de ouros para 0. Observe que não existe um método __init__ O método jogarVez recebe um argumento que para a classe MaoDeMico. Ele é herdado de Mao. indica de quem é a vez de jogar. O valor de retorno é o número de pares feitos durante essa rodada: class Mico(JogoDeCartas): 16.7 Classe Mico #... def jogarVez(self, i): Agora podemos focar nossa atenção no jogo em si. Mico é if self.maos[i].estahVazia(): uma subclasse de JogoDeCartas com um novo método chamado jogar que recebe uma lista de jogadores como return 0 argumento. vizinho = self.buscarVizinho(i) novaCarta = self.maos[vizinho].pegarCarta() Já que __init__ é herdado de JogoDeCartas, um self.maos[i].adicionarCarta(novaCarta) novo objeto Mico contém um novo baralho embaralhado: print "Mao", self.maos[i].nome, "pegou", class Mico(JogoDeCartas): novaCarta def jogar(self, nomes): conta = self.maos[i].descartarCasais() # remover a Dama de paus self.maos[i].embaralhar() self.baralho.removerCarta(Carta(0,12)) return conta Se a mão de um jogador estiver vazia, ele está # fazer uma mão para cada jogador fora do jogo, então, ele não faz nada e retorna 0. self.maos = [] Do contrário, uma jogada consiste em achar o for nome in nomes : primeiro jogador à esquerda que tenha cartas, pegar uma carta self.maos.append(MaoDeMico(nome)) dele, e tentar fazer pares. Antes de retornar, as cartas na mão são embaralhadas, para que a escolha do próximo jogador seja aleatória. Capitulo 16: Herança #78
  • 79. Como pensar como um cientista da Computação usando Python O método buscarVizinho começa com o jogador Rei de espadas imediatamente à esquerda e continua ao redor da mesa até 10 de ouros encontrar um jogador que ainda tenha cartas: class Mico(JogoDeCartas): Mão Jair contém #... Valete de espadas def buscarVizinho(self, i): Valete de copas numMaos = len(self.maos) Rei de ouros for next in range(1,numMaos): vizinho = (i + next) % numMaos Mão Clara contém if not self.maos[vizinho].estahVazia(): Valete de ouros return vizinho Rei de paus Se buscarVizinho alguma vez circulasse pela 10 de copas mesa sem encontrar cartas, retornaria None e causaria um erro em outra parte do programa. Felizmente, podemos provar que Mão Alice pegou o Rei de ouros isso nunca vai acontecer (desde que o fim do jogo seja Mão Alice: Rei de copas faz par com Rei de ouros detectado corretamente). Mão Jair pegou 10 de copas Não mencionamos o método exibirBaralhos. Mão Clara pegou Valete de paus Esse você mesmo pode escrever. Mão Alice pegou Valete de copas A saída a seguir é produto de uma forma Mão Jair pegou Valete de ouros reduzida do jogo, onde apenas as 15 cartas mais altas do Mão Clara pegou Dama de espadas baralho (do 10 para cima) foram dadas, para três jogadores. Mão Alice pegou Valete de ouros Com esse baralho reduzido, a jogada pára depois que 7 Mão Alice: Valete de copas faz par com Valete de combinações foram feitas, ao invés de 25: ouros Mão Jair pegou Rei de paus >>> import cartas Mão Clara pegou Rei de espadas >>> jogo = cartas.Mico() Mão Alice pegou 10 de copas >>> jogo.jogar(["Alice","Jair","Clara"]) Mão Alice: 10 de ouros faz par com 10 de copas ---------- As cartas foram dadas Mão Jair pegou Dama de espadas Mão Alice contém Mão Clara pegou Valete de espadas Rei de copas Mão Clara: Valete de paus faz par com Valete de Valete de paus espadas Rainha de espadas Mão Jair pegou Rei de espadas Rei de espadas Mão Jeff: Rei de paus faz par com Rei de espadas 10 de ouros ---------- Fim do jogo Mão Alice está vazia Mão Jair contém Rainha de copas Mão Jair contém Valete de espadas Rainha de espadas Valete de copas Rei de ouros Mão Clara está vazia Rainha de ouros Então, o Jair perdeu. Mão Clara contém Valete of ouros Rei de paus 10 de espadas 16.8 Glossário 10 de copas 10 de paus herança (inheritance) Habilidade de definir uma nova classe que é a versão modificada de Mão Jair: Dama de copas faz par com Dama de ouros uma classe definida anteriormente. Mão Clara: 10 de espadas faz par com 10 de paus classe mãe (parent A classe de quem a classe filha ---------- Os pares foram descartados, o jogo começa class) herda. Mão Alice contém classe filho (child Um nova classe criada herdando de Rei de copas class) uma classe existente; também Valete de paus chamada de "subclasse". Rainha de espadas Capitulo 16: Herança #79
  • 80. Como pensar como um cientista da Computação usando Python Capítulo 17: Listas encadeadas 17.1 Referências Embutidas Nós temos visto exemplos de atributos que referenciam outros objetos, que são chamados referências embutidas (veja a Seção 12.8). Uma estrutura de dados comum, a lista ligada, tira vantagem desta característica. Listas ligadas são constituídas de nós (nodos), onde cada nó contém uma referência para o próximo nó na Para ligar os nós, temos que fazer o primeiro nó lista. Além disto, cada nó contém uma unidade de dados da lista referir ao segundo e o segundo nó referir ao terceiro: chamada a carga. >>> no1.proximo = no2 Uma lista ligada é considerada uma estrutura de dados recorrente porque ela tem uma definição >>> no2.proximo = no3 recorrente. A referência do terceiro nó é None, que indica que ele é o final da lista. Agora o diagrama de estado se Uma lista ligada é: parece com: ● Uma lista vazia, representada por None, ou ● Um nó que contém um objeto carga e uma referência para uma lista ligada. Estruturas de dados recorrentes são adequadas para métodos recorrentes. Agora você sabe como criar nós e ligá-los em uma lista. O que pode estar menos claro neste ponto é por quê. 17.2 A classe No (Node) Como é usual quando se escreve uma nova classe, nós 17.3 Listas como Coleções começaremos com os métodos de inicialização e __str__ de modo que podemos testar o mecanismo básico de se criar e mostrar o novo tipo: Listas são úteis porque elas provêm um modo de montar múltiplos objetos em uma única entidade, algumas vezes class No: chamada uma coleção. No exemplo, o primeiro nó da lista def __init__(self, carga=None, proximo=None): serve como uma referência para toda a lista. self.carga = carga Para passar uma lista como um parâmetro, você self.proximo = proximo apenas tem que passar uma referência ao primeiro nó. Por exemplo, a função imprimeLista toma um único nó como um def __str__(self): argumento. Iniciando com o cabeça da lista, ela imprime cada return str(self.carga) nó até que chegue ao fim: Como de costume, os parâmetros para o método def imprimeLista(no): de inicialização são opcionais. Por omissão (default), ambos, a while no: carga e a ligação, proximo, são definidas como None. print no, A representação string de um nó é simplesmente no = no.proximo a representação string da carga. Como qualquer valor pode ser print passado para a função str, nós podemos armazenar qualquer Para chamar este método, nós passamos uma valor em uma lista. referência ao primeiro no: Para testar a implementação até agora, nós >>> imprimeLista(no1) criamos um No e o imprimimos: 1 2 3 >>> no = No("teste") Dentro de imprimeLista nós temos uma >>> print no referência para o primeiro nó da lista, mas não há variáveis teste que refiram aos outros nós. Nós temos que usar o valor Para ficar interessante, nós precisamos uma lista proximo de cada nó para alcançar o próximo nó. com mais do que um nó: Para percorrer uma lista ligada, é comum usar uma variável laço como no para referir a cada um dos nós >>> no1 = No(1) sucessivamente. >>> no2 = No(2) >>> no3 = No(3) Este diagrama mostra o valor de lista e os Este código cria três nós, mas nós ainda não valores que no assume: temos uma lista ainda porque os nós não estão ligados. O diagrama de estado é parecido com este: Capítulo 17: Listas encadeadas #80
  • 81. Como pensar como um cientista da Computação usando Python figura mostra uma lista com dois nós, um dos quais refere-se a si mesmo: Por convenção, listas são freqüentemente impressas em braquetes com vírgulas entre os elementos, como em [1, 2, 3]. Como um exercício, modifique imprimeLista para que ela gere uma saída neste formato. 17.4 Listas e Recorrência Se nós invocarmos imprimeLista nesta lista, ele ficará em laço para sempre. Se nós invocarmos É natural expressar muitas operações de listas utilizando imprimeDeTrasParaFrente, ele recorrerá infinitamente. Este métodos recorrentes. Por exemplo, o seguinte é um algoritmo tipo de comportamento torna as listas infinitas difíceis de se recorrente para imprimir uma lista de trás para frente. lidar. 1. Separe a lista em dois pedaços: o primeiro nó A despeito disto, elas ocasionalmente são úteis. (chamado a cabeça); e o resto (chamado o rabo). Por exemplo, podemos representar um número como uma lista de dígitos e usar uma lista infinita para representar uma fração 2. Imprima o rabo de trás para frente. repetente. 3. Imprima a cabeça. Mesmo assim, é problemático que não possamos provar que imprimeLista e Logicamente, o Passo 2, a chamada recorrente, imprimeDeTrasParaFrente terminem. O melhor que podemos assume que nós temos um modo de imprimir a lista de trás fazer é a afirmação hipotética, "Se a lista não contém laços, para frente. Mas se nós assumimos que a chamada recorrente então este método terminará." Este tipo de hipótese é chamado funciona -- o passo de fé -- então podemos nos convencer de uma pré-condição. Ele impõe uma limitação sobre um dos que o algoritmo funciona. parâmetros e descreve o comportamento do método se a Tudo o que precisamos são um caso base e um limitação é satisfeita. Você verá mais exemplos em breve. modo de provar que para qualquer lista, nós iremos, ao final, chegar no caso base. Dada a definição recorrente de uma lista, um caso base natural é a lista vazia, representada por None: 17.6 O Teorema da Ambigüidade Fundamental def imprimeDeTrasParaFrente(lista): if lista == None : return Uma parte de imprimeDeTrasParaFrente pode ter gerado cabeca = lista surpresa: rabo = lista.proximo cabeca = lista imprimeDeTrasParaFrente(rabo) rabo = lista.proximo print cabeca, Após a primeira atribuição, cabeca e lista têm o A primeira linha trata o caso base fazendo nada. mesmo tipo e o mesmo valor. Então por que nós criamos uma As próximas duas linhas dividem a lista em cabeca e rabo. As nova variável? duas últimas linhas imprimem a lista. A vírgula no final da última linha impede o Python de imprimir uma nova linha A razão é que as duas variáveis têm diferentes após cada nó. papéis. Quando pensamos em cabeca, pensamos como uma referência a um único nó, e quando pensamos em lista o Nós invocamos este método como invocamos o fazemos como uma referência ao primeiro nó da lista. Estes imprimeLista: "papéis" não são parte do programa; eles estão na mente do programador. >>> imprimeDeTrasParaFrente(no1) 3 2 1 Em geral não podemos dizer olhando para o programa qual o papel que uma variável tem. Esta O resultado é a lista de trás para frente. ambigüidade pode ser útil, mas também pode tornar os Você pode se perguntar por quê imprimeLista e programas difíceis de serem lidos. Usamos freqüentemente imprimeDeTrasParaFrente são funções e não métodos da nomes de variáveis como no e lista para documentar como classe No. A razão é que nós queremos usar None para pretendemos usar uma variável e algumas vezes criamos representa a lista vazia e não é legal invocar um método sobrevariáveis adicionais para remover a ambigüidade. None. Esta limitação torna complicado escrever código de Poderíamos ter escrito manipulação de lista em estilo orientado a objeto limpo. imprimeDeTrasParaFrente sem cabeca e rabo, que a tornaria Podemos provar que imprimeDeTrasParaFrente mais concisa mas possivelmente menos clara: sempre termina? Em outras palavras, irá ela sempre atingir o def imprimeDeTrasParaFrente(lista): caso base? De fato, a resposta é não. Algumas listas farão este método falhar. if lista == None : return imprimeDeTrasParaFrente(lista.proximo) print lista, 17.5 Listas Infinitas Olhando para as duas chamadas de função, temos que lembrar que imprimeDeTrasParaFrente trata seu argumento como uma coleção e print trata seu argumento Não há nada que impeça um nó de referenciar de volta um nó como um objeto único. anterior na lista, incluindo ele mesmo. Por exemplo, esta Capítulo 17: Listas encadeadas #81
  • 82. Como pensar como um cientista da Computação usando Python O teorema da ambigüidade fundamental cabeca = lista descreve a ambigüidade que é inerente à referência a um nó: rabo = lista.proximo Uma variável que refere a um nó pode tratar o imprimeDeTrasParaFrente(rabo) nó como um objeto único ou como o primeiro em uma lista print cabeca, de nós. print "]", Novamente, é uma boa idéia verificar métodos como este para ver se eles funcionam com casos especiais 17.7 Modificando Listas como uma lista vazia ou um singleton. Quando usamos este método em algum lugar no Existem duas maneiras de se modificar uma lista ligada. programa, invocamos imprimeDeTrasParaFrenteLegal Obviamente, podemos modificar a carga dos nós, mas as diretamente, e ele invoca imprimeDeTrasParaFrente por nós. operações mais interessantes são aquelas que adicionam, Neste sentido, imprimeDeTrasParaFrenteLegal atua como um removem ou reordenam os nós. envoltório, e usa imprimeDeTrasParaFrente como um ajudador. Como um exemplo, vamos escrever um método que remove o segundo nó na lista e retorna uma referência ao nó removido: 17.9 A Classe ListaLigada def removeSegundo(lista): if lista == None : return Existem alguns problemas sutis com o modo que primeiro = lista implementamos listas. Em um inverso de causa e efeito, segundo = lista.proximo proporemos uma implementação alternativa primeiro e então # faz o primeiro no referir ao terceiro explicaremos qual problema ela resolve. primeiro.proximo = segundo.proximo Primeiro, criaremos uma nova classe chamada # separa o segundo no do resto da lista ListaLigada. Seus atributos são um inteiro que contém o segundo.proximo = None comprimento da lista e uma referência para o primeiro nó. return segundo Objetos do tipo ListaLigada servem como cabos (handles) Novamente, estamos usando variáveis para se manipular listas de objetos No: temporárias para tornar o código mais fácil de ser lido. Aqui class ListaLigada: está como usar este método: def __init__(self): >>> imprimeLista(no1) self.comprimento = 0 1 2 3 self.cabeca = None >>> removido = removeSegundo(no1) Uma coisa legal acerca da classe ListaLigada é >>> imprimeLista(removido) que ela provê um lugar natural para se colocar funções 2 envoltórias como imprimeDeTrasParaFrenteLegal, que podemos transformar em um método da classe ListaLigada: >>> imprimeLista(no1) 1 3 class ListaLigada: Este diagrama de estado mostra o efeito da ... operação: def imprimeDeTrasParaFrente(self): print "[", if self.cabeca != None : self.cabeca.imprimeDeTrasParaFrente() print "]", class No: ... O que acontece se você invocar este método e def imprimeDeTrasParaFrente(self): passar uma lista com somente um elemento (um singleton)? O if self.proximo != None: que acontece se você passar a lista vazia como um argumento? rabo = self.proximo Existe uma pré-condição para este método? Se houver, corrija o método para tratar uma violação da pré-condição de modo rabo.imprimeDeTrasParaFrente() razoável. print self.carga, Apenas para tornar as coisas confusas, mudamos o nome de imprimeDeTrasParaFrenteLegal. Agora existem dois métodos chamados imprimeDeTrasParaFrente: 17.8 Envoltórios e Ajudadores um na classe No (o ajudador); e um na classe ListaLigada``(o envoltório). Quano o envoltório invoca Freqüentemente é útil dividir uma operação de lista em dois ``self.cabeca.imprimeDeTrasParaFrente, ele está invocando o métodos. Por exemplo, para imprimir uma lista de trás para ajudador, porque self.cabeca é um objeto No. frente no formato convencional de lista [3, 2, 1], podemos usar o método imprimeDeTrasParaFrente para imprimir 3, 2, Outro benefício da classe ListaLigada é que ela mas queremos um metodo separado para imprimir os torna mais fácil adicionar e remover o primeiro elemento de braquetes e o primeiro nó. Vamos chamá-lo de uma lista. Por exemplo, adicionaPrimeiro é um método para imprimeDeTrasParaFrenteLegal: ListaLigada; ele toma um item de carga como argumento e o coloca no início da lista: def imprimeDeTrasParaFrenteLegal(lista): class ListaLigada: print "[", ... if lista != None : Capítulo 17: Listas encadeadas #82
  • 83. Como pensar como um cientista da Computação usando Python def adicionaPrimeiro(self, carga): (embedded armazenada/associada em/a um no = No(carga) reference) atributo de um objeto. no.proximo = self.cabeca lista ligada (linked Uma estrutura de dados que self.cabeca = no list) implementa uma coleção usando self.comprimento = self.comprimento + 1 uma sequência de nós ligados. Como de costume, você deve conferir códigos como este para ver se eles tratam os casos especiais. Por nó ou nodo (node) Um elemento de uma lista, exemplo, o que acontece se a lista está inicialmente vazia? usualmente implementado como um objeto que contém uma referência para outro objeto do mesmo tipo. 17.10 Invariantes carga (cargo) Um item de dado contido em um nó. ligação (link) Uma referência embutida usada para Algumas listas são "bem formadas"; outras não o são. Por ligar/conectar um objeto a outro. exemplo, se uma lista contém um laço, ela fará muitos de nossos métodos falharem, de modo que podemos querer pré-condição Uma asserção que precisa/deve ser requerer que listas não contenham laços. Outro requerimento é (precondition) verdadeira para que um método que o valor de comprimento no objeto ListaLigada seja igual trabalhe corretamante. ao número real de nós da lista. teorema da Uma referência para um nó de uma Requerimentos como estes são chamados de ambigüidade lista pode ser tratada como um invariantes porque, idealmente, eles deveriam ser verdade para fundamental objeto único ou como o primeiro em cada objeto o tempo todo. Especificar invariantes para objetos (fundamental uma lista de nós. é um prática de programação útil porque torna mais fácil ambiguity theorem) provar a correção do código, verificar a integridade das singleton (singleton) Uma lista ligada com somente um estruturas de dados e detectar erros. nó. Uma coisa que algumas vezes é confusa acerca envoltório (wrapper) Um método que atua como um de invariantes é que existem momentos em que eles são intermediário (middleman) entre um violados. Por exemplo, no meio de adicionaPrimeiro, após chamador e um método ajudador termos adicionado o nó mas antes de termos incrementado (helper), freqüentemente tornando a comprimento, o invariante é violado. Este tipo de violação é invocação do método mais fácil ou aceitável; de fato, é freqüentemente impossível modificar um menos propensa a erros. objeto sem violar um invariante por, no mínimo, um pequeno instante. Normalmente, requeremos que cada método que ajudador (helper) Um método que não é invocado viola um invariante deve restaurar este invariante. diretamente pelo chamador (caller) mas é usado por outro método para Se há qualquer aumento significativo de código realizar parte de uma operação. no qual o invariante é violado, é importante tornar isto claro nos comentários, de modo que nenhuma operação seja feita invariante Uma asserção que deveria ser que dependa daquele invariante. (invariant) verdadeira sempre para um objeto (exceto talvez enquanto o objeto estiver sendo modificado). 17.11 Glossário referência embutida Uma referência Capítulo 17: Listas encadeadas #83
  • 84. Como pensar como um cientista da Computação usando Python Capítulo 18: Pilhas 18.1 Tipos abstratos de dados 18.3 Implementando pilhas com listas de Python Os tipos de dados que você viu até agora são todos concretos, As operações de lista que Python oferecem são similares às no sentido que nós especificamos completamente como eles operações que definem uma pilha. A interface não é são implementados. Por exemplo, a classe Card(XXX ver exatamente o que se supõe ser, mas podemos escrever um como foi traduzido) representa uma carta utilizando dois código para traduzir do TAD Pilha para as operações nativas. inteiros. Como discutimos no momento, esta não é a única maneira de representar uma carta; existem muitas Este código é chamado uma implementação do implementações alternativas. TAD Pilha. Geralmente, uma implementaçõa é um conjunto de métodos que satisfazem os requisitos sintáticos e Um tipo abstrato de dado, ou TAD, especifica semânticos de uma interface. um conjunto de operações (ou métodos) e a semântica das operações (o que elas fazem), mas não especifica a Aqui está uma implementação do TAD Pilha implementação das operações. Isto é o que o faz abstrato. que usa uma lista do Python: Por que isto é útil? class Stack : def __init__(self) : ● Simplifica a tarefa dde especificar um algoritmo se self.items = [] você pode XXXdenotar(denote) as operações que você precisa sem ter que pensar, ao mesmo tempo, como as operações são executadas. def push(self, item) : self.items.apend(item) ● Uma vez que existem geralmente muitas maneiras de implementar um TAD, pode ser útil escrever um algritmo que pode ser usado com qualquer das def pop(self) : possíveis implementações. return self.items.pop() ● TADs bastante conhecidos, como o TAD Pilha deste def isEmpty(self) : capítulo, já estão implementados em bibliotecas padrão, então eles podem ser escritos uma vez e return (self.items == []) usado por muitos programadores. Um objeto Stack contém um atributo chamado items que é uma lista de ítens na pilha. O método de ● As operações em TADs provêm uma linguagem de inicialização define items como uma lista vazia. alto nível comum para especificar e falar sobre algoritmos. Para adicionar um novo ítem na pilha, push o coloca em items. Para remover um ítem da pilha, pop usa o Quando falamos sobre TADs, geralmente método de lista homônimo¹ para remover e retornar um último distinguimos o código que usa o TAD, chamado cliente, do ítem da lista. código que implementa o TAD, chamado código fornecedor. Finalmente, para verificar se a pilha está vazia, isEmpty comprara items a uma lista vazia. 18.2 O TAD Pilha Uma implementação como esta, na qual os métodos consistem de simples invocações de métodos Neste capítulo, iremos olhar um TAD comum, a pilha. Uma existentes, é chamado revestimento. Na vida real, pilha é uma coleção, ou seja, é uma estrutura de dados que revestimento é uma fina camada de madeira de boa qualidade contei múltiplos elementos. Outras coleções que vimos usado em XXX*furniture-making* para esconder madeira de incluem dicionários e listas. menor qualidade embaixo. Cientistas da Computação usam esta metáfora para descrever um pequeno trecho de código Um TAd é definido pelo conjunto de operações que esconde os detalhes de uma implementação e fornece uma que podem ser executadas nele, que é chamado interface. A interface mais simples, ou mais padronizada. interface para uma pilha consiste nestas operações: __init__ : Inicializa uma nova pilha vazia. 18.4 Empilhando e desempilhando push : Adiciona um novo item na pilha pop : Remove um ítem da pilha e o retorna, O Uma pilha é uma estrutura de dados genérica, o que ítem que é retornadao é sempre o último adicionado. significa que podemos adicionar qualquer tipo de ítem a ela. O exemplo a seguir empilha dois inteiros e uma XXXstring na isEmpty : Verifica se a pilha está vazia. pilha: Uma às vezes é chamada uma estrutura de >>> s = Stack() dados "last in, first out" ou LIFO, porque o último ítem >>> s.push(54) adicionad é o primeiro a ser removido. >>> s.push(45) >>> s.push("+") Podemos usar isEmpty e pop para remover e imprimir todos os ítens da pilha: Capítulo 18: Pilhas #84
  • 85. Como pensar como um cientista da Computação usando Python while not s.isEmpty() : A função re.split é mais poderosa, permitindo- priint s.pop() nos fornecer uma expresão regular ao invés de um A saída é + 45 54. Em outras palavras, usamos a delimitador. Uma expressão regular é uma maneira de pilha para imprimir os ítens ao contrário! Sabidamente, este especificar um conjunto de strings. Por exemplo, [A-z] é o não é o formato padrão de imprimir uma lista, mas, usando conjunto de todas as letras e [0-9] é o conjunto de todos os uma pilha, foi notavelmente fácil de fazer. dígitos. O operador ^nega um conunto, então [^0-9] é o conjunto de tudo o que não é número, que é exatamente o que Você deve comparar este trecho de código com queremos para dividir expressões pós-fixas. a implementação de printBackward na seção 17.4. Existe um >>> import re paralelo natura entre a versão recursiva de printBackward e o algoritmo de pilha aqui. A diferenã é que printBackward usa a >>> re.split ("[^0-9]", "123+456*/") pilha de execução para XXXmanter a trilha(keep track) dos ['123', '+', '456', '*', '', '/', ' '] nós enquanto percorre a lista, e então imprime-a no retorno da Note que a ordem dos argumentos é diferente de recursão. o algoritmo de pilha faz a mesma coisa, exceto que string.split, o delimitador vem antes da string. usa o objeto Stack ao invés da pilha de execução. A lista resultante inclui os operandos 123 e 456, e os operadores * e /. Também inclui duas strings vazias que são inseridas depois dos operadores. 18.5 Usando uma pilha para avaliar expressões pós- fixas 18.7 Avaliando em pós-fixo. Em muitas linguagens de programação, expressões matemáticas são executadas com o poerador entre os roid Para avaliar uma expressão pós-fixa, usaremos o parser da operandos, como em 1 + 2. Este formato é chamado notação seção anterior e o algoritmo da seção anterior a ela. Para infixa. Uma alternativa usada por algumas calculadoras é manter as coisas simples, começaremos com um avaliador que chamada notação pós-fixa. Em notação pós-fixa, o operador implementa somente os operadores + e . segue os operandos, como em 1 2 +. def evalPostfix (expr) : A razão pela qual a notação pós-fixa é útil import re algumas vezes é que existe uma maneira natural de avaliar tokenList = re.split ("([^0-9])", expr) uma expressão pós-fixa usando uma pilha: stack = Stack() ● começando no início da expressão, peque um termo for token in tokenList (operador ou operando) de cada vez. if token == '' or token = ' ' : ● Se o termo é um operando, empilhe-o continue if token == '+' : ● Se o termo é um operador, desempilhe dois sum = stack.pop() + stack.pop() operandos, execute a operação neles, e empilhe o stack.push(sum) resultado. if token == '*' : ● Quando chegar ao fim da expressão, deve existir product = stack.pop() * stack.pop() exatamente um operando sobrando na pilha. Este stack.push(product) operando é o resultado. else: ● Como um exercício, aplique este algoritmo à stack.push(int(token)) expressão 1 2 + 3 * . return stack.pop() Este exemplo demonstra uma das vantagens da A primeira condição cuida de espaços e strings notação pós-fixa - não é necessário usar parênteses para vazias. As duas próximas condições manipulam os controlar a ordem das operações. Para ter o mesmo resultado operadores. Nós assumimos, agora que qualquer coisa é um em notação infixa, deveríamos escrever (1 + 2) * 3. operador. É claro, seria melhor chegar por entrada errônea e enviar uma mensagem de erro, mas faremos isto depois. Como um exercício, escreva a expressão pós- fixa que é equivalente a 1 + 2 * 3. Vamos testá-lo avaliando a forma pós-fixa de (56 + 47) * 2 >>> print evalPostfix("56 47 + 2 *") 18.6 Análise sintática 206 Para implementar o algoritmo anterior, necessitamos estar prontos para percorrer uma string e quebrá-la em operandos e 18.8 Clientes de fornecedores. operadores. Este processó é um exemplo de parsing, e o resultado - os pedaços da string - são chamados tokens. Você deve lembrar estas palavras do capítulo 1. Um dos objetivos de um TAD é separar os interesses do fornecedor, quem escreve o código que implementa o TAD, e Python fornece um método split nos módulos o cliente, que usa o TAD. O fornecedor tem que se preocupar string e re (expressões regulares). A função string.split separa apenas se a implementação está correta - de acordo com a uma string numa lista usando um único caracter como especificação do TAD - e não como ele será usado. delimitador. Por exemplo: Inversamente, o cliente assume que a >>> import string implementação do TAD está correta e não se preocupa com os >>> string.split ("Now is the time", " ") detalhes. Quando você está usando um tipo nativo do Python, ['Now', 'is', 'the', 'time'] tem o luxo de pensar exclusivamente como um cliente. Neste caso, o delimitador é o caracter de espaço, É claro, quanto você implementa um TAD, você então a string é dividida a cada espaço. também tem que escrever código cliente para testá-lo. Neste caso, você faz os dois papéis, o que pode ser confuso. Você Capítulo 18: Pilhas #85
  • 86. Como pensar como um cientista da Computação usando Python deve fazer algum esforçõ para manter a trilha do papel que está fazendo a cada momento. 18.9 Glossário tipo abstrato Um tipo de dado(geralmente uma coleção de dados de objetos) que é definidopor um conjunto (TAD) de operações, que podem ser implementadas (abstract data de várias maneiras. type(ADT)): interface É o conjunto de operações que definem um (interface): TDA. implementaçã Código que satisfaz(preenche?) os requisitos o sintáticos e semânticos de uma interface. (implementati on): cliente Um programa (ou o programador que o (client): escreveu) que faz utilização de um TDA. fornecedor Código (ou o programador que o escreveu) (provider): que implementa um TDA. revestimento Definição de classe que implementa um (veneer): TDA com definições de métodos que são chamadas a outros métodos, às vezes com pequenas modificações. A lâmina faz um trabalho insignificante, mas melhora ou padroniza a interface dada ao cliente. estrutura de Tipo de estrutura de dados que contem data dados de um tipo qualquer(tipo genérico). genérica (generic data structure): infixa (infix): Notação matemática em que os operadores se situam entre os operandos. pós-fixa Notação matemática em que os operadores (postfix): se situam após os operandos. parse (parse): Ler um conjunto de caracteres(string de caracteres) ou tokens(símbolos atômicos) e analisar sua estrutura gramatical. token (token): Conjunto de caracteres que são tratados como uma unidade atômica para fins de análise gramatical, como as palavras na linguagem natural. delimitador Um caracter que é utilizado para separar os (delimiter): símbolos atômicos(tokens), como a pontuação na linguagem natural. Capítulo 18: Pilhas #86
  • 87. Como pensar como um cientista da Computação usando Python Capítulo 19: Filas Este capítulo apresenta dois TDAs: Fila e Fila por Prioridade. else: Na nossa vida diária, fila é um alinhamento de consumidores # find the last node in the list aguardando algum tipo de serviço. Na maioria dos casos, o last = self.head primeiro da fila é o primeiro a ser atendido. Mas há exceções. No aeroporto, passageiros cujo vôo vai decolar logo, às vezes while last.next: last = last.next são chamados primeiro ao balcão do check-in, mesmo que # append the new node estejam no meio da fila. No supermercado, é comum na fila do last.next = node caixa alguém deixar passar na frente uma pessoa que chega à self.length = self.length + 1 fila só com um ou dois produtos na mão. A regra que determina quem é o próximo da fila def remove(self): chama-se política de enfileiramento. A política de cargo = self.head.cargo enfileiramento mais simples chama-se FIFO, sigla de first-in- self.head = self.head.next first-out: primeiro a entrar, primeiro a sair. A política de self.length = self.length - 1 enfileiramento mais geral é o enfileiramento por prioridade, em que se atribui uma prioridade a cada pessoa da fila e a que return cargo tiver maior prioridade vai primeiro, independente da sua Os métodos isEmpty e remove são idênticos aos ordem de chegada. Dizemos que essa é a política mais geral métodos isEmpty e removeFirst de LinkedList. O método de todas, porque a prioridade pode ser baseada em qualquer insert é novo e um pouco mais complicado. coisa: hora de partida do vôo; quantos produtos a pessoa vai passar pelo caixa; o grau de prestígio da pessoa. É claro que Queremos inserir novos itens no fim da lista. Se a fila estiver vazia, basta fazer head apontar ao novo nó. Se nem todas as políticas de enfileiramento são "justas", mas o que é justo depende do ponto de vista. não, percorremos a lista até o último nó e lá penduramos o novo nó. É possível identificar o último nó porque o seu O TDA Fila e o TDA Fila por Prioridade têm o atributo next é None. mesmo conjunto de operações. A diferença está na semântica das operações: a fila usa a política FIFO; e a fila por Existem duas invariantes para um objeto Fila prioridade (como o próprio nome sugere) usa a política de bem formado: o atributo length deve ser o número de nós na enfileiramento por prioridade. fila, e o último nó deve ter seu atributo next igual a None. Estude o método até ficar convencido de que ele preserva ambas invariantes. 19.1 Um TDA Fila 19.3 Características de performance O TDA Fila é definido pelas seguintes operações: __init__ Inicializar uma nova fila vazia. Quando invocamos um método, normalmente não estamos preocupados com os detalhes da sua implementação. Porém, insert Adicionar um novo item à fila. há um certo "detalhe" que pode ser bom conhecer: as características de performance do método. Quanto tempo leva, remove Remover e retornar um item da fila. O item e como o tempo de execução muda à medida em que aumenta retornado é o que foi adicionado primeiro. o número de itens da coleção? isEmpty Checar se a fila está vazia. Primeiro, olhe para remove. Não há laços ou chamadas de função aqui, o que sugere que o tempo de execução desse método é sempre o mesmo, toda vez. Um método assim é chamado de operação de tempo constante. 19.2 Fila encadeada Na verdade, o método pode ser ligeiramente mais rápido quando a lista está vazia, uma vez que ele pula o corpo da A primeira implementação que vamos ver de um TDA Fila condicional, mas essa diferença não é significativa. XXX: o chama-se fila encadeada porque é feita de objetos Nós condicional só aparece na re-implementação do método na encadeados. A definição da classe é a seguinte: classe ImprovedQueue, p.200; essa inconsistência pode ser conferida também nas páginas 198-199 do livro original (PDF class Queue: e impresso). def __init__(self): A performance de insert é muito diferente. No self.length = 0 caso geral, temos de percorrer a lista para achar o último self.head = None elemento. Este percurso leva um tempo proporcional à def isEmpty(self): extensão da lista. Uma vez que o tempo de execução é uma return (self.length == 0) função linear da extensão, dizemos que este método opera em tempo linear. Isso é bem ruim, se comparado com o tempo def insert(self, cargo): constante. node = Node(cargo) node.next = None if self.head == None: 19.4 Fila encadeada aprimorada # if list is empty the new node goes first self.head = node Queremos uma implementação do TDA Fila que possa Capítulo 19: Filas #87
  • 88. Como pensar como um cientista da Computação usando Python realizar todas as operações em tempo constante. Uma maneira de fazer isso é modificar a classe Fila, de modo que ela 19.5 Fila por prioridade mantenha a referência tanto ao primeiro quanto ao último nó, como mostra a figura: O TDA Fila por Prioridade tem a mesma interface que o TDA Fila, mas semântica diferente. Mais uma vez, a interface é a seguinte: __init__ Inicializar uma nova fila vazia. insert Adicionar um novo item à fila. remove Remover e retornar um item da fila. O item retornado é aquele que tiver maior prioridade. A implementação de ImprovedQueue tem essa isEmpty Checar se a fila está vazia. cara: A diferença semântica é que o item removido da class ImprovedQueue: fila não é necessariamente o que foi incluído primeiro e, sim, def __init__(self): o que tem maior prioridade. Que prioridades são essas e como self.length = 0 elas se comparam umas com as outras não é especificado pela implementação Fila por Prioridade. Isso depende de quais self.head = None itens estão na fila. self.last = None Por exemplo, se os itens da fila tiverem nome, def isEmpty(self): podemos escolhê-los por ordem alfabética. Se for a pontuação de um jogo de boliche, podemos ir da maior para a menor, return (self.length == 0) mas se for pontuação de golfe, teríamos que ir da menor para Até agora, a única mudança é o atributo last. Ele a maior. Se é possível comparar os itens da fila, é possível é usado nos métodos insert e remove: achar e remover o que tem maior prioridade. Essa implementação da Fila por Prioridade tem como atributo uma class ImprovedQueue: lista Python chamada items, que contém os itens da fila. # ... def insert(self, cargo): class PriorityQueue: node = Node(cargo) def __init__(self): node.next = None self.items = [] if self.length == 0: #if list is empty, the new node is head & last def isEmpty(self): self.head = self.last = node return self.items == [] else: # find the last node def insert(self, item): last = self.last self.items.append(item) # append the new node O método de inicialização, isEmpty, e insert são last.next = node apenas uma fachada para operações básicas de lista. O único método interessante é remove: self.last = node self.length = self.length + 1 class PriorityQueue: Uma vez que last não perde de vista o ultimo # ... nó, não é necessário buscá-lo. Como resultado, esse método def remove(self): tem tempo constante. maxi = 0 Mas essa rapidez tem preço. É preciso adicionar for i in range(1,len(self.items)): um caso especial a remove, para configurar last para None if self.items[i] > self.items[maxi]: quando o ultimo nó é removido: maxi = i item = self.items[maxi] class ImprovedQueue: self.items[maxi:maxi+1] = [] #... return item def remove(self): cargo = self.head.cargo No início de cada iteração, maxi armazena o índice do maior item (a prioridade mais alta de todas) que self.head = self.head.next vimos até agora. A cada volta do laço, o programa compara o self.length = self.length - 1 i-ésimo item ao campeão. Se o novo item for maior, maxi if self.length == 0: recebe o valor de i. self.last = None Quando o comando for se completa, maxi é o return cargo índice do maior item. Esse item é removido da lista e Essa implementação é mais complicada que a retornado. primeira, e mais difícil de se demonstrar que está correta. A vantagem é que o objetivo foi atingido -- tanto insert quanto Vamos testar a implementação: remove` são operações de tempo constante. >>> q = PriorityQueue() Como exercício, escreva uma implementação >>> q.insert(11) do TDA Fila usando uma lista nativa do Python. Compare a >>> q.insert(12) performance dessa implementação com a de ImprovedQueue, >>> q.insert(14) para filas de diversos comprimentos. >>> q.insert(13) >>> while not q.isEmpty(): print q.remove() 14 Capítulo 19: Filas #88
  • 89. Como pensar como um cientista da Computação usando Python 13 >>> pq = PriorityQueue() 12 >>> pq.insert(tiger) 11 >>> pq.insert(phil) Se a fila contém números ou strings simples, >>> pq.insert(hal) eles são removidas em ordem numérica decrescente ou >>> while not pq.isEmpty(): print pq.remove() alfabética invertida (de Z até A). Pyhton consegue achar o Tiger Woods : 61 maior inteiro ou string porque consegue compará-los usando Hal Sutton : 69 os operadores de comparação nativos. Phil Mickelson : 72 Se a fila contém objetos de outro tipo, os Como exercício, escreva uma implementação do objetos têm que prover um método __cmp__. Quando remove TDA Fila por Prioridade usando uma lista encadeada. usa o operador > para comparar dois itens, o método __cmp__ Mantenha a lista em ordem para que a remoção seja uma de um dos itens é invocado, recebendo o segundo item como operação de tempo constante. Compare a performance dessa argumento. Desde que o método __cmp__ funcione de forma implementação com a implementação usando uma lista consistente, a Fila por Prioridade vai funcionar. nativa do Python. 19.6 A classe Golfer 19.7 Glossário Como exemplo de um objeto com uma definição não-usual de fila (queue) Conjunto de objetos ordenados esperando prioridade, vamos implementar uma classe chamada Golfer algum tipo de serviço. (golfista), que mantém registro dos nomes e da pontuação de golfistas. Como sempre, começamos definindo __init__ e Fila (Queue) TAD (Tipo Abstrato de Dado) que realiza __str__: operações comuns de acontecerem em uma fila. class Golfer: def __init__(self, name, score): política de As regras que determinam qual membro self.name = name enfileiramento de uma fila é o próximo a ser removido. self.score= score (queueing policy) def __str__(self): FIFO "First In, First Out," (primeiro a entrar, return "%-16s: %d" % (self.name, self.score) primeiro a sair) política de enfileiramento O método __str__ usa o operador de formato em que o primeiro membro a chegar é o para colocar nomes e pontuações em colunas arrumadas. primeiro a ser removido. Em seguida, definimos uma versão de __cmp__, fila por Política de enfileiramento em que cada ma qual a pontuação mais baixa fica com prioridade máxima. prioridade membro tem uma prioridade, determinada Como sempre, __cmp__ retorna 1 se self é "maior que" other, (priority queue) por fatores externos. O membro com a -1 se self é "menor que" other, e 0 se eles são iguais. maior prioridade é o primeiro a ser removido. class Golfer: Fila por TAD que define as operações comuns de #... Prioridade acontecerem em uma fila por prioridade. def __cmp__(self, other): (Priority if self.score < other.score: return 1 # less Queue) is more fila encadeada Implementação de uma fila usando uma if self.score > other.score: return -1 (linked queue) lista encadeada. return 0 Agora estamos prontos para testar a fila por tempo constante Operação cujo tempo de execução não prioridade com a classe Golfer: (constant time) depende do tamanho da estrutura de dados. >>> tiger = Golfer("Tiger Woods", 61) tempo linear Operação cujo tempo de execução é uma >>> phil = Golfer("Phil Mickelson", 72) (linear time) função linear do tamanho da estrutura de >>> hal = Golfer("Hal Sutton", 69) dados. >>> Capítulo 19: Filas #89
  • 90. Como pensar como um cientista da Computação usando Python Capítulo 20: Árvores Como listas ligadas, árvores são constituídas de células. Uma espécie comum de árvores é a árvore binária, em que cada 20.1 Construindo árvores célula contém referências a duas outras células (possivelmente nulas). Tais referências são chamadas de subárvore esquerda e O processo de montar uma árvore é similar ao processo de direita. Como as células de listas ligadas, as células de árvores montar uma lista ligada. cada invocação do construtor cria também contém uma carga. Um diagrama de estados para uma uma célula. árvore pode aparecer assim: 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 parâmetros left e right devem ser células. left e right são opcionais; o valor default é None. Para imprimir uma célula, imprimimos apenas a sua carga. Uma forma de construir uma árvore é de baixo para cima. Aloque os filhos primeiro: left = Tree(2) right = Tree(3) Em seguida crie a célula pai e ligue ela a seus filhos: Para evitar a sobrecarga da figura, nós frequentemente omitimos os Nones. tree = Tree(1, left, right); Podemos escrever este código mais O topo da árvore (a célula à qual o apontador concisamente encaixando as invocações do construtor: tree se refere) é chamada de raiz. Seguindo a metáfora das árvores, as outras células são chamadas de galhos e as células >>> tree = Tree(1, Tree(2), Tree(3)) nas pontas contendo as referências vazia são chamadas de De qualquer forma, o resultado é a árvore que folhas. Pode parecer estranho que desenhamos a figura com a apareceu no início do capítulo. raiz em cima e as folhas em baixo, mas isto nem será a coisa mais estranha. Para piorar as coisas, cientistas da computação 20.2 Percorrendo árvores misturam outra metáfora além da metáfora biológica - a árvore genealógica. Uma célula superior pode ser chamada de pai e as células a que ela se refere são chamadas de seus filhos. Cada vez que Você vê uma nova estrutura de dados, sua Células com o mesmo pai são chamadas de irmãos. primeira pergunta deveria ser "Como eu percorro esta estrutura?" A forma mais natural de percorrer uma árvore é Finalmente, existe também o vocabulário fazer o percurso recursivamente. Por exemplo, se a árvore geométrico para falar de árvores. Já mencionamos esquerda e contém inteiros na carga, a função abaixo retorna a soma das direita, mas existem também as direções "para cima" (na cargas: direção da raiz) e "para baixo" (na direção dos filhos/folhas). Ainda nesta terminologia, todas as células situadas à mesma def total(tree) : distância da raiz constituem um nível da árvore. if tree == None : return 0 return total(tree.left) + total(tree.right) + Provavelmente não precisamos de três metáforas para falar de árvores, mas aí elas estão. tree.cargo O caso base é a árvore vazia, que não contém Como listas ligadas, árvores são estruturas de nenhuma carga, logo a soma das cargas é 0. O passo recursivo dados recursivas já que elas são definidas recursivamente: faz duas chamadas recursivas para achar a soma das cargas das subárvores dos filhos. Ao finalizar a chamada recursiva, Uma árvore é adicionamos a carga do pai e devolvemos o valor total. ● a árvore vazia, representada por None, ou ● uma célula que contém uma referência a um objeto (a carga da célula) e duas referências a árvores. 20.3 Árvores de expressões Uma árvore é uma forma natural para representar a estrutura de uma expressão. Ao contrário de outras notações, a árvore pode representar a computação de forma não ambígua. Por exemplo, a expressão infixa 1 + 2 * 3 é ambígua, a menos que Capítulo 20: Árvores #90
  • 91. Como pensar como um cientista da Computação usando Python saibamos que a multiplicação é feita antes da adição. árvore numa ordem diferente, Você produzirá expressões numa notação diferente. Por exemplo, se Você imprime A árvore de expressão seguinte representa a subárvores primeiro e depois a raiz, Você terá: mesma computação: def printTreePostorder(tree) : if tree == None : return printTreePostorder(tree.left) printTreePostorder(tree.right) print tree.cargo, O resultado, 1 2 3 * +, está na notação pósfixa! Esta ordem de percurso é chamada de pósordem. Finalmente, para percorrer uma árvore em inordem, Você imprime a subárvore esquerda, depois a raiz e depois a subárvore direita: def printTreeInorder(tree) : if tree == None : return printTreeInorder(tree.left) print tree.cargo, printTreeInorder(tree.right) O resultado é 1 + 2 * 3, que é a expressão na notação infixa. Para sermos justos, devemos lembrar que As células de uma árvore de expressão podem acabamos de omitir uma complicação importante. algumas ser operandos como 1 e 2 ou operações como + e *. As células vezes quando escrevemos expressões na notação infixa contendo operandos são folhas; aquelas contendo operações devemos usar parêntesis para prescrever a ordem das devem ter referências aos seus operandos. (Todos os nossos operações. Ou seja, um percurso em inordem não é suficiente operandos são binários, significando que eles tem exatamente para gerar a expressão infixa. dois operandos.) Ainda assim, com alguns aperfeiçoamentos, a Podemos construir árvores assim: árvore de expressão e os três modos recursivos de percurso resultam em algoritmos para transformar expressões de uma >>> tree = Tree('+', Tree(1), Tree('*', Tree(2), notação para outra. Tree(3))) Examinando a figura, não há dúvida quanto à Como um exercício, modifique ordem das operações; a multiplicação é feita primeiro para `printTreeInorder` de modo que ele coloque parêntesis em calcular o segundo operando da adição. volta de cada operador e par de operandos. A saída é correta e não ambígua? Os parêntesis são sempre necessários? Árvores de expressão tem muitos usos. O exemplo neste capítulo usa árvores para traduzir expressões Se percorrermos uma árvore em inordem e para as notações pósfixa, prefixa e infixa. Árvores similares acompanharmos em qual nível na árvore estamos, podemos são usadas em compiladores para analisar sintaticamente, gerar uma representação gráfica da árvore: otimizar e traduzir programas. def printTreeIndented(tree, level=0) : if tree == None : return printTreeIndented(tree.right, level+1) 20.4 Percurso de árvores print ' '*level + str(tree.cargo) printTreeIndented(tree.left, level+1) Podemos percorrer uma árvore de expressão e imprimir o seu O parâmetro level registra aonde estamos na conteúdo como segue: árvore. Por 'default', o nível inicialmente é zero. A cada def printTree(tree) : chamada recursiva repassamos level+1 porque o nível do filho if tree == None : return é sempre um a mais do que o nível do pai. Cada item é indentado dois espaços por nível. Para o nosso exemplo print tree.cargo, obtemos: printTree(tree.left) printTree(tree.right) >>> printTreeIndented(tree) Em outras palavras, para imprimir uma árvore, 3 imprima primeiro o conteúdo da raiz, em seguida imprima * toda a subárvore esquerda e finalmente imprima toda a 2 subárvore direita. Esta forma de percorrer uma árvore é + chamada de préordem, porque o conteúdo da raiz aparece 1 antes dos conteúdos dos filhos. Para o exemplo anterior, a saída é: Se Você deitar a saída acima Você enxerga uma versão simplificada da figura original. >>> tree = Tree('+', Tree(1), Tree('*', Tree(2), Tree(3))) >>> printTree(tree) 20.5 Construindo uma árvore de expressão + 1 * 2 3 Esta notação é diferente tanto da notação Nesta seção analisamos expressões infixas e construímos as pósfixa quanto da infixa; é uma notação chamada de prefixa, árvores de expressã correspondentes. Por exemplo, para a em que os operadores aparecem antes dos seus operandos. expressão (3+7)*9 resultará a seguinte árvore: Você pode suspeitar que se Você percorre a Capítulo 20: Árvores #91
  • 92. Como pensar como um cientista da Computação usando Python Em seguida precisaremos da função getProduct, que constrói uma árvore de expressão para produtos. Os dois operandos de um produto simples são números, como em 3 * 7. Segue uma versão 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ó célula atribuímos o primeiro operando a à`. Se o próximo caractere for *, vamos Note que simplificamos o diagrama omitindo os buscar o segundo número e construir a árvore com a, b e o nomes dos campos. operador. O analisador que escreveremos aceitará Se o caractere seguinte for qualquer outra coisa, expressões que incluam números, parêntesis e as operações + então simplesmente devolvemos uma célula folha com a. e *. Vamos supor que a cadeia de entrada já foi tokenizada Seguem dois exemplos: numa lista do Python. A lista de tokens para a expressão (3+7)*9 é: >>> tokenList = [9, '*', 11, 'end'] >>> tree = getProduct(tokenList) ['(', 3, '+', 7, ')', '*', 9, 'end'] >>> printTreePostorder(tree) O token final end é prático para prevenir que o 9 11 * analisador tente buscar mais dados após o término da lista. A título de um exercício, escreva uma função que recebe uma expressão na forma de uma cadeia e >>> tokenList = [9, '+', 11, 'end'] devolve a lista de tokens. >>> tree = getProduct(tokenList) A primeira função que escreveremos é getToken >>> printTreePostorder(tree) que recebe como parâmetros uma lista de tokens e um token 9 esperado. Ela compara o token esperado com o o primeiro O segundo exemplo sugere que nós token da lista: se eles batem a função remove o token da lista e consideramos um operando unitário como uma espécie de devolve um valor verdadeiro, caso contrário a função devolve produto. Esta definição de "produto" talvez não seja intuitiva, um valor falso: mas ela será útil. def getToken(tokenList, expected) : Agora tratamos produtos compostos, como 3 * 5 if tokenList[0] == expected : * 13. Encaramos esta expressão como um produto de tokenList[0:1] = [] # remove the token produtos, mais precisamente como 3 * (5 * 13). A árvore resultante é: return 1 else : return 0 Já que tokenList refere a um objeto mutável, as alterações feitas aqui são visíveis para qualquer outra variável que se refira ao mesmo objeto. A próxima função, getNumber, trata de operandos. Se o primeiro token na tokenList for um número então getNumber o remove da lista e devolve uma célula folha contendo o número; caso contrário 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, convém testar getNumber isoladamente. Atribuímos uma lista de números a tokenList, extraímos o primeiro, imprimimos o resultado e imprimimos o Com uma pequena alteração em getProduct, que resta na lista de tokens: podemos acomodar produtos arbitrariamente longos: >>> tokenList = [9, 11, 'end'] def getProduct(tokenList) : >>> x = getNumber(tokenList) a = getNumber(tokenList) >>> printTreePostorder(x) if getToken(tokenList, '*') : 9 b = getProduct(tokenList) # this line changed >>> print tokenList return Tree('*', a, b) [11, 'end'] else : Capítulo 20: Árvores #92
  • 93. Como pensar como um cientista da Computação usando Python return a idéia dar a getNumber um nome mais descritivo do seu novo Em outras palavras, um produto pode ser um papel. singleton ou uma árvore com * na raiz, que tem um número como filho esquerdo e um produto como filho direito. Este tipo de definição recursiva devia começar a ficar familiar. 20.6 Manipulando erros Testemos a nova versão com um produto composto: Ao longo do analisador sintático tínhamos suposto que as expressões (de entrada) são bem formadas. Por exemplo, >>> tokenList = [2, '*', 3, '*', 5 , '*', 7, 'end'] quando atingimos o fim de uma subexpressão, supomos que o >>> tree = getProduct(tokenList) próximo caractere é um facha parêntesis. Caso haja um erro e >>> printTreePostorder(tree) o próximo caractere seja algo diferente, devemos tratar disto. 2 3 5 7 * * * def getNumber(tokenList) : A seguir adicionamos o tratamento de somas. if getToken(tokenList, '(') : De novo, usamos uma definição de "soma" que é ligeiramente não intuitiva. Para nós, uma soma pode ser uma árvore com + x = getSum(tokenList) na raiz, que tem um produto como filho esquerdo e uma soma if not getToken(tokenList, ')'): como filho direito. Ou, uma soma pode ser simplesmente um raise 'BadExpressionError', produto. 'missing parenthesis' Se Você está disposto a brincar com esta return x definição, ela tem uma propriedade interessante: podemos else : representar qualquer expressão (sem parêntesis) como uma # the rest of the function omitted soma de produtos. Esta propriedade é a base do nosso O comando raise cria uma exceção; neste caso algoritmo de análise sintática. criamos um novo tipo de exceção, chamada de getSum tenta construir a árvore com um produto BadExpressionError. Se ano traceback, manipular a exceção, uma das outras funções função que chamou getNumber, ou à esquerda e uma soma à direita. Mas, se ele não encontra uma então o programa pode continuar. caso contrário Python vai +, ele simplesmente constrói um produto. imprimir uma mensagem de erro e terminará o processamento def getSum(tokenList) : em seguida. a = getProduct(tokenList) A título de exercício, encontre outros locais nas if getToken(tokenList, '+') : funções criadas onde erros possam ocorrer e adiciona b = getSum(tokenList) comandos ``raise`` apropriados. Teste seu código com return Tree('+', a, b) expressões mal formadas. else : return a Vamos testar o algoritmo com 9 * 11 + 5 * 7: 20.7 A árvore dos animais >>> tokenList = [9, '*', 11, '+', 5, '*', 7, 'end'] Nesta seção, desenvolvemos um pequeno programa que usa >>> tree = getSum(tokenList) uma árvore para representar uma base de conhecimento. >>> printTreePostorder(tree) 9 11 * 5 7 * + O programa interage com o usuário para criar uma árvore de perguntas e de nomes de animais. Segue uma Quase terminamos, mas ainda temos que tratar amostra da funcionalidade: dos parêntesis. Em qualquer lugar numa expressão onde podemos ter um número, podemos também ter uma soma Are you thinking of an animal? y inteira envolvida entre parêntesis. Precisamos, apenas, Is it a bird? n modificar getNumber para que ela possa tratar de What is the animals name? dog subexpressões: What question would distinguish a dog from a bird? def getNumber(tokenList) : Can it fly if getToken(tokenList, '(') : If the animal were dog the answer would be? n x = getSum(tokenList) # get subexpression getToken(tokenList, ')') #eat closing parenthesis Are you thinking of an animal? y return x Can it fly? n else : Is it a dog? n x = tokenList[0] What is the animals name? cat if type(x) != type(0) : return None What question would distinguish a cat from a dog? tokenList[0:1] = [] # remove the token Does it bark return Tree(x, None, None) # return a leaf If the animal were cat the answer would be? n with the number Testemos este código com 9 * (11 + 5) * 7: Are you thinking of an animal? y Can it fly? n >>> tokenList = [9, '*', '(', 11, '+', 5, ')', '*', Does it bark? y 7, 'end'] Is it a dog? y >>> tree = getSum(tokenList) I rule! >>> printTreePostorder(tree) 9 11 5 + 7 * * Are you thinking of an animal? n O analisador tratou os parêntesis corretamente; a Aqui está a árvore que este diálogo constrói: adição é feita antes da multiplicação. Na versão final do programa, seria uma boa Capítulo 20: Árvores #93
  • 94. Como pensar como um cientista da Computação usando Python tree.setLeft(Tree(animal)) tree.setRight(Tree(guess)) A função yes é um auxiliar; ele imprime um prompt e em seguida solicita do usuário uma entrada. Se a resposta começar com y ou Y, a função devolve um valor verdadeiro: def yes(ques) : from string import lower ans = lower(raw_input(ques)) return (ans[0:1] == 'y') A condição do laço externo é 1`, que significa que ele continuará até a execução de um comando break, caso o usuário não pense num animal. No começo de cada rodada, o programa parte do topo da árvore e faz a primeira pergunta. Dependendo da O laço while interno caminha na árvore de cima resposta, ele segue pelo filho esquerdo ou direito e continua para baixo, guiado pelas respostas do usuário. até chegar numa folha. Neste ponto ele arrisca um palpite. Se o palpite não for correto, ele pergunta ao usuário o nome de Quando uma nova célula é adicionada à árvore, um novo animal e uma pergunta que distingue o palpite errado a nova pergunta substitui a carga e os dois filhos são o novo do novo animal. A seguir, adiciona uma célula à árvore animal e a carga original. contendo a nova pergunta e o novo animal. Uma falha do programa é que ao sair ele Aqui está o código: esquece tudo que lhe foi cuidadosamente ensinado! def animal() : A título de exercício, pense de várias jeitos para salvar a árvore do conhecimento acumulado num arquivo. # start with a singleton Implemente aquele que Você pensa ser o mais fácil. root = Tree("bird") # loop until the user quits while 1 : 20.8 Glossário print if not yes("Are you thinking of an animal? ") : árvore binária Uma árvore em que cada célula tem zero, break (binary tree) um ou dois descendentes. raiz (root) A célula mais alta de uma árvore, a (única) # walk the tree célula de uma árvore que não tem pai. tree = root folha (leaf) Uma célula mais baixa numa árvore; uma while tree.getLeft() != None : célula que não tem descendentes. prompt = tree.getCargo() + "? " if yes(prompt): pai (parent) A célula que aponta para uma célula dada. tree = tree.getRight() filho (child) Uma célula apontada por uma célula dada. else: tree = tree.getLeft() irmãos Células que tem o mesmo pai. (siebling) # make a guess nível (level) Um conjunto de células equidistantes da guess = tree.getCargo() raiz. prompt = "Is it a " + guess + "? " operador Um operador sobre dois operandos. if yes(prompt) : binário print "I rule!" (binary continue operator) subexpressão Uma expressão entre parêntesis que se # get new information (subexpressio comporta como um operando simples numa prompt = "What is the animal's name? " n) expressão maior. animal = raw_input(prompt) pré-ordem Uma forma de percorrer uma árvore prompt = "What question would distinguish a %s (preorder) visitando cada célula antes dos seus filhos. from a %s? " question = raw_input(prompt % (animal,guess)) notação Uma forma de escrever uma expressão prefixa (prefix matemática em que cada operador aparece notation) antes dos seus operandos. # add new information to the tree tree.setCargo(question) pós-ordem Uma forma de percorrer uma árvore prompt = "If the animal were %s the answer would (postorder) visitando os filhos de cada célula antes da be? " própria célula. if yes(prompt % animal) : in-ordem Uma forma de percorrer uma árvore tree.setLeft(Tree(guess)) (inorder) visitando a subárvore esquerda, seguida da tree.setRight(Tree(animal)) raiz e finalmente da subárvore direita. else : Capítulo 20: Árvores #94
  • 95. Como pensar como um cientista da Computação usando Python GNU Free Documentation License Version 1.2, November 2002 matters, or of legal, commercial, philosophical, ethical or political position regarding them. Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. The "Invariant Sections" are certain Secondary 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 Sections whose titles are designated, as being those of USA Invariant Sections, in the notice that says that the Document is Everyone is permitted to copy and distribute released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated verbatim copies as Invariant. The Document may contain zero Invariant of this license document, but changing it is not Sections. If the Document does not identify any Invariant allowed. Sections then there are none. The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, 0. PREAMBLE in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a The purpose of this License is to make a manual, textbook, or Back-Cover Text may be at most 25 words. other functional and useful document "free" in the sense of A "Transparent" copy of the Document means a freedom: to assure everyone the effective freedom to copy and machine-readable copy, represented in a format whose redistribute it, with or without modifying it, either specification is available to the general public, that is suitable commercially or noncommercially. Secondarily, this License for revising the document straightforwardly with generic text preserves for the author and publisher a way to get credit for editors or (for images composed of pixels) generic paint their work, while not being considered responsible for programs or (for drawings) some widely available drawing modifications made by others. editor, and that is suitable for input to text formatters or for This License is a kind of "copyleft", which automatic translation to a variety of formats suitable for input means that derivative works of the document must themselves to text formatters. A copy made in an otherwise Transparent be free in the same sense. It complements the GNU General file format whose markup, or absence of markup, has been Public License, which is a copyleft license designed for free arranged to thwart or discourage subsequent modification by software. readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy We have designed this License in order to use it that is not "Transparent" is called "Opaque". for manuals for free software, because free software needs free documentation: a free program should come with manuals Examples of suitable formats for Transparent providing the same freedoms that the software does. But this copies include plain ASCII without markup, Texinfo input License is not limited to software manuals; it can be used for format, LaTeX input format, SGML or XML using a publicly any textual work, regardless of subject matter or whether it is available DTD, and standard-conforming simple HTML, published as a printed book. We recommend this License PostScript or PDF designed for human modification. principally for works whose purpose is instruction or Examples of transparent image formats include PNG, XCF reference. and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated 1. APPLICABILITY AND DEFINITIONS HTML, PostScript or PDF produced by some word processors for output purposes only. This License applies to any manual or other work, in any The "Title Page" means, for a printed book, the medium, that contains a notice placed by the copyright holder title page itself, plus such following pages as are needed to saying it can be distributed under the terms of this License. hold, legibly, the material this License requires to appear in Such a notice grants a world-wide, royalty-free license, the title page. For works in formats which do not have any unlimited in duration, to use that work under the conditions title page as such, "Title Page" means the text near the most stated herein. The "Document", below, refers to any such prominent appearance of the work's title, preceding the manual or work. Any member of the public is a licensee, and beginning of the body of the text. is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission A section "Entitled XYZ" means a named under copyright law. subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates A "Modified Version" of the Document means XYZ in another language. (Here XYZ stands for a specific any work containing the Document or a portion of it, either section name mentioned below, such as "Acknowledgements", copied verbatim, or with modifications and/or translated into "Dedications", "Endorsements", or "History".) To "Preserve another language. the Title" of such a section when you modify the Document A "Secondary Section" is a named appendix or a means that it remains a section "Entitled XYZ" according to front-matter section of the Document that deals exclusively this definition. with the relationship of the publishers or authors of the The Document may include Warranty Document to the Document's overall subject (or to related Disclaimers next to the notice which states that this License matters) and contains nothing that could fall directly within applies to the Document. These Warranty Disclaimers are that overall subject. (Thus, if the Document is in part a considered to be included by reference in this License, but textbook of mathematics, a Secondary Section may not only as regards disclaiming warranties: any other implication explain any mathematics.) The relationship could be a matter that these Warranty Disclaimers may have is void and has no of historical connection with the subject or with related effect on the meaning of this License. GNU Free Documentation License #95
  • 96. Como pensar como um cientista da Computação usando Python those of previous versions (which should, if there 2. VERBATIM COPYING were any, be listed in the History section of the Document). You may use the same title as a previous You may copy and distribute the Document in any medium, version if the original publisher of that version gives either commercially or noncommercially, provided that this permission. License, the copyright notices, and the license notice saying • B. List on the Title Page, as authors, one or more this License applies to the Document are reproduced in all persons or entities responsible for authorship of the copies, and that you add no other conditions whatsoever to modifications in the Modified Version, together with those of this License. You may not use technical measures to at least five of the principal authors of the Document obstruct or control the reading or further copying of the copies (all of its principal authors, if it has fewer than five), you make or distribute. However, you may accept unless they release you from this requirement. compensation in exchange for copies. If you distribute a large • C. State on the Title page the name of the publisher enough number of copies you must also follow the conditions of the Modified Version, as the publisher. in section 3. • D. Preserve all the copyright notices of the Document. You may also lend copies, under the same • E. Add an appropriate copyright notice for your conditions stated above, and you may publicly display copies. modifications adjacent to the other copyright notices. • F. Include, immediately after the copyright notices, a license notice giving the public permission to use the 3. COPYING IN QUANTITY Modified Version under the terms of this License, in the form shown in the Addendum below. If you publish printed copies (or copies in media that • G. Preserve in that license notice the full lists of commonly have printed covers) of the Document, numbering Invariant Sections and required Cover Texts given in more than 100, and the Document's license notice requires the Document's license notice. Cover Texts, you must enclose the copies in covers that carry, • H. Include an unaltered copy of this License. clearly and legibly, all these Cover Texts: Front-Cover Texts • I. Preserve the section Entitled "History", Preserve its on the front cover, and Back-Cover Texts on the back cover. Title, and add to it an item stating at least the title, Both covers must also clearly and legibly identify you as the year, new authors, and publisher of the Modified publisher of these copies. The front cover must present the full Version as given on the Title Page. If there is no title with all words of the title equally prominent and visible. section Entitled "History" in the Document, create You may add other material on the covers in addition. one stating the title, year, authors, and publisher of Copying with changes limited to the covers, as long as they the Document as given on its Title Page, then add an preserve the title of the Document and satisfy these item describing the Modified Version as stated in the conditions, can be treated as verbatim copying in other previous sentence. respects. • J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of If the required texts for either cover are too the Document, and likewise the network locations voluminous to fit legibly, you should put the first ones listed given in the Document for previous versions it was (as many as fit reasonably) on the actual cover, and continue based on. These may be placed in the "History" the rest onto adjacent pages. section. You may omit a network location for a work If you publish or distribute Opaque copies of the that was published at least four years before the Document numbering more than 100, you must either include Document itself, or if the original publisher of the a machine-readable Transparent copy along with each Opaque version it refers to gives permission. copy, or state in or with each Opaque copy a computer- • K. For any section Entitled "Acknowledgements" or network location from which the general network-using public "Dedications", Preserve the Title of the section, and has access to download using public-standard network preserve in the section all the substance and tone of protocols a complete Transparent copy of the Document, free each of the contributor acknowledgements and/or of added material. If you use the latter option, you must take dedications given therein. reasonably prudent steps, when you begin distribution of • L. Preserve all the Invariant Sections of the Opaque copies in quantity, to ensure that this Transparent Document, unaltered in their text and in their titles. copy will remain thus accessible at the stated location until at Section numbers or the equivalent are not considered least one year after the last time you distribute an Opaque part of the section titles. copy (directly or through your agents or retailers) of that • M. Delete any section Entitled "Endorsements". Such edition to the public. a section may not be included in the Modified Version. It is requested, but not required, that you contact • N. Do not retitle any existing section to be Entitled the authors of the Document well before redistributing any "Endorsements" or to conflict in title with any large number of copies, to give them a chance to provide you Invariant Section. with an updated version of the Document. • O. Preserve any Warranty Disclaimers. If the Modified Version includes new front- matter sections or appendices that qualify as Secondary 4. MODIFICATIONS Sections and contain no material copied from the Document, you may at your option designate some or all of these sections You may copy and distribute a Modified Version of the as invariant. To do this, add their titles to the list of Invariant Document under the conditions of sections 2 and 3 above, Sections in the Modified Version's license notice. These titles provided that you release the Modified Version under must be distinct from any other section titles. precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and You may add a section Entitled modification of the Modified Version to whoever possesses a "Endorsements", provided it contains nothing but copy of it. In addition, you must do these things in the endorsements of your Modified Version by various parties-- Modified Version: for example, statements of peer review or that the text has been approved by an organization as the authoritative • A. Use in the Title Page (and on the covers, if any) a definition of a standard. title distinct from that of the Document, and from GNU Free Documentation License #96
  • 97. Como pensar como um cientista da Computação usando Python You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back- 7. AGGREGATION WITH INDEPENDENT Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and WORKS one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document A compilation of the Document or its derivatives with other already includes a cover text for the same cover, previously separate and independent documents or works, in or on a added by you or by arrangement made by the same entity you volume of a storage or distribution medium, is called an are acting on behalf of, you may not add another; but you may "aggregate" if the copyright resulting from the compilation is replace the old one, on explicit permission from the previous not used to limit the legal rights of the compilation's users publisher that added the old one. beyond what the individual works permit. When the Document is included in an aggregate, this License does not The author(s) and publisher(s) of the Document apply to the other works in the aggregate which are not do not by this License give permission to use their names for themselves derivative works of the Document. publicity for or to assert or imply endorsement of any Modified Version. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket 5. COMBINING DOCUMENTS the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. You may combine the Document with other documents Otherwise they must appear on printed covers that bracket the released under this License, under the terms defined in section whole aggregate. 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that 8. TRANSLATION you preserve all their Warranty Disclaimers. Translation is considered a kind of modification, so you may The combined work need only contain one copy distribute translations of the Document under the terms of of this License, and multiple identical Invariant Sections may section 4. Replacing Invariant Sections with translations be replaced with a single copy. If there are multiple Invariant requires special permission from their copyright holders, but Sections with the same name but different contents, make the you may include translations of some or all Invariant Sections title of each such section unique by adding at the end of it, in in addition to the original versions of these Invariant Sections. parentheses, the name of the original author or publisher of You may include a translation of this License, and all the that section if known, or else a unique number. Make the same license notices in the Document, and any Warranty adjustment to the section titles in the list of Invariant Sections Disclaimers, provided that you also include the original in the license notice of the combined work. English version of this License and the original versions of In the combination, you must combine any those notices and disclaimers. In case of a disagreement sections Entitled "History" in the various original documents, between the translation and the original version of this License forming one section Entitled "History"; likewise combine any or a notice or disclaimer, the original version will prevail. sections Entitled "Acknowledgements", and any sections If a section in the Document is Entitled Entitled "Dedications". You must delete all sections Entitled "Acknowledgements", "Dedications", or "History", the "Endorsements." requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title. 6. COLLECTIONS OF DOCUMENTS 9. TERMINATION You may make a collection consisting of the Document and other documents released under this License, and replace the You may not copy, modify, sublicense, or distribute the individual copies of this License in the various documents Document except as expressly provided for under this with a single copy that is included in the collection, providedLicense. Any other attempt to copy, modify, sublicense or that you follow the rules of this License for verbatim copying distribute the Document is void, and will automatically of each of the documents in all other respects. terminate your rights under this License. However, parties You may extract a single document from such a who have received copies, or rights, from you under this collection, and distribute it individually under this License, License will not have their licenses terminated so long as such provided you insert a copy of this License into the extracted parties remain in full compliance. document, and follow this License in all other respects regarding verbatim copying of that document. 10. FUTURE REVISIONS OF THIS LICENSE The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a GNU Free Documentation License #97
  • 98. Como pensar como um cientista da Computação usando Python version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation. How to use this License for your documents To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page: Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License". If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, replace the "with...Texts." line with this: with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back- Cover Texts being LIST. If you have Invariant Sections without Cover Texts, or some other combination of the three, merge those two alternatives to suit the situation. If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. GNU Free Documentation License #98