• Nenhum resultado encontrado

Sumário Resumo

8 ABSTRACÇÃO DE DADOS PARES EM SCHEME 37

Exercícios

Exercício 8.1

Diga qual o resultado de avaliar cada uma das seguintes expressões. Se alguma delas der origem a um erro, explique porquê.

(cons 2 3)

(car (cons 2 3))

(cddr (cons 2 3))

(cdr (cons "ola" "bom dia")) (list (cons 1 3) 4)

(cdr (list 2 3)) (cdr (cons 2 3)) ()

(list ())

(cons (integer? (sqrt 4)) (integer? 2.0)) (pair? (cons 2 3)) (list? (cons 2 3)) (list? (list 2 3)) (pair? (list 2 3 4)) (cadr (list 2 3 4)) Resposta: >(cons 2 3) (2 . 3) >(car (cons 2 3)) 2 >(cddr (cons 2 3))

cddr: expects argument of type <cddrable value>; given (2 . 3) >(cdr (cons "ola" "bom dia"))

8 ABSTRACÇÃO DE DADOS PARES EM SCHEME 38 >(list (cons 1 3) 4) ((1 . 3) 4) >(cdr (list 2 3)) (3) >(cdr (cons 2 3)) 3 >() () >(list ()) (())

>(cons (integer? (sqrt 4)) (integer? 2.0)) (#t . #t) >(pair? (cons 2 3)) #t >(list? (cons 2 3)) #f >(list? (list 2 3)) #t >(pair? (list 2 3 4)) #t >(cadr (list 2 3 4)) 3 Exercício 8.2

Represente as seguintes listas e pares usando a notação de caixas e ponteiros: 1. (1) 2. (1 . 2) 3. (1 2) 4. (1 (2 (3 (4 5)))) 5. (1 (2 . 3) 4) 6. (((2 (6 (7 . 8) 3)) 1)) 7. (1 (((2)))) Resposta:

8 ABSTRACÇÃO DE DADOS PARES EM SCHEME 39 Exercício 8.3

Considere as seguintes definições para o procedimento make-rat, que, dados dois inteiros, retorna o racional em que o primeiro é o numerador e o segundo é o denominador:

(define (make-rat n d) (cons n d))

(define (make-rat n d) (let ((g (gcd n d)))

(cons (/ n g) (/ d g))))

Em relação à primeira definição, a segunda tem a vantagem de reduzir o numerador e o denomi- nador aos números mais pequenos possível.

(Livro — 2.1) Defina uma versão melhor demake-ratque considere argumentos positivos e negativos.make-ratdeve normalizar o sinal, de forma a que, se o número racional for positivo, tanto o numerador como o denominador são positivos; e se o número racional for negativo, só o numerador é que é negativo.

Resposta: (define (gcd a b) (if (= b 0) a (gcd b (remainder a b)))) (define (make-rat n d)

(let ((g (gcd (abs n) (abs d)))) (if (>= (* n d) 0)

(cons (/ (abs n) (abs g)) (/ (abs d) (abs g))) (cons (- (/ (abs n) g)) (/ (abs d) g)))))

Exercício 8.4

(Livro — 2.2) Considere o problema de representar segmentos de recta num plano. Cada seg- mento é representado por um par de pontos: um ponto inicial e um ponto final.

Defina um construtor make-segmente os selectores start-segment e end-segment que definem a representação dos segmentos em termos de pontos.

Adicionalmente, um ponto pode ser representado como um par de números: a coordenadax e a coordenaday.

Especifique o construtormake-pointe os selectoresx-pointey-pointque definem esta representação.

Usando os seus selectores e construtores, defina um procedimentomidpoint-segmentque recebe um segmento de recta como argumento e retorna o seu ponto médio (o ponto cujas coor- denadas são a média das coordenadas dos pontos que definem o segmento).

Resposta:

(define (make-segment ip fp) (cons ip fp))

8 ABSTRACÇÃO DE DADOS PARES EM SCHEME 40

(define (start-segment seg) (car seg))

(define (end-segment seg) (cdr seg)) ;==== (define (make-point x y) (cons x y)) (define (x-point p) (car p)) (define (y-point p) (cdr p)) ;====

(define (midpoint-segment seg)

(let ((x1 (x-point (start-segment seg))) (y1 (y-point (start-segment seg))) (x2 (x-point (end-segment seg))) (y2 (y-point (end-segment seg)))) (make-point (/ (+ x1 x2) 2)

(/ (+ y1 y2) 2))))

Exercício 8.5

(Livro — 2.3) Implemente uma representação de rectângulos num plano. (Pode ser útil usar os resultados do exercício anterior.)

Com base nos seus construtores e selectores, crie procedimentos que calculem o perímetro e a área de um dado rectângulo.

Implemente uma representação diferente para os rectângulos.

Consegue conceber o seu sistema com as barreiras de abstração adequadas, de forma a que os procedimentos que calculam a área e o perímetro funcionem com qualquer das representações? Resposta: Antes de mais nada, devemos definir a interface dos procedimentos que vão manipular rectân-

gulos. Os rectângulos vão ser sempre definidos à custa de quatro valores: as coordenadas y do seu lado superior (top) e do seu lado inferior (bottom) e as coordenadas x do seu lado esquerdo (left) e do seu lado direito (right).

Os selectores vão dar precisamente cada um destes valores, independentemente da representação interna que for usada para os rectângulos.

; Numa implementacao, vamos definir os rectangulos como um par de pontos, ; o TopLeft e o BottomRight

(define (make-rect top left right bottom)

(cons (make-point left top) ; a primeira coordenada dos pontos e’ x (make-point right bottom)))

Um melhoramento seria escrever o procedimento make-rect de forma a que este verificasse se os valoes recebidos fazem sentido (por exemplo, left < right).

8 ABSTRACÇÃO DE DADOS PARES EM SCHEME 41

(define (top-left-rect rect) (car rect))

(define (bottom-right-rect rect) (cdr rect))

(define (top rect)

(y-point (top-left-rect rect))) (define (left rect)

(x-point (top-left-rect rect))) (define (bottom rect)

(y-point (bottom-right-rect rect))) (define (right rect)

(x-point (bottom-right-rect rect))) (define (width rect)

(- (right rect) (left rect))) (define (height rect)

(- (top rect) (bottom rect))) (define (perimeter rect)

(* 2

(+ (width rect) (height rect)))) (define (area rect)

(* (width rect) (height rect)))

Exercício 8.6

Com base nas respostas aos exercícios anteriores, escreva um procedimentodentro-rectangulo, que recebe um rectângulo e um ponto e retorna#tse o ponto estiver dentro do rectângulo (in- cluindo a fronteira) e#fse estiver fora do rectângulo.

Resposta:

(define (dentro-rectangulo pt rect)

(and (>= (top rect) (y-point pt) (bottom rect)) (>= (right rect) (x-point pt) (left rect))))

Exemplos:

>(dentro-rectangulo (make-point 2 3) (make-rect 5 1 5 1)) #t

8 ABSTRACÇÃO DE DADOS PARES EM SCHEME 42 >(dentro-rectangulo (make-point 10 10) (make-rect 5 1 5 1)) #f >(dentro-rectangulo (make-point 1 5) (make-rect 5 1 5 1)) #t Exercício 8.7

Defina os seguintes procedimentos que operam sobre listas. Os seus procedimentos devem dar erro (usando oerror) quando isso se justificar. Quando for possível, escreva dois procedimen- tos, um que gera um processo recursivo e outro que gera um processo iterativo.

1. O procedimento primeiro-par que recebe uma lista e retorna um par com os dois primeiros elementos da lista.

2. O procedimento maior-elementoque recebe uma lista de inteiros e retorna o maior elemento dessa lista.

3. O procedimentosoma-elementos que recebe uma lista e retorna a soma de todos os elementos dessa lista.

4. O procedimento aplica-op-com-passoque recebe uma lista e dois procedimentos e retorna outra lista, cujos elementos são obtidos aplicando o primeiro procedimento ao primeiro elemento da lista inicial, e das sucessivas listas que são obtidas por aplicação do segundo procedimento (até que a lista fique vazia). Por exemplo,

(aplica-op-com-passo (list 1 2 3 4 5) (lambda (x) (* 2 x)) cddr) deverá retornar(2 6 10).

5. O procedimentoimprime-lista-de-paresque recebe uma lista de pares e imprime os pares, um por linha. O seu procedimento deve assinalar quando é que chega ao fim da lista. Por exemplo,

(imprime-lista-de-pares (list (cons "Luisa" 12345678) (cons "Jorge" 23456789) (cons "Maria" 34567890) (cons "Rui" 45678901))) Deverá imprimir Luisa -> 12345678 Jorge -> 23456789 Maria -> 34567890 Rui -> 45678901 Fim da lista Resposta:

8 ABSTRACÇÃO DE DADOS PARES EM SCHEME 43

1. (define (primeiro-par l)

(if (and (pair? l) (pair? (cdr l))) (cons (car l) (cadr l))

(error "primeiro-par: espera uma lista com pelo menos 2 elementos, recebeu" l)))

Exemplos:

>(primeiro-par ())

primeiro-par: espera uma lista com pelo menos 2 elementos, recebeu () >(primeiro-par (list 1))

primeiro-par: espera uma lista com pelo menos 2 elementos, recebeu (1) >(primeiro-par (list 1 2 3 4)))

