Apostila Lisp

Embed Size (px)

Citation preview

UVV Centro Universitrio Vila Velha

Linguagens de Programao IIProgramao Funcional usando LISP

Professor: Cristiano Biancardi

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

Sumrio1 - Elementos da Linguagem .............................................................................................. 4 1.1 - Elementos primitivos .......................................................................................... 4 1.2 - Combinaes ....................................................................................................... 5 1.3 - Avaliao de Combinaes ............................................................................... 7 1.4 - Definio de Funes......................................................................................... 8 1.5 Funes bsicas .............................................................................................. 10 1.6 Testando nmeros ........................................................................................... 12 1.7 Avaliando funes............................................................................................ 14 1.8 - Smbolos............................................................................................................. 15 1.9 Inserindo comentrios.......................................................................................... 16 2 Expresses Condicionais ............................................................................................ 17 2.1 - Predicados ......................................................................................................... 17 2.2 - Operadores Lgicos ......................................................................................... 18 2.3 - Seleo simples ................................................................................................ 19 2.4 - Seleo Mltipla ................................................................................................ 19 3 - Funes........................................................................................................................ 21 3.1 - Funes Recursivas ......................................................................................... 21 3.2 Depurao de funes .......................................................................................... 23 3.3 Funes de ordem superior .................................................................................. 23 3.4 Lambda ................................................................................................................ 25 3.5 Variveis Locais................................................................................................... 25 3.6 Funes Locais..................................................................................................... 27 4 mbito e Durao ....................................................................................................... 29 4.1 mbito de uma referncia.................................................................................... 29 4.2 Durao de uma referncia .................................................................................. 30 5 Dados .......................................................................................................................... 32 5.1 - tomos.................................................................................................................. 32 5.2 Combinao de dados .......................................................................................... 33 5.3 Abstrao de dados .............................................................................................. 34 5.4 - Tipos Abstratos de Informao............................................................................. 36 6 Entrada e sada ............................................................................................................ 38 6.1 Leitura de dados a partir do teclado..................................................................... 38 6.1 Impresso de dados .............................................................................................. 39 7 Listas ........................................................................................................................... 41 7.1 Operaes sobre listas............................................................................................. 41 7.2 Funes teis........................................................................................................ 43 7.4 Usando as operaes ............................................................................................ 46 7.5 Listas de argumentos............................................................................................ 49 7.6 Tipos aglomerados ............................................................................................... 50 8 Programao imperativa ............................................................................................. 52 8.1 Atribuio ............................................................................................................ 52 8.2 Sequenciao........................................................................................................ 53 8.3 Alterao de dados............................................................................................... 54 8.4 Repetio.............................................................................................................. 56

2

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi 9 Modelos de ambientes................................................................................................. 59 9.1 mbito lxico....................................................................................................... 60 9.2 mbito dinmico ................................................................................................. 62 10 Parmetros especiais ................................................................................................. 64 10.1 Parmetros opcionais ......................................................................................... 64 10.2 Parmetros de resto ............................................................................................ 64 10.3 Parmetros de chave ......................................................................................... 65 11 Macros....................................................................................................................... 68 11.1 Avaliao de macros .......................................................................................... 68 11.2 Escrita de macros ............................................................................................... 68 11.3 Depurao de macros......................................................................................... 69 11.4 Caracteres de macro........................................................................................... 70 11.5 Macros utis ....................................................................................................... 71 11.6 Iteradores............................................................................................................ 73 11.7 Fichas ................................................................................................................. 74 Referncias........................................................................................................................ 79

3

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

1 - Elementos da LinguagemEm linguagens Funcionais toda a programao realizada por meio da aplicao de funes a argumentos. As variveis e as operaes de atribuio usadas pelas linguagens imperativas no so necessrias. Os laos de repetio so substitudos por chamadas a funes recursivas. Baseiam-se fortemente nos conceitos das funes matemticas. Qualquer linguagem de programao lida com duas espcies de objetos: dados e procedimentos. Os dados so os objectos que pretendemos manipular. Os procedimentos so descries das regras para manipular esses dados. Se considerarmos a linguagem da matemtica, podemos identificar os nmeros como dados e as operaes algbricas como procedimentos e podemos combinar os nmeros entre si usando aquelas operaes. Por exemplo, 2 2 uma combinao, tal como 2 2 2 e 2 2 2 2. No entanto, a menos que pretendamos ficar eternamente a resolver problemas de aritmtica elementar, somos obrigados a considerar operaes mais elaboradas que representam padres de clculos. Neste ltimo exemplo, evidente que o padro que est a emergir o da operao de potenciao, i.e, multiplicao sucessiva, tendo esta operao sido definida na matemtica h j muito tempo. Tal como a linguagem da matemtica, uma linguagem de programao deve possuir dados e procedimentos primitivos, deve ser capaz de combinar quer os dados quer os procedimentos para produzir dados e procedimentos mais complexos e deve ser capaz de abstrair padres de clculo de modo a permitir trat-los como operaes simples, definindo novas operaes que representem esses padres de clculo.

1.1 - Elementos primitivosElementos primitivos so as entidades mais simples com que a linguagem lida. Um nmero, por exemplo, um dado primitivo. Como dissemos anteriormente, o Lisp executa um ciclo read-eval-print. Isto implica que tudo o que escrevemos no Lisp tem de ser avaliado, i.e., tem de ter um valor, valor esse que o Lisp escreve no ecran. Assim, se dermos um nmero ao avaliador, ele devolve-nos o valor desse nmero. Quanto vale um nmero? O melhor que podemos dizer que ele vale aquilo que vale. Por exemplo, o nmero 1 vale 1.>1 1 > 12345 12345

4

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi> 4.5 4.5

Como se v no exemplo, em Lisp, os nmeros podem ser inteiros ou reais. Exerccio 2 Descubra qual o maior real que o seu Lisp aceita. Consegue fazer o mesmo para os inteiros?

1.2 - CombinaesCombinaes so entidades complexas feitas a partir de entidades mais simples. Por exemplo, na matemticas nmeros podem ser combinados usando operaes como a soma ou o produto. Como exemplo de combinaes matemticas, temos 1 + 2 e 1 + 2 3. A soma e o produto de nmeros so exemplos de operaes extremamente elementares consideradas procedimentos primitivos. Em Lisp, criam-se combinao escrevendo uma sequncia de expresses entre um par de parnteses. Uma expresso um elemento primitivo ou uma outra combinao. A expresso (+ 1 2) uma combinao dos elementos primitivos 1 e 2 atravs do procedimento +. J no caso (+ 1 (* 2 3)) a combinao ocorre entre 1 e (* 2 3), sendo esta ltima expresso uma outra combinao. No difcil de ver que as nicas combinaes com utilidade so aquelas em que as expresses correspondem a operadores e operandos. Por conveno, o Lisp considera que o primeiro elemento de uma combinao um operador e os restantes so os operandos. A notao que o Lisp utiliza para construir expresses (operador primeiro e operandos a seguir) designada por notao prefixa. Esta notao costuma causar alguma perplexidade a quem inicia o estudo da linguagem, que espera uma notao mais prxima da que aprendeu em aritmtica e que usada habitualmente nas outras linguagens de programao. Nestas, a expresso (+ 1 (* 2 3)) usualmente escrita na forma 1 + 2 * 3 (designada notao infixa) que, normalmente, mais simples de ler por um ser humano. No entanto, a notao prefixa usada pelo Lisp tem largas vantagens sobre a notao infixa:x x x x

muito fcil usar operadores que tm um nmero varivel de argumentos, como por exemplo:> (+ 1 2 3) 6 > (+ 1 2 3 4 5 6 7 8 9 10)

5

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardix 55

Numa linguagem do tipo Pascal apenas existem operadores unrios ou binrios, e necessrio explicitar os operador binrios entre cada dois operandos: 1+2+3 ou 1+2+3+4+5+6+7+8+9+10. Um operador deste tipo diz-se infixo (in significa entre). Se se pretender um operador ternrio (ou outro) j no se consegue escrever do mesmo modo, sendo necessrio implement-lo como uma funo.x

x

No existe precedncia entre os operadores. Em Pascal, a expresso 1+2*3 tem de ser calculada como se se tivesse escrito 1+(2*3), e no (1+2)*3, i.e., foi criada uma precedncia que elimina as ambiguidades. Essa precedncia pode ser alterada atravs do emprego de parnteses. Em Lisp, as expresses seriam necessariamente distintas, (+ 1 (* 2 3)) ou (* (+ 1 2) 3), no podendo haver qualquer ambiguidade. Os operadores que ns definimos usam-se exatamente da mesma maneira que os operadores da linguagem: operador primeiro e operandos a seguir. Em Pascal, os operadores so infixos, i.e., esto entre os operandos, e as funes e procedimentos por ns definidos so prefixos, i.e., primeiro a funo ou procedimento e depois os operandos. Isto impede a extenso da linguagem de uma forma coerente. Para exemplificar este ltimo aspecto, consideremos a operao de exponenciao em Pascal. Para ser coerente com o resto da linguagem, deveria existir um operador, por exemplo ** que permitisse escrever 3**4 para indicar a quarta potncia de 3. Como esse operador no existe na linguagem (standard), somos obrigados a criar uma funo que o substitua mas, neste caso, a sintaxe muda radicalmente. Esta funo, como se usa de forma prefixa no se pode tornar coerente com as restantes operaes do Pascal, como a soma e a multiplicao. Em Lisp, pelo contrrio, tanto podemos escrever (* 3 3 3 3) como definir uma funo que permita escrever (** 3 4).

A desvantagem da notao prefixa est na escrita de combinaes complexas. A expresso Pascal 1+2*3-4/5*6, equivale expresso Lisp (- (+ 1 (* 2 3)) (* (/ 4 5) 6)) que embora seja mais simples de ler para uma mquina, pode ser mais difcil de ler para um ser humano devido acumulao de parnteses. No entanto, esta expresso pode ser escrita de forma mais clara usando indentao. A regra para indentao de combinaes Lisp extremamente simples. Numa linha coloca-se o operador e o primeiro operando. Os restantes operandos vm alinhados por debaixo do primeiro.(- (+ 1 (* 2 3)) (* (/ 4 5) 6))

6

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Quando a regra de indentao no suficiente, usam-se pequenas variaes, como seja colocar o operador numa linha e os operandos por debaixo, por exemplo:(umaoperacaocomumnomemuitogrande 1 2 3 4)

