sexta-feira, 16 de outubro de 2009

De que massa são feitos os programas

Programar é uma actividade que nos obriga a usar mecanismos de abstracção. Deste modo produzimos código modular, reutilizável e capaz de resolver problemas mais gerais. Um dos mecanismos fundamentais de abstracção em programação são as definições. Têm a forma geral:




def [nome]([lista de parâmetros]):
[corpo]


É pois preciso usar a palavra reservada def seguida do nome que demos ao programa, seguido de um parênteses de abertura, seguido da lista dos parâmetros- eventualmente vazia, separados por vírgulas, seguida do parênteses de fecho, seguida de dois pontos. Uff!! Isto constitui o chamado cabeçalho da definição. Na linha seguinte, e avançado uma posições para a direita, vem o corpo do programa. Sem isto a definição está mal feita e dará erro de sintaxe. Ao criarmos uma definição, internamente é feita a associação do nome do programa com um objecto que representa a definição. É pois semelhante ao que se passa quando usamos a instrução de atribuição. A listagem que se segue ilustra esse facto.



>>> def dobro(numero):
... """ Calcula o dobro do múmero. """
... return 2 * numero
...
>>> dobro
<function dobro at 0xccf870>
>>> id(dobro)
13432944
>>> type(dobro)
<type 'function'>
>>>


Uma definição é um objecto!

Mas não basta definir é preciso usar!!! Isso faz-se chamando o programa pelo seu nome (a definição neste caso) e associando aos parâmetros os argumentos (este últimos, colocados de forma ordenada e separados por vírgulas, entre parênteses e a seguir ao nome da definição). Os argumentos podem ser objectos ou nomes associados a objectos:



>>> dobro(5)
10
>>> x=7
>>> dobro(7)
14
>>>


Também aqui o processo de ligação dos objectos aos parâmetros, ou dos nomes de objectos aos parâmetros, é feito por associação entre identidades. No caso de eu fazer dobro(5) é o objecto 5 que fica associado localmente na definição, ao nome numero; no caso da chamada dobro(x) é na mesma a identidade do objecto, que está associado ao nome x, que é comunicada (associada) ao parâmetro numero.


Mas repetimos à exaustão: é preciso definir e usar as definições. Senão nada se passará (definir sem usar) ou teremos um erro (usar sem estar definido).


Mas regressemos ao corpo dos programas. São formados essencialmente por instruções que vão, durante a execução, alterando o estado da computação. Podem ser divididas em dois grandes grupos: as que alteram a memória e as instruções de controlo. As instruções que alteram a memória são de três tipos: a atribuição, entrada e saída. As de controlo subdividem-se em instruções sequenciais, condicionais e de repetição.

A instrução de atribuição já é uma velha conhecida. O mesmo se passa com a instrução de entrada input() e o comando de saída print. No caso das instruções de controlo, a sequência, em Python é dada implicitamente pelo alinhamento das instruções. Assim duas instruções alinhadas e consecutivas no código são executadas em sequência. Já as instruções condicionais têm três variedades: uma via, duas vias ou vias múltiplas. A listagem ilustra as três alternativas.



# uma via
if [condição]:
[corpo-sim]

# duas vias
if [condição]:
[corpo-sim]
else:
[corpo-não]

# vias múltiplas
if [condição]:
[corpo-1]
elif [condição]:
[corpo-2]
...
elif [condição]:
[corpo-n]
else:
[corpo-final]


No primeiro caso, é executado as instruções do [corpo-sim] apenas se a [condição] for verdadeira. No segundo caso, é executado um dos corpos de acordo com o valor lógico da [condição]. Finalmente, no terceiro caso, é executado o corpo correspondente à primeira (pela ordem de escrita) condição que é verdadeira. Se nenhuma for verdadeira é executada incondicionalmente a [condição-final].


Terminemos com as repetições que podem ser de dois tipos: fixas ou variáveis.


# fixo
for [nome] in [objecto-iterável]:
[corpo]
# variável
while [condição]:
[corpo]


As repetições fixas são executadas em geral um número fixo de vezes. Na repetição de ordem n o [nome] fica associado ao objecto de ordem n do [objecto-iterável]. Cadeias de caracteres, listas, dicionários, tuplos e ficheiros são exemplos de tipos de objectos iteráveis. No caso dos ciclos variáveis, em geral, o [corpo] só é executado de forma repetida enquanto a [condição] for verdadeira. Exemplos triviais:


# fixo
for i in [0,1,2]:
print i
# variável
j = 3
while j <5
print j
j = j + 1


Notar que na repetição variável algo tem que modificar a situação da condição, sob pena de podermos ter um ciclo eterno.
Porque dizemos acima em geral? Porque em Python, existem algumas instruções que quebram a ordem natural de execução. Por exemplo, é possível abandonar um ciclo for antes de ele completar o número de repetições previsto. Mas isso fica para outra altura...

5 comentários:

  1. Boa tarde professor,

    Nos casos condicionais, o elif não tem condição? porquê que quando temos muitas condições utilizamos vários if e não um if e elif? (claro esta que depende tambem do programa!) mas qual é a grande diferença?

    Obrigado,
    Saudações.

    ResponderEliminar
  2. Tem sim senhor. Foi um lapso meu, que acabo de corrigir. Obrigado.

    ResponderEliminar
  3. A diferença é que isto
    if [condicao]:
    . [instruções]
    if [outra condição]
    . [instruções]
    vai sempre fazer as duas comparações. Se fizesses com elif esse elif só ia ser executado caso o primeiro if fosse falso.

    Na prática usa-se quando só queres fazer outros if sse os anteriores forem falsos.

    eg: dada uma nota o programa devolve se passou, se chumbou ou se vai provar que merece mais de 16 ou lá como isso funciona.

    def ola(a)
    .if a < 9.5:
    ..return "Reprovado"
    .elif a >= 9.5 and a < 16 #nao te interessa fazer esta condição se a primeira for verdadeira, certo?
    ..return "Aprovado"
    .elif a >= 16 #nem esta, certo?
    ..return "Aprovado mas coiso"

    Penso que era esta a tua dúvida e espero estar bem.

    ResponderEliminar