Mostrando postagens com marcador Declarativa. Mostrar todas as postagens
Mostrando postagens com marcador Declarativa. Mostrar todas as postagens

domingo, 29 de março de 2015

Gxref

Glider
Estou experimentando o ambiente gxref, parte do XPCE do SWI-Prolog, e tenho algumas impressões para compartilhar.


Editor de texto

O editor de texto, PceEmacs, é muito ruim. Baseado na porcaria do Emacs, possui comandos muito verborrágicos, é dolorosamente dependente do mouse e o search-replace é uma bosta.

Para quem está acostumado com o Vim, de comando curtos e eficientes, que não precisa do mouse pra nada – você não precisa tirar a mão do teclado enquanto edita –, o PceEmacs é um retorno às cavernas.

A única vantagem é que o syntax highlight é bastante eficiente.

File info

A aba File info é simplesmente maravilhosa! Relaciona todos os predicados, onde eles são usados e que predicados de outros módulos são usados no módulo atual.

É uma ferramenta caída dos céus para depurar erros de sintaxe e até mesmo de lógica.

Dependencies

A aba Dependencies exibe um grafo das relações entre os módulos que deixa muito claro como sua imagem se comporta.

Conclusão

O XPCE+gxref perde pontos pelo editor de texto ruim e por depender do X (não roda nativo no Mac OS X), mas todo o resto é muito eficiente, merecendo uma nota, de zero a dez, oito (8).

[]’s
ℭacilhας, ℒa ℬatalema

sábado, 24 de maio de 2014

Erlang vs Prolog

Poliedro

Simon Thompson Joe Armstrong criou Erlang a partir de Prolog – aliás, o primeiro compilador era escrito em Prolog.

[update 2014-05-25]
Faglia nostra: o programador Prolog e criador da linguagem Erlang é Joe Armstrong. Simon Thompson é o mantenedor e um dos resposáveis pela reescrita em C.
[/update]
Apesar de ser uma linguagem funcional, Erlang traz muita herança de Prolog e suporta o paradigma declarativo. Para demonstrar essa similaridade, vou colocar o código de fatorial: em Erlang e em Prolog, implementação simples (e ruim) e usando tail-call optimization.

Código Q&D

Vamos ao rápido e sujo, primeiro em Prolog:

fact(0, 1) :- !.
fact(N, F) :- N > 0,
              N1 is N - 1,
              fact(N1, F1),
              F is N * F1.

Agora em Erlang:

fact(0) -> 1;
fact(N) when N > 1 -> N * fact(N - 1).

A intenção é mostrar como os códigos são parecidos.

Tail call optimization

Tanto Prolog quanto Erlang têm sistemas inteligentes de esvaziamento de pilha e usar um acumulador ajuda a tornar o programa mais eficiente.

O fatorial em Prolog fica assim:

fact(N, F) :- N >= 0,
              fact(N, 1, F).

fact(0, F, F) :- !.
fact(N, A, F) :- N1 is N - 1,
                 A1 is N * A,
                 fact(N1, A1, F).

Agora em Erlang:

fact(N) when N >= 0 -> fact(N, 1).

fact(0, A) -> A;
fact(N, A) -> fact(N-1, A*N).

Bônus

Tente adivinhar em qual linguagem é este código:

fact(N, F) :- N >= 0, fact(N, 1, F).
fact(0) --> { ! }, '='.
fact(N) --> { N1 is N - 1 }, mul(N), fact(N1).
mul(N, A, R) :- R is N * A.


[]’s
ℭacilhας, ℒa ℬatalema

segunda-feira, 19 de maio de 2014

PL Framework

Glider
Falei sobre como gerenciar dados mutáveis em Prolog. O código final ficou assim:


-*- Prolog -*-
:- module(profile, [profile/2]).
:- dynamic profile/2.

set(ID, X) :- profile(ID, X), !.
set(ID, X) :- X =.. [Attr, _],
              E =.. [Attr, _],
              retractall(profile(ID, E)),
              assertz(profile(ID, X)), !.