A indentao fundamental em Lisp pois muito fcil escrever cdigo complexo. A grande maioria dos editores preparados para Lisp (Emacs, por exemplo) formatam automaticamente os programas medida que os escrevemos e mostram o emparelhamento de parnteses. Desta forma, aps algum tempo de prtica, torna-se muito fcil escrever e ler os programas, por mais complexa que possa parecer a sua estrutura. Exerccio 3 Converta as seguintes expresses da notao infixa da aritmtica para a notao prefixa do Lisp:1+2-3 1-2*3 1*2-3 1*2*3 (1 - 2) * 3 (1 - 2) + 3 1 - (2 + 3) 2*2+3*3*3

Exerccio 4 Converta as seguintes expresses da notao prefixa do Lisp para a notao infixa da aritmtica:(* (/ 1 2) 3) (* 1 (- 2 3)) (/ (+ 1 2) 3) (/ (/ 1 2) 3) (/ 1 (/ 2 3)) (- (- 1 2) 3) (- 1 2 3)

1.3 - Avaliao de CombinaesComo j vimos, o Lisp considera que o primeiro elemento de uma combinao um operador e os restantes so os operandos. O avaliador determina o valor de uma combinao como o resultado de aplicar o procedimento especificado pelo operador ao valor dos operandos. O valor de cada operando designado de argumento do procedimento. Assim, o valor da combinao (+ 1 (* 2 3)) o resultado de somar o valor de 1 com o valor de (* 2 3). 7

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Como j se viu, 1 vale 1 e (* 2 3) uma combinao cujo valor o resultado de multiplicar o valor de 2 pelo valor de 3, o que d 6, e que somado a 1 d 7.> (+ 1 2) 3 > (+ 1 (* 2 3)) 7

Exerccio 5 Calcule o valor das seguintes expresses Lisp:(* (/ 1 2) 3) (* 1 (- 2 3)) (/ (+ 1 2) 3) (/ (/ 1 2) 3) (/ 1 (/ 2 3)) (- (- 1 2) 3) (- 1 2 3) (- 1)

1.4 - Definio de FunesTal como em matemtica, pode-se definir numa linguagem de programao a operao de elevar um nmero ao quadrado. Assim, se pretendermos determinar o produto de um nmero por ele prprio, escrevemos a combinao (* x x) sendo x o nmero, i.e.> (* 5 5) 25 > (* 6 6) 36

Os nmeros 5 e 6 e a operao * so elementos primitivos. As expresses (* 5 5) e (* 6 6) so combinaes. combinao genrica (* x x) queremos associar um nome, por exemplo, quadrado. Isso equivale a acrescentar uma nova funo linguagem. A ttulo de exemplo, vamos definir a funo quadrado:> (defun quadrado (x) (* x x)) quadrado

Para se definirem novas funes em Lisp, necessrio criar uma combinao de quatro elementos. O primeiro elemento desta combinao a palavra defun, que informa o avaliador que estamos a definir uma funo. O nome defun uma abreviatura de ``define function''. O segundo elemento o nome da funo que queremos definir, o terceiro elemento uma combinao com os parmetros da

8

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi funo e o quarto elemento a combinao que determina o valor da funo para aqueles parmetros. Quando se d uma expresso desta forma ao avaliador, ele acrescenta a funo ao conjunto de funes da linguagem, associando-a ao nome que lhe demos e devolve como valor da definio o nome da funo definida. A definio da funo quadrado diz que para se determinar o quadrado de um nmero x, devemos multiplicar esse nmero por ele prprio (* x x). Esta definio associa a palavra quadrado a um procedimento. Este procedimento possui parmetros e um corpo de expresses. De forma genrica temos:(defun nome (parmetro-1 ...parmetro-n) corpo)

Os parmetros de um procedimento so designados parmetros formais e so os nomes usados no corpo de expresses para nos referirmos aos argumentos correspondentes. Quando escrevemos no avaliador de Lisp (quadrado 5), 5 o argumento. Durante o clculo da funo este argumento est associado ao parmetro formal x. Os argumentos de uma funo so tambm designados parmetros atuais.> (quadrado 5) 25 > (quadrado 6) 36

Note-se que a regra de avaliao de combinaes tambm vlida para as funes por ns definidas. Assim, a avaliao da expresso (quadrado (+ 1 2)) passa pela avaliao do operando (+ 1 2). Este operando tem como valor 3, valor esse que usado pela funo no lugar do parmetro x. O corpo da funo ento avaliado, i.e., o valor final ser o da combinao (* 3 3). O seguinte exemplo mostra um caso um pouco mais complexo. Nele esto apresentadas as etapas de avaliao dos operandos e de avaliao do corpo da funo.(quadrado (quadrado (+ 1 2))) (quadrado (quadrado 3)) (quadrado (* 3 3)) (quadrado 9) (* 9 9) 81

A definio de funes permite-nos associar um procedimento a um nome. Isto implica que o Lisp tem de possuir uma memria onde possa guardar o procedimento e a sua associao ao nome dado. Esta memria do Lisp designase ambiente.

9

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Note-se que este ambiente apenas existe enquanto estamos a trabalhar com a linguagem. Quando terminamos, perde-se todo o ambiente. Isto implica que, se no queremos perder o trabalho que estivemos a escrever, devemos escrever as funes num ficheiro e ir passando-as para o Lisp. A grande maioria das implementaes do Lisp permite fazer isto de forma automtica.

1.5 Funes bsicas(+) ( Soma ) ( + 3.1 2.7 ) o 5.800000000000001 ( + 4 7 9 ) o 20 ( + 9/2 4 ) o 17/2 Note que 9/2 e 17/2 so nmeros racionais que podem ser manipulados da mesma forma de inteiros e floats, e que a funo soma no se restringe a apenas dois parmetros. (-) ( Subtrao ) ( - 3.1 2.7 ) o 0.3999999999999999 ( - 1 3 5 7 ) o -14 ( - -4 ) o 4 (*) ( Multiplicao ) ( * 3.1 7.0 ) o 21.7 ( * 3.1 7.1 10.0 ) o 220.09999999999997 (*43) o 12 (/) ( Diviso ) ( / 21.7 7.0 ) o 3.1 ( / 9 2 ) o 9/2 ( / 30 2 3 ) o5 (/2) o 1/2 - inverso do nmero Note que se houver mais de dois argumentos na diviso, o primeiro deles ser dividido sucessivamente pelos demais. sqrt ( Raiz quadrada )

10

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

( sqrt 4 ) o 2.0 ( sqrt 4.0 ) o 2.0 ( sqrt 4 ) o #C(0,0 2.0) Se o argumento for um nmero negativo, a funo no retornar um erro mas sim um nmero complexo na forma #C(3 2) que equivalente a 3 + 2i. expt ( Exponencial ) ( expt 2 10 ) o 1024 ( expt 2 10.0 ) o 1024.0 ( expt 2/3 3 ) o 8/27 ( expt 4 ) o #C(1.22514845490862E-16 2.0) Se algum dos argumentos for do tipo float, o resultado tambm ser convertido para float, podendo ocorrer aproximaes do nmero. No caso de 0.0, o resultado aproximado para #C(1.22514845490862E-16 2.0). log ( Logaritmo ) ( log 1 ) o 0.0 ( log 10 ) o 2.302585092994046 ( log 10 2 ) o 3.3219280948873626 abs ( Valor Absoluto ) ( abs 8 ) o8 ( abs 8 ) o 8 ( abs 8.9 ) o 8.9 truncate ( Trunca ) o 13 0.5 0.7 o -1 -0.7

( truncate 13.5 ) ( truncate 0.7 ) o 0 ( truncate 1.7 )

Truncar retornar o valor inteiro de um nmero. Note que a funo retorna dois valores: o primeiro o valor truncado, a parte inteira, e a segunda a parte decimal do nmero, o resto.

11

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

round ( Arredonda ) ( round 3.1 ) o 3 0.1 ( round 3.7 ) o 4 -0.3 ( round 3.5 ) o 4 -0.5

Esta funo arredonda um nmero ao inteiro mais prximo. Novamente, h o retorno de dois valores: o nmero inteiro arredondado e a parte decimal restante do arredondamento, que pode ser positiva ou negativa. rem ( Resto ) ( rem 9 3 ) o0 ( rem 3 9 ) o3 ( rem 17 4 ) o -1 Esta funo retorna o resto inteiro da diviso. float ( Float ) ( float 4 ) ( float 135 ) o 4.0 o 135.0

Converte um dado do tipo inteiro em ponto flutuante ( float). Atribuio O comando ( setf varivel valor ) faz a atribuio de valores a variveis. ( setf x 4 ) o 4 ;a varivel x recebe o valor 4 ( setf zero 0 one 1 two 2 three 3 four 4 ) o 4 ; como foram feitas atribuies mltiplas, o ; interpretador retorna apenas o ltimo valor.

1.6 Testando nmerosA linguagem Lisp possui algumas funes de testes que so chamadas predicados. Esses predicados so funes que retornam apenas um dos dois valores: Verdadeiro (t) ou Falso (nil). Os predicados listados a seguir podem ser usados com nmeros e smbolos que possuam um valor numrico.

12

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

Vamos inicializar quatro variveis com quatro valores distintos para que os exemplos a seguir sejam executados corretamente. ( setf zero 0 one 1 two 2 three 3 four 4 ) o4 (>) ( Ordem Descendente ) ( > 100 10 one 0.1 ) ( > 10 100 1 0.1 ) o o T NIL

