all() e any()

A linguagem Python é recheada de atalhos úteis, que não só facilitam nossa vida na hora de resolver problemas, mas que também fazem você pensar soluções de forma diferente. Este post é sobre duas funções Python que agem sobre listas de booleanos, e se revelam úteis na prática.

Imagine que você tenha uma lista de inteiros, e você precisa verificar se todos são pares. Você pode pensar: “Fácil, é só percorrer a lista, e verificar para cada elemento se ele é divisível por 2. Se algum não for, posso setar uma variável avisando que é falso e tá feito!” Tendo o raciocínio pronto, você senta o traseiro na cadeira e escreve um código tipo esse:

listaDeInteiros = [2, 6, 4, 7, -2]
todosSaoPares = True
for i in listaDeInteiros:
    if i % 2 == 0:
        todosSaoPares = False
        break
if todosSaoPares:
    print 'Todos sao pares'
else:
    print 'Tem algum impar'

E está ótimo, o código funciona numa boa, as variáveis estão claras, e tudo o mais. Todavia, com o passar do tempo você se dá conta que esse tipo de problema começa a repetir. Seguidamente você precisa verificar se uma condição é verdadeira (ou falsa) para os elementos de uma lista, e acaba fazendo muitas muitas versõezinhas parecidas desse código. Agora você pode precisar descobrir se todos os elementos de uma lista são inteiros positivos, e lá vai você de novo:

listaDeInteiros = [2, 6, 4, 7, -2]
todosSaoPositivos = True
for i in listaDeInteiros:
    if i <= 0:
        todosSaoPositivos = False
        break
if todosSaoPositivos:
    print 'Todos sao positivos'
else:
    print 'Tem algum negativo'

Perceba como o código é bem similar ao anterior, praticamente só altera a condição, e os nomes das coisas. Devido a frequência com que aparecem problemas parecidos com esse, Python fornece um jeito mais sucinto de escrever esse tipo de lógica. Usando as funções pré-definidas all e any, seu código ficará mais fácil tanto de escrever como de ler, uma vez que você as aprende. Veja como ficaria o código para verificar se todos os elementos são pares usando all:

listaDeInteiros = [2, 6, 4, 7, -2]
if all(i % 2 == 0 for i in listaDeInteiros):
    print 'Todos sao pares'
else:
    print 'Tem algum impar'

Podemos obter resultado semelhante usando a função any. Tente perceber todas as diferenças:

listaDeInteiros = [2, 6, 4, 7, -2]
if not any(i % 2 != 0 for i in listaDeInteiros):
    print 'Todos sao pares'
else:
    print 'Tem algum impar'

all()

A função all() é muito simples e também muito útil. A documentação oficial da função diz o seguinte:

`all(iterável)`
Retorna `True` se todos os elementos do iterável forem verdadeiros (ou se o iterável for vazio).

Ou seja, a função recebe como parâmetro um objeto iterável (uma lista ou uma tupla, por exemplo) e verifica se todos os elementos desse iterável possuem o valor True. Como o próprio nome diz, all() (todos, em português) verifica se todos os elementos contidos em uma sequência são verdadeiros.

Isso pode ser muito útil quando temos uma lista e precisamos, antes de qualquer coisa, verificar se os elementos dessa lista satisfazem determinada condição.

Por exemplo, antes de passarmos uma lista de strings para uma função que não trata strings vazias, poderíamos fazer o seguinte:

if all(s != '' for s in lista_de_strings):
    funcao_que_nao_trata_strings_vazias(lista_de_strings)
else:
    print "Erro: strings vazias existem na lista!"

No exemplo acima usamos uma generator expression (s != '' for s in lista_de_strings) para gerar o iterável contendo booleanos para passar como entrada para a função all().

Simples, expressivo e eficiente.

any()

A função builtin any() é parecida com a função all(), porém ela retorna True se algum dos elementos do iterável for True. Por exemplo, poderíamos usá-la para verificar se pelo menos um dos elementos de lista_de_strings é uma string vazia.

if any(s == '' for s in lista_de_strings):
    print "Erro: strings vazias existem na lista!"
else:
    funcao_que_nao_trata_strings_vazias(lista_de_strings)

all() e any() como alternativas a and e or

Uma expressão booleana como:

if cond1 and cond2 and cond3 and cond4 and cond5:
    # faça algo

será verdadeira somente se todas as condições testadas forem verdadeiras. Como poderíamos escrever a expressão acima utilizando a função all()?

if all([cond1, cond2, cond3, cond4, cond5]):
    # faça algo

E uma expressão composta por ors lógicos, como poderia ser reescrita?

if cond1 or cond2 or cond3 or cond4 or cond5:
    # faça algo

A expressão acima (cond1 or cond2 or cond3 or cond4 or cond5) será verdadeira se alguma das condições for verdadeira. Ou seja, poderíamos usar a função any():

if any([cond1, cond2, cond3, cond4, cond5]):
    # faça algo

As funções all() e any() podem então ser vistas como aplicações dos operadores and e or aos elementos de sequências.

all() e any() combinados com map()

Já vimos como as funções all() e any() são práticas. Vamos ver agora que, quando combinadas com outros recursos que Python oferece, essas funções passam a ser mais poderosas, dando maior expressividade ao código escrito.

Anteriormente, vimos o funcionamento da função map(): ela aplica uma função a cada elemento de uma sequência e retorna o resultado disso em uma nova lista. Por exemplo:

>>> import math
>>> lista1 = [1, 4, 9, 16, 25]
>>> lista2 = map(math.sqrt, lista1)
>>> print lista2
[1.0, 2.0, 3.0, 4.0, 5.0]

Como poderíamos combinar a função map() com as funções all() ou any()? Vamos a um exemplo.

Considere que temos duas listas contendo números inteiros ([1,2,3,4,5] e [1,4,2,3,5]) e precisamos saber se essas listas possuem elementos de mesmo valor posicionados nas mesmas posições em ambas as listas. Isso poderia ser feito da seguinte forma:

>>> import operator
>>> map(operator.eq, [1,2,3,4,5], [1,4,2,3,5])
[True, False, False, False, True]
>>> any( map(operator.eq, [1,2,3,4,5], [1,4,2,3,5]) )
True

Na segunda linha, usamos a função map() para comparar a igualdade (operator.eq) dos elementos das duas listas (mais informações sobre o módulo operator aqui). Tal linha aplica a função eq (que recebe dois argumentos, os elementos a serem comparados) a pares de elementos das duas listas passadas como argumentos, iniciando por 1 e 1, passando por 2 e 4, 3 e 2, 4 e 3, e, por fim, 5 e 5. Cada comparação gera um booleano que é adicionado à lista que a função map() retorna como resultado. Então, basta que apliquemos a função any() para verificar se para algum dos pares de elementos o eq retornou True. Se em qualquer das posições a lista de resultado do map tiver o valor True, significa que as duas listas possuem elemento cujo valor e posição sejam iguais nas duas listas.

Para finalizar

all() e any() fazem parte do grupo de recursos de Python que todo pythonista deve ter no bolso para utilizar sempre que for preciso. Além de evitar longas linhas de expressões booleanas, essas funções nos permitem dar maior legibilidade ao nosso código.

Ah, fique atento, pois ambas as funções estão disponíveis somente a partir da versão 2.5 do Python.

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s