4 - implementaçãoiceb.ssdi.di.fct.unl.pt/1718/files/ice-b-04.pdf · • a partir da...

37
ICE-B 4 - Implementação Ludwig Krippahl

Upload: duongmien

Post on 28-Dec-2018

215 views

Category:

Documents


0 download

TRANSCRIPT

ICE-B

4 - Implementação

Ludwig Krippahl

1

Implementação

Resumo■ Exemplo: cálculo de pH, agora estruturado■ Testes unitários■ Ciclo de vida de um programa■ Erros e precisão numérica

2

Implementação

Exemplo:Calcular pH

3

Calcular pH

Solução de ácido benzóicoC H COOH H + C H COO6 5 ⇔

+6 5

■ Constante de dissociação, ■ Ignorando auto-dissociação da água:

■ Resolvemos equação quadrática, • (só precisamos da solução positiva)

■ Depois calculamos o pH:

= 6.5 ×Ka 10−5

= = ⇔ + x − = 0Ka

[ ][ CO ]H+ C6H5 O−

[ COOH]C6H5

x2

− xCi

x2 Ka CiKa

x =−b± −4acb2√

2a

pH = −lo ([ ]) = −lo (x)g10 H+ g10

4

Calcular pH

Compreender, generalizar e decompor■ Generalização: Calcular o pH de um ácido fraco monoprótico■ Decomposição: Obter a raíz positiva e calcular o pH■ Criamos ficheiro phcalc.py com o "esqueleto" do programa

# -*- coding: utf-8 -*- """ Computes pH of an acid from concentration """ def positive_root(a, b, c): "return positive root of quadratic equation" def compute_pH(Ka, Ci): """ return pH value for weak acid from dissociation constant and initial concentration """

5

Calcular pH

"Esqueleto" do programa■ É uma boa ideia fazer primeiro um esboço do programa• Permite ver se encaixa tudo e se é preciso decompor mais

■ Estas funções ainda não fazem nada mas este código já cria osobjectos.

def positive_root(a, b, c): "return positive root of quadratic equation"