Retorna verdadeiro se os elementos esto dispostos em ordem descendente. ( (x+y*z 1 2 3) 7

Lisp atribui um significado muito especial aos smbolos. Reparemos que, quando definimos uma funo, os parmetros formais da funo so smbolos. O nome da funo tambm um smbolo. Quando escrevemos uma combinao, o avaliador de Lisp usa a definio de funo que foi associada ao smbolo que constitui o primeiro elemento da combinao. Quando, no corpo de uma funo, usamos um smbolo para nos referimos a um parmetro formal, esse smbolo tem como valor o respectivo parmetro atual que lhe foi associado naquela combinao. Por esta descrio se v que os smbolos constituem um dos elementos fundamentais da linguagem Lisp.

15

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Exerccio 6 - Crie uma funo que some trs nmeros. - Crie uma funo que calcule o delta de uma equao do segundo grau. Delta = b2 4*a*c Resposta

1.9 Inserindo comentriosPara facilitar a compreenso de um cdigo faz-se uso de comentrios. Em Lisp, os comentrios devem ser precedidos de ponto-e-vrgula ; , caracter que indica que a respectiva linha de cdigo no deve ser avaliada pelo interpretador. ( defun novogosto ( nome ) ; adiciona nome no incio da lista gosta ( setf gosta ( cons nome gosta ) ) ; deleta nome da lista detesta ( setf detesta ( remove nome detesta) ) )

16

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

2 Expresses CondicionaisExistem muitas operaes cujo resultado depende da realizao de um determinado teste. Por exemplo, a funo matemtica abs --que calcula o valor absoluto de um nmero-equivale ao prprio nmero, se este positivo, ou equivale ao seu simtrico se for negativo. Estas expresses, cujo valor depende de um ou mais testes a realizar previamente, permitindo escolher vias diferentes para a obteno do resultado, so designadas expresses condicionais. No caso mais simples de uma expresso condicional, existem apenas duas alternativas a seguir. Isto implica que o teste que necessrio realizar para determinar a via de clculo a seguir deve produzir um de dois valores, em que cada valor designa uma das vias. Seguindo o mesmo raciocnio, uma expresso condicional com mais de duas alternativas dever implicar um teste com igual nmero de possveis resultados. Uma expresso da forma ``caso o valor do teste seja 1, 2 ou 3, o valor da expresso 10, 20 ou 30, respectivamente'' um exemplo desta categoria de expresses que, embora seja considerada pouco intuitiva, existe nalgumas linguagens (Basic, por exemplo). Contudo, estas expresses podem ser facilmente reduzidas primeira. Basta decompor a expresso condicional mltipla numa composio de expresses condicionais simples, em que o teste original tambm decomposto numa srie de testes simples. Assim, poderamos transformar o exemplo anterior em: ``se o valor do teste 1, o resultado 10, caso contrrio, se o valor 2, o resultado 20, caso contrrio 30''.

2.1 - PredicadosDesta forma, reduzimos todos os testes a expresses cujo valor pode ser apenas um de dois, e a expresso condicional assume a forma de ``se ...ento ...caso contrrio ...''. Nesta situao, a funo usada como teste denominada predicado e o valor do teste interpretado como sendo verdadeiro ou falso. Nesta ptica, o fato de se considerar o valor como verdadeiro ou falso no implica que o valor seja de um tipo de dados especial, como o booleano ou lgico de algumas linguagens (como o Pascal), mas apenas que a expresso condicional considera alguns dos valores como representando o verdadeiro e os restantes como o falso. Em Lisp, as expresses condicionais consideram como falso um nico valor. Esse valor representado por nil. Qualquer outro valor diferente de nil considerado como verdadeiro. Assim, do ponto de vista de uma expresso condicional, qualquer nmero um valor verdadeiro. Contudo, no faz muito sentido para o utilizador humano considerar um nmero como verdadeiro ou falso, pelo que se introduziu uma constante na linguagem para representar verdade. Essa constante representa-se por t.

17

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Note-se que, se t diferente de nil, e se nil o nico valor que representa a falsidade, ento t representa verdade. Desta forma, existem muitos predicados em Lisp cujo valor t quando a expresso por eles designada considerada verdadeira.> (> 4 3) t > (< 4 3) nil

Existem muitos predicados em Lisp. Os predicados numricos mais usados so o zerop, =, >, =, (zerop 1) nil > (zerop 0) t

O fato de zerop terminar com a letra ``p'' deve-se a uma conveno adotada em Common Lisp segundo a qual os predicados devem ser distinguidos das restantes funes atravs da concatenao da letra ``p'' (de predicate) ao seu nome. Apesar da adoo dos smbolos t e nil, convm alertar que nem todos os predicados devolvem t ou nil exclusivamente. Alguns h que, quando querem indicar verdade, devolvem valores diferentes de t (e de nil, obviamente).

2.2 - Operadores LgicosPara se poder combinar expresses lgicas entre si existem os operadores and, or e not. O and e o or recebem qualquer nmero de argumentos. O not s recebe um. O valor das combinaes que empregam estes operadores lgicos determinado do seguinte modo:x

x

x

O and avalia os seus argumentos da esquerda para a direita at que um deles seja falso, devolvendo este valor. Se nenhum for falso o and devolve o valor do ltimo argumento. O or avalia os seus argumentos da esquerda para a direita at que um deles seja verdade, devolvendo este valor. Se nenhum for verdade o or devolve o valor do ltimo argumento. O not avalia para verdade se o seu argumento for falso e para falso em caso contrrio.

Note-se que embora o significado de falso seja claro pois corresponde necessariamente ao valor nil, o significado de verdade j no to claro pois, desde que seja diferente de nil, considerada verdade. Exerccio 7 Qual o valor das seguintes expresses? 18

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi(and (or (> 2 3) (not (= 2 3))) (< 2 3)) (not (or (= 1 2) (= 2 3))) (or (< 1 2) (= 1 2) (> 1 2)) (and 1 2 3) (or 1 2 3) (and nil 2 3) (or nil nil 3)

2.3 - Seleo simplesO if a expresso condicional mais simples do Lisp. O if determina a via a seguir em funo do valor de uma expresso lgica. A sintaxe do if :(if condio consequente alternativa)

Para o if, a condio o primeiro argumento, o consequente no caso de a condio ser verdade o segundo argumento e a alternativa no caso de ser falso o terceiro argumento.> (if (> 4 3) 5 6) 5

Uma expresso if avaliada determinando o valor da condio. Se ela for verdade, avaliado o consequente. Se ela for falsa avaliada a alternativa. Exerccio 8 Defina uma funo soma-grandes que recebe trs nmeros como argumento e determina a soma dos dois maiores. Exerccio 9 Escreva uma funo que calcule o factorial de um nmero. A funo fact um exemplo de uma funo recursiva, i.e., que se refere a ela prpria.

2.4 - Seleo MltiplaAlm do if, existe outra expresso condicional em Lisp. O cond uma verso mais potente que o if. uma espcie de switch-case do C. A sua sintaxe :(cond (condio-1 expresso-1)

19

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi(condio-2 expresso-2) (condio-n expresso-n))

Designa-se o par (condio-i expresso-i) por clusula. O cond testa cada uma das condies em sequncia, e quando uma delas avalia para verdade, devolvido o valor da expresso correspondente, terminando a avaliao. Um exemplo ser:> (cond ((> 4 3) 5) (t 6)) 5

O cond permite uma anlise de casos mais simples do que o if.(defun teste (x y z w) (cond ((> x y) z) ((< (+ x w) (* y z)) x) ((= w z) (+ x y)) (t 777)))

A funo equivalente usando if seria mais complicada.(defun teste (x y z w) (if (> x y) z (if (< (+ x w) (* y z)) x (if (= w z) (+ x y) 777))))

20

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

3 - Funes

3.1 - Funes RecursivasUma funo recursiva uma funo que se refere a si prpria. A idia consiste em utilizar a prpria funo que estamos a definir na sua definio. Em todas as funes recursivas existe:x x

Um passo bsico (ou mais) cujo resultado imediatamente conhecido. Um passo recursivo em que se tenta resolver um subproblema do problema inicial.

Se analisarmos a funo fatorial, o caso bsico o teste de igualdade a zero (zerop n), o resultado imediato 1, e o passo recursivo (* n (fact (- n 1))). Geralmente, uma funo recursiva s funciona se tiver uma expresso condicional, mas no obrigatrio que assim seja. A execuo de uma funo recursiva consiste em ir resolvendo subproblemas sucessivamente mais simples at se atingir o caso mais simples de todos, cujo resultado imediato. Desta forma, o padro mais comum para escrever uma funo recursiva :x x

Comear por testar os casos mais simples. Fazer chamadas (ou chamada) recursivas com subproblemas cada vez mais prximos dos casos mais simples.

Dado este padro, os erros mais comuns associados s funes recursivas so, naturalmente:x x

No detectar os casos simples A recurso no diminuir a complexidade do problema.

No caso de erro em funo recursiva, o mais usual a recurso nunca parar. O nmero de chamadas recursivas cresce indefinidamente at esgotar a memria (stack), e o programa gera um erro. Em certas linguagens (Scheme) e implementaes do Common Lisp, isto no assim, e pode nunca ser gerado um erro. A recurso infinita o equivalente das funes recursivas aos ciclos infinitos dos mtodos iterativos do tipo while-do e repeatuntil. Repare-se que uma funo recursiva que funciona perfeitamente para os casos para que foi prevista pode estar completamente errada para outros casos. A funo fact um exemplo. Quando o argumento negativo, o problema torna-se cada vez mais complexo, cada vez mais longe do caso simples. (fact -1) => (fact -2) => (fact -3) =>

21

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Exerccio 10 Considere uma verso extremamente primitiva da linguagem Lisp, em que as nicas funes numricas existentes so zerop e duas funes que incrementam e decrementam o seu argumento em uma unidade, respectivamente, 1+ e 1-. Isto implica que as operaes >, -3. Exerccio 13 Agora que a linguagem nanoLisp pode tambm trabalhar com nmeros inteiros negativos, defina o predicado positivo?, que recebe um nmero e indica se ele positivo ou no. Exerccio 14 Defina o teste de igualdade de dois nmeros na linguagem nanoLisp contemplando a possibilidade de trabalhar tambm com nmeros inteiros negativos. Exerccio 15 Defina a funo simtrico de um nmero qualquer na linguagem nanoLisp . Exerccio 16 possvel definir a soma de dois nmeros inteiros positivos em nanoLisp , i.e., apenas recorrendo s funes 1+ e 1- que somam e subtraem uma unidade, respectivamente. Defina a operao soma. Exerccio 17

22

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Generalize a funo de soma de modo a poder receber nmeros inteiros positivos e negativos. Exerccio 18 Do mesmo modo que a soma pode ser definida exclusivamente em termos de sucessor 1+ e predecessor 1-, a multiplicao pode ser definida exclusivamente em termos da soma. Defina a funo mult que recebe dois nmero e os multiplica usando a funo soma.

3.2 Depurao de funesEm Lisp, possvel analisar as chamadas s funes atravs da forma especial trace. Ela recebe o nome das funes que se pretendem analisar e altera essas funes de forma a que elas escrevam no terminal as chamadas com os respectivos argumentos em cada chamada, e os valores retornados. Esta informao extremamente til para a depurao das funes. Para se parar a depurao de uma funo, usa-se a forma especial untrace, que recebe o nome da funo ou funes de que se pretende tirar o trace. Se se usar a forma especial trace sem argumentos ela limita-se a indicar quais as funes que esto em trace. Se se usar a forma especial untrace sem argumentos, so retiradas de trace todas as funes que estavam em trace. Exerccio 19 Experimentar o trace do fact.

3.3 Funes de ordem superiorVimos que as funes permitem-nos dar um nome a um conjunto de operaes e trat-lo como um todo. Muitas vezes, porm, h um padro que se repete, variando apenas uma ou outra operao. Por exemplo, consideremos uma funo que soma os quadrados de todos os inteiros entre a e b, .

(defun soma-quadrados (a b) (if (> a b) 0 (+ (quadrado a) (soma-quadrados (1+ a) b))))> (soma-quadrados 1 4) 30

Consideremos agora uma outra funo que soma as razes quadradas de todos os inteiros entre a e b, .

(defun soma-raizes (a b) 23

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi (if (> a b) 0 (+ (sqrt a) (soma-raizes (1+ a) b))))> (soma-raizes 1 4) 6.146264369941973

Em ambas as funes existe uma soma de expresses matemticas entre dois limites, por . O somatrio uma abstrao matemtica para exemplo, existe um somatrio uma soma de nmeros. Dentro do somatrio possvel colocar qualquer operao matemtica relativa ao ndice do somatrio. Esse ndice varia desde o limite inferior at ao limite superior. Para que se possa definir o processo do somatrio na nossa linguagem de programao ela deve ser capaz de fazer abstrao sobre as prprias operaes a realizar, e deve poder us-las como parmetros do processo. O padro a executar seria qualquer coisa do estilo: (defun soma-??? (a b) (if (> a b) 0 (+ (aplica-??? a) (soma-??? (1+ a) b)))) O smbolo ??? representa a operao a realizar dentro do somatrio, e que pretendemos transformar num parmetro. Em Lisp, para se aplicar uma funo que o valor de um argumento, usa-se a funo funcall, que recebe essa funo e os seus parmetros atuais. Para se indicar que pretende-se passar a funo associada a um determinado smbolo, usa-se a forma especial function. > (funcall (function 1+) 9) 10 > (defun teste (f x y) (funcall f x y)) teste > (teste (function +) 1 2) 3 Deste modo pode-se escrever a implementao do nosso somatrio: (defun somatorio (fun a b) (if (> a b) 0 (+ (funcall fun a) (somatorio fun (1+ a) b)))) Pode-se testar a funo para o exemplo anterior. > (somatorio (function quadrado) 1 4) 30

24

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Como se v, a funo somatorio representa a abstrao associada ao somatrio matemtico. Para isso, ela recebe uma funo como argumento e aplica-a aos sucessivos inteiros includos no somatrio. As funes que recebem e manipulam outras funes so designadas funes de ordem superior. Exerccio 20 Repare-se que, tal como a funo somatrio, podemos escrever a abstrao correspondente ao produtrio (tambm designado piatrio) . Esta abstrao corresponde ao produto dos valores de uma determinada expresso para todos os inteiros de um intervalo. Escreva uma funo Lisp que a implemente. Exerccio 21 Escreva a funo fatorial usando o produtrio.

3.4 LambdaSe voc somente deseja criar uma funo temporria e no deseja perder tempo dandolhe um nome, lambda justamente o que voc precisa. Lambda pode ser vista como uma funo sem nome. A sintaxe da lambda igual da defun, mas em que se omite o nome. > ((lambda (z) (+ z 3)) 2) 5> (defun teste (fun x y) (funcall fun x y)) > (teste (function (lambda (x y) (* x y))) 2 2) 4

As lambdas so a essncia do Lisp. A qualquer funo corresponde uma lambda. Na realidade, a forma especial defun no faz mais do que criar uma lambda com os parmetros e o corpo da funo e associ-la ao nome da funo que se est a definir. Quando a forma especial function recebe um nome (um smbolo) ela devolve a lambda associada a esse nome. A designao de lambda ( ) deriva duma rea da matemtica que se dedica ao estudo dos conceitos de funo e de aplicao de funo, e que se designa por clculo- . O clculo uma ferramenta muito utilizada para estudar a semntica das linguagens de programao.

3.5 Variveis LocaisImagine-se que pretendemos escrever uma funo que calcula a seguinte expresso: . Em Lisp, temos a seguinte traduo: (defun f (x y)

25

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi (+ (* (quadrado (+ 1 (* (quadrado x) y))) x) (* (+ 1 (* (quadrado x) y)) y))) Como se v, a expresso (+ 1 (* (quadrado x) y)) aparece repetida duas vezes. Isto, alm de dificultar a leitura da funo torna-a menos eficiente pois aquela expresso vai ter de ser calculada duas vezes. Quase todas as linguagens de programao fornecem os meios para se criarem variveis locais, temporrias, para guardarem resultados parciais que vo ser utilizados em outros stios. Em Lisp, isso pode ser obtido definindo funes intermdias: (defun f (x y) (f* x y (+ 1 (* (quadrado x) y))))(defun f* (x y temp) (+ (* (quadrado temp) x) (* temp y)))

Mas como j vimos, no h necessidade de se definir uma funo f* no ambiente pois podemos usar as lambdas diretamente. (defun f (x y) ((lambda (temp) (+ (* (quadrado temp) x) (* temp y))) (+ 1 (* (quadrado x) y)))) Repare-se que dentro do corpo da lambda h referncias quer aos parmetros da lambda (temp) quer aos parmetros da funo f em que a lambda est inserida. Uma vez que no muito conveniente separar os valores das variveis, Lisp providencia uma forma especial designada let que convertida para uma lambda. A sua sintaxe : (let ((var-1 exp-1) (var-2 exp-2) (var-n exp-n)) corpo) Quando se avalia um let, cada smbolo var-i associado ao valor da expresso correspondente exp-i (em paralelo) e em seguida o corpo avaliado como se as referncias a var-i estivessem substitudas pelos valores correspondentes de exp-i. Esta forma especial absolutamente equivalente a escrever: ((lambda (var-1 var-2 ...var-n) corpo) exp-1exp-2 ...exp-n)

26

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Embora equivalentes, a utilizao da forma let mais fcil de ler. Este gnero de formas especiais se limita a ser uma traduo mais agradvel para outras formas especiais e so designadas por acar sinttico. O let aucar sinttico para uma lambda. Exerccio 22 Usando o let, reescreva a funo f anterior. Exerccio 23 Qual o valor das seguintes expresses:a) > (let ((x 10)) (+ (let ((x 20)) (+ x 5)) (+ x 2))) b) > (let ((x 10)) (+ (let ((x 11) (y (+ x 4))) (+ y x)) (+ x 2)))

