sábado, 17 de maio de 2014

Dados em Prolog

Glider Há uns anos falei um pouquinho sobre Prolog: sobre predicados, átomos, termos compostos, fatos, regras, incógnitas, etc.

Só pra relembrar: um programa em Prolog é um conjunto de predicados – fatos e regras – e sua execução consistem em perguntas (queries) feitas a esse domínio de verdades. Na programação declarativa, a ideia não é dizer como o computador deve resolver o problema, mas sim descrever o problema.

O entendimento de Prolog é essencial ao entendimento da programação declarativa e, assim, para poder extrair o máximo do poder de linguagens mais modernas, como Erlang.

É uma história bonita, mas um exemplo um pouco mais prático iria bem, não?

Então imaginei um exemplo: um módulo que representa perfis de dados – por exemplo, dados de perfil de usuário.

Podemos começar o código dizendo para o sistema nosso módulo:
% -*- Prolog -*-
:- module(profile, [profile/2]).

A primeira linha é um comentário que avisa a alguns editores que o arquivo se trata de um código Prolog, a segunda declara o módulo profile, que exporta o predicado profile/2 (com aridade 2).

A seguir, podemos declarar nosso predicado profile/2:
:- dynamic profile/2.

Essa declaração diz que o predicado profile/2 é dinâmico, ou seja, muda ao longo da execução do programa.

Agora precisamos de um predicado para gravar novos dados de perfil. Façamos isso com uma regra:
set(ID, X) :- profile(ID, X), !.

Essa regra diz ao sistema que, se alguém questionar set(ID, X) – onde ID é o identificador do perfil e X é um atributo do perfil – e essa combinação for verdadeira, ele não deve assumir isso como ver dade e não avaliar demais regras ou fato (!).

A próxima regra é um pouco longa, então vou falar dela linha a linha:
set(ID, X) :- X =.. [Attr, _],

Aqui diz que o predicado é set(ID, X) (exatamente igual ao anterior), e X é um termo composto de aridade 1, cujo cabeçalho será atrelado à incógnita Attr.

Caso verdade, continua a próxima linha da regra:
              E =.. [Attr, _],

Então E é termo composto com mesmo cabeçalho e aridade de X, porém com parâmetro _, que significa qualquer coisa.

Então, se X for name(john), E será name(_).
              retractall(profile(ID, E)),

Remove o predicado com o dado de perfil com o mesmo identificador e o mesmo cabeçalho informados.
              assertz(profile(ID, X)).

Cria o predicado armazenando o atributo de perfil informada – e assim termina nossa regra.

E se quisermos criar uma lista de atributos para o mesmo identificador de perfil?

Podemos criar um predicado para isso. Comecemos pelo ponto de parar, que é quando nossa lista de atributos já foi esgotada:
set(_, []) :- !.

Essa regra diz que, se a lista de atributos está vazia, não precisa fazer mais nada. Mas e se contiver algum elemento? Precisaremos questionar o set/2 para aquele atributo e continuar chamando para o resto da lista:
set(ID, [X|Rest]) :- set(ID, X), set(ID, Rest).

Resolvido.

Podemos criar um predicado para retornar todos os atributos de uma identidade:
get(ID, R) :- findall(X, profile(ID, X), R).

Precisamos agora de um predicado de limpeza, um para remover todos os atributos de um identificador (o que significa apagar o identificador):
drop(ID) :- retractall(profile(ID, _)).

Vamos testar agora nosso módulo:
?- [profile].
true.

?- profile:set(1, [name(john), age(20), city(rio)]).
true.

?- profile:set(2, [name(jack), age(21), city(sao_paulo)]).
true.

?- profile(ID, city(City)).
ID = 1
City = rio ;
ID = 2
City = sao_paulo.

?- profile:get(1, R).
R = [name(john), age(20), city(rio)].

?- profile:drop(1).
true.

?- profile(ID, _).
2 ;
2 ;
2.

?- drop(_).
true

?- profile(ID, _)
false.

?- 

Espero que o exemplo tenha sido esclarecedor. Para fazer um tracing, você pode fazer a pergunta gtrace.

Observação: para a execução, foi usando SWI Prolog, uma das mais confiáveis e robustas implementações de Prolog da atualidade.

Complemento a este artigo: Pequeno glossário de Prolog.

[]’s
ℭacilhας, ℒa ℬatalema
blog comments powered by Disqus