set(_, []) :- !.
set(ID, [X|Rest]) :- set(ID, X), set(ID, Rest).

get(ID, R) :- findall(X, profile(ID, X), R).

drop(ID) :- retractall(profile(ID, _)).

Mas e se quisermos que os dados sejam persistentes, ou seja, sobrevivam entre execuções, como um banco de dados?

O dialeto SWI Prolog oferece um arcabouço com inúmeros recursos chamado PL Framework.

Para persistência, há a biblioteca persistency.pl.

O que temos de fazer é substituir algumas partes de nosso código pelos predicados da biblioteca. Começaremos importando o módulo: logo após a declaração do nosso módulo, na linha abaixo, acrescente a importação:
:- use_module(library(persistency)).

Em seguida, não precisamos mais declarar o predicado profile/2 como dinâmico, em vez disso vamos declarar uma persistência. Substituia a linha com dynamic por:
:- persistent profile(id:atom, attr:compound).

Agora precisamos subtituir as chamadas de retractall/1 por retractall_profile/2, protegidas por with_mutex/2.

Onde está:
              retractall(profile(ID, E)),

Substitua por:
              with_mutex(profile,
                         retractall_profile(ID, E)),

E substitua:
drop(ID) :- retractall(profile(ID, _)).

Por:
drop(ID) :- with_mutex(profile,
                       retractall_profile(ID, _)).

Agora precisamos substituir assertz/1 por assert_profile/2. Onde está:
              assertz(profile(ID, X)), !.

Substitua por:
              with_mutex(profile, 
                         assert_profile(ID, X)), !.

Você pode juntar os predicados dentro de with_mutex/2.

Precisamos agora de uma regra para conectar a uma base de dados em disco, por exemplo:
connect(File) :- db_attach(File, [gc]).

Se quiser um arquivo padrão:
connect :- connect('profile.db').

Código final com persistência

-*- Prolog -*-
:- module(profile, [profile/2]).
:- persistent profile(id:atom, attr:compound).

set(ID, X) :- profile(ID, X), !.
set(ID, X) :- X =.. [Attr, _],
              E =.. [Attr, _],
              with_mutex(profile,
                         (retractall_profile(ID, E),
                          assert_profile(ID, X))), !.

set(_, []) :- !.
set(ID, [X|Rest]) :- set(ID, X), set(ID, Rest).

get(ID, R) :- findall(X, profile(ID, X), R).

drop(ID) :- with_mutex(profile,
                       retractall_profile(ID, _)).

connect :- connect('profile.db').

connect(File) :- db_attach(File, [gc]).

Este artigo foi apenas um exemplo prático de Prolog.

[]’s
ℭacilhας, ℒa ℬatalema

sábado, 17 de maio de 2014

Pequeno glossário de Prolog

Glider Ontem eu escrevi um artigo simples sobre Prolog.

O artigo na Wikipédia em português me pareceu pouco completo e bastante confuso. Tem informações legais lá, mas em comparação com a versão em inglês deixa a desejar.

Não pretendo completar o que está escrito na Wikipédia (senão deveria completar lá), apenas descomplicar um pouco.

Pra começar, é preciso entender a estrutura de um programa em Prolog: consiste em um domínio de verdades, parecido com uma base de dados, e o ponto de entrada é uma pergunta (query). Dito isso, vamos definir os conceitos dentro do código:
  • Termo: qualquer dado em Prolog é chamado termo.
  • Átomo: é um nome de propósito geral sem significado inerente. É representado por uma sequência de letras e alguns caracteres, começando por uma letra minúscula, ou qualquer sequência entre piclas ('…'). Por exemplo: hanoi
  • Número: pode ser inteiro ou de ponto flutuante, não diferente de outras linguagens.
  • Termo composto: consiste em um átomo, chamado functor (que é uma cabeça ou cabeçalho) seguido de uma sequência de termos e/ou incógnitas separados por vírgulas e entre parêntesis, chamada argumentos. Por exemplo: hanoi(5, R)
  • Predicado: é uma função booleana, ou seja, que retorna verdadeiro ou falso. Um predicado pode ser um átomo ou um termo composto e deve ser definido por um fato ou por uma regra.
  • Operador: é um predicado normal de aridade 2, mas que pode ser representado de forma diferente. Por exemplo, o sinal de igual:
X = 2

É o mesmo que:
'='(X, 2)

  • Variável: prefiro evitar a expressão “variável”, pois ela é falsa, apesar de ser o nome oficial. Prefiro “incógnita”: a incógnita pode estar ligada (bound) ou não (unbound) a um termo e tem comportamento diferente de acordo com estar ou não ligada. É representada por uma sequência de letras iniciando por uma letra maiúscula ou um underscore (_). Por exemplo: X
  • Fato: indica que determinado predicado retorna verdadeiro. É um átomo ou termo composto seguido de um ponto. Por exemplo:
default(5).

  • Regra: condiciona o resultado de um predicado ao resultado de outro. É um átomo ou termo seguido de :- e então o predicado ao qual ele está condicionado. Por exemplo:.
move(_, X, Y, _, R) :- move(1, X, Y, _, R).

  • Pergunta (query): É a chamada de um predicado. Pode retornar verdadeiro, falso ou gerar uma exceção. No código, é uma linha iniciada por :- e terminada por ponto. No prompt ?- basta digitar o predicado seguido de um ponto e pressionar a tecla Enter.

Um detalhe importante é que coisas que não parecem predicados, também são! Por exemplo:
  • Vírgula: é operador (','/2) que avalia o primeiro um predicado (argumento) e só avalia o segundo se o primeiro for verdadeiro, ou seja, E lógico.
N1 is N - 1, fact(N1, F1)

  • Ponto e vírgula: operador (';'/2) que avalia o primeiro predicado e só avalia o segundo se o primeiro for falso, ou seja, OU lógico.
  • Exclamação: é um predicado de aridade 0 ('!'/0) que indica que, se a regra atual “casar” (match) com a pergunta, os próximos fatos ou regras não serão avaliados.
  • Qualquer operação matemática.

Como exemplo, segue uma implementação da resolução da torre de Hanói:
% -*- Prolog -*-
:- module(hanoi, [hanoi/0, hanoi/1, hanoi/2]).

hanoi :- hanoi(R), findall(_, show(R), _).
hanoi(R) :- default(N), hanoi(N, R).
hanoi(N, R) :- N > 0,
               findall(X,
                       move(N,
                            'a esquerda',
                            'a direita',
                            'o centro', X),
                       R).

default(5).

move(1, X, Y, _, move(X, Y)) :- !.
move(N, X, Y, Z, R) :- N1 is N - 1, move(N1, X, Z, Y, R).
move(_, X, Y, _, R) :- move(1, X, Y, _, R).
move(N, X, Y, Z, R) :- N1 is N - 1, move(N1, Z, Y, X, R).

show(move(X, Y)) :- format('mova o disco d~w para ~w~n', [X, Y]).
show([X|_]) :- show(X).
show([_|Rest]) :- show(Rest).

Consegue entender como funciona?

Para gerar um executável com SWI Prolog:
bash$ prolog -f hanoi.pl \
-g 'use_module(library(save)), qsave_program(hanoi, [goal(hanoi)])' \
-t halt

Bônus

Há um açúcar sintático em prolog, que é -->:
a --> b, c(x), d.

Significa o mesmo que:
a(Start, End) :- b(Start, V1), c(x, V1, V2), d(V2, End).

[]’s
ℭacilhας, ℒa ℬatalema

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

terça-feira, 12 de julho de 2011

Prolog

Certamente Prolog não é meu forte… nunca fiz nada útil em Prolog e não conheço a linguagem tanto quanto gostaria, mas já tive meu samadhi.