3.6 Funes LocaisTal como se podem criar variveis locais com a forma especial let, tambm possvel criar funes locais com a forma especial flet. A sua sintaxe extremamente parecida com a do let, s que o valor de cada varivel a definio de uma funo. A ttulo de exemplo, estude-se a seguinte definio:(defun teste (x) (flet ((f-local1 (y) (+ x y)) (f-local2 (z) (* x z)) (f-local3 (x) (+ x 2))) (+ (f-local1 x) (f-local2 x) (f-local3 x)))) > (teste 2) 12 > (f-local1 2) Error: Undefined function F-LOCAL1

As funes f-local1, f-local2 e f-local3 so locais funo teste, sendo estabelecidas a cada aplicao desta funo. Tal como as variveis do let, as funes locais de um flet no se podem referir umas s outras, pois so avaliadas em paralelo. Alm disso, tambm no se podem referir a si prpias, impedindo a criao de funes locais recursivas. 27

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Atendendo a que a maioria das vezes as funes que definimos so recursivas, independentemente de serem locais ou no, interessa possuir um meio de o podermos fazer. A forma especial labels providencia esta possibilidade. A sua sintaxe igual do flet, mas a sua semntica ligeiramente diferente. Para o flet, o mbito do nome das funes definidas apenas o corpo do flet. Para o labels, esse mbito extendido prpria forma especial. Isto permite que se possam definir funes locais recursivas ou mutuamente recursivas. Observe o seguinte exemplo: (defun teste (x) (labels ((f-local1 (y) (if (zerop y) 1 (* y (f-local1 (- y 1)))) ) (f-local2 (z) (* x z)) (f-local3 (x) (+ x 2))) (+ (f-local1 x) (f-local2 x) (f-local3 x)))) Para f-local1 calculado o fatorial do valor associado a x. > teste 2 10

28

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

4 mbito e Durao

4.1 mbito de uma refernciaDesigna-se por mbito de uma referncia, a zona textual em que ela pode ser corretamente referida. Assim, o mbito de um parmetro de uma lambda a zona textual correspondente ao corpo da funo. Isto implica que qualquer parmetro da lambda pode ser referido dentro desse corpo, mas no fora dele. > ((lambda (z) (+ z z)) 3) 6 > (+ z z) Error: Unbound variable Z Uma vez que o mbito de um parmetro o corpo da lambda correspondente, possvel escrever: > ((lambda (z) ((lambda (w) (+ w z)) 3) 4) 7 Reescrevendo o exemplo usando o let, temos > (let ((z 4)) (let ((w 3)) (+ w z))) 7 Neste exemplo, cada lambda (ou cada let) estabelece um valor para uma varivel. Quando se encontra uma referncia a uma varivel, o seu valor dado pela ligao correspondente ao contexto menor. Se no existe qualquer ligao em nenhum dos contextos, a varivel diz-se no ligada. A avaliao de variveis no ligadas produz um erro. Quando uma mesma varivel aparece ligada repetidamente em contextos sucessivos, a ligao mais ``interior'' obscurece todas as ``exteriores''. Isto no quer dizer que as ligaes exteriores sejam destrudas. Elas so apenas localmente substitudas durante a avaliao do corpo mais interior. Assim, temos o seguinte exemplo: > (let ((x 10)) (+ (let ((x 20)) x) x)) 30 Diz-se que uma referncia de mbito lxico quando ela s pode ser corretamente referida dentro da regio textual da expresso que a criou. Diz-se que uma referncia de mbito vago (ou indefinido) quando ela pode ser corretamente referida a partir de qualquer regio do programa. 29

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

Exerccio 24 Que tipo de mbito possui uma varivel de um let? Que tipo de mbito possui o nome de uma funo?

4.2 Durao de uma refernciaDesigna-se por durao de uma referncia o intervalo de tempo durante o qual ela pode ser corretamente referida. Diz-se que uma referncia de durao dinmica quando s pode ser corretamente referida no intervalo de tempo que decorre durante a avaliao da expresso que a criou. Diz-se que uma referncia de durao vaga (ou indefinida) quando pode ser corretamente referida em qualquer instante aps a avaliao da expresso que a criou. Em Pascal, os parmetros de uma funo ou procedimento tm mbito lxico e durao dinmica. A ligao dos parmetros formais aos parmetros atuais existe apenas durante a execuo da funo ou procedimento. Em Scheme ou Common Lisp, os parmetros das lambdas tm mbito lxico e durao vaga. Isto implica que possvel aceder a uma varivel mesmo depois de a funo que a criou ter terminado, desde que essa varivel seja acedida dentro da regio textual dessa funo. A ttulo de exemplo, se tentarmos escrever a funo que determina o mximo de uma funo numrica mas de forma a que ela possa receber uma tolerncia como parmetro, podemos ser conduzidos a qualquer coisa do gnero:(defun maximo-func (func a b tol) (acumulatorio (function max) func (funcall func a) a (function (lambda (x) (+ x tol))) b))

Repare-se que neste exemplo a funo que estabelece o incremento refere-se varivel livre tol. Uma das capacidades fundamentais das lambdas a sua referncia a variveis livres. Uma varivel diz-se livre numa lambda quando no um dos parmetros da lambda onde referida. Quando se aplica uma lambda aos seus argumentos, os parmetros tomam como valor os argumentos correspondentes, enquanto que as variveis livres tomam como valor o valor da primeira varivel igual no contexto em que a lambda definida. por esse motivo que quando a lambda que realiza o incremento aplicada a um nmero, ela sabe qual o valor 30

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi correto de tol. Ele dado pelo contexto lxico (e.x. textual) em que a lambda foi definida. Exerccio 25 Analise os seguintes casos:a) ((lambda (x) (+ x y)) 10) b) (let ((y 5)) ((lambda (x) (+ x y)) 10))