(1 . 2)

>(primeiro-par (list 1 (cons 2 3) 4)) (1 2 . 3)

Neste caso, não vamos gerar nenhum processo recursivo, pois isso não tem interesse. 2. (define (maior-elemento l) (define (m-e-aux l) (if (null? (cdr l)) (car l) (max (car l) (m-e-aux (cdr l))))) (if (or (null? l) (not (pair? l)))

(error "maior-elemento: espera uma lista com pelo menos 1 elemento, recebeu" l)

(m-e-aux l)))

Para o procedimento maior-elemento gerar um processo iterativo, usamos o seguinte:

(define (maior-elemento l) (define (m-e-aux l maximo)

(if (null? l) maximo

(m-e-aux (cdr l) (max (car l) maximo)))) (if (or (null? l) (not (pair? l)))

(error "maior-elemento: espera uma lista com pelo menos 1 elemento, recebeu" l)

(m-e-aux (cdr l) (car l))))

Exemplos:

>(maior-elemento ())

maior-elemento: espera uma lista com pelo menos 1 elemento, recebeu () >(maior-elemento (list 1 5 2 10 4)) 10 3. (define (soma-elementos l) (if (null? l) 0 (+ (car l)

8 ABSTRACÇÃO DE DADOS PARES EM SCHEME 44

(soma-elementos (cdr l)))))

Para gerar um processo iterativo, precisamos de um argumento auxiliar e por causa disso de um procedimento auxiliar

