Entrada e Saída Prof. Elaine Faria e Hiran Nonato Programação Lógica UFU 2012 Créditos • O material a seguir consiste de adaptações e extensões dos originais gentilmente cedidos pelo Prof. Alexsandro Santos Soares Arquivos de Dados • A comunicação com o Prolog até agora foi feita por meio de consultas realizadas pelo usuários que são respondidas através de instanciações de variáveis • Muitas vezes essa forma de comunicação não é suficientemente adequada – Entrada de dados na forma de linguagem natural – Saída em um dado formato desejado – Entrada e saída para qualquer periférico do computador Arquivos de Dados • Existem predicados prédefinidos com o intuito de auxiliar na entrada/saída de dados – São dependentes da implementação da linguagem Prolog usada Arquivos de Dados Luis, A. M. Palazzo, Introdução à Programação Prolog, Educat, 1997. Arquivos de Dados • Durante a execução de um programa Prolog dois arquivos estão ativos – um para entrada "fonte de entrada corrente" – um para saída "fonte de saída corrente “ • No início da execução essas duas fontes correspondem ao terminal do usuário • A fonte de entrada corrente pode ser mudada a qualquer momento para um outro arquivo see(novoArqEnt). Arquivo de Entrada • Exemplo do uso do see ... see(arq1). le_do_arquivo(Informacao). see(user). ... Arquivos de Saída • A fonte de saída corrente pode ser mudada – tell(novoArqSai). • Exemplo do uso do tell ... tell(arq3). grava_no_arquivo(Informacao). tell(user). ... Arquivos de Dados • Dois outros predicados devem ser utilizados para fechar os arquivos correntes de entrada e saída respectivamente – seen/0 e told/0 Abertura de um arquivo • Podese também abrir um fluxo (stream) … open(‘arquivo.txt‘, write, Fluxo), ... close(Fluxo), … Abertura de um arquivo • Para estender um arquivo existente, temos que abrir um fluxo em modo append … open(‘arquivo.txt‘, append, Fluxo), .... close(Fluxo), ... Arquivos de Dados • Os arquivos podem ser processados somente na forma sequencial • A requisição para a leitura irá ocasionar a leitura a partir da posição corrente dessa fonte de entrada • Após a leitura, a posição corrente da fonte de entrada será, o próximo item que ainda não foi lido • Se uma requisição de leitura é feita para o fim do arquivo retorna a constante "end_of_file“ • Predicado predefinido at_end_of_stream(F) usado para verificar se o fim da stream foi atingido Arquivos de Dados • A saída de informações ocorre de maneira similar à entrada – A requisição de saída irá adicionar a informação requisitada no final da fonte de saída corrente Arquivos de Dados • Há duas maneiras de se utilizar os arquivos em Prolog – Considerando o caractere como elemento básico do arquivo – Considerando unidades maiores como elemento básico do arquivo Arquivos de Dados • Usando o caractere como elemento básico arquivo – Predicados: get e put • Usando unidades maiores como elemento básico do arquivo – As unidades são os termos Prolog: read e write • O uso de cada uma dessas modalidades depende do contexto do problema Processamento de Arquivos de Termos • read – Usada para leitura de dados a partir da fonte corrente read([A],X) leitura do próximo termo que irá unificar com X Se X é uma variável então X será instanciada com T Se a unificação não for possível então o objetivo read(X) irá falhar Cada termo deve ser seguido por um ponto e um espaço ou enter A representa o arquivo não é obrigatório – É determinístico não ocorre backtracking Processamento de Arquivos de Termos • write – Fornece a saída de um termo para a fonte corrente write([A], X) • tab – O predicado tab(N) irá ocasionar a saída de N espaços • nl – O predicado nl(sem argumentos) irá ocasionar o início de uma nova linha Exemplo 1 Cubo cubo : read(X), processa(X). processa(fim) : !. processa(N) : C is N*N*N, write(C), cubo. ?cubo. 2. 8 5. 25 12. 1728 fim. true Exemplo 2 Cubo cubo : write('Próximo valor: '), read(X), processa(X). processa(fim) : !. processa(N) : C is N*N*N, write('O cubo de '), write(N), write('é '), write(C), nl, cubo. Dependendo da implementação, uma requisição ("flush/0" para descarregamento dos buffers de I/O) pode ser necessária após o comando de escrita no prompt Exemplo 3 – Escrevendo Lista • Procedimento escreveLista(L) escreveLista([]). escreveLista([X | L]) : write(X), nl, escreveLista(L). • Procedimento escreveLista2(L) escreveLista2([]). escreveLista2([L | LL]) : imprime(L), nl, escreveLista2(LL). imprime([]). imprime([X | L]) : write(X), tab(1), imprime(L). Exemplo 4 • Programa em Prolog que lê o arquivo (casas.txt) e o exibe na tela. casas.txt: grifinoria. lufa_lufa. corvinal. sonserina. principal: open('casas.txt',read,F), read(F,C1), read(F,C2), read(F,C3), read(F,C4), close(F), write([C1,C2,C3,C4]), nl. Exemplo 5 principal: open('casas.txt',read,F), leiaCasas(F,Casas), close(F), write(Casas), nl. leiaCasas(F,[]): at_end_of_stream(F). leiaCasas(F,[X|L]): \+ at_end_of_stream(F), read(F,X), leiaCasas(F, L). Exemplo 5 com cortes verdes principal: open('casas.txt',read,F), leiaCasas(F,Casas), close(F), write(Casas), nl. leiaCasas(F,[]): at_end_of_stream(F), !. leiaCasas(F,[X|L]): \+ at_end_of_stream(F), !, read(F,X), leiaCasas(F, L). Exemplo 6 • Problema – Arquivo denominado arq1 contendo termos na forma item(Nro, Descrição, Preço, Fornecedor) – Desejase produzir um outro arquivo que contenha somente os itens fornecidos por um determinado fornecedor – O nome do fornecedor deve ser escrito no início do arquivo Exemplo – cont. principal(NomeForn): open('C:\\Entrada.txt',read,AEnt), open('C:\\Saida.txt',write,ASaida), write(ASaida, NomeForn), write(ASaida, '\n'), leiaForn(AEnt,ASaida, NomeForn), close(AEnt), close(ASaida). leiaForn(AEnt,_,_): at_end_of_stream(AEnt), !. leiaForn(AEnt,ASaida,NomeForn): read(AEnt,item(Nro,D,P,NomeForn)),!, write(ASaida, item(Nro,D,P)), write(ASaida,'\n'), leiaForn(AEnt,ASaida,NomeForn). leiaForn(AEnt,ASaida,NomeForn): leiaForn(AEnt,ASaida,NomeForn). Processamento de caracteres • Um caractere é escrito na fonte de saída corrente por meio do objetivo put(C) C é o código ASCII (um número entre 0 e 255) do caractere a ser escrito – Exemplo ?put(65), put(66), put(67). produz a saída: ABC Processamento de caracteres • Um caractere pode ser lido a partir da fonte de entrada corrente por meio do objetivo get0(C) ocasiona a leitura do caractere corrente e torna a variável C instanciada com o código ASCII do caractere Processamento de caracteres • Leitura de caracteres imprimíveis saltando sobre todos os caracteres nãoimprimíveis (espaços em branco) get(C) Processamento de caracteres • O predicado get_code/2 lê o próximo caracter disponível de um fluxo – Primeiro argumento: um fluxo – Segundo argumento: o código do caracter Exemplo usando get_code leiaPalavra(Fluxo,Palavra): get_code(Fluxo,Caracter), verificaELeiaResto(Caracter,Caracteres,Fluxo), atom_codes(Palavra,Caracteres). verificaELeiaResto(10, [], _): !. verificaELeiaResto(32, [], _): !. verificaELeiaResto(1, [], _): !. verificaELeiaResto(Caracter,[Caracter|Caracteres],F): get_code(F,ProxCaracter), verificaELeiaResto(ProxCaracter,Caracteres,F). Conversão de Termos • Predicado prédefinido atom_codes/2, que relaciona os átomos com o seu código ASCII. – atom_codes(X, L) é verdadeiro, se L é a lista dos códigos dos caracteres em A – Exemplo atom_codes(zx232, [122, 120, 50, 51, 50]) Dividindo programas em arquivos • Muitos predicados Prolog fazem uso dos mesmos predicados básicos – Por exemplo: member/2, append/3 • É claro que você não quer redefinilos a cada vez que necessitar deles – Prolog oferece muitos modos de fazer isto Leitura de programas • A forma mais simples de dizer ao Prolog para ler as definições de predicados armazenadas em um arquivo é usar os colchetes ? [meuArq]. {consulting(meuArq.pl)…} {meuArq.pl consulted, 233 bytes} true ? Leitura de programas • Você também pode consultar mais de um arquivo por vez ? [meuArq1, meuArq2, meuArq3]. {consulting meuArq1.pl…} {consulting meuArq2.pl…} {consulting meuArq3.pl…} Leitura de programas • Você não precisa fazer isto interativamente • Ao invés disto, você pode usar uma diretiva na base de dados : [meuArq1, meuArq2]. Leitura de programas • Talvez, muitos arquivos, independentemente, consultem o mesmo arquivo. • Verificação extra se as definições dos predicados já são conhecidas: ensure_loaded/1 : ensure_loaded([meuArq1, meuArq2]). Módulos • Imagine que você está escrevendo um programa que gerencie um banco de dados sobre filmes • Você projetou dois predicados: – imprimeAtores/1 – imprimeFilmes/1 • Eles estão armazenados em arquivos diferentes • Ambos usam um predicado auxiliar: – exibeLista/1 O arquivo principal .pl % Arquivo principal.pl : [imprimeAtores]. : [imprimeFilmes]. O arquivo principal .pl % Arquivo principal.pl : [imprimeAtores]. : [imprimeFilmes]. ? [principal]. O arquivo principal .pl % Arquivo principal.pl : [imprimeAtores]. : [imprimeFilmes]. ? [principal]. {consulting principal.pl} O arquivo principal .pl % Arquivo principal.pl : [imprimeAtores]. : [imprimeFilmes]. ? [principal]. {consulting principal.pl} {consulting imprimeAtores.pl} O arquivo principal .pl % Arquivo principal.pl : [imprimeAtores]. : [imprimeFilmes]. ? [principal]. {consulting principal.pl} {consulting imprimeAtores.pl} {imprimeAtores.pl consulted} O arquivo principal .pl % Arquivo principal.pl : [imprimeAtores]. : [imprimeFilmes]. ? [principal]. {consulting principal.pl} {consulting imprimeAtores.pl} {imprimeAtores.pl consulted} {consulting imprimeFilmes.pl} O arquivo principal .pl % Arquivo principal.pl : [imprimeAtores]. : [imprimeFilmes]. ? [principal]. {consulting principal.pl} {consulting imprimeAtores.pl} {imprimeAtores.pl consulted} {consulting imprimeFilmes.pl} The procedure exibeLista/1 is being redefined. Old file: imprimeAtores.pl New file: imprimeFilmes.pl Do you really want to redefine it? (y, n, p, or ?) Usando módulos • Predicado préconstruído module: – module/1 e module/2 – Para criar um módulo/biblioteca • Predicado préconstruído use_module: – use_module/1 e use_module/2 – Para importar predicados de uma biblioteca • Argumentos – O primeiro argumento é o nome do módulo – O segundo e opcional argumento é uma lista dos predicados exportados Nota sobre módulos em Prolog • Nem todos os interpretadores Prolog possuem este sistema de módulos • SWI Prolog e Sicstus possuem • O sistema de módulos do Prolog ainda não é compatível com a norma ISO O módulo imprimeAtores.pl % Este é o arquivo: imprimeAtores.pl : module(imprimeAtores,[imprimeAtores/1]). imprimeAtores(Filme): filmesAtor(Ator, Lista), exibeLista(Lista). exibeLista([]): nl. exibeLista([X|L]): write(X), tab(1), exibeLista(L). O módulo imprimeFilmes.pl % Este é o arquivo: imprimeFilmes.pl : module(imprimeFilmes,[imprimeFilmes/1]). imprimeFilmes(Diretor): diretoresFilme(Filme, Lista), exibeLista(Lista). exibeLista([]): nl. exibeLista([X|L]): write(X), nl, exibeLista(L). O arquivo revisado principal.pl • % Este é o arquivo revisado principal.pl : use_module(imprimeAtores). : use_module(imprimeFilmes). % Este é o arquivo revisado principal.pl : use_module(imprimeAtores,[imprimeAtores/1]). : use_module(imprimeFilmes,[imprimeFilmes/1]). Bibliotecas • Muitos dos predicados mais comuns já vem pré construídos nos interpretadores Prolog • Por exemplo, em SWI Prolog, member/2 e append/3 vem como parte de uma biblioteca • Uma biblioteca é um módulo que define predicados comuns e pode ser carregada usando os predicados normais para importar módulos Importando bibliotecas • Ao especificar o nome de uma biblioteca que você quer usar, você pode informar que este módulo é uma biblioteca • Prolog procurará no lugar certo, ou seja, em um diretório onde todas as bibliotecas estão guardadas : use_module(library(lists)). Referências • Luis, A. M. Palazzo, Introdução à Programação Prolog, Educat, 1997.