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:
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
A seguir, podemos declarar nosso predicado
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
Agora precisamos de um predicado para gravar novos dados de perfil. Façamos isso com uma regra:
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
A próxima regra é um pouco longa, então vou falar dela linha a linha:
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 é
Caso verdade, continua a próxima linha da regra:
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
Então, se
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:
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:
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
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.
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