Prolog é uma linguagem de programação declarativa de propósito geral, voltada para inteligência artificial e linguística computacional.

Seu nome significa programmation en logique e foi criada no começo da década de 1970 por um grupo liderado por Alain Colmerauer em Marseille, França.

Predicados e domínio


A programação Prolog consiste em definir um conjunto de verdades chamadas fatos e relações entre os fatos, chamadas regras.

De um modo geral, fatos e regras são chamados predicados e o conjunto de predicados do programa é chamado domínio do problema.

Um fato define uma afirmação, uma verdade:
flor(cravo).


Esta linha de código está dizendo «cravo é uma flor», e é um predicado do domínio.

Regra é um predicado condicional:
gosta(ana, X) :- flor(X).


Ana gosta de X se X for uma flor, ou de forma mais precisa: se Ana gosta de X implica que X é um flor, ou seja, leia :- como implica em.

Lembre-se que implicância é uma operação não recíproca.

Consultas


O uso de Prolog se dá através de consultas (queries). Por exemplo, salve as duas linhas acima no arquivo ana.pro, execute o interpretador e digite:
| ?- [ana].
compiling ~/ana.pro for byte code...
~/ana.pro compiled, 2 lines read - 419 bytes written, 14 ms

(1 ms) yes
| ?- gosta(ana, cravo).

yes
| ?- gosta(ana, gato).

no


Tipos


Prolog possui quatro tipos: átomo, termo composto, número e lista. Um fato precisa ser um átomo ou um termo composto.
  • Átomo: é uma constante. Representado por uma sequência de letras, números e underscore iniciada por uma letra minúscula ou uma sequência entre plicas. Ex.: cravo, 'ana.pro', 'Cacilhας, La Batalema'.

  • Termo composto: é uma estrutura complexa. Consiste em um funtor seguido por um ou mais parâmetros entre parêntesis. O funtor deve ser um átomo, os parâmetros podem ser de quaisquer tipos. Ex.: gosta(ana, gato).

  • Número: exatamente o que diz. É representado por dígitos numéricos e ponto (para ponto flutuante.) Ex.: 123, 3.1415.

  • Lista: é uma sequência de elementos de outros tipos. Os delimitadores de início e fim são [] e os elementos são separados por vírgulas. O símbolo pipe (|) por ser usado para representar resto da lista. Ex.: [a, b, c], [abc | SX] (SX é uma lista).

  • String: é um tipo especial de lista que contém apenas números inteiros entre 0 e 255. Ex.: "ABC"[65, 66, 67].


Incógnita


O equivalente a variável em Prolog é a incógnita, que consiste em um valor desconhecido, porém constante, representado por uma sequência similar ao átomo, mas iniciada por uma letra maiúscula.

Por exemplo, em:
gosta(ana, X) :- flor(X).


X é uma incógnita. A solução das consultas feitas acima é obtida encontrando o valor de X, o que é responsabilidade do programa.

Exemplo


Um exemplo clássico de Prolog é a implementação de fatorial:
factorial(0, 1).

factorial(N, F) :-
N > 0,
N1 is N - 1,
factorial(N1, F1),
F is N * F1.


O que o código diz é que o fatorial de 0 é 1 e se o fatorial de N é F implica que:
  • N é maior que 0 (já está coberto pelo predicado anterior);
  • N1 é igual a N menos 1;
  • O fatorial de N1 é F1;
  • E F é igual a N vezes F1.


Uso:
| ?- [factorial].
compiling ~/factorial.pro for byte code...
~/factorial.pro compiled, 7 lines read - 941 bytes written, 14 ms

(1 ms) yes
| ?- factorial(5, X).

X = 120 ?

yes


Um bom exemplo também é a implementação da solução da torre de Hanói, mas vai fritar seu cérebro:
hanoi(N) :- move(N, esquerda, direita, central).

move(1, X, Y, _) :-
write('mova o disco da torre '),
write(X),
write(' para a torre '),
write(Y),
nl.

