Erro

Secção dedicada à linguagem de programação favorita dos quarkianos: Python!

Re: Erro

Mensagempor Bruno Oliveira em Sexta Jul 01, 2011 10:58 am

Okay, cá vai, mas se achares algo estranho nesta explicação, não estranhes (lol), pois eu próprio demorei algum tempo a perceber programação funcional. Felizmente, talvez eu te possa dar a visão que tenho agora que é mais simplificada. Vou usar o exemplo que tu querias resolver, o de criar uma string a partir de elementos de uma lista.

Em primeiro lugar, para se ver o paralelismo entre os dois paradigmas de programação, o chamado imperativo (que consiste, como o nome diz em dar ordens ao computador) e o funcional, que descarta por completo o uso de variáveis, usa menos memória, e, como já deves ter percebido é muito mais compacto, vou resolver o problema das duas formas. Volto a referir que eu não sei Python, apenas sei programação de forma genérica e simplesmente combino-a com algumas caracteristicas úteis do Python, como, suporte para programação funcional, e todo o conjunto de métodos de string disponiveis, entre outras.

Método 1 - Paradigma imperativo

A forma mais directa de resolver o teu problema, é usar um idioma muito comum para resolver vários problemas, a que eu gosto de chamar acumulador ou contador conforme o caso. Neste caso, o nosso acumulador vai ser uma string vazia, a que eu vou chamar, palavra.

Código: Seleccionar Todos
palavra = ""
lista = ['M', 'e', 's', 's', 'i']


Portanto, em cima tenho uma string vazia, chamada palavra, que irá acumular os vários caracteres da lista, para formar a palavra desejada.

Relembro que, uma string é entendida como uma cadeia de caracteres, o que significa que se fizeres no interpretador:

1 + 2, ele devolver-te-á como resultado 3.

Mas se fizeres, '1' + '2', ele devolver-te-á como resultado, 12, pois agora '1' e '2' são caracteres, esta soma "especial" de caracteres, tem um nome que é concatenação.

Assim, vês que ao teres 'c' +'a' +'s' +'a', terás a palavra casa.

Para resolver o nosso problema, temos então de adicionar cada elemento da lista à string palavra, para termos o resultado. Fazemos isso com um ciclo for:

Código: Seleccionar Todos
for i in range(len(lista)):
   palavra = palavra + lista[i]


onde a variável do ciclo, i, serve como indice para cada um dos elementos da lista. Repara que teres: range(10), é equivalente a teres: range(0, 10), isto já é mais pitónico :P

por fim, escrevemos a palavra no ecrã, que será o nome do Melhor Jogador do Mundo.

Método 2 - Paradigma funcional

O método funcional, é, como já disse atrás, muito mais compacto, porque usa simplesmente funções implementadas na linguagem, que operam directamente sobre listas, e evita a criação de um acumulador, que tem a desvantagem de ocupar espaço na memória (embora torne o código mais legível).

Para esta explicação, começar-se-ão por apresentar 3 conceitos novos:

[*] Lambda functions;

[*] A função reduce;

[*] Listas passadas a funções por compreensão;

As funções lambda ou funções anónimas, têm este nome, pois não estão agarradas a nenhum identificador, isto é, não é necessário atribuir nenhuma variável especifica para guardar resultados da chamada da função, mas talvez a wikipedia explique isto melhor que eu.

As funções lambda, na sua forma mais simples, recebem um argumento, fazem alguma coisa com ele e retornam o valor dessa alguma coisa (uma operação qualquer)

Eis uma lambda function que eleva um número ao quadrado:

Código: Seleccionar Todos
Quad = lambda x: x**2


O argumento da função, chamada Quad, é um número x, e a função retorna o quadrado de x, pelo que, ao escreveres isto no interpretador, podes depois fazer simplesmente:

Quad(10), que te dará 100 como resultado.

A função reduce, recebe como argumento, uma função lambda, e uma lista, e aplica essa mesma função aos elementos da lista até a reduzir a um único elemento, ou esgotar os elementos da lista.

