Física Computacional - FSC-5705

só um divisor

Funções definidas pelo usuário

Um grande problema
Figura 01:Um problema complexo.

A ideia por traz do uso de funções é dividir para conquistar. A experiencia mostra que em geral resolver pequenos problemas é muito mais produtivo do que encarar diretamente um problema altamente complexo. Por exemplo, o problema da paz mundial pode ser resolvido se são solucionado cada um dos "pequenos" problemas que constituem o problema maior, tais como fome, moradia e direitos civis. Por sua vez cada um desse problemas de, por dizer assim, II nível pode ser sub-dividido em outros problemas ainda menos complexos. Assim, o problema de direitos civis pode ser constituído por problemas de liberdade de expressão e direito a voto. Dessa forma poderíamos continuar a sub-dividir o nosso problema em outros sub-problemas menos complexos o que nos motiva a formular uma pergunta: já que sempre podemos dividir um problema complexo em pequenos problema, quantos sub-níveis temos que criar para resolver um dado problema. A resposta é: até que a solução ao problema possa ser claramente enxergada. No que tange à programação muitas vezes essa subdivisão é escolhida tomando como base pedaços de código que serão utilizados varias vezes por diversas parte do programa e também alguns recomendam a que esses pedaços não sejam muito maiores do que 1 ou 2 telas de computador, a fim de serem simples de estudar.

Pensando nisso foi introduzido no Python uma forma de criar pequenos programas que possam ser acoplados a um programa maior, melhor ainda, na realidade é permitido ter um banco de pequenos programas capazes de resolver pequenas tarefas mas que quando juntos podem resolver problemas altamente complexos. A esse tipo de subprograma se lhes chama de funções.

como deveria ser
Figura 02:O ideal de uma função.

Uma função praticamente tem a mesma cara de um programa tradicional a no ser pelo fato de que o nome dela você é quem determina. As funções tem a caraterística de receberem dados de entrada vindos do programa chamador (que pode ser outra função), esses dados são processados dentro do corpo da função e como resultado ela devolve nenhum ou mais dados.

Estrutura de uma função
Figura 03:A estrutura de uma função.

Para definir uma função utilizamos a estrutura seguinte: def nomeDaFuncao (variaveisDeEntrada): Note os dos pontos no fim. Por ser um bloco, segue a mesma ideia dos outros blocos, fica definido pela endentação do código embaixo da definição da função. As funções podem ser definidas em qualquer parte do programa, desde que seja antes da primeira vez que for chamada, mas é recomendável sempre definir elas logo no inicio do script. Você pode definir ela em outro arquivo: suponhamos que o arquivo tenha por nome minhasFuncoes.py, e uma das funções que você definiu dentro dele seja a superFuncao, para que seu script possa acessa à superFuncao você deve importar a função como temos realizado ate agora com os módulos, exemplo: from minhasFuncoes import superFuncao. As funções só devolvem um objeto, mas lembre que um objeto em Python pode ser uma estrutura extremadamente complexa como um numero, uma lista, tupla, um array ou mesmo pode não devolver nada (por exemplo uma função que grava dados no disco), o retorno de dados se faz mediante a utilização do comando return estrutura. Vejamos uns exemplos

            #!/usr/bin/env python

            import numpy as np

            def ForcaEletrica(q, r1, r2):
              """Esta funcao recebe como dados de entrada
              um vetor de duas dimensoes com as cargas das
              particulas, a posicao da particula 1 e a posicao
              de particula 2 """

              k = 9.0e9

              Dr  = r2 - r1
              mDr = np.sqrt(np.dot(Dr,Dr))

              F = k * q[0]*q[1] / mDr**3 * Dr

              return F

            #---------------------------------------------

            #Programa Principal
            q  = np.array([10.0, 2.0])
            r1 = np.array([0,0,0])
            r2 = np.array([10,10,10])

            F = ForcaEletrica(q, r1, r2)

            print F
          

Funções Lambda ou anônimas

São funções definidas na linha. Elas são legais porque podem entrar em lugares que uma função comum não consegue, por exemplo, na hora de definir uma lista, você pode, utilizando lambda, definir uma função para cada elemento da lista. A sintaxe é simples:

lambda argumento 1, argumento 2, ..., argumento N : expressão usando argumentos

para esclarecer vejamos um exemplo de uma função definida de forma normal e depois utilizando lambda:

            #!/usr/bin/env python

            #define factorial utilizando uma funcao
            def fatorial_def(x):
              if (x <= 1):
                return 1
              else:
                return x * fatorial_def(x-1)

            #define factorial utilizando lambda
            fatorial_lab = lambda x: 1 if (x <= 1) else x*fatorial_lab(x-1)

            print fatorial_def(5), fatorial_lab(5)
          
Blocos if dentro de uma função lambda debemos em geral se vale de uma propriedade de operadores lógicos do python: Quando se avalia um teste lógico composto (utilizando operadores) o python sempre devolve o objeto do lado esquerdo do operador caso seja certo o teste lógico e se é falso devolve o objeto do lado esquerdo, como se mostra no seguinte exemplo
            #!/usr/bin/env python

            a = 12
            b = 7
            c = 5

            s = ((a > 13) and b) or c
            print s

            s = ((a > 6) and b) or c
            print s
          
Note que equivale a fazer:
            #!/usr/bin/env python

            if (a > 13):
              s = b
            else:
              s = c
            print s

            if (a > c):
              s = b
            else:
              s = c
            print s
          
A Função lambda pode ter comportamentos complexos, como quando está aninhada dentro de um outra função, é como se tivéssemos uma função de função
            #!/usr/bin/env python

            def funcao (x):
              return lambda y: x+ y

            f = funcao(99)
            print f

            print f(10)
          
o resultado desses print são <function <lambda> at 0xdaa5f0> e depois 109.

Argumentos nominais

Você pode colocar argumentos que podem ser opcionais, para que isso funcione você simplesmente atribui um valor dentro do próprio argumento da função que será o valor padrão que a função irá utilizar, vejamos o exemplo:

            #!/usr/bin/env python
            
            def MinhaFuncao(arg1, arg2=0, arg3=5):
              a = [arg1, arg2, arg3]
              return a
              
            print MinhaFuncao(1.0, 2, 0)
            
            print MinhaFuncao(arg3=7, arg1=2)
            
            print MinhaFuncao(arg1=2)
          

Tarefa

  1. Crie um programa que gere um sequencia de números pseudo-aleatorios e utilize a subrotina de calculo de média para encontrar o valor médio
  2. Crie uma função que calcule o valor da aceleração devida à gravidade a qualquer altura $h$ da superfície dos oceanos da Terra. Utilize a equação da gravitação de Newton:
    $F = G\frac{M_1 m_2}{r^2}$
  3. Um marinheiro bêbedo tem que caminhar através de uma doca para chegar no seu navio. A doca tem um comprimento de $50$ passos e $20$ passos de largura. Um companheiro colocá o bêbado no meio da doca numa ponta e indica onde está o navio. Suponha que a probabilidade do marinheiro caminhar na direção do barco é de $50%$, porém tem $25%$ de probabilidade de ira à esquerda ou direita (ele sempre está olhando na direção do navio). Simule o caminho seguido pelo marinheiro e estime a probabilidade de chegar até o barco sem cair na água.