■ A string logo a seguir à assinatura é a documentação• Podem usar ''' ou """ se o texto ocupar várias linhas

In : help(positive_root) Help on function positive_root in module __main__: positive_root(a, b, c) return positive root of quadratic equation

6

Calcular pH

Documentação do módulo■ A string no início do módulo é útil como documentação

# -*- coding: utf-8 -*- """ Computes pH of an acid from concentration """

■ Depois de executar com F5, podemos importá-lo e pedir ajuda

In : import phcalc In : help(phcalc) Help on module phcalc: NAME phcalc - Computes pH of an acid from concentration FUNCTIONS compute_pH(Ka, Ci) return pH value for weak acid from dissociation constant and initial concentration ...

7

Calcular pH

Implementar as funções■ Depois de perceber os objectivos e o algoritmo e de decompor em

tarefas podemos implementar cada função■ Devemos começar pelas "pontas", aquelas que não dependem de

nenhuma outra que tenhamos de implementar

def positive_root(a, b, c): "return positive root of quadratic equation" root = (-b + (b**2 - 4*a*c)**0.5)/(2*a) return root

■ Depois devemos testar:

In : positive_root(1,0,-1) #x**2 + 0x - 1 Out: 1.0 In : positive_root(1,-2,0) #x**2 - 2x + 0 Out: 2.0

8

Calcular pH

Implementar as funções■ Depois de implementar e testar uma função podemos passar a

outra que dependa desta■ Como o cálculo do pH precisa do logaritmo, temos de importarlog10

• É melhor fazer todos os import no início do módulo

# -*- coding: utf-8 -*- """ Computes pH of an acid from concentration """ from numpy import log10 def positive_root(a, b, c): ... def compute_pH(Ka, Ci): ...

9

Calcular pH

Implementar as funções■ Agora implementamos e testamos esta também

def compute_pH(Ka, Ci): """ return pH value for weak acid from dissociation constant and initial concentration """ H = positive_root(1, Ka, -Ka*Ci) return -log10(H)

■ Testar com valores que permitam confirmar o resultado:

In : compute_pH(6.5e-5,0.01) Out: 3.11104555393015

10

Testes Unitários

Testar sempre cada função■ Decompomos um problema complexo em problemas mais simples.■ Mas depois vai ser preciso juntar tudo.• Um erro descoberto só no final é mais difícil de encontrar e corrigir.

■ É preciso testar:• Sempre que implementamos algo de novo.• Sempre que corrigimos um erro, para confirmar.• Sempre que alteramos alguma função.

11

Testes Unitários

Sugestão: automatizar os testes todos■ Opção 1: uma função no final do nosso módulo• Numa função para não correr os testes sempre que usamos o módulo

... def tests(): """ run unit tests on all functions """ print("positive_root, 1.0:", positive_root(1,0,-1)) print("positive_root, 2.0:",positive_root(1,-2,0)) print("compute_pH, 3.111:",compute_pH(6.5e-5,0.01))

■ É importante que se possa verificar o resultado também

In : tests() positive_root, 1.0: 1.0 positive_root, 2.0: 2.0 compute_pH, 3.111: 3.11104555393

12

Testes Unitários

Sugestão: automatizar os testes todos■ Opção 2: um módulo só para testes (phtests.py)

# -*- coding: utf-8 -*- """ run unit tests on all functions in phcalc """ from phcalc import positive_root, compute_pH print("positive_root, 1.0:", positive_root(1,0,-1)) print("positive_root, 2.0:",positive_root(1,-2,0)) print("compute_pH, 3.111:",compute_pH(6.5e-5,0.01))

■ Basta correr com F5• Neste caso não é preciso função porque o módulo é só para testes

In : runfile('/pasta/phtests.py', wdir='/pasta') positive_root, 1.0: 1.0 positive_root, 2.0: 2.0 compute_pH, 3.111: 3.11104555393

13

Testes Unitários

Atenção: funções são criadas na execução do def...def tests(): """ run unit tests on all functions """ print("positive_root, 1.0:", positive_root(1,0,-1)) print("positive_root, 2.0:",positive_root(1,-2,0)) print("compute_pH, 3.111:",compute_pH(6.5e-5,0.01))

■ Sempre que alterarem alguma coisa no vosso programa precisamde criar novamente os objectos das funções alteradas

■ Para isso o interpretador tem de reler a assinatura da função e todaa "receita" no corpo da função

■ A forma mais prática de fazer isso no Spyder é F5• (Grava e corre todo o ficheiro)

■ O Spyder automaticamente recarrega outros módulos

14

Testes Unitários

Atenção: funções são executadas só em nome( ... )In : tests() positive_root, 1.0: 1.0 positive_root, 2.0: 2.0 compute_pH, 3.111: 3.11104555393

■ Quando se corre módulo as funções são criadas mas não usadas■ A menos que o módulo inclua também chamadas às funções

# -*- coding: utf-8 -*- ... def tests(): print("positive_root, 1.0:", positive_root(1,0,-1)) print("positive_root, 2.0:",positive_root(1,-2,0)) print("compute_pH, 3.111:",compute_pH(6.5e-5,0.01)) tests()

15

Testes Unitários

Atenção: função só devolve valor em return# -*- coding: utf-8 -*- ... def tests(): print("positive_root, 1.0:", positive_root(1,0,-1)) print("positive_root, 2.0:",positive_root(1,-2,0)) print("compute_pH, 3.111:",compute_pH(6.5e-5,0.01))

In : tests() positive_root, 1.0: 1.0 positive_root, 2.0: 2.0 compute_pH, 3.111: 3.11104555393

■ O print escreve na consola mas a função tests() não devolveuvalor (devolveu None).

16

Programação Estruturada

Erros

17

Erros

Erro de sintaxe■ Erro na escrita do código que impede o interpretador de o executar• Este é o mais fácil de corrigir, porque sabemos logo que o cometemos

In : 6 * * 2 6 * * 2 ^ SyntaxError: invalid syntax In : 8 + 5.3.2 8 + 5.3.2 ^ SyntaxError: invalid syntax

■ Spyder analisa o código conforme o escrevemos e assinala a linhacom o erro

18

Erros

Erro de execução (Exception)■ O interpretador sabe o que deve fazer mas não consegue fazê-lo• Estes erros só ocorrem durante a execução e podem ser mais difíceis de

diagnosticar porque o erro pode estar noutra parte do código

In : y = z*2 y = z*2 NameError: name 'z' is not defined In : y = 0 In : z = 1 / y z = 1 / y ZeroDivisionError: division by zero

19

Erros

Erro lógico■ O programa corre sem problemas mas não dá o resultado certo• Estes erros tendem a ser os mais difíceis de corrigir

■ Exemplo: pH negativo?

def positive_root(a, b, c): root = (-b + (b**2 - 4*a*c)**0.5)/(2*a) return root def compute_pH(Ka, Ci): H = positive_root(1, Ka, -Ka*Ci) return log10(H)

In : compute_pH(6.5e-5,0.01) Out: -3.11104555393015

(Falta o sinal negativo no cálculo do logaritmo)

20

Erros

Erro numérico■ Devido à representação finita de números fraccionários, em 64 bits• (Inteiros em Python 3.x usam o número de bits que for necessário)

• Obriga a arredondamentos e pode ser importante se o cálculo for iterado

In : (2**0.5)**2 Out: 2.0000000000000004

■ Representação de números fraccionários em 64 bits:• 1 bit para sinal, 11 para expoente (base 2) e 52 para fracção• Equivale a cerca de 17 algarismos significativos em decimal

21

Erros

Erro numérico■ Informação sobre a representação em 64 bits

In : import numpy In : info = numpy.finfo(float) In : info.bits Out: 64 In : info.eps Out: 2.2204460492503131e-16 In : info.tiny Out: 2.2250738585072014e-308 In : info.min Out: -1.7976931348623157e+308 In : info.max Out: 1.7976931348623157e+308

Informação: Número de bits na representação Menor valor que somado a 1 dá >1 In : 1+info.eps-1 Out: 2.2204460492503131e-16 In : 1+0.5*info.eps-1 Out: 0.0 Mais pequeno com plena precisão Menor valor representável Maior valor representável

22

Programação Estruturada

Código Fonte

23

Ciclo de vida

Ciclo de vida de um programa■ Edição do código fonte• Escrito, guardado em ficheiros .py

■ Interpretação do código fonte• O interpretador traduz as instruções em instruções para o CPU

■ Execução• O CPU executa o programa

■ Testar e avaliar o resultado• e voltar à edição as vezes que for preciso...

24

Ciclo de vida

Ciclo de vida de um programa, exemplo■ Concebemos, implementámos e testámos o cálculo do pH• A partir da concentração inicial e da constante de dissociação

Agora temos um novo problema■ Qual o pH de 0.01g de ácido benzóico em 0.250 dm ?• Calcular pH a partir da massa, volume e massa molecular• Basta calcular a concentração e usar o que já temos

■ Solução: acrescentamos uma nova função• Pensamos no algoritmo: calcular concentração• Decompomos se necessário (não é; é simples)• Implementamos e testamos

3

25

Ciclo de vida

Novo problema:■ Qual o pH de 0.01g de ácido benzóico em 0.250 dm ?3

Solução:■ Já temos praticamente tudo feito■ Basta uma função que calcule a concentração• Precisa da massa, massa molar e volume

■ E depois chamar a anterior com concentração e Ka

def compute_pH_mass(mass, mol_mass, volume, Ka): """ return pH value of a weak acid solution from mass of solute and volume of solution """ Ci = mass / mol_mass / volume return compute_pH(Ka,Ci)

26

Ciclo de vida

■ Uma maneira prática é acrescentar ao módulo phcalc

# -*- coding: utf-8 -*- """ Computes pH of an acid from concentration """ from numpy import log10 def positive_root(a, b, c): ... def compute_pH(Ka, Ci): ... def compute_pH_mass(mass, mol_mass, volume, Ka): """ return pH value of a weak acid solution from mass of solute and volume of solution """ Ci = mass / mol_mass / volume return compute_pH(Ka,Ci) def tests(): ...

27

Ciclo de vida

Ciclo de vida de um programa, exemplo■ Calcular o pH a partir da massa, volume e massa molecular:

def compute_pH_mass(mass, mol_mass, volume, Ka): """ return pH value of a weak acid solution from mass of solute and volume of solution """ Ci = mass / mol_mass / volume return compute_pH(Ka,Ci)

■ Testes:

def tests(): ... print("compute_pH, 3.111:",compute_pH(6.5e-5,0.01)) print("compute_pH_mass, 3.1111",compute_pH_mass(1.221, 122.1, 1, 6.5e-5)) print("compute_pH_mass, 3.000",compute_pH_mass(0.5, 122.1, 0.25, 6.5e-5))

28

Implementação

Estilo de Código

29

Estilo de Código

É importante escrever código legivel■ Porque o código fonte serve:• Para o interpretador executar• Para humanos lerem

■ Código difícil de compreender• É mais propenso a erros• É mais difícil de corrigir, adaptar e melhorar• (prejudica a avaliação em ICE)

30

Estilo de Código

Nomes de variáveis■ As variáveis devem ter nomes descritivos• E.g. mass, mol_mass, volume

def compute_pH_mass(mass, mol_mass, volume, Ka): Ci = mass / mol_mass / volume return compute_pH(Ka,Ci)

■ Excepto quando têm nomes convencionais• E.g. a, b, c, Ci, pH, Ka

def positive_root(a, b, c): root = (-b + (b**2 - 4*a*c)**0.5)/(2*a) return root

■ Importante: que seja fácil perceber o que representa

31

Estilo de Código

Nomes de funções■ Devem descrever o que a função faz■ Nomes compostos por várias palavras• Convenção 1: usar letras maiúsculas e minúsculas phMassVol

• Convenção 2: (mais usado em Python) usar underscore compute_pH_mass

def compute_pH_mass(mass, mol_mass, volume, Ka): Ci = mass / mol_mass / volume return compute_pH(Ka,Ci)

32

Estilo de Código

Inteligibilidade■ Cada linha de código deve corresponder a um passo simples■ Evitar linhas demasiado longas ou complexas• Decompor em vários passos para ser mais inteligivel

■ Exemplo: menos claro

def compute_pH_mass(mass, mol_mass, volume, Ka): return compute_pH(Ka, mass / mol_mass / volume)

■ Exemplo: mais claro

def compute_pH_mass(mass, mol_mass, volume, Ka): Ci = mass / mol_mass / volume return compute_pH(Ka, Ci)

33

Estilo de Código

Documentação e comentários■ Devemos documentar funções (e módulos)■ Podemos também acrescentar comentários se for preciso• Notas para o programador

def positive_root(a, b, c): "return positive root of quadratic equation" root = (-b + (b**2 - 4*a*c)**0.5)/(2*a) #This assumes a is greater than zero return root

Mais informação:■ https://www.python.org/dev/peps/pep-0008/

"PEP 8 -- Style Guide for Python Code"

34

Programação Estruturada

Resumo

35

Implementação

Resumo■ Programação estruturada: funções■ Testes unitários■ Tipos de erro: sintaxe, exception, lógicos e numéricos■ Ciclo de vida e reutilização de código■ Estilo de códigoLeitura adicional:■ Recomendada: Capítulo 4 dos apontamentos■ Opcional: Livro, Cap. 16 até pag. 479 e Cap. 17 até pag. 491.