quarta-feira, 26 de outubro de 2016

Devagar se vai ao longe

Uma das questões mais frequentes com que um programador novato se depara é o de saber como dominar a complexidade de um problema. Vamos tentar ajudar a ultrapassar essa dificuldade recorrendo a dois princípios simples: aproximar a solução por etapas e esquecer inicialmente alguns aspectos do problema que serão resolvidos posteriormente.

Suponhamos que nos pedem para escrever um programa que nos permita visualizar de modo natural uma matriz, mas não apenas os seus valores mas também a posição de cada elemento na matriz. Um exemplo seria:
Vamos então à solução. Comecemos por tomar uma decisão simples e razoável: mostrar a matriz linha a linha:
def mostra_mat(matriz):
    num_linhas = len(matriz)
    for linha in range(num_linhas):
        # mostra linha
        pass
(a instrução pass… não faz nada, mas permite que o programa esteja sintaticamente correcto!). Parece que não avançamos muito, mas também temos segurança sobre a correcção deste programa. Temos agora que tomar uma segunda decisão: como mostrar uma linha? E decidimos fazê-lo... coluna a coluna!
def mostra_mat(matriz):
    num_linhas = len(matriz)
    num_colunas = len(matriz[0])
    for linha in range(num_linhas):
        # mostra linha
        for coluna in range(num_colunas):
            # mostra coluna
            pass
Está na hora de concretizar. Vamos supor que a matriz está representada por um tuplo de tuplos, ou seja, cada elemento do duplo representa uma linha. Esquecendo o detalhe das posições vamos resolver o problema de mostrar apenas o conteúdo. Por cada linha, vamos imprimir os elementos de todas as colunas. Como queremos que tudo fique na mesma linha, usaremos a palavra chave end da instrução de print com a indicação de que os elementos devem estar separados por uma tabulação.
def mostra_mat(matriz):
    num_linhas = len(matriz)
    num_colunas = len(matriz[0])
    for linha in range(num_linhas):
        # mostra linha
        for coluna in range(num_colunas):
            # mostra coluna
            print(matriz[linha][coluna],end='\t')
        print()
Notar o uso do print() no fim da impressão da linha no final do segundo ciclo for, precisamente para forçar a mudança de linha. Já só falta resolver a questão dos índices da matriz. Mas agora isso é muito fácil, pois em cada momento, os nomes linha e coluna têm os valores de que precisamos:
def mostra_mat(matriz):
    num_linhas = len(matriz)
    num_colunas = len(matriz[0])
    for linha in range(num_linhas):
        # mostra linha
        for coluna in range(num_colunas):
            # mostra coluna
            print('(',linha,',',coluna,'): ',matriz[linha][coluna],end='\t')
        print()
Claro que se não gostarmos muito do aspecto podemos melhorar um pouco a saída, evitando o aparecimento de muitos espaços em branco:
def mostra_mat_2(matriz):
    num_linhas = len(matriz)
    num_colunas = len(matriz[0])
    for linha in range(num_linhas):
        # mostra linha
        for coluna in range(num_colunas):
            # mostra coluna
            print('(%d,%d): %s' % (linha,coluna,matriz[linha][coluna]),end='\t')
        print()
Moral da história: devagar se vai ao longe!

Padrão Ciclo - Acumulador

Acontece com muita frequência que ao fim de resolvermos vários problemas verificamos existir um padrão comum a todas as soluções. Una vez descoberto o padrão passamos a olhar para novos problemas entanto perceber em que medida esse padrão se pode aplicar, adaptado claro está à nova questão. Vejamos uns exemplos simples.

Comecemos pelo caso de um programa que retira de uma cadeia de caracteres os elementos repetidos.
def tira_repete_1(cadeia):
    acum = ''
    for car in cadeia:
        if car not in acum:
            acum = acum + car
    return acum
A ideia é ir juntando caracteres formando um novo objecto associado ao nome atum cada vez que o ciclo é percorrido, caso o elemento ainda não esteja na nova cadeia.

[ Mesmo um problema tão simples pode ter várias variantes em função do que a linguagem concreta permite:
def tira_repete_2(cadeia):
    acum = ''
    for i in range(len(cadeia)):
        if cadeia[i] not in cadeia[i+1:]:
            acum = acum + cadeia[i]
    return acum

def tira_repete_3(cadeia):
    acum = ''
    for i in range(len(cadeia)):
        if cadeia.find(cadeia[i],i+1) == -1:
            acum = acum + cadeia[i]
    return acum

def tira_repete_4(cadeia):
    return ''.join([cadeia[i] for i in range(len(cadeia)) if cadeia[i] not in cadeia[i+1:]])
Mas este aspecto não é o objectivo deste texto e por isso não comentamos as diferenças.]

