{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "##
Introdução à Lógica de Programação
\n", "###
Um curso prático para estudantes das Ciências da Vida
\n", "---\n", "##
Aula 2. Funções
\n", "#####
Instrutor: Pedro C. de Siracusa
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Funções são como liquidificadores! Inserimos bananas e um copo de leite, ajustamos alguns parâmetros como o tempo de processamento e por fim recebemos uma deliciosa vitamina! E o melhor é que não precisamos entender os mínimos detalhes sobre como os mecanismos internos do aparelho funcionam para fazer nossa vitamina. Tampouco precisamos ter construído o liquidificador para poder saboreá-la. Além disso, qualquer que fosse o liquidificador, esperaríamos obter algo não muito diferente de uma vitamina, desde que utilizássemos os mesmos ingredientes e regulagens.\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora de forma mais sofisticada, funções são construções que **encapsulam** um determinado comportamento que se espera executar múltiplas vezes durante a execução de um programa. São rotinas, que podem ter sido definidas pelo próprio programador ou por outros programadores. No caso do liquidificador, a rotina foi definida pelo próprio fabricante. O usuário só precisa saber o que precisa saber como operá-lo: que tipos de coisas deve fornecer como **entrada** (*input*) e o que deve esperar receber como **saída** (*output*).\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Funções facilitam sua vida por dois motivos principais. \n", "\n", "1. Permitem ao programador **abstrair** computações, sem precisar se preocupar a todo momento sobre os mínimos detalhes de como elas são de fato realizadas. Imagine se você tivesse que se preocupar com os detalhes sobre como um texto é imprimido na tela de seu computador toda vez que você precisasse desta funcionalidade... Felizmente a função `print`, que utilizamos no nosso programa \"Hello World\", permite que esta rotina seja abstraída para você, o que facilita bastante seu aprendizado! \n", "\n", "2. Permitem **compartilhar** e **reutilizar** código. Se você construir uma função que possa ajudar outras pessoas também, por que não compartilhar? Isso acontece bastante na comunidade de programadores, e os ajuda a não ficar \"reinventando a roda\" quando precisam de alguma funcionalidade que já foi implementada por alguém. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para começar a entender como trabalhar com funções, precisamos conhecer seus três componentes principais: *(i)* **nome**, *(ii)* **parâmetros** e *(iii)* **corpo**.\n", "\n", "Dar um **nome** às funções é uma forma simples de mantermos uma referência a elas. Embora possamos nomear funções conforme nossa vontade, é recomendável escolhermos nomes que nos digam algo sobre seu funcionamento. É fácil lembrar que a função `print`, por exemplo, serve para imprimir algo na tela.\n", "\n", "Os **parâmetros** fornecem um meio para \"afinarmos\" o comportamento de uma função para nossas necessidades. Por exemplo, no caso do liquidificador, alguns parâmetros relevantes seriam o tempo de processamento, ou a velocidade de rotação do motor. Os dados de entrada (*inputs*) são também passados para as funções através de parâmetros. No jargão da programação, nos referimos aos valores que passamos no lugar de cada um dos parâmetros como **argumentos**. Pense em um parâmetro como um *placeholder* para um valor, enquanto o argumento é o valor em si, passado para dentro da função através de um parâmetro.\n", "\n", "Por fim, no **corpo** da função especificamos todas as etapas que devem ser realizadas por ela. Estas etapas incluem o processamento dos dados de entrada (*inputs*) e a construção do resultado que ela produzirá como saída (*output*). No fim das contas, a ideia é que as computações descritas no corpo da função sejam abstraídas para o usuário da função." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Objetivos.\n", "\n", "Após esta aula você deverá ser capaz de:\n", "* **Definir** e **executar** funções;\n", "* Delimitar o **escopo** de uma função;\n", "* Reutilizar funcionalidades distribuídas em **pacotes** pela comunidade *Python*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Definindo uma nova função" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para definir uma nova função precisamos obedecer à seguinte sintaxe:\n", "* a palavra `def` indica que uma nova função está sendo definida;\n", "* após `def`, deve ser escrito o **nome** da função;\n", "* após o nome da função, entre parênteses, os **parâmetros** são separados por vírgulas;\n", "* Os dois pontos `:` após os parênteses indica que o **corpo** da função vem a seguir, no bloco de código abaixo;\n", "* O bloco de código deve ser escrito com uma **indentação**, o distanciamento em relação à margem esquerda da célula;\n", "* No fim do corpo da função, o valor de saída (*output*) é indicado após a palavra `return`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para ilustrar, vamos construir uma nova função, batizada com o nome `foo` (poderia ser qualquer outro nome, tipo `dinossauro`). Ela simplesmente deve imprimir no console os valores que passamos em cada um dos parâmetros e, no fim, retornar como *output* o valor $1$." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "def foo(par1, par2):\n", " print(par1)\n", " print(par2)\n", " return 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uma vez definida, a função fica guardada na memória, mas não é automaticamente executada. Para de fato executá-la, precisamos **chamá-la**, escrevendo seu nome e passando argumentos no lugar dos parâmetros, entre parênteses." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Santos\n", "Dumont\n" ] }, { "data": { "text/plain": [ "1" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "foo(\"Santos\", \"Dumont\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos definir agora uma função chamada `funcaoSoma`, que simplesmente soma dois números e retorna o resultado. Ela possui os parâmetros `n1` e `n2`, que esperam receber como valores (ou argumentos) dois valores numéricos. " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def funcaoSoma(n1,n2):\n", " res = n1 + n2\n", " return res" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "funcaoSoma(3,5)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "20" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "funcaoSoma(-3,23)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "42" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "funcaoSoma(42,0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos também armazenar o resultado de funções em variáveis! Vamos declarar as variáveis `a`, `b`, `c` e `d`, com quaisquer valores numéricos. Em seguida, usaremos a função `funcaoSoma` para somar `a` e `b` e armazene o resultado em uma variável `a_b`. Depois, usaremos novamente a função `funcaoSoma` para somar `c` e `d` e armazene o resultado em uma variável `c_d`. Finalmente, somaremos os valores em `a_b` e `c_d` usando a mesma `funcaoSoma`, armazenando o resultado final em uma variável `resFinal`." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "28" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 3\n", "b = 5\n", "c = 9\n", "d = 11\n", "\n", "a_b = funcaoSoma(a,b)\n", "c_d = funcaoSoma(c,d)\n", "\n", "resFinal = funcaoSoma( a_b, c_d )\n", "\n", "resFinal" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos também passar resultados de funções como argumentos para outras funções, sem precisar utilizar variáveis intermediárias! Podemos, por exemplo, realizar as mesmas computações da célula acima sem precisar declarar `a_b` e `c_d`." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "28" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 3\n", "b = 5\n", "c = 9\n", "d = 11\n", "\n", "resFinal = funcaoSoma( funcaoSoma(a,b), funcaoSoma(c,d) )\n", "\n", "resFinal" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Escopo de funções" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Funções operam em um \"contexto\" próprio. \n", "É como se criássemos uma \"bolha\" toda vez que executamos uma função. Tudo o que está dentro desta bolha não é visível de fora dela, mas todo o conteúdo que está fora dela é visível de dentro.\n", "Nos referimos ao contexto de \"dentro da bolha\" como o **escopo local** da função, e tudo o que está fora como o **escopo global**.\n", "\n", "Sendo assim, uma funçao \"enxerga\" todas as variáveis em escopo local, ou seja, aquelas que são definidas dentro dela própria (incluindo os parâmetros); e as variáveis em escopo global, definidas fora dela.\n", "No entanto, variáveis locais, definidas dentro de funções, não são visíveis de fora delas!" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Variável a ANTES da execução da função: 3\n", "Variável b ANTES da execução da função: 5\n", "---\n", "Variável a DENTRO da função foo: 7\n", "Variável b DENTRO da função foo: 5\n", "Variável m DENTRO da função foo: 11\n", "---\n", "Variável a APÓS a execução da função 3\n", "Variável b APÓS da execução da função: 5\n" ] }, { "ename": "NameError", "evalue": "name 'm' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Variável a APÓS a execução da função\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Variável b APÓS da execução da função:\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Variável m\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mm\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'm' is not defined" ] } ], "source": [ "a = 3\n", "print(\"Variável a ANTES da execução da função:\",a)\n", "\n", "b = 5\n", "print(\"Variável b ANTES da execução da função:\",b)\n", "\n", "def foo(): # A função é apenas definida aqui\n", " a = 7\n", " print(\"Variável a DENTRO da função foo:\",a)\n", " print(\"Variável b DENTRO da função foo:\",b)\n", " m = 11\n", " print(\"Variável m DENTRO da função foo:\",m)\n", " \n", "print('---')\n", "foo() # A função apenas é de fato executada aqui\n", "print('---')\n", "\n", "print(\"Variável a APÓS a execução da função\",a)\n", "print(\"Variável b APÓS da execução da função:\",b)\n", "print(\"Variável m\",m)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Obs:** Um mesmo nome pode ser dado para variáveis em escopos diferentes (que podem conter valores diferentes)! Nestes casos, a variável que foi definida em escopo local prevalece sobre a global." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Reutilizando funcionalidades" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O segredo para um bom desempenho como programador é jamais reinventar a roda! Sempre que possível, devemos buscar reutilizar código que já foi escrito por outros programadores. \n", "**Pacotes** são uma forma eficiente usada pelos programadores para estruturar e compartilhar seu código com a comunidade, sendo criados com o intuito de entregar aos usuários um conjunto de funcionalidades. \n", "O código dentro de um pacote é organizado em **módulos**, cada qual contendo um conjunto de funções e variáveis que implementam funcionalidades mais específicas.\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Veja uma lista de pacotes potencialmente interessantes para um biólogo:\n", "* [Biopython](http://biopython.org/) com funcionalidades específicas para aplicações em biologia molecular computacional, na **bioinformática**;\n", "* [Numpy](http://www.numpy.org/) para computação científica, com representações e operações otimizadas sobre objetos matemáticos;\n", "* [Pandas](http://pandas.pydata.org/) para representação de *Data frames* e análise de dados;\n", "* [Matplotlib](https://matplotlib.org/) para construção de figuras (visualização de dados);\n", "* [Statsmodels](http://www.statsmodels.org/stable/index.html) para trabalhar com modelos estatísticos;\n", "* [ScikitLearn](http://scikit-learn.org/stable/) para trabalhar com algoritmos de aprendizado de máquina;\n", "* [Networkx](https://networkx.github.io/) para construir modelos em redes complexas;\n", "* [ArcPy](http://desktop.arcgis.com/en/arcmap/10.3/analyze/arcpy/what-is-arcpy-.htm) é um pacote para automação de tarefas de análise e geração de mapas em ArcGis;\n", "* [Matplotlib Basemap](https://matplotlib.org/basemap/) é um conjunto de ferramentas para plotar mapas em python, utilizando como base a *Matplotlib*;\n", "* [Pygbif](http://pygbif.readthedocs.io/en/latest/) é um cliente *Python* que facilita o acesso a dados de ocorrência de espécies no [GBIF](https://www.gbif.org/) (Global Biodiversity Information Facility)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Em *Python*, existem algumas funções que são carregadas com a inicialização do interpretador, por serem utilizadas de forma mais ampla pelos programadores. Alguns exemplos são as funções `print`, `type`, `range` e `open`. \n", "Outras, no entanto, são mais específicas e portanto não são carregadas automaticamente.\n", "Usamos a palavra `import` para carregar um pacote ou módulo." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O módulo `math`, por exemplo, reúne um conjunto de funções e constantes que facilitam muito trabalhar com elementos da matemática. Estas funções não são carregadas automaticamente, e portanto devemos **importar o módulo** para utilizá-las.\n", "Vamos carregar o módulo `math` e, em seguida, executar a função `factorial` provida por ele. Conforme podemos consultar na documentação, esta função espera um número como *input* e retorna seu fatorial, como *output*." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fatorial de 1: 1\n", "Fatorial de 2: 2\n", "Fatorial de 3: 6\n", "Fatorial de 10: 3628800\n" ] } ], "source": [ "import math\n", "\n", "print(\"Fatorial de 1:\",math.factorial(1))\n", "print(\"Fatorial de 2:\",math.factorial(2))\n", "print(\"Fatorial de 3:\",math.factorial(3))\n", "print(\"Fatorial de 10:\",math.factorial(10))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercícios" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Ex 1.** Defina uma função `calculaMedia`, que calcula a média entre **três** números quaisquer. Em seguida, execute sua função com valores diferentes. O que acontece se você definir a função com três parâmetros mas passar um número diferente de argumentos?" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "def calculaMedia(n1,n2,n3):\n", " res = (n1+n2+n3)/3\n", " return res" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.0" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "calculaMedia(1,3,5)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.6666666666666665" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "calculaMedia(1,2,5)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "calculaMedia() missing 1 required positional argument: 'n3'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Este código vai gerar um erro: A função esperava três argumentos mas nós só passamos 2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mcalculaMedia\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: calculaMedia() missing 1 required positional argument: 'n3'" ] } ], "source": [ "# Este código vai gerar um erro: A função esperava três argumentos mas nós só passamos 2\n", "calculaMedia(1,5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Ex 2.** Defina uma função `multiplica`, que multiplica dois números quaisquer. Em seguida, declare as variáveis `a`, `b` e `c` com quaisquer valores numéricos. Multiplique `a` e `b` utilizando a função `multiplica` e, em seguida, execute novamente a função `multiplica` para multiplicar o resultado do passo anterior pelo número em `c`." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "def multiplica(n1,n2):\n", " return n1*n2" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "105" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a=3\n", "b=5\n", "c=7\n", "\n", "multiplica( multiplica(a,b), c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Ex 3.** A função `exponencia` abaixo deve realizar uma exponenciação do número `n1` à potência `n2`. Mas ao executar a célula para definir a função recebemos uma mensagem de erro. Corrija o código e experimente executar a função com alguns pares de números." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "def exponencia(n1,n2):\n", " return n1**n2" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "exponencia(2,3)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "25" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "exponencia(5,2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Ex 4.** A função `mensagemMeuNome` abaixo deveria receber como argumento o seu nome, imprimir uma mensagem com ele e, por fim, retornar o seu nome como *output*. No entanto, existe um *bug*. Você consegue identificá-lo e corrigí-lo? " ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Olá! Me chamo Pedro\n" ] }, { "data": { "text/plain": [ "'Pedro'" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nome = \"Luke Skywalker\"\n", "\n", "def mensagemMeuNome( meuNome ):\n", " print(\"Olá! Me chamo\", meuNome)\n", " return meuNome\n", "\n", "mensagemMeuNome(\"Pedro\") # insira seu nome entre parênteses" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Ex 5.** A função `type` é automaticamente carregada com a inicialização do interpretador *Python*. Você consegue descobrir o que ela faz? Experimente passar diferentes tipos de dados." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Tipo de 'Algum texto': \n", "Tipo de 43: \n", "Tipo de True: \n", "Tipo de 32.21: \n" ] } ], "source": [ "# A função type retorna o tipo do valor passado como argumento\n", "print(\"Tipo de 'Algum texto':\", type(\"Algum texto\") )\n", "print( \"Tipo de 43:\",type(43) )\n", "print( \"Tipo de True:\", type(True) )\n", "print( \"Tipo de 32.21:\", type(32.21) )" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.5" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": false, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }