{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Ferramentas funcionais\n", "================\n", "\n", "A linguagem Python fornece alguns comandos predefinidos, tais como `map`, `filter`, `reduce`, bem como `lambda` (para criar funções anônimas) e compreensões de lista. Tais comandos são típicos de linguagens funcionais, das quais LISP provavelmente é a mais conhecida.\n", "\n", "A programação funcional pode ser extremamente poderosa e um dos pontos fortes da linguagem Python é que ela permite programar usando (i) o estilo de programação procedural (ou imperativa), (ii) o estilo orientado a objetos e (iii) o estilo funcional. São os programadores quem escolhem quais ferramentas selecionar, qual estilo e como misturá-los para resolver melhor um determinado problema.\n", "\n", "Neste capítulo, fornecemos alguns exemplos para o uso dos comandos listados acima.\n", "\n", "Funções anônimas\n", "-------------------\n", "\n", "Todas as funções que vimos em Python até agora foram definidas através da palavra-chave `def`. Por exemplo:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def f(x):\n", " return x ** 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Esta função tem o nome `f`. Uma vez que a função está definida (ou seja, o interpretador Python encontrou a linha `def`), podemos chamar a função usando seu nome. Por exemplo:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "y = f(6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Às vezes, precisamos definir uma função que só é usada uma vez, ou queremos criar uma função, mas não precisamos de um nome para ela (como para criar fechamentos). Neste caso, isso é chamado de *função anônima*, pois não possui um nome. Em Python, a palavra-chave `lambda` pode criar uma função anônima.\n", "\n", "Criamos uma função (nomeada) primeiro, verificamos seu tipo e comportamento:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def f(x):\n", " return x ** 2\n", "\n", "f" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "function" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(f)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "100" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora fazemos o mesmo com uma função anônima:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ ">" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lambda x: x ** 2" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "function" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(lambda x: x ** 2)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "100" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(lambda x: x ** 2)(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Isto funciona exatamente da mesma maneira, mas - como a função anônima não tem um nome - precisamos definir a função (através da expressão `lambda`) - toda vez que precisamos dela.\n", "\n", "Funções anônimas podem levar mais de um argumento:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "30" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(lambda x, y: x + y)(10, 20)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "60" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(lambda x, y, z: (x + y) * z )(10, 20, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Veremos alguns exemplos usando `lambda` que esclarecerão os casos típicos de uso.\n", "\n", "`map`\n", "---\n", "\n", "A função `lst2 = map(f, s)` aplica uma função `f` a todos os elementos em uma seqüência `s`. O resultado de `map` pode ser convertido em uma lista com o mesmo comprimento que `s`:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def f(x): \n", " return x ** 2\n", "lst2 = list(map(f, range(10)))\n", "lst2" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Banana', 'Maçã', 'Laranja']" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(str.capitalize, ['banana', 'maçã', 'laranja']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Frequentemente, isto é combinado com a função anônima`lambda`:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(lambda x: x ** 2, range(10) ))" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Banana', 'Maçã', 'Laranja']" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(lambda s: s.capitalize(), ['banana', 'maçã', 'laranja']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`filter`\n", "------\n", "\n", "A função `lst2 = filter(f, lst)` aplica a função `f` a todos os elementos em uma sequencia `s`. A função `f` deve retornar `True` ou `False`. Isto faz com que uma lista contenha somente aqueles elementos *s**i* da sequencia `s` para os quais `f`(*s**i*) tem valor de retorno `True`." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[6, 7, 8, 9, 10]" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def maior_que_5(x):\n", " if x > 5:\n", " return True\n", " else:\n", " return False\n", "\n", "list(filter(maior_que_5, range(11)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O uso de `lambda` pode simplificar isto significativamente:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[6, 7, 8, 9, 10]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(filter(lambda x: x > 5, range(11)))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['smith', 'bob']" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nomes_conhecidos = ['smith', 'miller', 'bob']\n", "list(filter( lambda nome : nome in nomes_conhecidos, \\\n", " ['ago', 'smith', 'bob', 'carl']))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Compreensões de lista\n", "------------------\n", "\n", "Compreensões de lista fornecem uma maneira concisa de criar e modificar listas sem recorrer ao uso de `map()`, `filter()` e/ou `lambda`. A definição da lista resultante tende a ser mais clara do que as listas criadas usando essas construções. Cada compreensão de lista consiste de uma expressão seguida por uma cláusula `for`, zero ou mais cláusulas ` for` ou `if`. O resultado será uma lista resultante da avaliação da expressão no contexto das cláusulas `for` e `if` que a seguem. Se a expressão for avaliada para uma tupla, ela deverá estar entre parênteses.\n", "\n", "Alguns exemplos tornarão isso mais claro:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['banana', 'framboesa', 'maracujá']" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "frutas_frescas = [' banana', ' framboesa ', 'maracujá ']\n", "[weapon.strip() for weapon in frutas_frescas]" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[6, 12, 18]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "vec = [2, 4, 6]\n", "[3 * x for x in vec]" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[12, 18]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[3 * x for x in vec if x > 3]" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[3 * x for x in vec if x < 2]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[2, 4], [4, 16], [6, 36]]" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[[x, x ** 2] for x in vec]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos também usar compreensões de lista para modificar a lista de inteiros retornada pelo comando `range`, de modo que nossos elementos subsequentes na lista aumentem por frações não-inteiras. " ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x*0.5 for x in range(10)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos agora revisitar os exemplos da seção `filter`." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[6, 7, 8, 9, 10]" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x for x in range(11) if x>5 ]" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['smith', 'bob']" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[nome for nome in ['ago','smith','bob','carl'] \\\n", " if nome in nomes_conhecidos]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "e os exemplos da seção `map`" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x ** 2 for x in range(10) ]" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Banana', 'Maçã', 'Laranja']" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[fruta.capitalize() for fruta in ['banana', 'maçã', 'laranja'] ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "todas podem ser expressas através de compreensões de lista.\n", "\n", "Mais detalhes\n", "\n", "- Tutorial Python [5.1.4 Compreensões de lista](http://docs.python.org/tutorial/datastructures.html#list-comprehensions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`reduce`\n", "------\n", "\n", "A função `reduce` assume uma função binária `f(x, y)`, uma sequência `s` e um valor de início `a0`. Em seguida, aplica a função `f` ao valor de início` a0` e o primeiro elemento na seqüência: `a1 = f(a, s[0])`. O segundo elemento (`s [1]`) da sequência é processado da seguinte forma: a função `f` é chamada com argumentos `a1` e `s[1]`, ou seja, `a2 = f (a1, s[1])`. Desta forma, toda a sequência é processada. `reduce` retorna um único número.\n", "\n", "Isso pode ser usado, por exemplo, para calcular uma soma de números em uma seqüência se a função `f(x, y)` retornar `x + y`:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from functools import reduce" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "55" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def add(x,y):\n", " return x+y\n", "\n", "reduce(add, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 0)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "155" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reduce(add, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Podemos modificar a função `add` para fornecer algum detalhe a mais sobre o processo:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "add(x=0, y=1) -> 1\n", "add(x=1, y=2) -> 3\n", "add(x=3, y=3) -> 6\n", "add(x=6, y=4) -> 10\n", "add(x=10, y=5) -> 15\n", "add(x=15, y=6) -> 21\n", "add(x=21, y=7) -> 28\n", "add(x=28, y=8) -> 36\n", "add(x=36, y=9) -> 45\n", "add(x=45, y=10) -> 55\n" ] }, { "data": { "text/plain": [ "55" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def add_verbose(x, y):\n", " print(\"add(x=%s, y=%s) -> %s\" % (x, y, x+y))\n", " return x+y\n", "\n", "reduce(add_verbose, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pode ser instrutivo usar uma função assimétrica `f`, tal como `add_len(n,s)`, onde `s` é uma sequencia e a função retorna `n+len(s)` (sugestão de Thomas Fischbacher):" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "13" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def add_len(n,s):\n", " return n+len(s)\n", "\n", "reduce(add_len, [\"Este\",\"é\",\"um\",\"teste.\"],0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como antes, usaremos uma versão mais prolixa (verbosa) da função binária para vermos o que está acontecendo:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "add_len(n=0, s=Este) -> 4\n", "add_len(n=4, s=é) -> 5\n", "add_len(n=5, s=um) -> 7\n", "add_len(n=7, s=teste.) -> 13\n" ] }, { "data": { "text/plain": [ "13" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def add_len_verbose(n,s):\n", " print(\"add_len(n=%d, s=%s) -> %d\" % (n, s, n+len(s)))\n", " return n+len(s)\n", "\n", "reduce(add_len_verbose, [\"Este\",\"é\",\"um\",\"teste.\"],0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Outra maneira de entender o que a função `reduce` faz é examinar a seguinte função (gentilmente fornecida por Thomas Fischbacher), que se comporta como `reduce`, mas explica o que ela faz:\n", "\n", "Aqui está um exemplo usando a função `explain_reduce`." ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/Users/fangohr/hg/teaching-python/book/text/code\n" ] } ], "source": [ "cd code/" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from explain_reduce import explain_reduce\n", "def f(a,b):\n", " return a+b\n", "\n", "reduce(f, [1,2,3,4,5], 0)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Step 0: value-so-far=0 next-list-element=1\n", "Step 1: value-so-far=1 next-list-element=2\n", "Step 2: value-so-far=3 next-list-element=3\n", "Step 3: value-so-far=6 next-list-element=4\n", "Step 4: value-so-far=10 next-list-element=5\n", "Done. Final result=15\n" ] }, { "data": { "text/plain": [ "15" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "explain_reduce(f, [1,2,3,4,5], 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`reduce` é frequentemente combinado com `lambda`:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reduce(lambda x,y:x+y, [1,2,3,4,5], 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Existe também o módulo `operator` que fornece operadores Python padrão como funções. Por exemplo, a função `operator.__add__(a, b)` é executada quando o Python avalia o código como `a + b`. Estes geralmente são mais rápidos do que as expressões `lambda`. Poderíamos escrever o exemplo acima como" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import operator\n", "reduce(operator.__add__, [1,2,3,4,5], 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use `help(’operator’)` para ver a lista completa de funções `operator`.\n", "\n", "Por que não usar apenas laços `for`?\n", "---------------------------\n", "\n", "Vamos comparar o exemplo apresentado no início do capítulo escrito (i) usando um laço `for` e (ii) compreensão de lista. Mais uma vez, queremos calcular os números 02, 12, 22 , 32,... até (*n* - 1)2 para um dado *n*.\n", "\n", "Implementação (i) usando um laço `for` com *n* = 10:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": true }, "outputs": [], "source": [ "y = []\n", "for i in range(10):\n", " y.append(i ** 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Implementação (ii) usando compreensão de lista:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": true }, "outputs": [], "source": [ "y = [x ** 2 for x in range(10)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ou usando `map`:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": true }, "outputs": [], "source": [ "y = map(lambda x: x ** 2, range(10))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As versões que utilizam compreensão de lista e `map` encaixam-se em uma linha de código, enquanto um laço `for` precisa de três linhas. Este exemplo mostra que o código funcional resulta em expressões *concisas*. Normalmente, o número de erros que um programador comete é por linha de código escrita, de modo que quanto menos linhas de código tivermos, menos *bugs* teremos de encontrar.\n", "\n", "Muitas vezes, os programadores descobrem que, inicialmente, as ferramentas de processamento de lista apresentadas neste capítulo parecem menos intuitivas do que o uso de laços `for` para processar cada elemento em uma lista individualmente, mas isso - ao longo do tempo - tende a ser valorizado por eles como um estilo de programação mais funcional.\n", "\n", "Rapidez\n", "-----\n", "\n", "As ferramentas funcionais descritas neste capítulo também podem ser mais rápidas do que laços explícitos (`for` ou `while`) sobre os elementos da lista.\n", "\n", "O programa `rapidez_compreensao_lista.py` abaixo calcula $\\sum_{i=0}^{N-1} i^2$ para um grande valor de *N* usando 4 métodos diferentes e registra o tempo de execução:\n", "\n", "- Método 1: laço `for` (com lista pré-alocada, armazenamento de *i*2 na lista, em seguida, usando a função predefinida `sum`)\n", "\n", "- Método 2: laço `for` sem lista (atualizando `sum` à medida que o laço `for` progride)\n", "\n", "- Método 3: usando compreensão de lista\n", "\n", "- Método 4: usando `numpy`.\n", "\n", "O programa produz a seguinte saída:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('N =', 10000000)\n", "('for-loop1', 5.317609071731567)\n", "('for-loop2', 4.719789981842041)\n", "('listcomp', 3.4225449562072754)\n", "('numpy', 0.04654884338378906)\n", "O metodo mais lento eh 114.2 vezes mais lento do que o metodo mais rapido.\n" ] } ], "source": [ "!python static/data/rapidez_compreensao_lista.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O desempenho de execução real depende do computador. O desempenho relativo pode depender de versões do Python e suas bibliotecas de suporte (como `numpy`) que usamos.\n", "\n", "Com a versão atual (python 3.4, numpy 1.10, em uma máquina x64 que executa o OSX), vemos que os métodos 1 e 2 (para laço sem lista e com lista pré-alocada) são mais lentos, seguidos um tanto mais próximos de uma compreensão de lista levemente mais rápida. O método mais rápido é o número 4 (usando numpy)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para referência, aqui está o código-fonte do programa:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "# -*- coding: utf-8 -*-\r\n", "\"\"\"Compara cálculos de \\sum_i x_i^2 for i = 0,...N-1\r\n", "\r\n", "Usamos (i) laços for e list, (ii) laço for, (iii) compreensão de lista\r\n", "e (iv) numpy.\r\n", "\r\n", "Usamos números em ponto flutuante para evitar usar os longos inteiros do\r\n", "Python (que provavelmente fariam a medição do tempo menos representativa)\r\n", "\"\"\"\r\n", "\r\n", "import time\r\n", "import numpy\r\n", "N = 10000000\r\n", "\r\n", "\r\n", "def timeit(f, args):\r\n", " \"\"\" Dada uma função f e uma tupla contendo argumentos para f, \r\n", " esta funcao chama f(*args), e mede e retorna o tempo de \r\n", " execucao em segundos.\r\n", " \r\n", " O valor de retorno é uma tupla: a entrada 0 é o tempo, \r\n", " a entrada 1 é o valor de retorno de f.\"\"\"\r\n", "\r\n", " starttime = time.time()\r\n", " y = f(*args) # usa tupla de argumentos como entrada\r\n", " endtime = time.time()\r\n", " return endtime - starttime, y\r\n", "\r\n", "\r\n", "def forloop1(N):\r\n", " s = 0\r\n", " for i in range(N):\r\n", " s += float(i) * float(i)\r\n", " return s\r\n", "\r\n", "\r\n", "def forloop2(N):\r\n", " y = [0] * N\r\n", " for i in range(N):\r\n", " y[i] = float(i) ** 2\r\n", " return sum(y)\r\n", "\r\n", "\r\n", "def listcomp(N):\r\n", " return sum([float(x) * x for x in range(N)])\r\n", "\r\n", "\r\n", "def numpy_(N):\r\n", " return numpy.sum(numpy.arange(0, N, dtype='d') ** 2)\r\n", "\r\n", "# inicio do programa principal\r\n", "timings = []\r\n", "print(\"N =\", N)\r\n", "forloop1_time, f1_res = timeit(forloop1, (N,))\r\n", "timings.append(forloop1_time)\r\n", "print(\"for-loop1\", forloop1_time)\r\n", "forloop2_time, f2_res = timeit(forloop2, (N,))\r\n", "timings.append(forloop2_time)\r\n", "print(\"for-loop2\", forloop2_time)\r\n", "listcomp_time, lc_res = timeit(listcomp, (N,))\r\n", "timings.append(listcomp_time)\r\n", "print(\"listcomp\", listcomp_time)\r\n", "numpy_time, n_res = timeit(numpy_, (N,))\r\n", "timings.append(numpy_time)\r\n", "print(\"numpy\", numpy_time)\r\n", "\r\n", "#assegura que metodos diferentes forneçam resultados idênticos\r\n", "assert f1_res == f2_res\r\n", "assert f1_res == lc_res\r\n", "\r\n", "# Permite um pouco de diferença para o cálculo via numpy\r\n", "numpy.testing.assert_approx_equal(f1_res, n_res)\r\n", "\r\n", "print(\"O metodo mais lento eh {:.1f} vezes mais lento do que o metodo mais rapido.\".format(\r\n", " max(timings)/min(timings)))" ] } ], "source": [ "!cat static/data/rapidez_compreensao_lista.py" ] } ], "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.1" } }, "nbformat": 4, "nbformat_minor": 1 }