Admitamos agora que nos pedem para calcular o produto escalar de dois vectores.
Um programa simples para resolver esta questão é o seguinte:
def prod_esc(vec1,vec2):
    acum = 0
    for i in range(len(vec1)):
        acum = acum + (vec1[i] * vec2[i])
    return acum
Nesta solução vamos acumulando a soma parcial em acum. A cada etapa do ciclo vamos acrescentando à soma parcial o novo termo vec1[i] * vec2[i].

[ Esta solução supõe que os vectores têm a mesma dimensão. Caso não seja verdade uma alternativa seria:
def prod_esc(vec1,vec2):
    dim = min(len(vec1), len(vec2))
    acum = 0
    for i in range(dim):
        acum = acum + (vec1[i] * vec2[i])
    return acum
]

Estes dois exemplos mostram uma solução comum: um acumulador onde vamos juntando o resultado parcial efectuado em cada etapa do ciclo. Armados desta conhecimento vamos tentar uma nova questão: calcular o factorial de um inteiro não negativo, ou seja, n!. Como fazer? Sabemos que por definição n! = n X (n-1) X (n-2) X … X 1. Olhando para a fórmula verificamos que ela envolve produtos repetidos. Podemos então recorrer a um ciclo, controlado por uma variável i, em que na etapa i do ciclo já calculámos os factorial … de i.
def fact(n):
    acum = 1
    for i in range(1,n+1):
        acum = acum * i
    return acum
Olhando para estes três exemplos também resulta claro qual deve ser o valor inicial do acumulador: deve ser o elemento neutro para a operação realizada no interior do ciclo (cadeia vazia para concatenação de cadeias, 0 para somas, 1 para produtos.).

segunda-feira, 24 de outubro de 2016

Quem faz também desfaz...

Vamos fazer o exercício inverso da encriptação: dado um texto encriptado de acordo com um dado método refazer o texto original. Vamos fazer o exercício para cada um dos três métodos de que falámos.

Separação Pares - Ímpares

Este parece ser um caso trivial. Divididos o texto ao meio e depois tiramos alternadamente os caracteres de cada uma das componentes.
def desencripta_1(texto):
    # divide ao meio e separa
    meio = len(texto)//2
    pares = texto[:meio]
    impares = texto[meio:]
    # constrói texto
    novo_texto = ''
    for i in range(meio):
        novo_texto = novo_texto + pares[i] + impares[i]
    return novo_texto
Acontece que se testarmos com vários exemplos verificamos que nalguns casos o resultado não é o esperado. Não precisamos de reflectir muito para verificar que o problema acontece quando o texto tem um número ímpar da caracteres. Neste caso, o número de elementos na posição par é superior em uma unidade do número de elementos nas posições ímpares. Temos pois que ter cuidado quando fazemos a divisão ao meio, diferente em cada um dos casos. Resolvida esta questão uma solução será:
def desencripta_12(texto):
    # divide ao meio e separa
    comp = len(texto)
    meio, imp = divmod(comp,2)
    pares = texto[:meio]
    impares = texto[meio+imp:]
    # constrói texto
    novo_texto = ''
    for i in range(meio):
        novo_texto = novo_texto + pares[i] + impares[i]
    if imp:
        novo_texto = novo_texto + texto[meio]
    return novo_texto
Como se pode ver usamos a operação divido que nos permite obter as duas componentes através do mecanismo de desempacotamento:
meio,imp = divmod(com,2)
O nome imp estará associado ao objecto 1 (se texto tiver comprimento ímpar) ou ao objecto 0 (caso tenha comprimento par).

Distância Fixa

Neste método, se um caractere é deslocado n posições para a frente na codificação, então na descodificação cada caractere deve ser deslocado n posições para … trás!
def desencripta_2(texto_encriptado,chave):
    alfabeto = 'abcdefghijklmnopqrstuvwxyz '
    texto_normal = ''
    for car in texto_encriptado:
        indice = alfabeto.find(car) 
        texto_normal = texto_normal + alfabeto[(indice - chave)%len(alfabeto)]
    return texto_normal
Assumimos um alfabeto co mas 26 letras minúsculas mais o espaço em branco, mas a solução pode ser adaptada a qualquer alfabeto sem problemas.

Chave Aleatória

Este caso aparenta ser o mais complexo. Vejamos se é verdade. Comecemos por mostrar o código que permite definir uma chave e encriptar um texto:
def encripta_3(texto, alfabeto,chave):
    #chave = define_chave(alfabeto)
    novo_texto = ''
    for car in texto:
        ind = alfabeto.index(car)
        novo_texto = novo_texto + chave[ind]    
    return novo_texto

import random

def define_chave(alfabeto):
    simbolos = alfabeto
    chave = ''
    for car in alfabeto:
        novo_simb = random.choice(simbolos)
        ind = simbolos.index(novo_simb)
        simbolos = simbolos[:ind] + simbolos[ind+1:]
        chave = chave + novo_simb    
    return chave
Como a correspondência é um para um, se, por exemplo, a “b” no texto original corresponde “h” na chave então quando encontramos no texto codificado um “h” devemos ter um “b” no texto original. Afinal a solução não é assim tão complexa:
def desencripta_3(texto_encriptado,alfabeto,chave):
    texto_normal = ''
    for car in texto_encriptado:
        indice = chave.find(car) 
        texto_normal = texto_normal + alfabeto[indice]
    return texto_normal
Mas se pensarmos um pouco mais no que dissemos sobre a correspondência um a um, chegamos à conclusão que codificar e descodificar são o mesmo processo pelo que apenas precisamos de um programa. Dito de outro modo:
def desencripta_31(texto,alfabeto,chave):
    return encripta_3(texto,chave,alfabeto)
E pronto. Divirta-se a escrever variantes das soluções propostas. Por exemplo, usando sempre o alfabeto como parâmetro.

domingo, 23 de outubro de 2016

Codificar textos

Durante as aulas introduzimos as cadeias de caracteres. Elas são o modo natural de guardar textos. Uma tarefa relevante é a de codificar um texto de modo que possa ser transmitido com segurança. Vamos ver como a tarefa de encriptar um documento pode ser resolvida com facilidade em Python. Iremos usar três métodos diferentes, que naturalmente vão requerer algoritmos distintos. O primeiro, limita-se a separar o texto em duas partes em função da posição par ou ímpar do caractere no texto. Uma vez esta decomposição feita o texto é recomposto juntando as duas partes. O segundo método precisa conhecer o alfabeto usado e estabelece uma correspondência entre cada caractere do alfabeto e um outro caractere do mesmo alfabeto que se encontra n posições à sua frente. Finalmente o terceiro método usa uma correspondência entre caracteres não baseada num distância fixa mas antes definida estocasticamente. Vejamos então algumas soluções possíveis.

Separação pares ímpares

Comecemos com um pedaço de código que deixa claro qual a estratégia a seguir.
def encripta_0(texto):
    # selecciona pares
    # selecciona ímpares
    # junta pares e ímpares
    pass
Olhemos agora para cada um dos sub-problemas. Como extrair os caracteres que se encontram nas posições pares do texto. Não é preciso pensar muito para decidir que podemos recorrer ao modelo baseado na ideia de ciclo + acumulador:
def encripta_0(texto):
    # selecciona pares
    pares = ''
    for i in range(len(texto)):
        # índice par?
        if i%2 == 0:
            pares = pares + texto[i]
    # selecciona ímpares
    # junta pares e ímpares
    pass
Resolver o problema para os mares é semelhante, pelo que chegamos à nossa primeira solução.
def encripta_0(texto):
    # selecciona pares
    pares = ''
    for i in range(len(texto)):
        # índice par?
        if i%2 == 0:
            pares = pares + texto[i]
    # selecciona ímpares
    impares = ''
    for i in range(len(texto)):
        # índice ímpar?
        if i%2 != 0:
            impares = impares + texto[i]    
    # junta pares e ímpares
    novo_texto = pares + impares
    return novo_texto
Porque é que esta solução não nos agrada? Desde logo porque o texto é percorrido duas vezes. Isso decorre do facto de termos isolado a construção dos pares da dos ímpares. Mas é óbvio que podemos fazer a divisão par/ímpar percorrendo o texto uma só vez:
def encripta_1(texto):
    pares = ''
    impares = ''
    for i in range(len(texto)):
        if i % 2 == 0:
            pares = pares + texto[i]
        else:
            impares = impares + texto[i]
    return pares + impares
A solução pode ser ainda outra se usarmos o conhecimento que temos da operação de fatiamento:
def encripta_11(texto):
    pares = texto[0::2]
    impares = texto[1::2]
    return pares + impares
ou ainda, eliminando os nomes auxiliares:
def encripta_12(texto):
    return texto[0::2] + texto[1::2]
Distância Fixa

Nesta abordagem vamos necessitar conhecer o alfabeto, ou seja, os símbolos que podem aparecer no texto. Para simplificar vamos supor que o nosso alfabeto é formado pelas letras minúsculas mais o espaço em branco. O modelo ciclo - acumulador permite um primeiro esboço de solução:
def codifica(texto,n):
    alfabeto = 'abcdefghijklmnopqrstuvwxyz '
    novo_texto = ''
    for car in texto:
        # por cada caractere do texto procura o equivalente n posições “à frente”
        # junta ao acumulador
    return novo_texto
O sub-problema realmente diferente consiste em encontrar o novo caractere. Um ideia básica é usar o seu índice no alfabeto e somar-lhe n. O único problema que pode surgir é com os caracteres no final. Por exemplo, caso n=2 o equivalente a z deve ser o a. Podemos resolver esta questão usando a operação módulo:
def codifica(texto,n):
    alfabeto = 'abcdefghijklmnopqrstuvwxyz '
    novo_texto = ''
    for car in texto:
        novo_indice = (alfabeto.index(car) + n) % len(alfabeto)
        novo_car = alfabeto[novo_indice]
        novo_texto = novo_texto + novo_car
    return novo_texto
É claro que podemos chegar a outra solução. Por exemplo, podemos construir primeiro a correspondência e só depois encriptar.
def codifica_2(texto,n):
    alfabeto = 'abcdefghijklmnopqrstuvxyz '
    # constrói código
    cod_alfabeto = ''
    for car in alfabeto:
        indice = alfabeto.index(car)
        novo_indice = (indice + n) % len(alfabeto)
        cod_alfabeto = cod_alfabeto + alfabeto[novo_indice]
        
    # encripta
    novo_texto = ''
    for car in texto:
        ind = alfabeto.index(car)
        novo_texto = novo_texto + cod_alfabeto[ind]
    return novo_texto
Faz ainda sentido que o alfabeto seja um parâmetro do programa principal:
def codifica_3(texto,alfabeto, n):
    # define chave
    alfa_chave = chave(alfabeto,n)
    # encripta
    novo_texto = ''
    for car in texto:
        ind = alfabeto.index(car)
        novo_texto = novo_texto + alfa_chave[ind]
    return novo_texto

def chave(alfabeto,n):
    alfa_chave = ''
    for car in alfabeto:
        indice = alfabeto.index(car)
        novo_indice = (indice + n) % len(alfabeto)
        alfa_chave = alfa_chave + alfabeto[novo_indice] 
    return chave
Notar que criámos uma definição auxiliar para a construção da chave. Chave Aleatória

A ideia agora é estabelecer uma relação um para um entre o alfabeto e a chave de modo aleatório. Na linha do último exemplo vamos ver como podemos criar a chave. Em Python existem várias formas de o poder fazer, umas bem simples, mas vamos buscar uma solução à luz do que foi dados nas aulas. Vamos então caçar com gato…. A ideia é trivial: gerar uma permutação do alfabeto inicial recorrendo a uma cópia desse alfabeto ao qual vamos buscar aleatoriamente um símbolo para a chave. De seguida esse símbolo é retirado para garantir que geramos uma permutação.
import random

def define_chave(alfabeto):
    simbolos = alfabeto
    chave = ''
    for car in alfabeto:
        novo_simb = random.choice(simbolos)
        ind = simbolos.index(novo_simb)
        simbolos = simbolos[:ind] + simbolos[ind+1:]
        chave = chave + novo_simb    
    return chave
Tendo a chave, a encriptação é semelhante ao exemplo anterior.
def encrita_3(texto, alfabeto):
    chave = define_chave(alfabeto)
    novo_texto = ''
    for car in texto:
        ind = alfabeto.index(car)
        novo_texto = novo_texto + chave[ind]    
    return novo_texto

sexta-feira, 21 de outubro de 2016

Teste 1 - TP2

Pergunta 1
Em Python tudo são objectos. Os objectos têm atributos que dependem do tipo do objecto. No entanto existem três que todos têm: identidade, valor e tipo. O nome de um objecto é também um atributo que o objecto obtém através de uma atribuição. Quando fazemos x = x + 1 isso é informaticamente possível porque à esquerda do sinal de atribuição o ‘x’ remete para o nome, enquanto que à direita do sinal de atribuição remete o valor.