Listas por compreensão são, basicamente, listas criadas, na hora, ou seja, para criar por compreensão uma lista com os 10 primeiros números, pode-se fazer:

Código: Seleccionar Todos
L = [x for x in range(1,11)]


Juntando tudo isto no mesmo exemplo, vamos começar a raciocinar "de dentro" para "fora".

Temos uma lista que contém os elementos que queremos adicionar:

Código: Seleccionar Todos
lista = ['M', 'e', 's', 's', 'i']


De seguida, temos então de criar uma lista por compreensão, que nos vai dizer, qual elemento da lista chamada lista, estamos a adicionar:
Código: Seleccionar Todos
[ lista[i] for i in range(len(lista)) ]


Agora criamos uma função lambda que nos diz qual é a operação que queremos realizar sobre a nossa lista, neste caso, somar (mais correctamente, concatenar) os seus elementos, pelo que:
Código: Seleccionar Todos
lambda x,y: x+y


Por fim, queremos que a função lambda de soma, seja aplicada até todos os caracteres da lista serem um só: é a nossa palavra!!!

Para isso aplicamos a função reduce, que receberá como argumentos, a função anómina e a lista por compreensão:

Código: Seleccionar Todos
lista = ['M', 'e', 's', 's', 'i']
reduce( lambda x,y: x+y , [ lista[i] for i in range(len(lista)) ] )


Cá está novamente o Melhor Jogador do Mundo, agora concatenado via programação funcional :P

PS: Desculpem o testamento
e^{ix}=cos x + i\,sin x
Avatar do utilizador
Bruno Oliveira
top-Quark!
top-Quark!
 
Mensagens: 1553
Registado: Quarta Nov 14, 2007 10:19 pm
Localização: Lisboa

Re: Erro

Mensagempor filipematos em Domingo Jul 03, 2011 9:20 pm

Muito obrigado pela explicação :) Não percebi tudinho mas acho que consegui entender a maior parte :)
"If I have seen further than others, it is by standing upon the shoulders of giants" - Isaac Newton

“We build too many walls and not enough bridges.” - Isaac Newton
filipematos
down-Quark!
down-Quark!
 
Mensagens: 280
Registado: Sábado Jun 25, 2011 4:48 pm
Localização: Lisboa

Re: Erro

Mensagempor Bruno Oliveira em Domingo Jul 03, 2011 9:28 pm

Tentei ser o mais claro possível, mas, em que partes tiveste dúvidas ao certo?

Posso tentar alargar-me um pouco mais no que te causou confusão :)
e^{ix}=cos x + i\,sin x
Avatar do utilizador
Bruno Oliveira
top-Quark!
top-Quark!
 
Mensagens: 1553
Registado: Quarta Nov 14, 2007 10:19 pm
Localização: Lisboa

Re: Erro

Mensagempor jap em Terça Jul 05, 2011 6:36 pm

filipematos Escreveu:Se não for muito incómodo, gostava de ter um explicação :p


Deixo para o Bruno a explicação! :lol:

Mas aqui vai uma maneira mais simples (explícita) de converter uma lista de caracteres numa string:

Código: Seleccionar Todos
>>> x = ['p','a','l','a','v','r','a']
>>> s = "".join(x)
>>> print s
palavra
>>>


Explicação:

A uma string pré-existente podes juntar os caracteres contidos numa lista com o método "join".

Exemplo:

Código: Seleccionar Todos
"abc".join(['d,'e','f'])




Código: Seleccionar Todos
"abcdef"


o que eu fiz acima foi juntar a uma lista vazia os caracters da lista "x".

Fixe, não é? :wink:
José António Paixão
Departamento de Física da FCTUC
Avatar do utilizador
jap
Site Admin
Site Admin
 
Mensagens: 6790
Registado: Quinta Nov 09, 2006 9:34 pm
Localização: Univ. de Coimbra

Re: Erro

Mensagempor Bruno Oliveira em Terça Jul 05, 2011 7:42 pm

Obrigado prof, pelas explicações verdadeiramente pitónicas! :)

PS: Eu assim que li o que o Filipe pretendia fazer, lembrei-me logo do método join, mas como não o consegui implementar "à primeira" com sucesso, optei pela explicação tradicional, que funciona sempre!

PPS: É óptimo estar de volta aqui ao fórum :D
e^{ix}=cos x + i\,sin x
Avatar do utilizador
Bruno Oliveira
top-Quark!
top-Quark!
 
Mensagens: 1553
Registado: Quarta Nov 14, 2007 10:19 pm
Localização: Lisboa

Re: Erro

Mensagempor Tharis em Terça Jul 05, 2011 11:55 pm

Receio que o Professor se tenha equivocado.

Aplicando o método join a uma string e a uma lista de strings, o resultado é uma string na qual os elementos da lista estão separados pela string original.

Por exemplo:

Código: Seleccionar Todos
>>> "/".join(['a','b','c','x','y','z'])
'a/b/c/x/y/z'


No caso em que se faz:

Código: Seleccionar Todos
>>> "".join(['a','b','c','x','y','z'])
'abcxyz'


o que acontece é que os varios elementos da lista vão sendo separados por uma string vazia, na prática, não são separados.

Para se juntar caracteres contidos numa lista a uma string pré-existente pode-se fazer por exemplo:

Código: Seleccionar Todos
>>> "abc" + "".join(['d','e','f'])
'abcdef'



Cumprimentos ;)
Tharis
Avatar do utilizador
Tharis
up-Quark!
up-Quark!
 
Mensagens: 387
Registado: Quinta Out 23, 2008 4:26 pm

Re: Erro

Mensagempor jap em Quarta Jul 06, 2011 12:26 am

Tharis Escreveu:
Código: Seleccionar Todos
>>> "abc" + "".join(['d','e','f'])
'abcdef'



Cumprimentos ;)
Tharis


Ou isso, sim! :D

Na realidade, costumo apenas usar o método 'join' com a string vazia, pelo que já nem me lembrava que a string vazia não era o 'container' mas sim o separador! :lol:

Anyway, it works!
Pitonices! :wink:

Obrigado pelo reparo. :hands:
José António Paixão
Departamento de Física da FCTUC
Avatar do utilizador
jap
Site Admin
Site Admin
 
Mensagens: 6790
Registado: Quinta Nov 09, 2006 9:34 pm
Localização: Univ. de Coimbra

Re: Erro

Mensagempor filipematos em Sábado Jul 09, 2011 7:59 pm

Muito obrigado pelas respostas, acho que vou optar pelo método join, contudo acho que vou estudar o método 2 em especial a Lambda, eu no outro dia encontrei um livro de python no CFNUL e estive lá a ler um pouco sobre isso, mas mesmo assim ainda não assimilou bem, se alguém pudesse dar uma explicação prática sobre essa parte agradecia muito...

Muito obrigado pelas múltiplas respostas pitónicas :)
"If I have seen further than others, it is by standing upon the shoulders of giants" - Isaac Newton

“We build too many walls and not enough bridges.” - Isaac Newton
filipematos
down-Quark!
down-Quark!
 
Mensagens: 280
Registado: Sábado Jun 25, 2011 4:48 pm
Localização: Lisboa

Re: Erro

Mensagempor Bruno Oliveira em Terça Jul 12, 2011 1:12 pm

Bem, como eu disse atrás, a função lambda é talvez a instrução mais conhecida de programação funcional, já que aparece em todas as linguagens que suportam este paradigma, como Lisp e outras.

No entanto, e para te situar caso queiras pesquisar mais sobre isto, esta função lambda (que se insere no que se chama untyped lambda calculus) não tem qualquer relação com a função lambda formalmente definida em Matemática, e o uso que lhe é dado em Programação também é relativo, uma vez que se podem definir funções standard e escrever programas que estejam estruturados segundo o paradigma imperativo e façam exactamente a mesma coisa. As vantagens de usar lambda functions aparecem quando se querem escrever por uma ou outra razão programas com códigos mais curtos e compactos e usar menos memória (embora isto quase nunca seja um problema se se pensar o suficiente no algoritmo que estamos a usar para resolver o problema. ).

