Foto: Chris RiedNeste artigo, você entenderá o que é um paradigma funcional e como usar a programação funcional no Python. Você também aprenderá sobre
abstração de lista e outras compreensões de lista.
Paradigma funcional
Em um paradigma imperativo, você escreve um programa especificando uma sequência de ações que são executadas posteriormente. Neste momento, os estados (
aprox. Tradutor: variáveis, matrizes, etc. ) mudam. Por exemplo, deixe a variável A armazenar o valor 5, depois você altera o valor dessa variável. Você usa variáveis para que seus valores mudem.
Em um paradigma funcional, você não diz ao computador o que fazer, mas especifica a natureza das próprias ações. Qual é o maior divisor comum de um número, o resultado de cálculos de 1 a n etc.
Portanto, as variáveis não mudam. Depois que uma variável é inicializada, seu valor é salvo para sempre (observe que, em linguagens funcionais puras, elas nem são chamadas de variáveis). Portanto, no paradigma funcional, as funções não têm
efeitos colaterais . Um efeito colateral pode ser definido como o momento durante o qual uma função muda algo além de seus limites. Veja um exemplo:
a = 3 def some_func(): global a a = 5 some_func() print(a)
O resultado da execução desse código é 5. Na programação funcional, a alteração de variáveis é proibida e a alteração de funções de algo além de suas fronteiras. Tudo o que a função pode fazer é calcular / processar algo e retornar o resultado.
Agora, você pode estar pensando: “Sem variáveis, sem efeitos colaterais? Por que isso é bom? Realmente boa pergunta.
Se uma função foi chamada duas vezes com os mesmos parâmetros, obviamente ela retornará o mesmo resultado. Se você estudou algo sobre
funções matemáticas , apreciará esta oportunidade. Isso é chamado de transparência do link ou transparência referencial. Como as funções não têm efeitos colaterais, se você estiver desenvolvendo um programa de cálculo, poderá acelerar o processo de execução. Se o programa souber que func (2) é 3, podemos lembrar disso. Isso evita que a função seja chamada novamente quando já sabemos o resultado.
Geralmente, na programação funcional, os loops não são usados. Recursão é usada. Recursão é um conceito matemático; na verdade, significa "alimentar algo para si mesmo". Em uma função recursiva, a própria função se autodenomina o papel de uma subfunção. Aqui está um exemplo de uma função recursiva no Python:
def factorial_recursive(n):
Algumas linguagens de programação são
preguiçosas . Isso significa que eles estão calculando tudo no último momento. Suponha que se o código deve executar 2 + 2, o programa funcional calculará o resultado somente quando o resultado for necessário. Aprenderemos sobre a preguiça do Python um pouco mais tarde.
Mapa
Para entender o mapa, você deve primeiro lidar com contêineres iteráveis. Esse é um contêiner no qual você pode "passar por cima". Geralmente, são listas ou matrizes, mas existem muitos contêineres no Python. Você pode até criar seu próprio contêiner, introduzindo
métodos mágicos . Esses métodos, como APIs, ajudam os objetos a se tornarem mais pitônicos. Existem 2 métodos para tornar um objeto iterável:
class Counter: def __init__(self, low, high):
O primeiro método mágico é "___iter__" ou dunder (sublinhado duas vezes por sublinhados) iter retorna um objeto iterável, geralmente usado no início de um loop. Dunder next (__next__) retorna o próximo objeto.
Verifique isto:
for c in Counter(3, 8): print(c)
Resultado da execução:
3
4
5
6
7
8
No Python, um iterador é um objeto que possui apenas o método __iter__. Isso significa que você pode acessar o local das células do objeto (contêiner), mas não pode "caminhar" por elas. Alguns objetos têm apenas o maravilhoso método __next__, sem o método mágico __iter__, por exemplo, definido (mais sobre isso posteriormente). Neste artigo, abordaremos tudo relacionado a objetos iteráveis.
Agora que sabemos o que é um objeto iterável, vamos retornar à função map. Essa função nos permite aplicar a ação de alguma outra função a cada elemento em um contêiner iterado. Queremos aplicar uma função a cada elemento da lista, isso é possível para quase todos os contêineres iteráveis. Map, utiliza dois argumentos: a função a ser aplicada e o contêiner (lista, etc.).
map(function, iterable)
Suponha que tenhamos uma lista com os seguintes elementos:
[1, 2, 3, 4, 5]
E queremos colocar cada elemento ao quadrado, isso pode ser feito assim:
x = [1, 2, 3, 4, 5] def square(num): return num*num print(list(map(square, x)))
Funções funcionais no Python são preguiçosas. Se não adicionarmos "list ()", a função armazenará a descrição do contêiner (lista), e não a própria lista. Precisamos dizer diretamente ao Python para converter isso em uma lista.
É um pouco estranho passar de uma definição não preguiçosa para uma preguiçosa tão repentinamente. Você se acostumará a isso se pensar mais de uma maneira funcional do que um imperativo.
As funções de escrita, por exemplo, “quadrado (num)” são normais, mas não totalmente corretas. Precisamos declarar uma função inteira apenas para usá-la no mapa? Isso pode ser simplificado através da introdução de funções lambda (anônimas).
Expressões lambda
Expressões lambda são funções em uma linha, por exemplo, aqui está uma expressão lambda que esquadrinha o número resultante:
square = lambda x: x * x
E, execute o seguinte:
>>> square(3)
9
Eu posso te ouvir. "Brandon, onde estão os argumentos?" O que é isso tudo? Isso não é como uma função. ”
Sim, pode ser confuso, mas pode ser explicado. Nesta linha, atribuímos algo à variável "quadrado". Esta parte:
lambda x: x * x
Diz ao Python que estamos usando a função lambda, e a entrada é chamada x. Tudo depois dos dois pontos é o que acontecerá com a entrada e obteremos o resultado automaticamente mais tarde.
Para o nosso programa assumiu a forma de uma linha, você precisa fazer o seguinte:
x = [1, 2, 3, 4, 5] print(list(map(lambda num: num * num, x)))
Portanto, nas expressões lambda, os argumentos estão à esquerda e as ações nelas estão à direita. Isso é um pouco desarrumado, ninguém nega. A verdade é que há algo nele, para escrever esse código funcional. Além disso, é muito legal converter funções em linha única.
Reduzir
Reduzir é uma função que transforma um contêiner iterável em uma coisa. Ou seja, são feitos cálculos que transformam a lista em um único número. É assim:
reduce(function, list)
Nós podemos (e freqüentemente usaremos) funções lambda como argumento da função.
Se quisermos multiplicar todos os números da lista, isso pode ser feito assim:
product = 1 x = [1, 2, 3, 4] for num in x: product = product * num
E com reduzir ficará assim:
from functools import reduce product = reduce((lambda x, y: x * y),[1, 2, 3, 4])
O resultado será o mesmo, mas o código é mais curto e com o conhecimento de programação funcional para usá-lo com mais precisão.
Filtro
A função de filtro pega um contêiner iterável e o filtra de acordo com uma regra especificada (também uma função).
Normalmente, é necessária uma função e uma lista como entrada. Posteriormente, aplica uma função a cada elemento da lista, se a função retornar True, nada acontecerá e, se False, o elemento será removido da lista.
Sintaxe:
filter(function, list)
Vejamos um exemplo sem usar o filtro:
x = range(-5, 5) new_list = [] for num in x: if num < 0: new_list.append(num)
Juntamente com o filtro:
x = range(-5, 5) all_less_than_zero = list(filter(lambda num: num < 0, x))
Funções de ordem superior
Funções de ordem superior podem aceitar funções como argumentos e retorná-las. Um exemplo simples seria assim:
def summation(nums): return sum(nums) def action(func, numbers): return func(numbers) print(action(summation, [1, 2, 3]))
Ou um exemplo é ainda mais simples:
def rtnBrandon(): return "brandon" def rtnJohn(): return "john" def rtnPerson(): age = int(input("What's your age?")) if age == 21: return rtnBrandon() else: return rtnJohn()
Lembre-se anteriormente, eu disse que a programação funcional real não usa variáveis. Funções de ordem superior tornam isso possível. Você não precisa salvar a variável em algum lugar se estiver passando informações através de um longo "túnel" de funções.
Todas as funções no Python são objetos de primeira classe. Um objeto da primeira classe é definido como tal, que corresponde a um ou mais dos seguintes parâmetros:
- Cria um ciclo de serviço
- Atribuído a uma variável ou item em uma estrutura de dados
- Passado como argumento de função
- Retornado como resultado da execução da função
Portanto, todas as funções no Python são objetos de primeira classe e podem ser usadas como funções de ordem superior.
Aplicação parcial
O uso parcial (também interrupção) é um pouco estranho, mas muito legal. Você pode chamar a função sem usar todos os argumentos fornecidos. Vejamos um exemplo. Queremos criar uma função que use 2 argumentos, a base e o grau, e retorne a base elevada ao poder, assim:
def power(base, exponent): return base ** exponent
Agora precisamos criar uma função separada para quadratura e calculá-la usando a função power:
def square(base): return power(base, 2)
Funciona, mas e se quisermos cubar um número? Ou no 4º grau? Você deve escrever essas funções para sempre? Claro que você pode. Mas os programadores são preguiçosos. Se você repetir a mesma coisa várias vezes, provavelmente existe uma maneira de fazê-lo mais rapidamente e parar de repetir. Aplicação parcial pode ser usada aqui. Vejamos um exemplo da função power usando aplicação parcial:
from functools import partial square = partial(power, exponent=2) print(square(2))
Não é legal? Podemos chamar uma função que precisa de 2 argumentos, usando apenas 1 e especificando qual será o segundo por si próprio.
Você também pode usar um loop para simular uma função de potência que funcione com cubos até a 1000ª potência.
from functools import partial powers = [] for x in range(2, 1001): powers.append(partial(power, exponent = x)) print(powers[0](3))
A programação funcional não corresponde aos cânones pitônicos
Você deve ter notado que muitas das coisas que queremos fazer na programação funcional giram em torno de listas. Além da função de redução e aplicação parcial, todas as funções que você viu geram listas. Guido (o criador do Python`a) não gosta de coisas funcionais no Python`e, pois o Python possui seu próprio método de criação de listas.
Se você escrever "import this" no console, você obterá:
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one — and preferably only one — obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea — let's do more of those!
Este é o Python Zen. Este é um versículo sobre o que significa ser um pythonist. A parte que nos interessa é:
Deve haver uma - e de preferência apenas uma - maneira óbvia de fazê-lo.
Deve haver apenas uma - e de preferência apenas uma - maneira óbvia de fazer alguma coisa.
No Python, o mapa e o filtro podem fazer o mesmo que a abstração da lista (
link ). Isso viola uma das regras do Python-Zen, portanto, essa parte da programação funcional não é "pitônica".
As próximas coisas a serem discutidas são a função lambda. No Python, uma função lambda é uma função normal. E, de fato, é açúcar sintático. Ambas as partes fazem a mesma coisa:
foo = lambda a: 2 def foo(a): return 2
Uma função padrão ainda pode ser a mesma que uma função lambda, mas não vice-versa. Uma função Lambda não pode ser a mesma que uma função normal.
Essa foi uma pequena observação sobre por que a programação funcional não se encaixa perfeitamente na ideologia pitônica. Mencionei a abstração das listas (
também inclusão na lista ), agora vamos falar sobre isso.
Abstração de lista
Eu já disse que tudo o que pode ser feito usando o mapa e o filtro pode ser feito usando a abstração da lista. Nesta parte, discutiremos isso.
Abstração de lista é uma maneira de criar listas em Python. Sintaxe:
[function for item in iterable]
Vamos agrupar cada item da lista, por exemplo:
print([x * x for x in [1, 2, 3, 4]])
Ok, podemos ver como aplicar a função a cada elemento da lista. Como contornar o filtro? Dê uma olhada neste código:
x = range(-5, 5) all_less_than_zero = list(filter(lambda num: num < 0, x)) print(all_less_than_zero)
Agora use a abstração da lista:
x = range(-5, 5) all_less_than_zero = [num for num in x if num < 0]
A abstração de lista suporta expressões condicionais desse tipo. Você não precisa mais usar um milhão de funções para obter algo. De fato, se você está tentando fazer algo com uma lista, existe a possibilidade de ser mais limpo e fácil de obter com a abstração de listas.
E se quisermos colocar todos os elementos da lista abaixo de zero? Com a função lambda, mapear e filtrar, ficará assim:
x = range(-5, 5) all_less_than_zero = list(map(lambda num: num * num, list(filter(lambda num: num < 0, x))))
Esta entrada não é racional e não é muito simples. Usando a abstração da lista, ficará assim:
x = range(-5, 5) all_less_than_zero = [num * num for num in x if num < 0]
A abstração de lista é boa, curiosamente, para listas. Mapear e filtrar o trabalho para cada contêiner iterável, o que há de errado? .. Sim, você pode usar a abstração para cada contêiner iterável que encontrar.
Outras abstrações
Você pode aplicar a abstração para cada contêiner iterável.
Cada contêiner iterável pode ser criado usando abstração. A partir da versão 2.7, você pode até criar um dicionário (tabela de hash).
Se algo é um contêiner iterável, pode ser gerado algo. Vejamos o último exemplo usando set. Se você não souber o que é conjunto, consulte
este artigo escrito por mim também. Em resumo:
- Set é um contêiner de elementos; os elementos nele não são repetidos
- A ordem não é importante
Como você deve ter notado, o conjunto, como um dicionário, usa chaves. Python é realmente inteligente. Ele vai adivinhar se você está usando uma abstração de dicionário ou um conjunto de abstrações, com base em se você especifica parâmetros adicionais para o dicionário ou não. Se você quiser saber mais sobre abstrações, leia
isto . Se sobre abstrações e geração, então
este .
Sumário
A programação funcional é ótima. O código funcional pode ser limpo ou não muito. Alguns pythonists incondicionais não aceitam o paradigma funcional no Python. Você deve usar o que deseja e o que combina com você.
Página do autor .