Pergunta 2
Uma questão trivial que pedia para usar Python como se de uma vulgar calculadora se tratasse.
import math

def distancia(lat1,long1,lat2,long2):
    termo1 = math.sin(lat1) * math.sin(lat2)
    termo2 = math.cos(lat1) * math.cos(lat2) * math.cos(long1 - long2)
    return 6371.01 * math.acos(termo1 + termo2)

Pergunta 3
Desenhar um tiragulo rectângulo é semelhante a desenhar um triângulo, só que agora basta termos as duas medidas dos catetos. Tornar o desenho livre da dimensão dos lados, da orientação, da cor, obriga a usar esses elementos como parâmetros e a uma preparação para o desenho antes de concretizar. É isso que faz o código seguinte:
import turtle

def tri_rect(x,y,orienta,cor,lado_1,lado_2):
    # inicialização
    turtle.penup()
    turtle.goto(x,y)
    turtle.setheading(orienta)
    turtle.color(cor)
    turtle.pendown()
    turtle.begin_fill()
    # desenha
    
    turtle.forward(lado_1)
    turtle.left(90)
    turtle.forward(lado_2)
    turtle.goto(x,y)
    turtle.end_fill()
    turtle.hideturtle()  
Na segunda parte da pergunta era pedido que usassem a definição anterior como auxiliar para o desenho formado por um número variável de triângulos rectângulos alinhados circularmente e igualmente espaçados. A única dificuldade era controlar a orientação, o que obrigava a saber o espaçamento. É isso que é feito no programa:
def boneco(n, x,y,orienta,cor,menor,maior):
    angulo = 360/n
    for i in range(n):
        tri_rect(x,y,orienta,cor,menor,maior)
        orienta = orienta + angulo

Teste 1 - TP1

Pergunta 1
Em Python tudo são objectos. Os objectos têm atributos que dependem do tipo do objecto. No entanto existem três que todos têm: identidade, valor e tipo. Os objectos estão armazenados num espaço próprio da memória, designado por Espaço dos Objectos. A localização do objecto é a sua identidade, e, nesse local estão a descrição do valor do objecto e do seu tipo. O Espaço de Nomes é o local da memória onde estão guardados os nomes associados aos objectos. A ligação é feita através da identidade. Assim ao fazer a = 5, é criada a associação entre o nome ‘a’ e o objecto 5, cada um a “viver” no respectivo espaço.

Pergunta 2
Uma questão trivial que pedia para usar Python como se de uma vulgar calculadora se tratasse.
import math

def area(n,l):
    return (n * l**2)/(4 * math.tan(math.pi/4))
Pergunta 3
Desenhar um rectângulo é semelhante a desenhar um quadrado, só que agora temos duas medidas para os lados. Tornar o desenho livre da dimensão dos lados, da orientação, da cor, obriga a usar esses elementos como parâmetros e a uma preparação para o desenho antes de concretizar. É isso que faz o código seguinte:
import turtle

def rect(x,y,orienta,cor,menor,maior):
    # inicialização
    turtle.penup()
    turtle.goto(x,y)
    turtle.setheading(orienta)
    turtle.color(cor)
    turtle.pendown()
    turtle.begin_fill()
    # desenha
    for i in range(2):
        turtle.forward(menor)
        turtle.right(90)
        turtle.forward(maior)
        turtle.right(90)
    turtle.end_fill()
    turtle.hideturtle()
Na segunda parte da pergunta era pedido que usassem a definição anterior como auxiliar para o desenho formado por um número variável de rectângulos alinhados circularmente e igualmente espaçados. A única dificuldade era controlar a orientação, o que obrigava a saber o espaçamento. É isso que é feito no programa:
def boneco(n, x,y,orienta,cor,menor,maior):
    angulo = 360/n
    for i in range(n):
        rect(x,y,orienta,cor,menor,maior)
        orienta = orienta + angulo

segunda-feira, 3 de outubro de 2016

Livro

Depois de vários anos a ensinar programação em Python resolvi publicar um livro. O texto cobre toda a matéria de IPRP, isto é a parte procedimental da linguagem, mas acrescenta uma parte sobre programação orientada aos objectos e outros aspectos que aprofundam o conhecimento da linguagem.
Quem gostar de aprender a programar numa linguagem que hoje é ensinada nas melhores escolas e usada pelas grandes empresas, tem neste livro uma oportunidade de o fazer de modo completo e em português.

