(\x. x) (\x. x)
(\x. y) (\x. x)
(\x. x) (\x. x) y
(\x. x x) (\x. x x)
(\x. y) ((\x. x x) (\x. x x))
(\t. \f. \o. o f t) (\x y. x) (\x y. y) (\p q. p p q)
x (\x. x)
x (\y. y)
x (\x. y)
(\y. y) (\x. x)
(\y. y) (\z. \y. x y z)
add
e inc
,
use as definições dadas no programa do curso.)
inc two
add two two
inc inc
(\i. \t. i t) (\n. \f. \z. f (n f z)) (\f. \z. f (f z))
(y x y)[y ← \z.x x]
(\x.y x)[y <- \z.x]
((\x.y x) y)[y <- \z.x]
Prove as igualdades a seguir.
(Para funções como add
e inc
,
use as definições dadas no programa do curso.)
(\n.\f.\z.f (n f z)) (\f.\z.z) ≡ \f.\z.f z
add one b ≡ inc b
Você consegue provar a igualdade a seguir?
add b one ≡ inc b
Escreva a função (||)
("ou" lógico).
(||) :: Bool -> Bool -> Bool
Prove que sua função é associativa:
a || (b || c) = (a || b) || c
Sua função é comutativa?
a || b = b || a
Escreva uma função que verifique se um valor pertence a uma dada lista:
find :: (Eq a) => a -> [a] -> Bool
Prove que sua função satisfaz a equação abaixo:
find x (ys ++ zs) = find x yx || find x zs
Escreva uma função para ordenar uma lista:
sort :: (Ord a) => [a] -> [a]
Exp
.)
23 + 1 + 35
10 + -21 / (5 + 531 * 7)
Escreva as árvores de sintaxe abstrata para as expressões abaixo.
(Isto é, codifique as expressões como valores do tipo Exp
.)
a*
(a | b)+
a?
Prove as equivalências abaixo. (Observação: provar que duas expressões A e B são equivalentes significa provar que (eval A) = (eval B).)
(RESeq REEpsilon X)
≡ X
(REOr REEmpty X)
≡ X
eval (REKleene REAny) s = True
(REOr X Y)
≡ (REOr Y X)
Defina uma função empty :: RE -> Bool
,
tal que empty X
é verdadeiro se e somente se
eval X s
retorna falso para qualquer s
.
(A função deve ser uma recursão estrutural simples sobre
a estrutura da expressão regular.)
Mostre que sua função está correta.
Defina uma função hasEpsilon :: RE -> Bool
,
tal que hasEpsilon X = eval X ""
.
(Não vale usar eval
na sua definição.
A função deve ser uma recursão estrutural simples sobre
a estrutura da expressão regular.)
Prove que sua definição está correta,
ou seja, que hasEpsilon X = eval X ""
.
Defina uma função noEpsilon :: RE -> RE
,
satisfazendo as seguintes propriedades:
X
,
hasEpsilon (noEpsilon X) = False
.
X
e string não vazia s
,
eval X s = eval (noEpsilon X) s
.
noEpsilon X
aceita tudo que X
aceita,
menos a string vazia.)
(Se possível, procure simplificar a expressão regular resultante.)
Prove que sua implementação satisfaz as propriedades pedidas.
Defina uma função infinite :: RE -> Bool
,
tal que empty X
é verdadeiro se e somente se
existem infinitos s
tais que
eval X s
é verdadeiro.
Escreva as árvores de sintaxe abstrata para os programas abaixo.
(Isto é, escreva os valores Haskell do tipo Cmd
correspondentes.)
x = 10; y = 20; z = x + y
while 1 do {}
x = 10; while x do { y = y + 1; x = x - 1; }
Prove que
(x = 10; y = 20)
≡ (y = 20; x = 10)
.
Prove que, para qualquer comando C,
(skip ; C)
≡ C
Modifique o construtor CmdSeq
para ele conter uma
lista de comandos,
e adeque evalCmd
a essa nova definição.
Implemente o comando repeat until
.
Prove que, para qualquer comando C,
(repeat C until 1)
≡ C
.
É possivel provar a equivalência abaixo, para qualquer expressão E e comando C? Discuta sua resposta.
(if E then C else C) ≡ C
Escreva as árvores de sintaxe abstrata para as expressões abaixo.
(Isto é, escreva os valores Haskell do tipo Exp
correspondentes.)
(\x -> x + 10) 20
\x -> \y -> x
(\f -> f(10 * 30))(\x -> x)
Implemente funções com vários argumentos na linguagem.
(Dica: você deverá mudar ExpLambda
,
ExpApp
e ValFunc
para trabalharem com listas nos lugares adequados.)
Prove que, para qualquer expressão E,
((\x -> x) E)
≡ E
Impemente um operador "or" com curto circuito, isso é, a segunda expressão só deve ser avaliada se a primeira for falsa.
Prove que sua implementação do operador "or"
satisfaz as seguintes equivalências:
(0 || E)
≡ E
;
(1 || E)
≡ 1
;
Podemos ter valores pré-definidos nos nossos interpretadores simplesmente colocando-os no ambiente ou memória inicial usados para executar os programas. Por exemplo, considere as linhas abaixo no interpretador de linguagens funcionais:
initialEnv = bind "square" (ValFunc square) emptyEnv where square (ValInt i) = ValInt (i * i)Se usarmos o ambiente
initialEnv
ao avaliarmos uma expressão,
essa expressão poderá usar a função pré-definida square
.
Use essa técnica para criar uma função pré-definida addK
,
equivalente a definição abaixo:
addK = \k -> (\x -> x + k)(Note que a função
addK
retorna outra função.
Cuidado com os tipos!
Em particular, lembre-se de distinguir entre funções Haskell e
funções da linguagem,
que tem tipo Value
com construtor ValFunc
,
e converter de um para o outro quando necessário.)
Refaça todos os exercícios sobre a linguagem imperativa simples para essa nova implementação.
Implemente um comando CmdBreak
na linguagem,
com a semântica usual de break
em linguagens como C.
(Dica: modifique o tipo de evalCmd
para
evalCmd :: Cmd -> K -> K -> Mem -> Resultonde a segunda continuação indica para onde o
break
deve continuar a execução do programa.)
Prove que sua implementação de break
satisfaz a equivalência
(while 1 do break)
≡ skip
Após a implementação de break
,
você ainda consegue provar a equivalência
(repeat C until 1)
≡ C
?
Explique.
Refaça todos os exercícios sobre a linguagem funcional simples para essa nova implementação.
Implemente uma expressão return exp
,
que termina imediatamente a execução da função corrente
retornando o valor de exp
.
(Dica: use uma abordagem similar à usada na implementação
do comando break
,
com uma continuação separada para ser usada pelo return
.)
Com sua implementação de return
,
é possivel provar a equivalência abaixo
para qualquer expressão E?
Discuta sua resposta.
((\x -> return x) E) ≡ E
Reimplemente a expressão return
usando
a seguinte abordagem:
ao invés de return
ser uma expressão especial
na gramática da linguagem,
return
será uma função "comum".
Cada função, ao ser chamada, coloca no seu ambiente
uma nova variável return
,
cujo valor é um ValFunc
que,
ao ser executado,
faz o retorno desejado.
Como você compara essa implementação com a anterior?
Considerando as definições no código, prove as igualdades abaixo:
op1 (op0 x) f = f x
(para quaisquer f
e x
).
op1 c op0 = c
(para qualquer computação c
).
f
, g
e c
).
Nessa linguagem, a construção letrec
ainda
é necessária?
Mostre como podemos escrever uma função explet
que gere um código equivalente ao construtor ExpLet
.
(Você consegue provar que sua implementação está correta?)
Considerando as definições no código, prove as igualdades abaixo:
bind (unit x) f = f x
(para quaisquer f
e x
).
bind c unit = c
(para qualquer computação c
).
f
, g
e c
).
Execute o parser para as expressões abaixo e confira o resultado:
(\x -> x + 10) 20
\x -> \y -> x
(\f -> f(10 * 30))(\x -> x)
Acrecente ao parser a sintaxe let x = exp in exp'
,
como açucar sintático para ((\x -> exp')(exp)
).
Acrescente ao parser a sintaxe \x y z -> ...
(com uma ou mais variáveis),
como açucar sintático para \x -> \y -> \z -> ...
.
Conecte o parser com algum interpretador estudado, para criar um interpretador que lê a entrada padrão com um programa, cria uma árvore de sintaxe abstrata, executa o programa na árvore e imprime o resultado final.
Neste parser, usamos somente o combinador orelse
para opções, ao invés do +++
.
Para cada uso de orelse
,
discuta as vantagens e desvantagens de trocarmos para +++
.
Modifique o código do parser para usar o tipo Maybe
,
ao invés de listas.
Que operações não têm uma tradução natural para esta modificação?
Prove que sua implementação de monad do item anterior satisfaz as três leis monádicas.
Considere a relação definida como
(c, m) → (c', m')
sse
(c', m') = step c m
,
e a relação ⇒ definida como o fecho reflexivo-transitivo de →.
Prove que, se (c1 , m) ⇒ (c1', m')
,
então (c1;c2 , m) ⇒ (c1';c2 , m')
.
Usando a definição de evalCmd
dada
na semântica denotacional simples,
prove que:
(c, m) → (c', m')
,
então evalCmd c' m' = evalCmd c m
.
(c, m) ⇒ (c', m')
,
então evalCmd c' m' = evalCmd c m
.
Nesta semântica operacional,
podemos definir uma relação de equivalência entre
dois comandos c1 ≡ c2
sse evalCmd c1 m = evalCmd c2 m
.
Usando essa definição,
prove as equivalências abaixo:
skip ; c ≡ c
.
c ; skip ≡ c
.
Prove que, se c1 ≡ c2
nesta semântica operacional,
então c1 ≡ c2
na semântica denotacional simples.
Você consegue provar a volta dessa implicação?
Mostre passo a passo a avaliação da expressão
let f = \x -> x + 3 in f(4 + 5)
.
(Lembre-se que o let
é apenas açucar sintático.)
Prove que se uma expressão exp
não tem variáveis livres,
e todos os valores na memória m
não têm variáveis livres,
e (exp, m) → (exp', m')
,
então exp'
e m'
também não têm variáveis livres.
O inverso do exercício anterior é verdadeiro?
(Isso é, se exp'
não tem variáveis livres
então exp
também não tem.)