-
(Nova versão do exercício anterior.)
Considere o programa em Haskell:
f x = x * x
g x = f x + f x
g (f 2)
-
Escreva um termo do cálculo-λ que o represente.
-
Reduza o termo escrito na alínea anterior escolhendo sempre o
redex mais à esquerda.
-
Reduza o termo escrito na primeira alínea escolhendo sempre o
redex mais exterior mais à direita. (Um redex é
mais exterior se está no âmbito de menos abstracções que outro.
Por exemplo, no termo
(λz.z) (λx.(λy.y) x),
o redex (λz.z) (λx.(λy.y) x) é mais
exterior que (λy.y) x.)
-
Considere o programa em Haskell:
f x = x + x
g f x = f x + 1
h = g f
h 3
-
Escreva um termo do cálculo-λ que o represente.
-
Reduza o termo escrito na alínea anterior.
-
Repita o exercício anterior para o programa:
o f g x = f (g x)
h x = x + 1
o h h y
-
Recorrendo à semântica denotacional, determine se os seguintes
programas são formalmente equivalentes.
-
y := 2 * x + 2;
z := x * 2;
x := 2 * x + 1
-
x := 2 * x + 1;
y := x + 1;
z := x - 1
-
À linguagem dos programas while usada nas aulas teóricas, é
acrescentada a instrução de atribuição (ou
afectação) simultânea
x1, x2 := e1, e2
cujo efeito é atribuir, em "simultâneo", o valor da expressão
e1 à variável x1 e o valor
da expressão e2 à variável
x2.
Defina a semântica denotacional desta instrução e mostre que
x, y := y, x
troca os valores de x e de y.
-
Através da semântica denotacional, determine o que calcula o
programa seguinte (em particular, qual o valor final de q).
i := 0; q := 0;
while i < n do ( q := q + 2 * i + 1; i := i + 1 )
(Sugestões: considere que n0, o valor inicial de
n, é tal que i assume os valores 0,
1, 2, ..., n0-2,
n0-1, n0 e expanda o cálculo
da semântica denotacional em concordância (i.e., para os
valores de i: 0, 1, 2,
n0-2, n0-1 e
n0, substituindo o que acontece entre 2
e n0-2 por "..." onde for conveniente); mantenha
o valor de q na forma 0 + 1 + 3 + ... (ou 1 +
3 + ...).)
-
Aplique o algoritmo de inferência de tipos às definições e
expressões que se seguem, onde os nomes livres se referem a
definições de alíneas anteriores.
Tenha em conta os tipos pré-definidos:
0, 1, 2, ... : int
+, -, *, / : int -> int -> int
true, false : bool
= : 'a -> 'a -> bool
[] : 'a list
hd : 'a list -> 'a
tl : 'a list -> 'a list
:: : 'a -> 'a list -> 'a list
- fun f1 x y = x + y;
- fun f2 x y = f1 x y;
- fun f3 x = f1 x;
- fun id x = x;
- fun apply (f, x) = f x;
- fun applyc f x = f x;
- id applyc;
- applyc id;
- apply (f1, 5);
- fun curry f x y = f (x, y);
- fun flip f a b = f b a;
- fun fact 0 = 1
| fact n = n * fact (n - 1);
- fun fib n = fib (n - 1) + fib (n - 2);
- fun f4 (m, n) = f4 (m + n, m);
- fun f5 (h,x) = h (f5 (h, x - 1), x);
- fun f6 h x = h (f6 h (x - 1)) x;
- fun f7 g a = g (g a);
- fun e1 f = f (e1 0);
- fun e2 f = f f + 2;
- fun o f g x = f (g x);
- fun ou (f, g, x) = f (g x);
- fun pr (f, a, x) = f (x, pr (f, a, a x));
- fun len l = if l = [] then 0 else 1 + len (tl l);
- fun ape l m = if l = [] then m else ape (tl l) m;
- fun app l m = if l = [] then m else hd l :: app (tl l) m;
- fun rev [] = []
| rev l = app (rev (tl l)) (hd l :: []);
-
Usando o jlex
e o cup
e partindo dos ficheiros Makefile, expressoes.lex, expressoes.cup, Main.java e Expressao.java
-
Implemente um avaliador de expressões aritméticas com somas e
subtracções entre números naturais. O avaliador deverá construir
uma árvore de sintaxe abstracta durante a análise sintáctica e,
posteriormente, calcular o valor da expressão lida recorrendo a
essa árvore.
-
Acrescente os operadores de multiplicação ‘*’
e de divisão ‘/’ à linguagem.
-
Acrescente os parêntesis ‘(’ e
‘)’ à linguagem.
-
Acrescente o operador de potência ‘^’ à
linguagem. Este operador associa à direita e tem maior precedência
que os restantes operadores.
- [Mitchell, 7.1]
Considere o seguinte programa,
que compila sem erros:
1: #include <stdio.h>
2:
3: int main()
4: {
5: int n, norma;
6:
7: printf("introduza um número: ");
8: scanf("%d", &n);
9:
10: if (n > 0)
11: {
12: int norma = n;
13: }
14: else
15: {
16: int norma = -n;
17: }
18:
19: printf("norma de %d = %d \n", n, norma);
20: return 0;
21: }
-
Preveja o resultado do programa apresentado.
-
Explique o resultado do programa através da evolução da pilha de
execução, mostrando o conteúdo dos registos de activação lá
contidos ao longo da execução do programa.
-
Retire a declaração da variável norma da linha 5,
substitua a linha 19 pelo código seguinte
19a: {
19b: int norma;
19c:
19d: printf("norma de %d = %d \n", n, norma);
19e: }
e repita as alíneas anteriores.
(Nota: o resultado obtido com a versão 4 do GCC poderá não ser o
esperado.)
-
Substitua a linha 18 do programa obtido na alínea anterior por um
bloco com uma única linha de código que garanta que o resultado
escrito na linha 19d nunca estará correcto.
- [Mitchell, 12.3]
Assuma que A <: B e que B <: C.
-
Quais dos seguintes tipos são subtipo de B -> B?
- B -> B
- B -> A
- B -> C
- C -> B
- A -> B
- C -> A
- A -> A
- C -> C
- A -> C
-
De quais dos tipos da alínea anterior é B -> B subtipo?
-
Considere o seguinte programa em C+-:
class A {
public:
virtual void g(A* x) { printf("A-g(A)"); }
void g(B* x) { printf("A-g(B)"); }
}
class B: public A {
public:
void g(A* x) { printf("B-g(A)"); }
void g(B* x) { printf("B-g(B)"); }
}
main()
{
A *p1 = new A;
A *p2 = new A;
B *p3 = new B;
A *p4 = p3;
B *p5 = p2;
...
}
Para cada uma das instruções seguintes, indique qual o resultado e
que acções são efectuadas durante a compilação (em
compile-time) e durante a execução (em run-time).
- p1 -> g(p2);
- p1 -> g(p3);
- p1 -> g(p4);
- p3 -> g(p1);
- p3 -> g(p3);
- p4 -> g(p2);
- p4 -> g(p3);
- p3 -> g(p5);
- p5 -> g(p1);
- p5 -> g(p3);