sábado, 1 de outubro de 2016

Os benefícios da generalização e da abstracção

Nestas primeiras aulas dissemos que em Python tudo são objectos. Cada objecto tem um conjunto determinado de atributos, como a identidade, o valor e o tipo, e um conjunto de operações em que podem participar. Os atributos num dado instante definem o estado do objecto, enquanto que as operações determinam o seu comportamento.
Podemos usar estes elementos para usar Python como se fosse um vulgar calculadora que me ajuda a saber o peso ideal de uma pessoa do género masculino.
>>> 72.7 * 1.81 - 58 
        73.58700000000002
>>>
Podemos fazer o mesmo para calcular o peso-ideal de uma pessoa do género feminino. E até podemos escrever um único programa que nos permite calcular o peso ideal de qualquer pessoa.
# Definição
def peso_ideal(altura,genero):
    if genero == 'M':
        return round(72.7 * altura - 58,2)
    else:
        return round(62.1 * altura - 44.7,2)
      
# Uso
print(peso_ideal(1.81,'M'))
print(peso_ideal(1.74,'F'))
Este exemplo simples mostra a vantagem de soluções genéricas. Para as conseguir fazemos uso de uma abstracção procedimental, uma definição (def). Neste exemplo os dados são fornecidos ao programa no momento da chamada do programa através dos argumentos da definição, também designados de parâmetros formais, e o resultado é comunicado a quem o solicitou através do comando de regresso (return). Não tem que ser forçosamente assim.
# Definição

def peso_ideal_2():
    altura = float(input('A sua altura sff: '))
    genero = input('O seu género sff [M/F]: ')
    if genero == 'M':
        print(round(72.7 * altura - 58,2))
    else:
        print(round(62.1 * altura - 44.7,2))
    
    
# Uso
peso_ideal_2()

peso_ideal_2()
 
Como se pode ver a grande diferença entre as duas soluções reside no modo como se processa a entrada dos dados a a comunicação do resultado. Agora usamos input para introduzir os dados e print para comunicar o resultado. Neste exemplo, estão presentes o que designamos de instruções, que de um modo geral podem ser agrupadas em dois grandes grupos: destrutivas e de controlo. No primeiro grupo, estão as instruções de atribuição (=), entrada (input) e saída (print). No segundo grupo, as instruções de sequência (;), condicionais (if) e ciclos ou repetições (for). São os blocos construtores dos programas.
Outro exemplo que quer tratámos envolvia desenho de polígonos regulares. Mostrei como se podia passar de um programa muito simples para desenhar um quadrado:
# Definição
def quadrado():
    turtle.forward(50)
    turtle.right(90)
    turtle.forward(50)
    turtle.right(90)
    turtle.forward(50)
    turtle.right(90)
    turtle.forward(50)
    turtle.right(90)
       
# Uso
quadrado()
para outro programa mais completo.
import turtle

# Nova Definição
def vai_para(x,y):
    turtle.penup()
    turtle.goto(x,y)
    turtle.pendown()
    
def quadrado(x,y,orientacao,cor,lado):
    vai_para(x,y)
    turtle.setheading(orientacao)
    turtle.color(cor)
    turtle.begin_fill()
    for i in range(4):
        turtle.forward(lado)
        turtle.right(90)
    turtle.end_fill()
    turtle.hideturtle()
    
# Uso

quadrado(50, -50, 45,'red',50)
turtle.exitonclick()
Completámos a ideia de abstracção ao mostrar como um único programa podia ser usado para desenhar qualquer polígono regular.
import turtle

# Nova Definição
def vai_para(x,y):
    turtle.penup()
    turtle.goto(x,y)
    turtle.pendown()
    
def poligono_reg(num_lados,x,y,orientacao,cor,lado):
    vai_para(x,y)
    turtle.setheading(orientacao)
    turtle.color(cor)
    turtle.begin_fill()
    angulo = 360//num_lados
    for i in range(num_lados):
        turtle.forward(lado)
        turtle.right(angulo)
    turtle.end_fill()
    turtle.hideturtle()
    
# Uso
poligono_reg(7,50, -50, 45,'red',50)
turtle.exitonclick()
A moral desta história é simples: generalizar + abstrair compensa!

Começar de novo

Começar de novo Cá estamos em mais um início de ano lectivo. Para os estreantes quero apenas dizer que neste blogue podem encontrar vários elementos que vos ajudarão na disciplina de IPRP e, assim o espero, a tornarem-se melhores programadores. Para todos os outros bom regresso.