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


Def. vencimento(vbruto).
virs = (vbruto) * 0,25
vss = (vbruto) * 0,05
vcna = (vbruto) * 0,1
vliquido = (virs) + (vss) + (vcna)
return vliquido

if ‘__main__’ == __name__:
print vencimento()

Exemplo 2

def venc_liquido(n):
total = 0
irs = (n * 25) / 100
sc = (n * 5) /100
cna = (n * 10) / 100
total = n - (irs + sc + cna)
return total

if __name__ == ‘__main__’:
print venc_liquido()

Exemplo 3

def vencimento(bruto):
liquido = bruto - (bruto * 0.25 + bruto * 0.05 + bruto * 0.1)
return liquido

if __name__ == ‘__main__’:
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.

def dobro(n): # <-- n parâmetro formal
return 2 * n

def dobro(x): # <-- x parâmetro formal
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.


>>> def dobro(n): # <-- x parâmetro formal
... return 2 * n
...
>>> dobro(5) # <-- 5 parâmetro real
10
>>> a = 3
>>> dobro(a) # <-- a parâmetro real
6
>>> dobro(a * 3 + 5) # <-- a*3+5 parâmetro real
28
>>>

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:

>>> dobro() # <-- falta parâmetro real
Traceback (most recent call last):
File "", line 1, in
TypeError: dobro() takes exactly 1 argument (0 given)
>>> dobro(z) # <-- parâmtero real sem objecto associado
Traceback (most recent call last):
File "", line 1, in
NameError: name 'z' is not defined
>>>


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?

>>> def dobro(n):
... return 2 * n
...
>>> n = 6
>>> dobro(n)
12
>>> n
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!

>>> def aumenta(lista,elem):
... lista.append(elem)
... return lista
...
>>> aumenta(['a',2,'c'],4)
['a', 2, 'c', 4]
>>> minha_lista = [1,2,3]
>>> aumenta(minha_lista,4)
[1, 2, 3, 4] 3 # <-- lista alterada dentro do programa
>>> minha_lista
[1, 2, 3, 4] 3 <-- minha_lista alterada como resultado da alteração a lista
>>> lista = ['a','b','c']
>>> aumenta(lista,'d')
['a', 'b', 'c', 'd']
>>> lista
['a', 'b', 'c', 'd']
>>>

Para se certificar se entendeu veja se não tem dúvidas neste último exemplo:

>>> minha_lista = [1,2,3,4]
>>> aumenta(minha_lista + [5], 6)
[1, 2, 3, 4, 5, 6]
>>> minha_lista
[1, 2, 3, 4]
>>>


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