É também importante dizer aqui, que por si só, as lambda functions têm muito pouca ou nenhuma utilidade, sendo a sua utilidade revelada quando estas operam sobre listas por compreensão e são combinadas com outras funções de programação funcional, como o reduce, o map e o filter.

Depois deste paleio todo, e passando à prática, vou transcrever aqui a documentação oficial do Python na parte em que se referem as funções lambda:

"
Lambda forms (lambda expressions) have the same syntactic position as expressions. They are a shorthand to create anonymous functions; the expression
Código: Seleccionar Todos
lambda arguments: expression
yields a function object. The unnamed object behaves like a function object defined with:

Código: Seleccionar Todos
def name(arguments):
    return expression


"
As funções lambda são anónimas pois se leres acima, o function object que se refere acima não tem nenhum nome associado a ele. (novamente, a wikipedia pode explicar melhor este ponto melhor que eu, pois é daquelas coisas para as quais a pessoa está "formatada" para perceber, e torna-se um pouco dificil explicar estes fundamentos teóricos por palavras, sendo melhor ver exemplos.)

Portanto, passando agora a exemplos, vou tentar explicar e ilustrar 4 conceitos bastante usados em programação funcional, que são os que eu conheço:

[*] Lambda functions;

[*] A função map;

[*] A função filter;

[*] A função reduce;

Estas quatro funções, quando combinadas entre si, podem produzir alguns resultados interessantes e são uma excelente forma para obrigar a pensar out of the box. :)

Funções Lambda

As funções lambda criam objectos que não ficam agarrados a nenhum identificador, o que te obriga a declará-lo se as quiseres usar de forma isolada. (ou seja, tens de escrever o name da função, como está no exemplo acima.) Se não o declarares, apenas verás na Python shell a confirmação da criação de um objecto devido à declaração da função lambda, se esta não tiver erros de sintaxe:

Código: Seleccionar Todos
>>> lambda x: 2*x
<function <lambda> at 0x2381f50>
>>>


No teu pc, deverás obter uma coisa semelhante. Assim, para se poderem usar as funções lambda, como se usam as funções normais, tem de se lhes atribuir um identificador:

Código: Seleccionar Todos
>>> dobro = lambda x: 2*x
>>> dobro(5)
10
>>>


E creio que em termos práticos é apenas isto que é preciso ter presente para perceber a declaração e uso de lambda functions. Também podes ter funções lambda a receberem muitos argumentos, que são colocados na chamada da função, pela ordem em que são declarados, assim a minha função many_args, para ser chamada, teria a configuração, many_args(x, y, z) :

Código: Seleccionar Todos
>>> many_args = lambda x,y,z: (x*y)**z
>>> many_args(5,5,2)
625
>>>


Função map

A função map, e todas as outras de que tratarei em seguida, podem ser usadas juntamente com as funções lambda, mas apenas quando estas são aplicadas a listas por compreensão, ou outras.

Quando digo aqui aplicadas a listas, refiro-me a usar as funções lambda, não como as funções standard definidas com a keyword def, mas sim como uma função que modifica os conteúdos de uma lista, operando sobre ela. É importante referir que é apenas com as funções anónimas que se podem conseguir estes resultados de modificação de conteúdo de listas, que são muito úteis.

Imagine-se que temos uma lista, que contém os 4 primeiros números naturais, e que queremos agora duplicar cada elemento dessa lista. É óbvio que o que temos de fazer, é aplicar a função f(x) = 2\,x, a todos os elementos da lista. No entanto, o Python gere a memória mantendo apenas referências a listas já criadas, e não realiza operações directamente sobre elas, pelo que ao fazeres uma função normal, que te devolva o dobro de um número x, e lhe passares a nossa lista como argumento, o resultado será:

Código: Seleccionar Todos
[1, 2, 3, 4, 1, 2, 3, 4]


que não é o que nós queremos de todo!!

A função map, resolve o problema:

Basicamente, como o nome diz, existe um mapping ou uma correspondência, entre cada elemento da lista e a função lambda responsável por alterar essa mesma lista. O nosso problema resolve-se num instante:

Código: Seleccionar Todos
>>> map(lambda x: 2*x, [1, 2, 3, 4])
[2, 4, 6, 8]
>>>


Portanto aqui estão outra vez, as funções lambda usadas para realizar operações sobre listas, e fica introduzido o conceito da função map.

Função filter

A função filter, é muito simples de perceber, mas talvez mais díficil de interligar em expressões muito complicadas do que as outras já referidas até aqui. O que a função filter faz, é, tal como o nome diz, filtrar os conteúdos de uma dada lista caso estes verifiquem (ou não) a imposição de uma dada função lambda, que pode dar jeito ser uma função booleana, ou seja, uma função que retorna apenas dois valores: True ou False, caso o argumento verifique ou não a condição imposta na função. Já foi aqui discutido que, em Programação, uma maneira útil de saber se um número é ou não par, pode ser avaliando o resto da divisão desse número por 2. Se o resto for zero, o número é par, caso contrário não é. A condição de paridade de um número x, é dada então por:
Código: Seleccionar Todos
x%2 == 0
.

Isto pode ser metido no meio de uma função lambda, ficando a função a funcionar como uma função booleana:

Código: Seleccionar Todos
>>> par = lambda x: x%2 == 0
>>> par(8)
True
>>> par(9)
False
>>>


Assim sendo, tendo uma lista com os dez primeiros números naturais, para ficarmos apenas com os números pares, ou para "filtrarmos" os pares, basta-nos fazer:

Código: Seleccionar Todos
>>> filter( lambda x: x%2 == 0, [1,2,3,4,5,6,7,8,9,10] )
[2, 4, 6, 8, 10]
>>>


E é esta a explicação mais simples da função filter.

Função reduce

A função reduce, que já foi vista anteriormente, utiliza-se, como eu disse (informalmente) atrás, para "gastar" os elementos de uma lista, até que esta seja apenas reduzida a um único elemento. No entanto, em Programação, pode dar jeito conhecer as definições que seguem os padrões da linguagem que estamos a usar, para podermos usar estas funções que estão directamente implementadas na linguagem (as chamadas built-in functions) da melhor forma possível.

Assim sendo, de maneira formal, a função reduce é uma built-in function que recebe uma função lambda de dois argumentos e a aplica cumulativamente aos itens de uma estrutura de dados iterável, de modo a que esta seja reduzida a um único elemento. As únicas estruturas de dados iteráveis que vimos até aqui, foram as listas (na realidade, podemos também iterar sobre os caracteres de uma string, mas esta não constitui uma estrutura de dados).

Como exemplo, imagine-se uma lista com os 10 primeiros números, e que os queremos somar a todos. Para isso, temos então de criar uma função lambda que denote a operação de soma, que, como vês, tem de receber forçosamente 2 argumentos diferentes, os operandos:

Código: Seleccionar Todos
lambda x, y: x+y


Depois, segue-se o idioma usado até aqui, criamos uma lista por compreensão que contenha os 10 primeiros números naturais, e somamo-los usando a função lambda para soma, e passando-a (juntamente com a lista) como argumentos da função reduce:

Código: Seleccionar Todos
>>> reduce(lambda x,y: x+y , [x for x in range(1,11)])
55
>>>


Cá está, as várias funções explicadas em detalhe e bastantes exemplos ilustrativos. O problema 6 do projectEuler, pode ajudar-te a praticar os conceitos que eu aqui ilustrei, qualquer questão, podem colocar.