move(N, X, Y, Z) :-
N > 1,
M is N - 1,
move(M, X, Z, Y),
move(1, X, Y, _),
move(M, Z, Y, X).


É claro que isso não é nem o começo. O objetivo é apenas causar água na boca.

[]’s
Cacilhας, La Batalema

quinta-feira, 26 de agosto de 2010

Paradigmas de programação

Glider Muito mais empolgante do que aprender uma nova linguagem de programação é aprender um novo paradigma de programação.

A parte mais louca é perceber como o paradigma funciona. Isso acontece de modo intuitivo, muito parecido com como quando se aprende uma língua nova: nada faz sentido no começo, tudo é muito mecânico; de repente um estalo e tudo faz sentido!

Há muitos paradigmas de programação, já andei falando disso antes, mas muitos são variações dos mesmos tipos básicos: paradigmas imperativo, funcional e declarativo.

Programação imperativa


É o primeiro paradigma com o qual a maioria dos programadores trava contato.

Na programação imperativa, o programa se parece com uma receita de bolo: uma sequência de ordens, chamadas comandos ou instruções (statements em inglês), que devem ser executadas.

A partir desse conceito, derivam diversos paradigmas secundários. O principal é a programação estruturada ou procedimental (procedural em inglês), onde os comandos são organizados em grupos, chamados funções, que podem ser evocados em momentos diferentes.

As funções podem receber ou não parâmetros, que alteram seu comportamento, e podem retornar valores. Em algumas linguagens, quando o grupo de comandos não recebe parâmetros nem retorna valores, ele é chamado subrotina.

Para exemplificar o funcionamento da programação imperativa, vamos implementar fatorial em C – mais simples impossível.

Para quem não sabe, fatorial consiste em uma função recursiva cuja parada é:
0! = 1


E o passo é:
n! = n * (n - 1)!


Outra forma de entender é reiterativamente:
n! = 1 * 1 * 2 * 3 * … * (n - 2) * (n - 1) * n


Vamos implementar a função reiterativa em C para ilustrar melhor o paradigma:
#include <stdlib.h>
#include <stdio.h>

int factorial(int);


int main(int argc, char **argv) {
printf("O fatorial de 5 é %d\n", factorial(5));
return EXIT_SUCCESS;
}


int factorial(int x) {
int result = 1;
int i;
for (i=1; i<=x; ++i)
result *= i;
return result;
}


Cada comando é uma ordem dada ao sistema: atribua 1 à variável result; para cada i de 1 até o valor do argumento x, multiplique o valor de result pelo valor de i; retorne o valor de result.

São ordens dadas ao sistema.
[update 2011-07-24]
Na programação imperativa, é muito comum a manipulação de alterações de estado, daí o uso de variáveis – contentores (ou slots) que podem sofrer alterações de valor ao longo da existência do processo em execução.
[/update]


Orientação a objetos


Orientação a objetos não passa de um variante da programação imperativa, mas um variante digno de citação.

Na orientação a objetos, as ordens não são dadas a um sistema abstrato, mas objetos recebem as ordens e as executam.

O conceito de objeto é bem genérico: qualquer coisa pode ser um objeto, um número, uma janela na tela, uma coleção…

Na orientação a objetos as ordens dadas a um objeto são chamadas mensagens e a definição de como o objeto reage a cada mensagem é chamada método.

O exemplo será a versão recursiva de fatorial implementada em Smalltalk:
!Integer methodsFor: 'mathematical functions'!

factorial
self = 0 ifTrue: [↑ 1].
self > 0 ifTrue: [↑ self * (self - 1) factorial].
self error: 'Not valid for negative integers'.
!
!!


Transcript
show: 'O fatorial de 5 é ';
show: 5 factorial printString;
cr.


[update 2011-07-16]
Atualizei o código em Smalltalk segundo implementação da máquina virtual Pharo.
[/update]