(define (soma-elementos l) (define (s-e-aux l acc)

(if (null? l) acc

(s-e-aux (cdr l) (+ (car l) acc)))) (s-e-aux l 0)) Exemplos: >(soma-elementos (list 1 5 2 10 4)) 22 4. (define (aplica-op-com-passo l p1 p2) (if (null? l) () (cons (p1 (car l)) (aplica-op-com-passo (p2 l) p1 p2)))) Exemplos:

(aplica-op-com-passo (list 1 2 3 4 5) (lambda (x) (* 2 x)) cddr) cddr: expects argument of type <cddrable value>; given (5)

O problema foi que no último passo da recursão chamámosp2com(5), o que originou este erro. No entanto, no procedimento aplica-op-com-passonão podemos resolver este problema, pois não sabemos qual vai ser o procedimento passado no terceiro argumento. Assim, quando chamarmosaplica-op-com-passoé que vamos ter o cuidado de passar uma lista que não vá originar um erro. Neste exemplo, a lista deveria ter mais um elemento, mas que não vai ser usado para nada. Por exemplo:

(aplica-op-com-passo (list 1 2 3 4 5 ()) (lambda (x) (* 2 x)) cddr) (2 6 10)

Outra alternativa seria que o segundo procedimento testasse se pode ser aplicado, de modo a não dar erro. Por exemplo:

(aplica-op-com-passo (list 1 2 3 4 5) (lambda (x) (* 2 x))

(lambda (x) (if (>= (length x) 2) (cddr x)

())))

Para gerar um processo iterativo, precisamos de um argumento auxiliar e por causa disso de um procedimento auxiliar. Uma possibilidade é:

(define (aplica-op-com-passo l p1 p2) (define (a-o-c-p-aux l res)

(if (null? l) res

(a-o-c-p-aux (p2 l) (cons (p1 (car l)) res)))) (a-o-c-p-aux l ()))

8 ABSTRACÇÃO DE DADOS PARES EM SCHEME 45

Exemplos:

(aplica-op-com-passo (list 1 2 3 4 5 ()) (lambda (x) (* 2 x)) cddr) (10 6 2)

O problema é que assim ficamos com a lista de resultados invertida! Não é trivial criar um processo iterativo neste caso, pois não sabemos acrescentar elementos no fim de listas. Para isso deveríamos definir um outro procedimento, mas ficaria muito pouco eficiente porque tinha que percorrer sempre toda a lista, e assim iríamos perder as vantagens dos processos iterativos.

5. (define (imprime-lista-de-pares lista)

(cond ((null? lista) (display "Fim da lista")) ((not (pair? (car lista)))

(error "imprime-lista-de-pares: espera uma lista de pares, recebeu" lista))

(else (display (caar lista)) (display " -> ")

(display (cdar lista)) (newline)

(imprime-lista-de-pares (cdr lista)))))

Exemplos:

>(imprime-lista-de-pares (list (cons "Luisa" 12345678) (cons "Jorge" 23456789) (cons "Maria" 34567890) (cons "Rui" 45678901))) Luisa -> 12345678 Jorge -> 23456789 Maria -> 34567890 Rui -> 45678901 Fim da lista

>(imprime-lista-de-pares (list (cons "Luisa" 12345678) (cons "Jorge" 23456789) (cons "Maria" 34567890) "Rui")) Luisa -> 12345678 Jorge -> 23456789 Maria -> 34567890

imprime-lista-de-pares: espera uma lista de pares, recebeu ("Rui")

9 LISTAS EM SCHEME 46

9

Listas em Scheme

Sumário Resumo

9 LISTAS EM SCHEME 47

Exercícios

Exercício 9.1

(Livro — 2.17) Defina um procedimentolast-pair, que retorna a lista que contém apenas o último elemento de uma dada lista não vazia:

(define (last-pair l) (if (null? (cdr l)) l (last-pair (cdr l)))) Exemplos: >(last-pair (list 23 72 149 34)) (34) >(last-pair ())

cdr: expects argument of type <pair>; given () >(last-pair (list ()))

(()) Resposta:

Exercício 9.2

(Livro — 2.18) Defina um procedimento reverse, que recebe como argumento uma lista e retorna uma lista com os mesmos elementos, mas pela ordem inversa:

(reverse (list 1 4 9 16 25)) (25 16 9 4 1) Resposta: (define (reverse l) (if (null? l) () (cons (car l) (reverse (cdr l))))) Exemplos: >(reverse (list 1 4 9 16 25)) (1 4 9 16 25)

Mas este procedimento não inverte a lista! Precisamos de ir tirando os elementos do início da lista e ir colocando numa lista auxiliar. Para isso, definimos um procedimento auxiliar:

(define (reverse l) (define (r-aux l res)

(if (null? l) res

(r-aux (cdr l) (cons (car l) res)))) (r-aux l ()))

9 LISTAS EM SCHEME 48

>(reverse (list 1 4 9 16 25)) (25 16 9 4 1)

Exercício 9.3

Defina um procedimentomap, que recebe como argumentos um procedimento de um argumento e uma lista, e retorna a lista dos resultados produzidos aplicando o procedimento a cada elemento da lista.

(map abs (list -10 2.5 -11.6 17)) (10 2.5 11.6 17)

Resposta:

(define (map proc l) (if (null? l)

()

(cons (proc (car l))

(map proc (cdr l)))))

Exemplos:

>(map abs (list -10 2.5 -11.6 17)) (10 2.5 11.6 17)