31

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

5 DadosEm todos os exemplos anteriores, so apresentadas funes essencialmente numricas. Os nmeros so um exemplo dos dados que os procedimentos podem usar e produzir. Neste ponto sero apresentados outros tipos de dados que se podem utilizar.

5.1 - tomosOs nmeros so um dos elementos primitivos do Lisp. Os smbolos (nomes de funes e variveis) so outro dos exemplos. Estes elementos dizem-se atmicos, pois no podem ser decompostos. Para se testar se um elemento atmico pode-se usar a funo atom:> (atom 1) T

Exerccio 26 Ao testar se o smbolo xpto atmico, escreve-se a expresso (atom xpto) e recebe-se um erro. Explique o que se passa. Para que o Lisp possa considerar um smbolo por si s, e.x., sem o considerar uma varivel, preciso usar a forma especial quote, que devolve o seu argumento sem o avaliar.> (quote xpto) XPTO > (atom (quote xpto)) T

Existem vrias funes para se testar a igualdade de elementos primitivos. Como j se viu, a igualdade de nmeros dada pela funo =. Esta funo compara nmeros de todos os tipos.> (= 1 1) t > (= 1 1.0) t

Em Lisp, existe unicidade de smbolos, e.x., dados dois smbolos com o mesmo nome, eles representam necessariamente o mesmo objeto, ou seja, o mesmo espao da memria do computador. Isto permite que a comparao entre dois smbolos possa ser feita testando se eles representam o mesmo espao, e.x., se apontam para a mesma zona da memria. A funo eq realiza essa operao.> (eq (quote a) (quote a))

32

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardit > (eq (quote a) (quote b)) nil

Para se testar smbolos e nmeros do mesmo tipo existe uma outra funo designada eql.> (eql (quote a) (quote a)) t > (eql 111111111111111111111111111111 111111111111111111111111111111) t > (eql 1 1.0) nil

5.2 Combinao de dadosPara se combinar dados, preciso que a linguagem possua uma ``cola'' que permita agrupar esses dados. Em Lisp, essa ``cola'' implementada pela funo cons. A funo cons cria um novo objeto que consiste na aglomerao de dois outros objetos, argumentos do cons. O cons para o Lisp o mesmo que as tabelas (arrays) e estruturas (records, structs) so para as outras linguagens como Pascal ou C.> (cons 1 2) (1 . 2) > (cons (cons 1 2) 3) ((1 . 2) . 3)

Dada uma combinao de objetos (um ``cons'') podemos obter o primeiro elemento da combinao usando a funo car e o segundo usando a funo cdr.> (car (cons 1 2)) 1 > (cdr (cons 1 2)) 2

Note-se que aplicar o car ou o cdr a um cons no destri esse cons. O cons de dois objetos designado um par com ponto (dotted pair). Como natural, um cons no um objeto atmico:> (atom (cons 1000 2000)) nil

Note-se que para combinar dois objetos quaisquer necessrio arranjar espao na memria para indicar qual o primeiro objeto e qual o segundo. a funo cons que arranja esse espao. De cada vez que ela chamada, mesmo que seja para juntar os

33

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi mesmos objetos, ela arranja um novo espao de memria. Isto implica que a funo eq sempre falsa para o cons.> (eq (cons 1 2) (cons 1 2)) nil

J a funo equal teste se dois objetos (com relao ao seu contedo) so iguais.> (equal (cons 1 2) (cons 1 2)) t > (equal (cons (cons 1 2) (cons 2 3)) (cons (cons 1 2) (cons 2 3))) t

5.3 Abstrao de dadosA abstrao de dados uma forma de aumentar a modularidade. Se decidirmos implementar nmeros racionais, teremos de pensar em combinar dois nmeros--o numerador e o denominador, e de os tratar como um todo. Se no fosse possvel considerar aquela combinao de nmeros como uma abstrao (um racional), toda a sua utilizao seria extremamente difcil. Por exemplo, para se somar dois nmeros racionais, seria necessrio usar uma operao para o clculo do numerador, e outra operao para o clculo do denominador, em vez de se pensar numa operao genrica, soma-racional, que receberia dois argumentos-- dois racionais--e calcularia um terceiro nmero--um racional. Para nos abstrairmos da complexidade de um nmero racional, devemos definir funes que os manipulam internamente. Podemos comear por definir uma funo que constri um nmero racional a partir do numerador e do denominador.(defun racional (numerador denominador) (cons numerador denominador))

Para sabermos qual o numerador ou o denominador de um dado nmero racional podemos definir:(defun numerador (racional) (car racional))

(defun denominador (racional) (cdr racional))

Assim, j j podemos escrever a funo que calcula a soma de dois racionais, usando a frmula . 34

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi(defun +racional (r1 r2) (racional (+ (* (numerador r1) (denominador r2)) (* (numerador r2) (denominador r1))) (* (denominador r1) (denominador r2))))

Para simplificar a escrita de racionais, podemos definir uma funo que escreve um racional de acordo com uma conveno qualquer.(defun escreve-racional (racional) (format t "~a/~a" (numerador racional) (denominador racional)))

Agora, j podemos calcular a seguinte expresso:> (escreve-racional (+racional (racional 1 2) (racional 1 3))) 5/6

Obs: Veja captulo 6: Impresso Exerccio 27 Defina as restantes funes do tipo abstrato de informao racional: -racional, *racional, e /racional. Como se v, tratamos um nmero racional como um s objeto, e separamos a parte do programa que usa os racionais da parte que os implementa como pares de inteiros. Esta tcnica designa-se por abstrao de dados. A abstrao a melhor maneira de lidar com a complexidade. A abstrao de dados permite-nos isolar a utilizao dos dados do modo como eles esto implementados, atravs da utilizao de barreiras de abstrao. Essas barreiras consistem em limitar a utilizao dos dados a um pequeno conjunto de funes (racional, numerador e denominador) que escondem a maneira como eles esto implementados. Ao utilizador de um dado tipo de dados, apenas se diz quais as funes que ele pode usar para os manipular, e no qual o funcionamento das funes que implementam aquele tipo de dados. Seguindo esta metodologia, se precisarmos testar a igualdade de racionais, devemos escrever uma funo que o faa usando apenas as funes de manipulao de racionais, i.e., racional, numerador e denominador:(defun =racional (r1 r2) (and (= (numerador r1) (numerador r2)) (= (denominador r1) (denominador r2))))

Exerccio 28

35

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi A funo que compara dois racionais no funciona corretamente para todos os casos. Assim,> (=racional (racional 4 6) (racional 4 6)) t > (=racional (racional 4 6) (racional 2 3)) nil

Qual o problema? Como que se pode resolver? Exerccio 29 Escreva uma funo que calcule o maior divisor comum entre dois nmeros. Para isso, use o algoritmo de Euclides que diz que se r o resto da diviso de a por b, ento o maior divisor comum entre a e b tambm o maior divisor comum entre b e r: mdc(a,b)=mdc(b,r). Como natural, quando o resto zero, o maior divisor comum o prprio b. Exerccio 30 Empregue o mtodo de Euclides para reescrever a funo racional de modo a s construir nmeros na forma reduzida.

5.4 - Tipos Abstratos de InformaoA teoria dos tipos abstratos de informao diz que o conceito fundamental para a abstrao de dados a definio de uma interface entre a implementao dos dados e a sua utilizao. Essa interface constituda por funes que se podem classificar em categorias: construtores, seletores, reconhecedores e testes. Estas funes so definidas em termos dos objetos mais primitivos que implementam o tipo de dados que se quer definir. Os construtores so as funes que criam um objeto composto a partir dos seus elementos mais simples. Por exemplo, a funo racional um construtor para o tipo racional. Os seletores so as funes que recebem um objeto composto e devolvem as suas partes. As funes numerador e denominador so exemplos de seletores. Os reconhecedores so as funes que reconhecem certos objetos especiais do tipo de dados que se est a definir. A funo zerop um reconhecedor para o tipo nmero do Lisp. Finalmente, os testes so funes que comparam objetos do tipo que se est a definir. A funo =racional um exemplo de uma funo desta categoria. Como se pode verificar pela funo =racional, por vezes, os testes so implementados usando os prprios seletores.

36

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Para que abstrao de dados seja corretamente realizada, fundamental definir o conjunto de construtores, seletores, reconhecedores e testes. Todos os programas que pretenderem utilizar aquele tipo de dados so obrigados a usar apenas aquelas funes. Isso permite que se possa alterar a implementao do tipo de dados sem afetar os programas que o utilizam. A implementao de estruturas de dados complexas s corretamente realizada quando se segue esta metodologia com extremo rigor. Exerccio 31 Defina o teste >racional. Quando um tipo abstrato de informao tem de interagir com um utilizador, quer para lhe pedir uma descrio de um elemento do tipo, quer para lhe apresentar uma descrio de um elemento do tipo, usa os denominados transformadores de entrada/sada. A funo escreve-racional um exemplo de um transformador de sada para o tipo racional. Ela limita-se a a apresentar uma representao compreensvel de um nmero racional. O transformador de entrada realiza a operao inversa, i.e., constri um elemento do tipo abstrato a partir de uma representao fornecida.

37

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

6 Entrada e sada

6.1 Leitura de dados a partir do tecladoEsta funo no recebe nenhum argumento. O prprio comando no avalia sua entrada, apenas recebe os dados que o usurio digitar no teclado (sempre seguidos de ). Geralmente vem combinado com outras funes. Definindo uma funo retorna a diferena de dois valores lidos (defun diferenca () (setf x (read)) (setf y (read)) (print (- x y))) Avaliao da funo diferena >(diferena) 3 4 -1 -1 Definindo uma funo retorna a mdia de quadro valores lidos a partir do teclado (defun mediaquatro () ;a funo no recebe nenhum argumento. (/ (+ (read) ;soma quatro valores e divide-os por 4. (read) (read) (read)) 4)) Avaliao da funo mediaquatro >(mediaquatro) 2 ;Digite quatro nmeros, 4 ;aps cada nmero tecle Enter. 6 8 5 Para chamar esta funo, basta digitar (mediaquatro) seguido por seus parmetros (recebidos pelos comandos read dentro do corpo da funo). Os quatro valores passados para a funo so somados e divididos por 4, resultando em 5.