O princípio é parecido com o anterior, mas em vez de dar uma ordem ao sistema – calcule o fatorial de 5 –, é dada ao próprio número – número 5, qual seu fatorial?

Observação: a orientação a objetos pode ser aplicada também à programação funcional.

Programação funcional


Enquanto na programação imperativa ordens são dadas ao sistema ou a objetos, no paradigma funcional são definidas funções, como as matemáticas, e o programa nasce da interação entre as funções: o resultado de umas funções é passado como parâmetro para outras.
[update 2011-07-24]
Na programação funcional, o estado do sistema tende a ser constante, havendo apenas a troca de informação por parâmetros. Assim é comum que não haja variáveis, mas incógnitas constantes, que não sofrem (ou não devem sofrer) alteração de valor ao longo da execução do programa.
[/update]


O mesmo exemplo, fatorial, em Common Lisp:
(defun factorial ((x integer))
(if (zerop x)
1
(* x (factorial (- x 1)))))


(format t "~%O fatorial de 5 é ~A~&" (factorial 5))


O conceito é ligeiramente difente: a função factorial, que recebe um parâmetro inteiro nomeado x, é definida como o resultado da função if; a função if recebe como primeiro parâmetro o resultado da função zerop, que é verdadeiro quando x é igual a zero; o segundo parâmetro de if, 1, é o resultado caso o primeiro parâmetro seja verdadeiro; o terceiro parâmetro é o resultado caso seja falso; o terceiro parâmetro é o resultado da função de multiplicação. Entendendo até aqui, o resto é autoexplicativo.

[update 2011-06-19]

Bônus!



Fatorial em Scheme:
(define (factorial x)
(if (zero? x)
1
(* x (factorial (- x 1)))))

(display "O fatorial de 5 é ")
(display (factorial 5))
(newline)


E em Erlang:
-module (fact).
-export([factorial/1]).

factorial(0) -> 1;
factorial(X) -> X * factorial(X - 1).


Salve como fact.erl. No prompt:
1> c("fact.erl").
{ok,fact}
2> fact:factorial(5).
120

[/update]


Programação declarativa


Este paradigma é um dos mais complicados para pescar

[update 2011-07-24]
Na programação declaração o código diz o que o programa deve fazer, não como.
[/update]


O programa consiste em uma lista de declarações de verdades.

Nos paradigmas anteriores havia variáveis, que sofriam atribuições. Na programação declarativa há incógnitas, que não sofrem atribuições! O valor de cada incógnita é constante e inicialmente desconhecido. O que o programa faz é cruzar as consultas (queries) com as declarações que formam o conjunto verdade para deduzir o valor das incógnitas.

O exemplo consiste no fatorial implementado em Prolog:
factorial(0, 1).

factorial(N, F) :-
N > 0,
N1 is N - 1,
factorial(N1, F1),
F is N * F1.


O que este conjunto verdade diz é que o fatorial de 0 é 1 e que o fatorial de N é F quando:
  1. N é maior que zero;
  2. N1 é N menos 1;
  3. o fatorial de N1 é F1;
  4. e F é igual a N vezes F1.


Para a consulta:
| ?- [factorial].
fatorial.pro compiled, 7 lines read - 869 bytes written, 7 ms
(1 ms) yes
| ?- factorial(5, X),
write('O fatorial de 5 é '),
write(X),
nl.


Para entender como funciona, há um applet em Java em um tutorial que demonstra o chinês. É só encontrar o applet na página, ir clicando em Step e ver acontecer.

A linguagem declarativa da vez é Erlang.

[update 2011-06-19]
Na época deste artigo, em vez de estudar Erlang, perguntei por aí e fui informado que Erlang seria uma linguagem declarativa. Agora que tomei vergonha na cara e estudei um pouco, descobri que é uma linguagem funcional, extremamente similar a Haskell.

Para experimentar Erlang, há o applet Try Erlang.
[/update]


[]’s
Cacilhας, La Batalema