Cumprimentos,
Bruno
última vez editado por Bruno Oliveira s Segunda Jul 18, 2011 10:42 am, editado 9 vezes no total
Avatar do utilizador
Bruno Oliveira
top-Quark!
top-Quark!
 
Mensagens: 1553
Registado: Quarta Nov 14, 2007 10:19 pm
Localização: Lisboa

Re: Erro

Mensagempor filipematos em Terça Jul 12, 2011 6:57 pm

Já pensaste em escrever um livro? Muito obrigado :) Consegui perceber os diversos conceitos muito bem acho eu, pelo menos não fiquei com nenhuma dúvida.

Mais uma vez muito obrigado pela explicação! :D
"If I have seen further than others, it is by standing upon the shoulders of giants" - Isaac Newton

“We build too many walls and not enough bridges.” - Isaac Newton
filipematos
down-Quark!
down-Quark!
 
Mensagens: 280
Registado: Sábado Jun 25, 2011 4:48 pm
Localização: Lisboa

Re: Erro

Mensagempor Bruno Oliveira em Terça Jul 12, 2011 8:31 pm

filipematos Escreveu:Já pensaste em escrever um livro? Muito obrigado :) Consegui perceber os diversos conceitos muito bem acho eu, pelo menos não fiquei com nenhuma dúvida.

Mais uma vez muito obrigado pela explicação! :D


Filipe,

Fico contente por ver que achas úteis os meus textos, espero que mais gente pense o mesmo, pois é para isso que aqui estamos no Quark! :D

Já editei o post acima, e completei-o com o que faltava da função reduce e deixei-te ainda uma sugestão de problema para treinares estes novos conceitos. :wink:

Em relação a escrever um livro... :lol: Era algo que gostaria de fazer, talvez daqui a uns anos quando melhorar os meus textos quem sabe.

Mas, se achas a informação útil, pode ser que mais gente também ache, e nesse caso, será, prof. JAP, que não valeria a pena meter este tutorial como sticky? :roll:

EDIT: Corrigi umas gralhas que encontrei ao reler o post e dei uns retoques no Português, agora deve estar mais legível!!
e^{ix}=cos x + i\,sin x
Avatar do utilizador
Bruno Oliveira
top-Quark!
top-Quark!
 
Mensagens: 1553
Registado: Quarta Nov 14, 2007 10:19 pm
Localização: Lisboa

Re: Erro

Mensagempor filipematos em Quarta Jul 13, 2011 9:39 pm

Muito obrigado pela sugestão :). Esse foi um dos problemas que passei à frente mas acho que agora consigo resolve-lo. Alguma dúvida posto aqui!! Muito obrigado pela explicação :) Foi bem mais explicita que um calhamaço de Python que há na CFNUL.
última vez editado por filipematos s Sexta Jul 15, 2011 2:07 pm, editado 1 vez no total
"If I have seen further than others, it is by standing upon the shoulders of giants" - Isaac Newton

“We build too many walls and not enough bridges.” - Isaac Newton
filipematos
down-Quark!
down-Quark!
 
Mensagens: 280
Registado: Sábado Jun 25, 2011 4:48 pm
Localização: Lisboa

Re: Erro

Mensagempor Bruno Oliveira em Quarta Jul 13, 2011 9:41 pm

Ainda bem! :lol:

Eu prefiro como te sugeri inicialmente, o "How to think like a computer scientist" do Allen B. Downey, influenciou muito a minha forma de pensar em Programação, assim como a documentação oficial do Python. Já agora, tens o nome do livro e autor? :roll:

PS: Alguma dúvida manda-me por PM, que é mais fácil e isto não fica tão off-topic :wink:
e^{ix}=cos x + i\,sin x
Avatar do utilizador
Bruno Oliveira
top-Quark!
top-Quark!
 
Mensagens: 1553
Registado: Quarta Nov 14, 2007 10:19 pm
Localização: Lisboa

Anterior

Voltar para Pitónica

Quem está ligado

Utilizadores a navegar neste fórum: Nenhum utilizador registado e 1 visitante

cron