38

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

6.1 Impresso de dadosAlgumas funes podem provocar uma sida. A mais simples print, que imprime o seu argumento e ento o retorna.> (print 3) 3 3

O primeiro 3 acima foi impresso, o segundo retornado. Se voc deseja um output mais complexo, voc necessita utilizar format:>(format t "An atom: ~S~%and a list: ~S~%and an integer:~D~%" nil (list 5) 6) An atom: NIL and a list: (5) and an integer: 6

O primeiro argumento a format ou t, ou NIL ou um arquivo.x x x

T especifica que a sada deve ser dirigida para o terminal, NIL especifica que no deve ser impresso nada, mas que format deve retornar um string com o contedo ao invs, uma referncia a um arquivo especifica o arquivo para onde a sada vai ser redirigida.

O segundo argumento um template de formatao, o qual um string contendo opcionalmente diretivas de formatao, de forma similar Linguagem "C": "An atom:~S~%and a list: ~S~%and an integer:~D~%"

Todos os argumentos restantes devem ser referenciados a partir do string de formatao.x

x

As diretivas de formatao do string sero repostas por LISP por caracteres apropriados com base nos valores dos outros parmetros a que eles se referem e ento imprimir o string resultante. Format sempre retorna NIL, a no ser que seu primeiro argumento seja NIL, caso em que no imprime nada e retorna o string resultante.

No exemplo acima, h trs diretivas de formatao: ~S, ~D e ~%.:x x x x

A primeira,~S, aceita qualquer objeto LISP e substituda por uma representao passvel de ser impressa deste objeto (a mesma produzida por print). A segunda, ~D, s aceita inteiros. A terceira, ~%, no aceita nada. Sempre reposta por uma quebra de linha. Outra diretiva til ~~, que substituda por um simples ~.

39

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Algumas Derivaes de Print A funo print possui algumas funes derivadas que tambm realizam a impresso dos dados, mas de uma forma diferente. Entre elas, podemos citar: x x x x terpri imprime uma linha em branco (no recebe nenhum argumento). prin1 imprime o dado na linha corrente e inicia uma nova linha. princ imprime o dado na linha corrente e no inicia nova linha. print inicia nova linha e imprime o dado.

Exemplo: (defun mediaquatro (primeiro segundo terceiro quarto) (princ "A mdia de") (terpri) ; salto de linha (princ primeiro) (princ " ") (princ segundo) (princ " ") (princ terceiro) (princ " ") (princ quarto) (terpri) (princ "") (/ (+ primeiro segundo terceiro quarto) 4)) MEDIAQUATRO > (mediaquatro 2 4 6 8) A mdia de 2468 5

40

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

7 ListasAs listas so um dos componentes fundamentais da linguagem Lisp. O nome da linguagem , alis, uma abreviao de ``list processing''. Como iremos ver, as listas constituem uma estrutura de dados extremamente flexvel.

7.1 Operaes sobre listasEm Lisp, quando o segundo elemento de um cons outro cons, o Lisp escreve o resultado sob a forma de uma lista:> (cons 1 (cons 2 (cons 3 (cons 4 5)))) (1 2 3 4 . 5)

Se o ltimo elemento a constante nil, o Lisp considera que ela representa a lista vazia, pelo que escreve:> (cons 1 (cons 2 (cons 3 (cons 4 nil)))) (1 2 3 4)

Esta notao designa-se de lista e esta que o Lisp usa para simplificar a leitura e a escrita. Uma lista ento uma sequncia de elementos. Nesta ptica, a funo car devolve o primeiro elemento de uma lista, enquanto a funo cdr devolve o resto da lista. A funo cons pode ser vista como recebendo um elemento e uma lista e devolve como resultado uma nova lista correspondente juno daquele elemento no princpio daquela lista. Segundo esta abordagem, a funo cons um construtor do tipo abstrato de informao lista, enquanto as funes car e cdr so seletores. Uma lista vazia uma sequncia sem qualquer elemento e pode ser escrita como nil ou ainda mais simplesmente (). A lista vazia o elemento mais primitivo do tipo lista. nil o construtor do elemento primitivo. Pode-se testar se uma lista vazia com a funo null. A funo null , portanto, um reconhecedor do tipo lista.> (null nil) t > (null (cons 1 (cons 2 nil))) nil

Exerccio 32 Escreva uma funo que calcula uma lista de todos os nmeros desde a at b.> (enumera 1 10) (1 2 3 4 5 6 7 8 9 10)

Embora as listas no sejam mais do que uma estruturao particular de clulas cons, podendo por isso ser acedidas com as funes car e cdr, considerado melhor estilo de 41

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi programao usar as funes equivalentes first e rest. first devolve o primeiro elemento da lista enquanto rest devolve o resto da lista, i.e., sem o primeiro elemento. Do mesmo modo, o predicado null deve ser substitudo pelo seu equivalente endp.> (first (enumera 1 10)) 1 > (rest (enumera 1 10)) (2 3 4 5 6 7 8 9 10)

Exerccio 33 Escreva uma funo que filtra uma lista, devolvendo uma lista com os elementos que verificam um determinado critrio. Utilize-a para encontrar os nmeros pares entre 1 e 20.> (filtra (function par?) (enumera 1 20)) (2 4 6 8 10 12 14 16 18 20)

Obs.: lembre-se de definir a funo par?. Esta funo j existe em Lisp e denomina-se remove-if-not. Quando se pretendem construir listas pode-se usar tambm a funo list. Esta funo recebe qualquer nmero de argumentos e constri uma lista com todos eles.> (list 1 2 3 4) (1 2 3 4) > (first (list 1 2 3 4)) 1 > (rest (list 1 2 3 4)) (2 3 4) > (list 1 2 (list 10 20) 3 4) (1 2 (10 20) 3 4)

Como se v possvel construir listas dentro de listas. Lisp permite tambm a construo de listas diretamente no avaliador. Idealmente, bastaria escrever (1 2 3 ...), s que isso seria avaliado segundo as regras de avaliao das combinaes. O nmero 1 seria considerado um operador e os restantes elementos da lista os operandos. Para evitar que uma lista possa ser avaliada podemos usar a forma especial quote, que devolve o seu argumento sem o avaliar.> (quote (1 . (2 . (3 . nil)))) (1 2 3) > (quote (1 2 3 4 5 6 7 8 9 10)) (1 2 3 4 5 6 7 8 9 10) > (filtro (function par?) (quote (1 2 3 4 5 6 7 8 9 10))) (2 4 6 8 10)

Uma vez que as formas especiais quote e function so bastante utilizadas, Lisp fornece um meio de se simplificar a sua utilizao. Se dermos ao avaliador uma expresso 42

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi precedida por uma plica (quote em Ingls), como se tivessemos empregue a forma especial quote. A substituio feita durante a leitura da expresso. Do mesmo modo, se precedermos uma funo ou uma lambda por #' (cardinal-plica) como se tivssemos empregue a forma especial function. 'exp equivalente a (quote exp), enquanto que #'exp equivalente a (function exp).> '(1 2 3 4 5) (1 2 3 4 5) > (filtra #'par '(1 2 3 4 5 6)) (2 4 6)