>(map (lambda (x) (* x 5)) (list 1 2 3 4 5)) (5 10 15 20 25)

Exercício 9.4

(Livro — 2.21) O procedimentosquare-listrecebe como argumento uma lista de números e retorna uma lista com os quadrados desses números.

(square-list (list 1 2 3 4)) (1 4 9 16)

Seguem-se duas definições diferentes para o procedimentosquare-list. Complete ambas as definições, preenchendo as expressões que faltam:

(define (square-list items) (if (null? items)

()

(cons <??> <??>))) (define (square-list items)

(map <??> <??>)) Resposta:

(define (square-list items) (if (null? items)

()

(cons ((lambda (x) (* x x)) (car items)) (square-list (cdr items)))))

(define (square-list items)

9 LISTAS EM SCHEME 49

Exemplos:

>(square-list (list 1 2 3 4)) (1 4 9 16)

Exercício 9.5

(Livro — 2.22) O Luís tenta re-escrever o primeiro procedimentosquare-listdo exercício anterior de modo a que ele passe a gerar um processo iterativo:

(define (square-list items) (define (iter things answer)

(if (null? things) answer

(iter (cdr things)

(cons ((lambda (x) (* x x)) (car items)) answer))))

(iter items ()))

Infelizmente, definir o procedimentosquare-list desta maneira produz a lista de resposta pela ordem inversa à desejada. Porquê?

O Luís tenta então corrigir este erro trocando os argumentos docons: (define (square-list items)

(define (iter things answer) (if (null? things)

answer

(iter (cdr things) (cons answer

((lambda (x) (* x x)) (car items)))))) (iter items ()))

Isto também não funciona. Explique porquê. Resposta:

Exemplos:

Exercício 9.6

(Livro — 2.23) O procedimento for-each é semelhante aomap. Recebe como argumentos um procedimento e uma lista de elementos. No entanto, em vez de formar uma lista com os resultados,for-eachapenas aplica o procedimento a cada um dos elementos de cada vez, da esquerda para a direita. Os valores retornados pela aplicação do procedimento aos elementos não são usados —for-eaché usado com procedimentos que executam uma acção, tal como imprimir. Por exemplo:

9 LISTAS EM SCHEME 50

(for-each (lambda (x) (newline) (display x)) (list 57 321 28))

57 321 28

O valor retornado pela chamada a for-each (não ilustrado acima) pode ser qualquer coisa, como verdadeiro. Apresente uma implementação para o procedimentofor-each.

Resposta: (define (for-each p l) (cond ((null? l) #t) (else (p (car l)) (for-each p (cdr l))))) Exemplos:

>(for-each (lambda (x) (newline) (display x)) (list 57 321 28)) 57 321 28 #t Exercício 9.7

Implemente o procedimentoimprime-lista-de-paresda aula passada usando o proce- dimento for-each. Lembre-se que o procedimento recebe uma lista de pares e imprime os pares, um por linha, e deve assinalar quando é que chega ao fim da lista. Por exemplo,

(imprime-lista-de-pares (list (cons "Luisa" 12345678) (cons "Jorge" 23456789) (cons "Maria" 34567890) (cons "Rui" 45678901))) Deverá imprimir Luisa -> 12345678 Jorge -> 23456789 Maria -> 34567890 Rui -> 45678901 Fim da lista Resposta:

(define (imprime-elemento el) (cond ((not (pair? el))

(error "imprime-elemento: espera um par, recebeu" el)) (else (display (car el))

(display " -> ") (display (cdr el)) (newline))))

(define (imprime-lista-de-pares lista) (for-each imprime-elemento lista))

9 LISTAS EM SCHEME 51

Nesta definição, o fim da lista é assinalado pelo valor retornado pelo procedimentofor-each. Exem- plos:

>(imprime-lista-de-pares (list (cons "Luisa" 12345678) (cons "Jorge" 23456789) (cons "Maria" 34567890) (cons "Rui" 45678901))) Luisa -> 12345678 Jorge -> 23456789 Maria -> 34567890 Rui -> 45678901 #t

>(imprime-lista-de-pares (list (cons "Luisa" 12345678) (cons "Jorge" 23456789) (cons "Maria" 34567890) "Rui")) Luisa -> 12345678 Jorge -> 23456789 Maria -> 34567890

imprime-elemento: espera um par, recebeu "Rui"

Exercício 9.8

(Livro — 2.24) Suponha que avaliamos a expressão(list 1 (list 2 (list 3 4))). Mostre o resultado impresso pelo interpretador, a estrutura de caixas e ponteiros correspondente. Resposta:

>(list 1 (list 2 (list 3 4))) (1 (2 (3 4)))

Exercício 9.9

(Livro — 2.25) Apresente combinações decars ecdrs que seleccionem o 7 de cada uma das listas seguintes: (1 3 (5 7) 9) ((7)) (1 (2 (3 (4 (5 (6 7)))))) Resposta: >(car (cdaddr ’(1 3 (5 7) 9))) 7 >(caar ’((7))) 7

>(cadadr (cadadr (cadadr ’(1 (2 (3 (4 (5 (6 7))))))))) 7

Exercício 9.10

9 LISTAS EM SCHEME 52

(define x (list 1 2 3)) (define y (list 4 5 6))

Qual é o resultado impresso pelo interpretador como resposta a cada uma das seguintes expres- sões? (append x y) (cons x y) (list x y) Resposta: >(append x y) (1 2 3 4 5 6) >(cons x y) ((1 2 3) 4 5 6) >(list x y) ((1 2 3) (4 5 6))

10 LISTAS EM ÁRVORE EM SCHEME 53

10

Listas em árvore em Scheme

Sumário Resumo

10 LISTAS EM ÁRVORE EM SCHEME 54

Exercícios

Exercício 10.1

(Livro — 2.27) Modifique o procedimentoreverse(do Livro — 2.18) para produzir um pro- cedimentodeep-reverseque recebe uma lista como argumento e retorna a lista com os seus elementos invertidos e com todas as suas sublistas tambem invertidas. Por exemplo,

(define x (list (list 1 2) (list 3 4))) x ((1 2) (3 4)) (reverse x) ((3 4) (1 2)) (deep-reverse x) ((4 3) (2 1))

Lembre-se que o procedimentoreverserecebe como argumento uma lista e retorna uma lista com os mesmos elementos, mas pela ordem inversa:

(define (reverse l) (define (r-aux l res)

(if (null? l) res

(r-aux (cdr l) (cons (car l) res)))) (r-aux l ()))

Resposta:

(define (deep-reverse l) (cond ((null? l) ())

((pair? (car l)) (append (deep-reverse (cdr l))

(list (deep-reverse (car l))))) (else (append (deep-reverse (cdr l))

(list (car l))))))

Exemplos:

>(define x (list (list 1 2) (list 3 4))) >(deep-reverse x)

((4 3) (2 1))

>(deep-reverse (list x x)) (((4 3) (2 1)) ((4 3) (2 1)))

Exercício 10.2

(Livro — 2.28) Escreva um procedimento fringe que recebe como argumento uma árvore (representada como uma lista de listas) e retorna uma lista cujos elementos são todas as folhas da árvore da esquerda para a direita. Por exemplo,

10 LISTAS EM ÁRVORE EM SCHEME 55

(define x (list (list 1 2) (list 3 4))) (fringe x) (1 2 3 4) (fringe (list x x)) (1 2 3 4 1 2 3 4) Resposta: (define (fringe l) (cond ((null? l) ())

((pair? (car l)) (append (fringe (car l)) (fringe (cdr l)))) (else (cons (car l)

(fringe (cdr l))))))

Exemplos:

>(define x (list (list 1 2) (list 3 4))) >(fringe x)

(1 2 3 4)

>(fringe (list x x)) (1 2 3 4 1 2 3 4)

>(fringe (list 1 2 (cons 3 4) (list 5 6))) car: expects argument of type <pair>; given 4

Exercício 10.3

(Livro — 2.30) Defina o procedimentosquare-treeanálogo aosquare-list(do Livro — 2.18). O procedimentosquare-treedeve-se comportar da seguinte forma:

(square-tree (list 1

(list 2 (list 3 4) 5) (list 6 7)))

(1 (4 (9 16) 25) (36 49))

Deve definir este procedimento directamente (isto é, sem usar procedimentos de ordem superior) e também usando o procedimentomap.

Resposta:

(define (square-tree l) (cond ((null? l) ())

((pair? (car l)) (cons (square-tree (car l)) (square-tree (cdr l)))) (else (cons (square (car l))

(square-tree (cdr l)))))) (define (square x)

10 LISTAS EM ÁRVORE EM SCHEME 56 Exemplos: >(square-tree (list 1 (list 2 (list 3 4) 5) (list 6 7))) (1 (4 (9 16) 25) (36 49))

Usando o procedimentomap, ficaria:

(define (square-tree-com-map tree) (map (lambda (x) (if (pair? x) (square-tree-com-map x) (square x))) tree)) Exemplos: >(square-tree-com-map (list 1 (list 2 (list 3 4) 5) (list 6 7))) (1 (4 (9 16) 25) (36 49)) Exercício 10.4

(Livro — 2.31) Abstraia a sua resposta ao exercício anterior para produzir um procedimento tree-map, com a propriedade quesquare-treepoderia ser definido como:

(define (square-tree tree) (tree-map square tree)) Resposta:

(define (tree-map proc tree) (cond ((null? tree) ())

((pair? (car tree)) (cons (tree-map proc (car tree)) (tree-map proc (cdr tree)))) (else (cons (proc (car tree))

(tree-map proc (cdr tree)))))) (define (square-tree tree)

(tree-map square tree))

Exemplos: >(square-tree (list 1 (list 2 (list 3 4) 5) (list 6 7))) (1 (4 (9 16) 25) (36 49))

10 LISTAS EM ÁRVORE EM SCHEME 57 Exercício 10.5

(Livro — 2.32) Podemos representar um conjunto como uma lista de elementos distintos, e podemos representar o conjunto de todos os subconjuntos de um conjunto como uma lista de listas. Por exemplo, se o conjunto é(1 2 3), então o conjunto de todos os seus subconjuntos é(() (3) (2) (2 3) (1) (1 3) (1 2) (1 2 3)). Complete a seguinte definição de um procedimento que gera o conjunto dos subconjuntos de um conjunto e dê uma explicação clara de porque é que ele funciona.

(define (subsets s) (if (null? s)

(list ())

(let ((rest (subsets (cdr s)))) (append rest (map <??> rest))))) Resposta:

(define (subsets s) (if (null? s)

(list ())

(let ((rest (subsets (cdr s))))

(append rest (map (lambda (x) (cons (car s) x)) rest)))))

Exemplos:

>(subsets (list 1 2 3))

11 MAIS EXERCÍCIOS SOBRE LISTAS 58

11

Mais exercícios sobre listas

Sumário Resumo

11 MAIS EXERCÍCIOS SOBRE LISTAS 59

Exercícios

Exercício 11.1

Considere que foi definido o tipo árvore binária. Para este tipo, estão definidas as operações: • constroi-arvoreque recebe a raiz, a árvore esquerda e a árvore direita e constrói a

árvore correspondente.

• arvore-raizque recebe uma árvore binária e retorna a sua raiz.

• arvore-esquerdaque recebe uma árvore binária e retorna a sua árvore esquerda. • arvore-direitaque recebe uma árvore binária e retorna a sua árvore direita.

• arvore-vazia?que recebe um objecto e retorna verdadeiro se ele corresponder a uma árvore vazia e falso caso contrário.

Com base nas operações descritas, escreva os seguintes procedimentos para percorrer árvores binárias:

1. percorre-inorderrecebe uma árvore binária e retorna uma lista com todas as sua folhas, percorrendo primeiro a árvore esquerda, depois a raiz e depois a árvore direita da árvore inicial.

2. percorre-preorderrecebe uma árvore binária e retorna uma lista com todas as sua folhas, percorrendo primeiro a raíz, depois a árvore esquerda e depois a árvore direita da árvore inicial.

3. percorre-posorderrecebe uma árvore binária e retorna uma lista com todas as sua folhas, percorrendo primeiro a árvore esquerda, depois a árvore direita e depois a raiz da árvore inicial.

Resposta:

Exercício 11.2

Uma árvore binária de procura é uma árvore binária em que todos os elementos que estão na sua árvore esquerda são menores que a raiz e todos os elementos que estão na sua árvore direita são maiores que a raiz.

Com base nas operações definidas no exercício anterior, escreva os seguintes procedimentos: 1. insere-elementoque recebe um elemento e uma árvore binária de procura e o insere

na árvore.

2. ordena-lista que recebe uma lista de elementos e retorna uma nova lista com os elementos ordenados.

Resposta:

Exercício 11.3

Escreva um procedimentofiltraque recebe um predicado e uma lista e retorna uma lista que contém apenas os elementos da lista inicial que satisfazem o predicado. Por exemplo:

11 MAIS EXERCÍCIOS SOBRE LISTAS 60

(filtra even? (list 1 2 3 4 5)) (2 4)

(filtra even? (list 1 3 5 7)) ()

Resposta:

(define (filtra pred lst) (cond ((null? lst) ())

((pred (first lst)) (cons (first lst)

(filtra pred (rest lst)))) (else (filtra pred (rest lst)))))

Exercício 11.4

Escreva um procedimentotodos?que recebe um predicado e uma lista e retorna verdadeiro se todos os elementos da lista satisfizerem o predicado e falso caso contrário. Por exemplo:

(todos? even? (list 1 2 3 4 5)) #f

(todos? even? (list 2 4 6)) #t

Resposta:

(define (todos? pred lst) (cond ((null? lst) #t)

((pred (first lst))

(todos? pred (rest lst))) (else #f)))

Exercício 11.5

Escreva um procedimentoalgum?que recebe um predicado e uma lista e retorna verdadeiro se algum dos elementos da lista satisfizer o predicado e falso caso contrário. Por exemplo:

(algum? odd? (list 1 2 3 4 5)) #t

(algum? odd? (list 2 4 6)) #f

Resposta:

(define (algum? pred lst) (cond ((null? lst) #f)

((pred (first lst)) #t)

11 MAIS EXERCÍCIOS SOBRE LISTAS 61 Exercício 11.6

Escreva um procedimento substitui que recebe dois elementos e uma lista e retorna uma outra lista que resulta de substituir todas as ocorrências do primeiro elemento pelo segundo na lista inicial. Por exemplo:

(substitui 2 3 (list 1 2 3 2 5)) (1 3 3 3 5)

(substitui 2 3 (list 1 3 5 7)) (1 3 5 7)

Resposta:

(define (substitui velho novo lst) (cond ((null? lst) ())

((= (first lst) velho) (cons novo

(substitui velho novo (rest lst)))) (else (cons (first lst)

(substitui velho novo (rest lst))))))

Exercício 11.7

Escreva um procedimentofold-right que recebe um procedimento de dois argumentos, o valor inicial de um acumulador e uma lista e retorna o resultado de aplicar o procedimento ao elemento inicial e ao resultado de aplicar o procedimento a todos os elementos que estão à sua direita. Quando a lista for vazia, este procedimento deve retornar o valor inicial. Por exemplo: (fold-right + 0 (list 1 2 3 4))

10

(fold-right + 0 ()) 0

Resposta:

(define (fold-right proc inicial lst) (if (null? lst)

inicial

(proc (first lst)

(fold-right proc inicial (rest lst)))))

Exercício 11.8

Com base no procedimentofold-rightescreva os seguintes procedimentos:

1. multiplica-listaque recebe uma lista e retorna o produto de todos os seus elemen- tos.

2. maximo-listaque recebe uma lista e retorna o maior dos seus elementos.

3. inverte-lista que recebe uma lista e retorna outra lista com os elementos da lista inicial pela ordem inversa.

11 MAIS EXERCÍCIOS SOBRE LISTAS 62

4. junta-listasque recebe duas listas e retorna outra lista que resulta de juntar as duas. Resposta:

1. (define (multiplica-lista lst) (if (null? lst)

(error "multiplica-lista: espera uma lista com pelo menos 1 elemento, recebeu" lst)

(fold-right * 1 lst))) (multiplica-lista (list 1 2 3 4)) 24

(multiplica-lista ())

multiplica-lista: espera uma lista com pelo menos 1 elemento, recebeu ()

2. (define (maximo-lista lst) (if (null? lst)

(error "maximo-lista: espera uma lista com pelo menos 1 elemento, recebeu" lst)

(fold-right max (first lst) (rest lst)))) (maximo-lista (list 1 2 3 4))

4

(maximo-lista ())

maximo-lista: espera uma lista com pelo menos 1 elemento, recebeu ()

3. (define (inverte-lista lst)

(fold-right (lambda (x y) (append y (list x))) () lst)) (inverte-lista (list 1 2 3 4)) (4 3 2 1) (inverte-lista ()) () 4. (define (junta-listas lst1 lst2) (fold-right cons lst2 lst1))

(junta-listas (list 1 2 3 4) (list 5 6 7)) (1 2 3 4 5 6 7)

(junta-listas () (list 5 6 7)) (5 6 7)

Exercício 11.9

Uma forma de compactar listas de números é, dada uma lista de números (possivelmente re- petidos), transformá-la numa lista em que ocorrências consecutivas de um mesmo número são substituídas por um par, em que o primeiro elemento é o número de vezes que o número aparece repetido e o segundo elemento é o número.

11 MAIS EXERCÍCIOS SOBRE LISTAS 63

(run-length-encode ’(1 1 1 1 1 1 1 2 3 3 3 3 4 4 4 4 1 3 3 3 3)) ((7 . 1) 2 (4 . 3) (4 . 4) 1 (4 . 3))

(run-length-encode ’(1 2 1 2 3 3 3 3 4 4 4 4 1 1 3 3 3 3 3)) (1 2 1 2 (4 . 3) (4 . 4) (2 . 1) (5 . 3))

Repare que as sequências de apenas um elemento não são substituidas.

Depois de ter uma lista compactada, pode ser necessário saber qual era a lista original. Escreva o procedimentorun-length-decodeque, dada uma lista de inteiros compactada, retorna a lista original. Por exemplo,

(run-length-decode ’((7 . 1) 2 (4 . 3) (4 . 4) 1 (4 . 3))) (1 1 1 1 1 1 1 2 3 3 3 3 4 4 4 4 1 3 3 3 3) (run-length-decode ’(1 2 1 2 (4 . 3) (4 . 4) (2 . 1) (5 . 3))) (1 2 1 2 3 3 3 3 4 4 4 4 1 1 3 3 3 3 3) Resposta: (define (run-length-encode lst)

(define (encode-seq lst last count) (define (make-code last count)

(if (> count 1)

(cons count last) last))

(cond ((null? lst) (list (make-code last count))) ((= last (first lst))

(encode-seq (rest lst) last (add1 count))) (else (cons (make-code last count)

(encode-seq (rest lst) (first lst) 1))))) (if (null? lst)

()

(encode-seq (rest lst) (first lst) 1)))

(define (run-length-decode encoded-lst) (define (decode code)

(define (make-list count elem) (if (zero? count)

()

(cons elem (make-list (sub1 count) elem)))) (if (pair? code)

(make-list (car code) (cdr code)) (list code)))

(if (null? encoded-lst) ()

(append (decode (first encoded-lst))

12 QUOTE 64

12

Quote

Sumário Resumo

12 QUOTE 65

Exercícios

Exercício 12.1

(Livro — exemplo das páginas 143-4) Considere que foram feitas as definições: (define a 1)

(define b 2)

Diga qual o valor de cada uma das seguintes expressões: (list a b) (list ’a ’b) (list ’a b) (first ’(a b c)) (rest ’(a b c)) Resposta: (list a b) (1 2) (list ’a ’b) (a b) (list ’a b) (a 2) (first ’(a b c)) a (rest ’(a b c)) (b c) Exercício 12.2

(Livro — exemplo da página 144) Defina o procedimentomemq, que recebe um símbolo e uma lista e retorna falso se o símbolo não estiver contido na lista (isto é, não foreq? a nenhum dos elementos da lista) e a sublista que começa com a primeira ocorrência do símbolo na lista caso contrário. Por exemplo,

(memq ’apple ’(pear banana prune)) #f

(memq ’apple ’(x (apple sauce) y apple pear)) (apple pear)

Resposta:

(define (memq item lst) (cond ((null? lst) #f)

((eq? item (first lst)) lst) (else (memq item (rest lst)))))

12 QUOTE 66 Exercício 12.3

(Livro — 2.53) O que é que o interpretador de Scheme imprime como resposta à avaliação de cada uma das seguintes expressões:

(list ’a ’b ’c)

(list (list ’george)) (cdr ’((x1 x2) (y1 y2))) (cadr ’((x1 x2) (y1 y2))) (pair? (car ’(a short list)))

(memq ’red ’((red shoes) (blue socks))) (memq ’red ’(red shoes blue socks)) Resposta:

(list ’a ’b ’c) (a b c)

(list (list ’george)) ((george))

(cdr ’((x1 x2) (y1 y2))) ((y1 y2))

(cadr ’((x1 x2) (y1 y2))) (y1 y2)

(pair? (car ’(a short list))) #f

(memq ’red ’((red shoes) (blue socks))) #f

(memq ’red ’(red shoes blue socks)) (red shoes blue socks)

Exercício 12.4

(Livro — 2.54) Duas listas sãoequal? se contiverem elementos iguais e estes estiverem pela mesma ordem. Por exemplo,

(equal? ’(this is a list) ’(this is a list)) é verdade, mas

(equal? ’(this is a list) ’(this (is a) list))

é falso. Para sermos mais precisos, podemos definir equal? recursivamente em termos da igualdade básica entre símboloseq?, dizendo queaebsãoequal?se forem ambos símbolos e foremeq? ou forem ambos listas em que(first a)éequal? a(first b)e(rest a)éequal?a(rest b). Usando esta ideia, implementeequal?como um procedimento. Resposta:

12 QUOTE 67

(define (equal? l1 l2)

(cond ((and (number? l1) (number? l2)) (= l1 l2)) ((and (symbol? l1) (symbol? l2)) (eq? l1 l2)) ((and (null? l1) (null? l2)) #t)

((or (atom? l1) (atom? l2)) #f)

(else (and (equal? (first l1) (first l2)) (equal? (rest l1) (rest l2)))))) (equal? ’(this is a list) ’(this is a list))

#t

(equal? ’(this is a list) ’(this (is a) list)) #f (equal? ’(2 3 a 4) ’(2 3 a 4)) #t (equal? ’(2 3 a 4) ’(2 3)) #f Exercício 12.5

(Livro — 2.55) O resultado de avaliar a expressão (first ’’abracadabra)

équote. Explique porquê. Resposta:

A expansão de(first ”abracadabra)é

(first (quote (quote abracadabra))). Para avaliar a segunda expressão:

1. Avaliamos o operador. Neste caso,first, que tem como resultado o procedimento que dado uma lista retorna o seu primeiro argumento.

2. Avaliamos os seus argumentos, por qualquer ordem. Neste caso, avaliar a expressão

(quote (quote abracadabra))tem como resultado a lista(quote abracadabra). 3. Aplicamos o operador aos valores dos argumentos e retornamos o resultado. Neste caso, aplicar o

procedimentofirstà lista(quote abracadabra)tem como resultado o primeiro elemento da lista, que é o símboloquote.

13 MODULARIDADE, OBJECTOS E ESTADO 68

13

Modularidade, objectos e estado

Sumário Resumo

13 MODULARIDADE, OBJECTOS E ESTADO 69

Exercícios

Exercício 13.1

Diga o que é impresso pelo interpretador de Scheme ao avaliar cada uma das seguintes expres- sões: (define a 3) (set! a "ola") (+ a 1) (begin (let ((a 5)) (+ a (* 45 327))

(sqrt (length ’(1 a b "bom dia" (2 5) 3)))) (display ’a) a) (set! c 78) Resposta: >(define a 3) >(set! a "ola") >(+ a 1)

+: expects type <number> as 1st argument, given: "ola"; other arguments were: 1 >(begin

(let ((a 5))

(+ a (* 45 327))

(sqrt (length ’(1 a b "bom dia" (2 5) 3)))) (display ’a)

a) a "ola"

>(set! c 78)

set!: cannot set undefined identifier: c

Exercício 13.2

(Livro — 3.1) Um acumulador é um procedimento que é chamado repetidamente com apenas um argumento numérico e acumula os seus argumentos numa soma. De cada vez que é chamado, retorna a soma acumulada até ao momento.

Escreva um procedimentomake-accumulatorque gera acumuladores, cada um dos quais mantendo uma soma independente. O valor de entrada para o procedimentomake-accumulator deve especificar o valor inicial da soma. Por exemplo,

13 MODULARIDADE, OBJECTOS E ESTADO 70 (define A (make-accumulator 5)) (A 10) 15 (A 10) 25 Resposta:

(define (make-accumulator init) (lambda (num)

(set! init (+ init num)) init))

Exercício 13.3

(Livro — 3.2) Em aplicações para testar software, é útil ser capaz de contar o número de vezes que um procedimento é chamado durante o decurso de uma computação.

Escreva um procedimentomake-monitoredque recebe um procedimentofcomo argumento, que por sua vez é um procedimento de um argumento. O resultado retornado pelo procedimento make-monitoredé um terceiro procedimentomfque mantém um registo do número de vezes que foi chamado através de um contador interno. Se o valor de entrada paramffor o símbolo how-many-calls?, entãomfdeve retornar o valor do contador. Se o valor de entrada for o símboloreset-count, entãomfdeve inicializar o contador a zero. Para qualquer outro valor de entrada,mfretorna o valor de aplicarfa esse valor e incrementa o contador. Por exemplo, podemos criar uma versão monitorizada do procedimentosqrt:

(define s (make-monitored sqrt)) (s 100) 10 (s ’how-many-calls?) 1 Resposta:

(define (make-monitored proc) (let ((calls 0))

(lambda (input)

(cond ((eq? input ’how-many-calls?) calls) ((eq? input ’reset-count) (set! calls 0)) (else (set! calls (add1 calls))

(proc input))))))

Exercício 13.4

13 MODULARIDADE, OBJECTOS E ESTADO 71 1. (define x 63) (define square (lambda (x) (* x x))) (define sum-sq (lambda (x y)

(+ (square x) (square y)))) (sum-sq 3 4)

O resultado é 25.

2. (define (make-adder n) (lambda (x) (+ x n)))

(define addthree (make-adder 3)) (define addfive (make-adder 5)) (addfive 7)

(addthree 7)

3. Os ambientes permitem-nos perceber como é que podemos usar procedimentos como re-

Documentos relacionados