sábado, 5 de novembro de 2011

Erros ao vivo (1)

Uma das perguntas que os alunos da segunda fase tiveram que responder, para o seu teste 1, era a seguinte:

O vencimento bruto de um trabalhador está sujeito a descontos: 25% para o IRS, 5% para a Segurança social e 10% para a Caixa Nacional de Aposentações. O vencimento líquido é o que resulta da subtracção destes descontos ao vencimento bruto. Desenvolva um programa que dado o vencimento bruto devolve o correspondente vencimento líquido.

Eis algumas das soluções que me apresentaram.

Exemplo 1

01.Def. vencimento(vbruto).
02. virs = (vbruto) * 0,25
03. vss = (vbruto) * 0,05
04. vcna = (vbruto) * 0,1
05. vliquido = (virs) + (vss) + (vcna)
06. return vliquido
07. 
08.if ‘__main__’ == __name__:
09. print vencimento()

Exemplo 2
01.def venc_liquido(n):
02. total = 0
03. irs = (n * 25) / 100
04. sc = (n * 5) /100
05. cna = (n * 10) / 100
06. total = n - (irs + sc + cna)
07. return total
08. 
09.if __name__ == ‘__main__’:
10. print venc_liquido()

Exemplo 3
1.def vencimento(bruto):
2. liquido = bruto - (bruto * 0.25 + bruto * 0.05  + bruto * 0.1)
3. return liquido
4. 
5.if __name__ == ‘__main__’:
6. print vencimento(bruto)

O que todas estas soluções têm em comum é ... terem problemas. Há erros de sintaxe, há erros de lógica e há erros conceptuais graves. O primeiro exemplo é o pior de todos. Ttodos aqueles parênteses à volta dos nomes são desnecessários (interrogo-me se não os colocou levado pelo facto de quando se define a função termos que usar parênteses.). Nos números em vírgula flutuante usa a vírgula e não o ponto como separador entre a parte inteira e a parte fraccionária. Tem um erro de lógica: não subtrai ao vencimento bruto os descontos. No segundo exemplo, a lógica está correcta, só não se entendendo o porquê da inicialização de total a zero. Não é necessário. O terceiro exemplo, é o que está mais limpo. Nada a dizer sobre o modo como a definição é construída. Mas todos têm um erro conceptual grave, e isso manifesta-se no modo como usam a função que definiram, isto é, o que colocam depois do if. Nos dois primeiros casos a função é chamada sem argumento, enquanto que no terceiro caso a função é chamada tendo como argumento um nome que não está associado a nenhum objecto. O primeiro caso tem paralelo na situação de eu carregar numa tecla de função da minha calculadora, por exemplo sin, e não fazer mais nada ficando à espera que a máquina calcule qualquer coisa (embora não se saiba o quê).

Vamos ver se nos entendemos. Os problemas resolvem-se escrevendo programas. O programa é feito de definições e estas, para serem úteis, têm que ser usadas. Usar uma definição traduz-se por chamar a definição, indicando na altura da chamada quais os argumentos concretos a usar. Os argumentos das definições chamam-se parâmetros formais, e têm que ser nomes; os argumentos que usamos nas chamadas chamam-se parâmetros reais e têm que traduzir, directa ou indirectamente um objecto. Directamente se usamos o valor do objecto, indirectamente se usamos ou umaexpressão que quando calculada dá como resultado um objecto, ou um nome que está associado a um objecto. Os parâmetros formais, os que usamos nas definições, podem ter um nome qualquer, são mudos. Por exemplo, as duas definições abaixo definem exactamente a mesma coisa.
1.def dobro(n): # <-- n parâmetro formal
2. return 2 * n
3. 
4.def dobro(x): # <-- x parâmetro formal
5. return 2 * x

Usar n ou x como parâmetro formal é irrelevante. Vejamos agora com um exemplo a questão da definição e da chamada.

01.>>> def dobro(n): # <-- x parâmetro formal
02....  return 2 * n
03....
04.>>> dobro(5) # <-- 5 parâmetro real
05.10
06.>>> a = 3
07.>>> dobro(a) # <-- a parâmetro real
08.6
09.>>> dobro(a * 3 + 5) # <-- a*3+5 parâmetro real
10.28
11.>>>

No primeiro caso, chamamos com um objecto (5), no segundo, com o nome (a) associado a um objecto (3), no terceiro com uma expressão associada a um objecto de valor 14.

O que é que acontece se chamamos, ou sem argumento ou usando como argumento real um nome que não está associado a nenhum objecto? Um erro, claro:
01.>>> dobro() # <-- falta parâmetro real
02.Traceback (most recent call last):
03.  File "<string>", line 1, in <fragment>
04.TypeError: dobro() takes exactly 1 argument (0 given)
05.>>> dobro(z) # <-- parâmtero real sem objecto associado
06.Traceback (most recent call last):
07.  File "<string>", line 1, in <fragment>
08.NameError: name 'z' is not defined
09.>>>
10.</fragment></string></fragment></string>


Mas se disse que o nome usado como parâmetro formal pode ser qualquer o que acontece no caso de parâmetro formal (ou da definição) e parâmetro real (o da chamada) forem o mesmo nome?
1.>>> def dobro(n):
2....  return 2 * n
3....
4.>>> n = 6
5.>>> dobro(n)
6.12
7.>>> n
8.6

Como se pode ver, não acontece nada de anormal neste caso. Repito: o nome do parâmetro formal é arbitrário e só existe na realidade durante a execução da definição de que faz parte. Nessa altura é feita a associação do nome do parâmetro formal ao objecto passado como parâmetro real. No caso de o parâmetro real ser também um nome então o que é comunicado ao parâmetro formal é a referência (a identidade) do objecto a que está associado. Se o objecto correspondente ao parâmetro real for imutável nada de especial acontece, a não ser a definição ser executada e o resultado ser devolvido. Se o parâmetro real corresponder a um objecto mutável o caso muda de figura, pois as alterações ao parâmetro formal podem ser transferidas para o objecto associado ao parâmetro real. E nem é preciso os nomes dos parâmetros formal e real serem o mesmo!
01.>>> def aumenta(lista,elem):
02....  lista.append(elem)
03....  return lista
04....
05.>>> aumenta(['a',2,'c'],4)
06.['a', 2, 'c', 4]
07.>>> minha_lista = [1,2,3]
08.>>> aumenta(minha_lista,4)
09.[1, 2, 3, 4] 3 # <-- lista alterada dentro do programa
10.>>> minha_lista
11.[1, 2, 3, 4] 3 <-- minha_lista alterada como resultado da alteração a lista
12.>>> lista = ['a','b','c']
13.>>> aumenta(lista,'d')
14.['a', 'b', 'c', 'd']
15.>>> lista
16.['a', 'b', 'c', 'd']
17.>>>

Para se certificar se entendeu veja se não tem dúvidas neste último exemplo:
1.>>> minha_lista = [1,2,3,4]
2.>>> aumenta(minha_lista + [5], 6)
3.[1, 2, 3, 4, 5, 6]
4.>>> minha_lista
5.[1, 2, 3, 4]
6.>>>


Enquanto não entender isto terá sempre dificuldades no futuro. Por isso batalhe até entender. Se as dúvidas se mantiverem, fale com o seu professor!

Sem comentários:

Enviar um comentário