Prova Final 1 de Linguagens de Programação - DCC024B Ciência da Computação Nome: “Eu dou minha palavra de honra que não trapacearei neste exame.” Número de matrı́cula: As regras do jogo: • A prova é sem consulta. • Quando terminar, não entregue nada além do caderno de provas para o instrutor. • Quando escrever código, a sintaxe correta é importante. • Cada estudante tem direito a fazer uma pergunta ao instrutor durante a prova. Traga o caderno de provas quando vier à mesa do instrutor. • A prova termina uma hora e quarenta minutos após seu inı́cio. • Seja honesto e lembre-se: você deu sua palavra de honra. Alguns conselhos: • Escreva sempre algo nas questões, a fim de ganhar algum crédito parcial. • Se não entender a questão, e já tiver gasto sua pergunta, escreva a sua interpretação da questão junto à resposta. • A prova não é difı́cil, ela é divertida, então aproveite! Tabela 1: Pontos acumulados (para uso do instrutor) Questão 1 1O Questão 2 Questão 3 Questão 4 não... 1 Questão 5 Questão 6 1. Escreva um predicado eqv(L), em Prolog, que seja verdade quando L for uma lista de átomos, e cada átomo aparecer o mesmo número de vezes em L. Em outra palavra, se L possui N átomos distintos, e um deles aparece em L M vezes, então L deve possuir N × M átomos, cada um deles ocorrendo M vezes em L. Por exemplo: ?- eqv([a, b, c, b, a, c]). true. ?- eqv([a, b, c]). true. ?- eqv([a, b, c, b]). false. ?- eqv([]). true. ?- eqv([a, a, a, b, b, b, 1, 1, 1]). true. 2 2. Nesta questão você deverá escrever pelo menos uma caracterı́stica comum entre cada par de linguagens de programação. Você não pode repetir uma mesma caracterı́stica entre dois pares de linguagens diferentes. Cada item vale um ponto. (a) Simula e Java. (b) Java e Python. (c) Python e C. (d) C e Fortran. (e) Fortran e Cobol. (f) As primeiras versões de Cobol e as primeiras versões de Lisp. (g) Lisp e SML. (h) SML e Algol. (i) Algol e C++. (j) C++ e Simula. 3 3. Existem alguns mecanismos de passagem de parâmetros que são considerados não estritos. Um mecanismo de parâmetros não estrito não requer a avaliação dos argumentos passados para uma função antes da invocação dessa função. Os principais mecanismos não estritos são a passagem por expansão de macros, a passagem por nome, e a passagem por necessidade. (a) (5 pontos) Escreva um programa, em uma linguagem hipotética L, cuja sintaxe seja igual à sintaxe de C, que retornaria diferentes resultados, caso o mecanismo de passagem de parâmetros fosse a expansão de macros, ou fosse a passagem por nome. Explique qual seria o resultado em cada caso. (b) (5 pontos) A passagem por necessidade somente pode ser empregada em linguagens que não possuem a noção de efeito colateral 2 . Porque tal é o caso? 2 Nem pense em perguntar, durante a prova, o que vem a ser efeito colateral. 4 4. É fato notório que a linguagem C não possui um coletor de lixo, muito embora esse tipo de ferramenta incremente em muito a produtividade do desenvolvedor de software. Uma das consequências negativas que advêm da ausência de um coletor de lixo são os erros de alocação de memória, como os presentes no programa abaixo, escrito em C: 1 int main() { 2 char *p, *q; 3 p = NULL; 4 printf("%s", p); 5 q = (char*) malloc(100); 6 p = q; 7 free(q); 8 *p = ’x’; 9 free(p); 10 p = (char*) malloc(100); 11 q = p; 12 mystrcat(p, q); 13 } void mystrcat(char* dst, char* src) { while (*dst != ’\0’) { dst++; } while (*src != ’\0’) { *dst = *src; dst++; src++; } } (a) (4 pontos) O programa acima possui quatro erros. Liste cada um desses erros, indicando, em poucas palavras, o que está errado. • • • • (b) (2 pontos) Quais dentre os erros acima, não poderiam ser evitados, mesmo que C possuı́sse um coletor de lixo similar ao que existe em Java? (c) (4 pontos) A linguagem C não possui um coletor de lixo porque a semântica dessa linguagem torna a construção de tal ferramenta muito difı́cil. Explique uma das razões por trás de tal dificuldade. 5 5. Nesta questão você irá implementar uma lista circular em Python. Parte do código da lista já está dado abaixo. Você deve preencher as lacunas. class EmptyListEx(Exception): def __init__(self, msg): self.value = msg def __str__(self): return repr(self.value) class CircList: def __init__(self): self.current = None def empty(self): return self.current == None def get(self): (2 pontos) (a) O método get retorna o elemento na posição corrente da lista circular. O estado da lista não deve ser modificado. Uma tentativa de retornar um elemento de uma lista vazia dispara EmptyListEx. def move(self): (2 pontos) (b) O método move modifica o estado da lista, alterando seu ponteiro corrente para a próxima posição, considerando a circularidade da lista. Uma tentativa de mover o elemento corrente em uma lista vazia dispara EmptyListEx. def add(self, element): (3 pontos) (c) O método add insere element uma posição antes do elemento corrente da lista. O elemento corrente passa então a ser element. Em outras palavras, c.get(c.add("oi")) deve retornar o próprio elemento "oi". Esse método não dispara exceções. def find(self, element): (3 pontos) (d) O método find posiciona o ponteiro corrente sobre a primeira ocorrência de element dentro da lista circular. Se a lista estiver vazia, nada acontece. 6 6. Ao contrário de Python, a linguagem Java provê exceções verificáveis estaticamente. Estas exceções precisam ser tratadas explicitamente pelo desenvolvedor. Por exemplo, o método Java abaixo não compilaria caso removêssemos as cláusulas de tratamento de erro: import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public static void fileReader(final String fileName) { try { Scanner s = new Scanner(new File(fileName)); // ... read the file here ... } catch (FileNotFoundException e) { e.printStackTrace(); } } (a) (2 pontos) O que distingue uma exceção verificável estaticamente de uma exceção que não precisa ser verificada? (b) (3 pontos) Cite uma vantagem desta abordagem adotada por Java (c) (3 pontos) Java é a única linguagem muito popular que adota exceções verificáveis estaticamente. Obviamente existem desvantagens nesta abordagem, pois linguagens posteriores à Java não a seguiram. Cite uma desvantagem das exceções verificadas estaticamente. (d) Exceções verificáveis estaticamente são uma forma de documentação que o compilador verifica. Cite outras duas formas de documentação que o compilador javac consegue verificar. 7