Exerccio 44 O que que o avaliador de Lisp devolve para a seguinte expresso: (first ''(1 2 3))? Como natural, as operaes car e cdr podem ser encadeadas:> (car 1 > (cdr (2 3) > (car 2 > (car 3 '(1 2 3)) '(1 2 3)) (cdr '(1 2 3)) (cdr (cdr '(1 2 3))))

Dado que aquele gnero de expresses muito utilizado em Lisp, foram compostas as vrias combinaes, e criaram-se funes do tipo (caddr exp), que correspondem a (car (cdr (cdr exp))). O nome da funo indica quais as operaes a realizar. Um ``a'' representa um car e um ``d'' representa um cdr.> (cadr '(1 2 3)) 2 > (cdddr '(1 2 3)) nil

7.2 Funes teisExerccio 35 Escreva uma funo n-esimo que devolva o n-simo elemento de uma lista. Note que o primeiro elemento da lista corresponde a n igual a zero. Esta funo j existe em Lisp e denomina-se nth. Exerccio 36

43

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Escreva uma funo muda-n-esimo que recebe um nmero n, uma lista e um elemento, e substitui o n-simo elemento da lista por aquele elemento. Note que o primeiro elemento da lista corresponde a n igual a zero. Exerccio 37 Escreva uma funo que calcula o comprimento de uma lista, i.e., determina quantos elementos ela tem. Esta funo j existe em Lisp e denomina-se length. Exerccio 38 Escreva uma funo que recebe um elemento e uma lista que contm esse elemento e devolve a posio desse elemento na lista. Exerccio 39 Escreva uma funo que calcula o nmero de tomos que uma lista (possivelmente com sublistas) tem. Exerccio 40 Escreva uma funo junta que recebe duas listas como argumento e devolve uma lista que o resultado de as juntar uma frente da outra. Esta funo j existe em Lisp e denomina-se append. Exerccio 41 Defina uma funo inverte que recebe uma lista e devolve outra lista que possui os mesmos elementos da primeira s que por ordem inversa. Esta funo j existe em Lisp e denomina-se reverse. Exerccio 42 Escreva uma funo designada inverte-tudo que recebe uma lista (possivelmente com sublistas) e devolve outra lista que possui os mesmo elementos da primeira s que por ordem inversa, e em que todas as sublistas esto tambm por ordem inversa, i.e.:> (inverte-tudo '(1 2 (3 4 (5 6)) 7)) (7 ((6 5) 4 3) 2 1)

Exerccio 43

44

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Escreva uma funo mapear que recebe uma funo e uma lista como argumentos e devolve outra lista com o resultado de aplicar a funo a cada um dos elementos da lista. Esta funo j existe em Lisp e denomina-se mapcar. Exerccio 44 Escreva uma funo denominada alisa que recebe uma lista (possivelmente com sublistas) como argumento e devolve outra lista com todos os tomos da primeira e pela mesma ordem, i.e.> (alisa '(1 2 (3 4 (5 6)) 7)) (1 2 3 4 5 6 7)

Exerccio 45 Escreva uma funo membro? que recebe um objecto e uma lista e verifica se aquele objecto existe na lista. Esta funo j existe em Lisp e denomina-se member. Quando ela encontra um elemento igual na lista devolve o resto dessa lista.> (member 3 '(1 2 3 4 5 6)) (3 4 5 6)

Exerccio 46 Escreva uma funo elimina que recebe um elemento e uma lista como argumentos e devolve outra lista onde esse elemento no aparece. Esta funo j existe em Lisp e denomina-se remove. Exerccio 47 Escreva uma funo substitui que recebe dois elementos e uma lista como argumentos e devolve outra lista com todas as ocorrncias do segundo elemento substitudas pelo primeiro. Esta funo j existe em Lisp e denomina-se subst. Exerccio 48 Escreva uma funo remove-duplicados que recebe uma lista como argumento e devolve outra lista com todos os elementos da primeira mas sem duplicados, i.e.:> (remove-duplicados '(1 2 3 3 2 4 5 4 1)) (3 2 5 4 1)

45

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Esta funo no mantm a anterior ordenao da lista. Se pretendermos preservar a ordem original, temos de testar se um elemento j existe na lista que estamos a construir e no na que estamos a analisar.> (remove-duplicados2 '(1 2 3 3 2 4 5 4 1)) (1 2 3 4 5)

Esta funo j existe em Lisp e denomina-se remove-duplicates. Exerccio 49 Escreva as funes inversas do cons, car e cdr, designadas snoc, rac e rdc. O snoc recebe um elemento e uma lista e junta o elemento ao fim da lista. O rac devolve o ltimo elemento da lista. O rdc devolve todos os elementos da lista menos o primeiro. A funo snoc j existe em Lisp atravs da combinao das funes append e list. A funo rac j existe em Lisp atravs da combinao das funes first e last. A funo rdc j existe em Lisp e denomina-se butlast. Exerccio 50 As funes todos?, algum?, nenhum? e nem-todos? so predicados que recebem uma funo e uma lista e verificam, respectivamente, se a funo verdade para todos os elementos da lista, se verdade para pelo menos um, se no verdade para todos e se no verdade para pelo menos um. Defina estas funes. Esta funo j existe em Lisp e denomina-se every. Esta funo j existe em Lisp e denomina-se some. Esta funo j existe em Lisp e denomina-se notany. Esta funo j existe em Lisp e denomina-se notevery.

7.4 Usando as operaesQuando se pretendem construir listas pode-se usar tambm a funo list. Esta funo recebe qualquer nmero de argumentos e constri uma lista com todos eles.> (list 1 2 3 4) (1 2 3 4) Retorna o primeiro elemento: > (first (list 1 2 3 4))

46

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi1 Retorna o resto de uma lista: a lista restante a partir do primeiro elemento: > (rest (list 1 2 3 4)) (2 3 4) Construindo uma lista dentro de outra lista: > (list 1 2 (list 10 20) 3 4) (1 2 (10 20) 3 4)

Como se v possvel construir listas dentro de listas. Lisp permite tambm a construo de listas diretamente no avaliador. Idealmente, bastaria escrever (1 2 3 ...), s que isso seria avaliado segundo as regras de avaliao das combinaes. O nmero 1 seria considerado um operador e os restantes elementos da lista os operandos. Para evitar que uma lista possa ser avaliada podemos usar a forma especial quote, que devolve o seu argumento sem o avaliar.> (quote (1 2 3) (1 2 3) > (quote (1 2 3 4 5 6 7 8 9 10)) (1 2 3 4 5 6 7 8 9 10)

Uma vez que as formas especiais quote e function so bastante utilizadas, Lisp fornece um meio de se simplificar a sua utilizao. Se dermos ao avaliador uma expresso precedida por uma plica (quote em Ingls), como se tivessemos empregue a forma especial quote. A substituio feita durante a leitura da expresso. Do mesmo modo, se precedermos uma funo ou uma lambda por #' (cardinal-plica) como se tivssemos empregue a forma especial function. 'exp equivalente a (quote exp), enquanto que #'exp equivalente a (function exp).> '(1 2 3 4 5) (1 2 3 4 5) > (remove-if-not #'evenp '(1 2 3 4 5 6)) (2 4 6)

Devolve o n-simo elemento de uma lista. Note que o primeiro elemento da lista corresponde a n igual a zero. nth 0 '(1 2 3) 1 Devolve o comprimento de uma lista, i.e., determina quantos elementos ela tem. length '(1 2 3 4) 4

47

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

Recebe duas listas como argumento e devolve uma lista que o resultado de as juntar uma frente da outra. append (list 1 2 3) (list 4 5 6) (1 2 3 4 5 6)Inverte uma lista e devolve outra lista que possui os mesmos elementos da primeira s

que por ordem inversa.reverse (list 1 2 3) (3 2 1)

Escreva uma funo designada inverte-tudo que recebe uma lista (possivelmente com sublistas) e devolve outra lista que possui os mesmo elementos da primeira s que por ordem inversa, e em que todas as sublistas esto tambm por ordem inversa, i.e.: Recebe uma funo e uma lista como argumentos e devolve outra lista com o resultado de aplicar a funo a cada um dos elementos da lista. mapcar (function sqrt) (list 1 2 3)(1.0 1.4142135623730951 1.7320508075688772)

Recebe um objeto e uma lista e verifica se aquele objeto existe na lista. Esta funo j existe em Lisp e denomina-se member. Quando ela encontra um elemento igual na lista devolve o resto dessa lista.> (member 3 '(1 2 3 4 5 6)) (3 4 5 6)

Recebe um elemento e uma lista como argumentos e devolve outra lista onde esse elemento no aparece. remove 3 (list 12 3 4) (12 4) Recebe dois elementos e uma lista como argumentos e devolve outra lista com todas as ocorrncias do segundo elemento substitudas pelo primeiro. subst 1 2 (list 2 3 4 3 2 2) (1 3 4 3 1 1) Recebe uma lista como argumento e devolve outra lista com todos os elementos da primeira mas sem duplicados. remove-duplicates (list 1 1 1 2 2 2 3 3 3 4 4 4) (1 2 3 4) Recebe uma lista e devolve uma outra lista sem o ltimo elemento. 48

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

butlast (list 2 3 4) (2 3) Recebe uma funo e uma lista e verifica se se a funo verdade para todos os elementos da lista every (function zerop) (list 0 1 2) NIL every (function zerop) (list 0 0 0) T Recebe uma funo e uma lista e verifica se se a funo verdade para pelo menos um elemento da lista some (function zerop) (list 0 1 2) T some (function zerop) (list 2 3 4) NIL

7.5 Listas de argumentosSendo as listas uma das estruturas bsicas do Lisp, a linguagem permite aplicar funes directamente a listas de argumentos atravs da funo apply. Esta em tudo idntica funo funcall, mas em vez de ter os argumentos da funo a aplicar como argumentos da funo funcall, tem-nos numa lista, i.e.:(funcall func arg-1 arg-2 ...arg-n) (apply func (list arg-1 arg-2 ...arg-n))

Na linguagem Common Lisp, a funo apply uma fuso entre a funo funcall e a funo apply primitiva, pois da forma:(apply func arg-1 arg-2 ...rest-args)

Nesta aplicao o ltimo argumento rest-args uma lista com os restantes argumentos. Desta forma, pode-se escrever qualquer das seguintes equivalncias:(funcall func arg-1 arg-2 ...arg-n) (apply func (list arg-1 arg-2 ...arg-n)) (apply func arg-1 (list arg-2 ...arg-n)) (apply func arg-1 arg-2 ...(list arg-n)) (apply func arg-1 arg-2 ...arg-n nil))

49

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

7.6 Tipos aglomeradosUm tipo aglomerado um tipo abstrato de informao que composto exclusivamente pela aglomerao de outros tipos abstratos. O conjunto dos racionais um exemplo pois, como vimos, um racional no mais do que uma aglomerao de dois inteiros. As operaes fundamentais de um tipo aglomerado so os seus construtores e seletores, embora possam existir outras. Como vimos, para um racional, as operaes mais utilizadas eram o construtor racional e os seletores numerador e denominador, mas tambm foram definidos alguns testes e os transformadores de entrada/sada. Os tipos aglomerados so extremamente utilizados. Por este motivo costume as linguagens de alto nvel fornecerem ferramentas prprias para os tratar. Pascal, por exemplo, permite defini-los com a forma especial record, enquanto que a linguagem C usa, para o mesmo efeito, o struct. Para exemplificarmos a utilizao de tipos aglomerados podemos considerar a definio de um automvel. Um automvel caracterizado por uma marca, um modelo, um dado nmero de portas, uma cilindrada, uma potncia, etc. Para simplificar, podemos considerar s as trs primeiras. O construtor de um objeto do tipo automvel no tem mais que agrupar as informaes relativas a cada uma daquelas caractersticas. Para isso, podemos usar a funo list. Assim, criamos o construtor do tipo da seguinte forma:(defun novo-automovel (marca modelo portas) (list marca modelo portas))

Os seletores do tipo automvel limitam-se a determinar de que que um dado objeto daquele tipo composto:(defun automovel-marca (automovel) (nth 0 automovel)) (defun automovel-modelo (automovel) (nth 1 automovel)) (defun automovel-portas (automovel) (nth 2 automovel))

Estando na posse destas funes, podemos criar um automvel especfico, por exemplo:> (novo-automovel 'honda 'civic 2) (honda civic 2)

Dado aquele objeto do tipo automvel, podemos estar interessados em alterar-lhe o nmero de portas, passando-as de 2 para 4, por exemplo. Contudo, para manipularmos um tipo abstrato devemos restringirmo-nos s operaes desse tipo. Precisamos, portanto,

50

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi de criar novas operaes que nos permitem modificar um objeto. Para o tipo automvel poderamos definir:(defun muda-automovel-marca (automovel nova-marca) (muda-n-esimo 0 automovel nova-marca)) (defun muda-automovel-modelo (automovel novo-modelo) (muda-n-esimo 1 automovel novo-modelo)) (defun muda-automovel-portas (automovel novo-portas) (muda-n-esimo 2 automovel novo-portas))

A funo muda-n-esimo recebia um nmero n, uma lista e um novo elemento, e substitua o n-simo elemento da lista pelo novo elemento. Esta funo no alterava a lista original, produzindo uma nova lista. Desta forma, qualquer destas funes do tipo automvel deixa o automvel a modificar absolutamente inalterado, produzindo um novo automvel. Por este motivo, estas operaes devem ser vistas como construtores do tipo automvel, pois elas criam um novo automvel a partir de um outro j existente. Elas no permitem alterar um automvel j criado.

51

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

8 Programao imperativaTodas as funes que apresentadas anteriormente realizam operaes muito variadas e algumas so at relativamente complexas, mas nenhuma afeta os seus argumentos. Elas limitam-se a produzir novos objetos a partir de outros j existentes, sem alterar estes ltimos seja de que forma for. At as prprias variveis que introduzimos nas funes e que se destinavam a guardar valores temporrios no eram mais do que parmetros de uma lambda, e a sua inicializao correspondia a invocar a lambda com os valores iniciais como argumentos, sendo por isso inicializadas uma nica vez e nunca modificadas. Por este motivo, nem sequer foi apresentado nenhum operador de atribuio, to caracterstico em linguagens como C e Pascal. Este estilo de programao, sem atribuio, sem alterao dos argumentos de funes, e em que estas se limitam a produzir novos valores, designado programao funcional. Neste paradigma de programao, qualquer funo da linguagem considerada uma funo matemtica pura que, para os mesmos argumentos produz sempre os mesmos valores. Nunca nada destrudo. Uma funo que junta duas listas produz uma nova lista sem alterar as listas originais. Uma funo que muda o nmero de portas de um automvel produz um novo automvel. A programao funcional tem muitas vantagens sobre outros estilos de programao, em especial no que diz respeito a produzir programas muito rapidamente e minimizando os erros. Contudo, tem tambm as suas limitaes, e a sua incapacidade em modificar seja o que for a maior. A partir do momento em que introduzimos a modificao de objetos, estamos a introduzir o conceito de destruio. A forma anterior do objeto que foi modificado deixou de existir, passando a existir apenas a nova forma. A modificao implica a introduo do conceito de tempo. Os objetos passam a ter uma histria, e isto conduz a um novo estilo de programao.

8.1 AtribuioPara se alterar o valor de uma varivel Lisp possui um operador de atribuio. A forma especial setq recebe uma varivel e um valor e atribui o valor varivel.> (let ((x 2)) (setq x (+ x 3)) (setq x (* x x)) (setq x (- x 5)) x) 20

Cada vez que se realiza uma atribuio, perde-se o valor anterior que a varivel possua. Muito embora a forma especial setq, como todas as funes e forma especiais, retorne um valor, esse valor no possui qualquer interesse. O operador de atribuio serve apenas para modificar o estado de um programa, neste exemplo, alterando o valor de x. Por este motivo, a atribuio assemelha-se mais a um comando do que a uma funo. A atribuio uma ordem que tem de ser cumprida e de que no interessa o resultado. A ordem a 52

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi operao fundamental da programao imperativa, em que um programa composto por sucessivos comandos. Neste estilo de programao, os comandos funcionam por efeitos secundrios. O valor de cada comando no tem interesse e muitas vezes nem sequer tem significado falar dele.

8.2 SequenciaoA forma especial progn est especialmente vocacionada para este gnero de utilizao. Ela recebe um conjunto de expresses que avalia sequencialmente retornando o valor da ltima. Desta forma possvel incluir vrias aes no consequente ou alternativa de um if, por exemplo.(if (> 3 2) (progn (print 'estou-no-consequente) (+ 2 3)) (progn (print 'estou na alternativa) (* 4 5)))

Algumas das formas especiais do Lisp incluem um progn implcito. Vimos atrs um exemplo de um let que realizava quatro operaes em sequncia. Isto implica que o let e, por arrastamento, as lambdas e tudo o que for definido com defun, possuem tambm a capacidade de realizar operaes em sequncia. O cond est tambm nesta categoria. A sua sintaxe , na realidade, a seguinte:(cond (condio-1 expresso-11...expresso-1l) (condio-2 expresso-21...expresso-2m) (condio-n expresso-n1...expresso-nk))

O cond testa cada uma das condies em sequncia, e quando uma delas avalia para verdade, so avaliadas todas as expresses da clusula correspondente sendo devolvido o valor da ltima dessas expresses. Usando o cond, o exemplo anterior ficar mais elegante:(cond ((> 3 2) (print 'estou-no-consequente) (+ 2 3)) (t (print 'estou na alternativa) (* 4 5)))

A forma especial prog1 semelhante ao progn. Ela recebe um conjunto de expresses que avalia sequencialmente retornando o valor da primeira. A expresso equivalente ao exemplo anterior mas utilizando o prog1 ser:(if (> 3 2)

53

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi(prog1 (+ 2 3) (print 'estou-no-consequente)) (prog1 (* 4 5) (print 'estou na alternativa)))

Note-se que a ordem de avaliao das expresses de um prog1 igual de um progn. Apenas o valor retornado diferente: o primeiro no caso do prog1 e o ltimo no caso do progn. A sequenciao tambm suportada por qualquer lambda. Em consequncia, as formas especiais que implicam a criao de lambdas, como o let e o prprio defun permitem tambm especificar mais do que uma expresso, sendo estas avaliadas em sequncia e devolvido o valor da ltima.

8.3 Alterao de dadosA atribuio no est restricta a variveis. tambm possvel alterar o contedo da maioria dos tipos de dados Lisp. Uma clula cons, por exemplo, pode ser alterada com as funes rplaca e rplacd, significando, respectivamente ``replace-car'' e ``replacecdr''.> (let ((x (cons 1 2))) (rplaca x 3) x) (3 . 2) > (let ((x (cons 1 2))) (rplacd x 3) x) (1 . 3)

Note-se que estas funes so uma forma de atribuio e, como tal, destroem o contedo anterior da estrutura a que se aplicam. Por este motivo, este gnero de funes diz-se destrutivo. Na sequncia lgica da conveno usual em Lisp para a definio de reconhecedores, que terminam sempre com um ponto de interrogao (ou com a letra ``p'' de predicado), deve-se colocar um ponto de exclamao no fim do nome das funes destrutivas para salientar o seu carcter imperativo. Exerccio 51 Escreva uma funo junta! (note-se o ponto de exclamao) que recebe duas listas como argumento e devolve uma lista que o resultado de as juntar destrutivamente uma frente da outra, i.e., faz a cauda da primeira lista apontar para a segunda. Esta funo j existe em Lisp e denomina-se nconc.

54

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Exerccio 52 Analise o seguinte exemplo funcional e imperativo:> (let ((x '(1 2 3))) (junta x x)) (1 2 3 1 2 3) > (let ((x '(1 2 3))) (junta! x x)) (1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 ...

Exerccio 53 Escreva uma funo muda-n-esimo! (note-se o ponto de exclamao) que recebe um nmero n, uma lista e um elemento, e substitui o n-simo elemento da lista por aquele elemento. Note que o primeiro elemento da lista corresponde a n igual a zero. Exerccio 54 Reescreva as operaes do tipo abstrato de informao automvel que alteravam as caractersticas de um elemento do tipo de forma a torn-las destrutivas. Quando as operaes de um tipo abstrato alteram um elemento do tipo, essas operaes so classificadas como modificadores do tipo abstrato. Os modificadores, como caso especial da atribuio, so muito empregues em programao imperativa. Note-se que os modificadores possuem todos os problemas da atribuio simples, nomeadamente a alterao ser destrutiva. Isto levanta problemas quando se testa igualdade em presena de modificao.> (let ((x (novo-automovel 'honda 'civic 2))) (let ((y (muda-automovel-portas x 4))) (eql x y))) NIL > (let ((x (novo-automovel 'honda 'civic 2))) (let ((y (muda-automovel-portas! x 4))) (eql x y))) T

Repare-se que no primeiro exemplo (funcional), o automvel modificado , logicamente, diferente do automvel original. x e y representam automveis diferentes. No segundo exemplo (imperativo), a modificao do automvel que x representa realizada sobre esse prprio automvel, de modo que x e y acabam por representar um mesmo automvel (modificado). Muito embora esta situao possa ter vantagens, ela permite tambm a introduo de erros muito subtis e extremamente difceis de tirar. Para dar apenas um pequeno exemplo, repare-se que o automvel que x representa passou a ter quatro portas embora uma leitura superficial do cdigo sugira que ele foi criado com apenas duas. 55

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

8.4 RepetioPara alm dos operadores de atribuio (setq, rplaca, rplacd, etc.) e de sequenciao (progn, prog1, etc.) a linguagem Common Lisp possui muitas outras formas especiais destinadas a permitir o estilo de programao imperativa. De destacar so as estruturas de controle de repetio, tais como o loop, o do, o dotimes e ainda outras adequadas para iterar ao longo de listas. O loop a mais genrica de todas as formas de repetio. Ela recebe um conjunto de expresses que avalia sequencialmente, repetindo essa avaliao em ciclo at que seja avaliada a forma especial return. Esta ltima recebe uma expresso opcional e termina o ciclo em que est inserida, fazendo-o devolver o valor daquela expresso. A seguinte expresso exemplifica um ciclo que escreve todos os nmeros desde 0 at 100, retornando o smbolo fim no final do ciclo.(let ((n 0)) (loop (print n) (setq n (1+ n)) (when (> n 100) (return 'fim))))

A forma especial do um pouco mais sofisticada que o loop. Ela permite estabelecer variveis, inicializ-las e increment-las automaticamente, testar condies de paragem com indicao do valor a retornar e repetir a execuo de cdigo. Se reescrevermos o exemplo anterior usando a forma especial do, obtemos:(do ((n 0 (1+ n))) ((> n 100) 'fim) (print n))

Tal como o loop, a forma especial do pode ser interrompida em qualquer altura com um return, retornando o valor opcional fornecido com o return. Apesar do estilo mais utilizado na maioria das linguagens de programao ser o imperativo, ele muito pouco natural em Lisp. A falta de naturalidade resulta, por um lado, de os programas em Lisp se decomporem geralmente em pequenas funes que calculam valores, invalidando uma abordagem baseada em ciclos de alterao de variveis, tpica da programao imperativa. Por outro lado, a grande maioria de tipos de dados existentes em Lisp so inerentemente recursivos, o que dificulta o seu tratamento segundo o estilo imperativo.

56

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi Apesar de muito pouco prtico para usar em Lisp, a programao imperativa tem algumas vantagens, das quais a possibilidade de atribuio a maior (e tambm a mais perigosa).

57

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

58

Linguagem de ProgramaoII: Programao Funcional usando Lisp Crisitano Biancardi

9 Modelos de ambientesAt agora vimos que as variveis eram apenas designaes para valores. Quando se avaliava uma expresso, as variveis desapareciam, sendo substitudas pelos seus valores. A partir do momento em que podemos alterar o valor de uma varivel, o seu comportamento torna-se menos claro. Para se explicar correctamente este comportamento necessrio passar para um modelo de avaliao mais elaborado designado modelo de avaliao em ambientes. Neste modelo, uma varivel j no uma designao de um valor mas sim uma designao de um objecto que contm um valor. Esse objecto pode ser visto como uma caixa onde se guardam coisas. Em cada instante, a varivel designa sempre a mesma caixa, mas esta pode guardar coisas diferentes. Segundo o modelo de ambientes, o valor de uma varivel o contedo da caixa que ela designa. A forma especial setq a operao que permite meter valores dentro da caixa. As variveis so guardadas em estruturas denominadas enquadramentos. Por exemplo, cada vez que usamos a forma let criado um novo enquadramento para conter as variveis estabelecidas pelo let. Todas as expresses pertencent