%% Programa testador de resolucoes de chapa. %% %% Este programa testa um predicado chamado %% resolve(+Chapa,-Expr) que deve receber uma %% chapa e devolver uma expressao correta com %% os seus digitos. Nos comentarios que seguem, %% este predicado resolve e todos os que ele usa %% sao chamados de "seu programa". %% %% Para evitar conflitos, todos os predicados %% definidos aqui comecam com jm, inclusive append, %% que esta' agora com o nome jmAppend. %% %% COMO USAR %% %% Carregue no seu prolog: %% %% ?- reconsult('testador.prolog'). %% %% Carregue tambem seu resolvedor. A partir dai', %% use os predicados abaixo para testar seu programa. %% %% PREDICADOS UTEIS %% %% jmOk(+Chapa,+Expr). %% %% Satisfeito quando a Expr esta' correta e corresponde %% `a Chapa. Imprime algumas mensagens de erro relacionadas %% ao operador DEC. %% %% jmTest. %% %% Gera uma chapa ao acaso e testa seu programa com ela. %% %% jmTest(+Chapa). %% %% Testa uma chapa especifica. %% %% jmConta(+N,-Dificeis). %% %% Recebe um numero positivo N, gera N chapas ao %% acaso e testa seu programa com elas. Ao final, %% imprime a percentagem de acertos e devolve em %% Dificeis uma lista com as chapas que seu programa %% nao conseguiu resolver. %% %% jmRealTest. %% %% Testa TODAS as possiveis chapas, e imprime aquelas %% que seu programa nao conseguiu resolver. Esta %% chamada pode ser demorada. Multiplique por 10^4 o %% tempo que seu programa leva para resolver uma chapa %% para ter uma ideia de quanto vai demorar isto. %%------------------------------------------------------------ %% Parte inicial. Tenta determinar se o ambiente e' Unix %% ou PC, calculando log(10) e vendo se da' 1.0 (o que "indica" %% que seria um PC). Cuidado: isto nao foi muito testado. %% %% De qualquer forma, a ideia e' incluir na base de dados %% um fato jmUnix ou jmPC, conforme o caso. :- asserta(jmUnix), 1 is log(10), retract(jmUnix), asserta(jmPC). :- jmUnix, write('Ambiente Unix'), nl. :- jmPC, write('Ambiente PC'), nl. %% se for Unix, carregar pacote de numeros aleatorios :- jmUnix, [-'/n/lang/ed/lib/data/numbers/random.pl']. %%------------------------------------------------------------ %% jmRandigit(-D) produz um digito aleatorio. O codigo e' %% diferente conforme estejamos em Unix ou PC. jmRandigit(D) :- jmPC, X is random, jmSelect(X,D). jmRandigit(D) :- jmUnix, random(10, D). %% jmSelect usado para PC somente. jmSelect(X,0) :- X < 0.1, !. jmSelect(X,N) :- Y is X - 0.1, jmSelect(Y,M), N is M+1. %%------------------------------------------------------------ %% jmRandChapa(-Chapa) produz uma chapa aleatoria. jmRandChapa([D1,D2,D3,D4]) :- jmRandigit(D1), jmRandigit(D2), jmRandigit(D3), jmRandigit(D4). %%------------------------------------------------------------ %% jmTest (sem argumentos) produz uma chapa aleatoria e chama %% resolve. Imprime chapa e solucao proposta. A solucao e' %% verificada por jmOk. jmTest :- jmRandChapa(Chapa), write('Chapa = '), write(Chapa), nl, resolve(Chapa,Expr), write('Solucao = '), write(Expr), nl, jmOk(Chapa,Expr), write('Solucao aprovada!'), nl. %%------------------------------------------------------------ %% jmTest(+Chapa). Testa uma chapa especifica, nos mesmos %% moldes de jmChapa. jmTest(Chapa) :- resolve(Chapa,Expr), write('Solucao = '), write(Expr), nl, jmOk(Chapa,Expr), write('Solucao aprovada!'), nl. %%------------------------------------------------ %% testador percentual %% %% jmConta(+N,-L) provoca a feitura de N testes %% colocando em L as chapas nao conseguidas. %% Imprime tambem a porcentagem de acertos. jmConta(N,L) :- jmFaz(N,L), length(L,Erros), P is 100*(N-Erros)/N, write('Acertos = '), write(P), write(' %'), nl. %% jmFaz(+N,-L) executa os testes e mantem a lista %% para o predicado jmConta. jmFaz(N,L) :- N > 0, M is N-1, jmFaz(M,L1), jmMaisUm(X), jmAppend(X,L1,L). jmFaz(0,[]). %% jmMaisUm(-X) executa mais um teste, colocando %% em X a lista vazia, se a chapa foi resolvida, %% ou entao uma lista contendo a chapa jmMaisUm(X) :- jmRandChapa(Chapa), jmDificil(Chapa,X). %% jmDificil(+Chapa,-Lista) predicado auxiliar de %% jmMaisUm, que executa um teste em cima da %% chapa dada. Se ela for "dificil", entra na %% Lista (unitaria); senao Lista e' vazia. jmDificil(Chapa,Lista) :- resolve(Chapa,Expr), jmOk(Chapa,Expr), !, Lista = []. jmDificil(Chapa,[Chapa]). %%------------------------------------------------- %% jmRealTest: para aqueles que querem saber quais %% chapas seu programa resolve entre TODAS as %% possiveis! Prepare-se para esperar!! %% Este predicado imprime as nao conseguidas jmRealTest :- % esta primeira linha seria para imprimir % o instante inicial, para o caso de querer-se % medir o tempo. So' funciona no PC, por isso % comentei % time(X), write(X), nl, jmChapa(Chapa), jmDificil(Chapa,[Chapa]), write(Chapa), nl, fail. jmRealTest :- % parte que imprime o instante final. % time(X), write(X), nl, write('Fim do jmRealTest'), nl. %% jmChapa itera para obter todas as possiveis chapas %% mediante re-satisfacao. jmChapa([A,B,C,D]) :- jmDigito(A), jmDigito(B), jmDigito(C), jmDigito(D). %% auxiliares de jmChapa jmDigito(D) :- jmZeroANove(Lista), jmMember(D,Lista). jmZeroANove([0,1,2,3,4,5,6,7,8,9]). jmMember(X,[X|_]). jmMember(X,[_|Y]) :- jmMember(X,Y). %%------------------------------------------------------------ %% Programa para verificar expressoes geradas %% a partir de chapas de carro. %% 26/SET/97 %% modificado em 05/OUT/97: %% ==> restricoes sobre operador DEC %% ==> correcao de formula para LOG %% ==> verificacao de valores que dao erro %% (negativo para sqrt, etc.) %% ==> impelmentacao de fatorial (jmFat) %% ==> todos os predicados agora comecam com jm %% ==> jmOk agora imprime msgs. apenas se houver erro %% ==> jmOk usava Lista em vez de Chapa % ==> jmValor exige jmDigito no caso base %%--------------------------------------------------- %% jmOk(+Chapa,+Expr) verifica se a expressao %% Expr e' valida para a chapa Chapa. %% %% Formato da chapa: lista de 4 digitos decimais. %% %% Formato da expressao: definida recursivamente %% como segue: %% (1) um digito decimal e' uma expressao. %% (2) uma lista da forma [Un,Ex] , onde Un e' %% um operador unario e Ex e' uma expressao, %% e' uma expressao. %% (3) uma lista da forma [Bin,Ex1,Ex2] , onde %% Bin e' um operador binario e Ex1 , Ex2 , %% sao expressoes, e' uma expressao. %% %% Metodo de verificacao: primeiro verifica se %% a sintaxe da expressao e' valida, depois avalia %% a expressao para ver se da' TRUE, e depois %% verifica se os digitos sao aqueles da chapa, %% na mesma ordem. jmOk(Chapa,Expr) :- % ve se e' expressao valida (sintaxe apenas) jmExpr(Expr), % avalia jmAvalia(Expr), % verifica digitos jmDigitos(Expr,Chapa). %%--------------------------------------------------- %% Verificacao da sintaxe da expressao. %% Usa diretamente a definicao recursiva de uma %% expressao. %% %% Nao verifica aqui se ha' apenas um sinal de %% igualdade. Isto sera' indiretamente verificado %% na avaliacao. %% caso base: numeros. jmExpr(X) :- jmDigito(X), !. %% caso geral: operador unario. jmExpr([Un,Ex]) :- jmUnario(Un), !, jmExpr(Ex). jmExpr([Un,_]) :- write('Operador unario invalido: '), write(Un), nl, fail. %% caso geral: operador binario diferente de DEC. jmExpr([Bin,Ex1,Ex2]) :- Bin \== dec, jmBinario(Bin), !, jmExpr(Ex1), jmExpr(Ex2). %% operador DEC - seu primeiro argumento deve ser %% digito ou DEC; seu segundo argumento deve ser digito. jmExpr([dec,Ex1,Ex2]) :- jmDec(Ex1), jmDigito(Ex2), !. jmExpr([dec,Ex1,Ex2]) :- write('Erro no uso de DEC: '), write([dec,Ex1,Ex2]), nl, !, fail. jmExpr([Bin,_,_]) :- write('Operador binario invalido: '), write(Bin), nl, fail. %% auxiliar para verificar sintaxe do DEC %% jmDec(X) satiseito quando X e' valido como %% primeiro argumento de DEC. jmDec(D) :- jmDigito(D), !. jmDec([dec,Ex1,Ex2]) :- jmDec(Ex1), jmDigito(Ex2), !. jmDec(X) :- write('Primeiro argumento de DEC deve ser '), write('DEC ou digito: '), write(X), nl, fail. %% operadores unarios permitidos jmUnario(+). jmUnario(-). jmUnario(fat). jmUnario(sqrt). %% operadores binarios permitidos jmBinario(=). jmBinario(+). jmBinario(-). jmBinario(*). jmBinario(/). jmBinario(mod). jmBinario(^). % exponenciacao jmBinario(log). % nao e' base 10 nem base e; % a base deve ser dada explicitamente. % ex.: [log,2,4] = log de 4 na base 2. jmBinario(raiz). % ex.: [raiz,4,7] = raiz quarta de sete. jmBinario(dec). % para construcao de numeros decimais. %%------------------------------------------------------- %% Avaliacao da expressao. %% %% O predicado jmValor(+Expr,-Val) calcula o valor de %% Expr e joga o resultado em Val . %% Observe a maneira como garante-se que havera' apenas %% um sinal de igualdade: toda chamada ao predicado %% jmValor e' seguida de uma verificacao de se o resultado %% e' numerico antes de aplicar o calculo. O unico %% lugar onde isso nao e' feito e' na definicao de %% jmAvalia, que pode receber valor nao numerico. jmAvalia(Expr) :- jmValor(Expr,true). jmValor(X,X) :- jmDigito(X), !. jmValor([Un,Ex], Val) :- jmValor(Ex,Vex), Vex \== true, jmCalcUn(Un,Vex,Val). jmValor([Bin,Ex1,Ex2], Val) :- jmValor(Ex1,Vex1), Vex1 \== true, jmValor(Ex2,Vex2), Vex2 \== true, jmCalcBin(Bin,Vex1,Vex2,Val). %% jmCalcUn : executa calculos para %% operadores unarios. jmEpsilon(1.0e-14). % precisao para sqrt jmCalcUn(+,Val,Val). jmCalcUn(-,Val,Res) :- Res is 0 - Val. %% jmCalcUn agora verifica suas contas jmCalcUn(fat,Val,Res) :- Val >= 0, jmFat(Val,Res), jmFatVerif(Val,Res). jmCalcUn(sqrt,Val,Res) :- Val > 0, Res is sqrt(Val), Delta is abs(Val - Res*Res), jmEpsilon(Epsilon), Delta < Epsilon. jmFat(Z,1) :- Z == 0, !. jmFat(X,F) :- X > 0, Y is X-1, jmFat(Y,FY), F is FY*X. jmFatVerif(0,1) :- !. jmFatVerif(X,F) :- Y is X-1, FY is F/X, jmFatVerif(Y,FY). %% jmCalcBin : executa calculos para %% operadores binarios. %% jmCalcBin agora verifica suas contas jmCalcBin(+, Val1, Val2, Res) :- Val2 >= 0, Res is Val1 + Val2, Res >= Val1. jmCalcBin(+, Val1, Val2, Res) :- Val2 < 0, Res is Val1 + Val2, Res < Val1. jmCalcBin(-, Val1, Val2, Res) :- Val2 >= 0, Res is Val1 - Val2, Res =< Val1. jmCalcBin(-, Val1, Val2, Res) :- Val2 < 0, Res is Val1 - Val2, Res > Val1. jmCalcBin(*, _Val, Val2, Res) :- Val2 == 0, Res is 0. jmCalcBin(*, Val1, Val2, Res) :- Val2 =\= 0, Res is Val1 * Val2, Val1 is Res / Val2. jmCalcBin(/, Val1, Val2, Res) :- Val2 =\= 0, Res is Val1 / Val2, Val1 is Res * Val2. jmCalcBin(mod, Val1, Val2, Res) :- integer(Val1), integer(Val2), Val2 > 0, Res is Val1 mod Val2. jmCalcBin(^, Val1, Val2, Res) :- Val1 > 0, Val2 == 0, Res is 1. jmCalcBin(^, Val1, Val2, Res) :- Val1 > 0, Val2 =\= 0, Res is Val1 ^ Val2, Res > 0, Val1 is Res ^ (1/Val2). jmCalcBin(^, Val1, Val2, Res) :- Val1 == 0, Val2 > 0, Res is Val1. jmCalcBin(dec, Val1, Val2, Res) :- Res is 10*Val1 + Val2. jmCalcBin(log, Val1, Val2, Res) :- Val1 > 0, Val1 =\= 1, Val2 > 0, Res is log(Val2)/log(Val1), Delta is abs(Val2 - Val1 ^ Res), jmEpsilon(Epsilon), Delta < Epsilon. jmCalcBin(raiz,Val1, Val2, Res) :- Val2 > 0, Val1 =\= 0, Res is Val2 ^ (1/Val1), Res > 0, Delta is abs(Val2 - Res ^ Val1), jmEpsilon(Epsilon), Delta < Epsilon. jmCalcBin(=, Val1, Val2, true):- Delta is abs(Val1 - Val2), jmEpsilon(Epsilon), Delta < Epsilon. %%--------------------------------------------------- %% Verificacao dos digitos. %% O predicado jmDigitos(+Expr, Lista) e' satisfeito %% quando Lista contem todos os numeros que aparecem %% na expressao Expr , na ordem em que aparecem em %% Expr . jmDigitos(N,[N]) :- jmDigito(N), !. jmDigitos(Un,[]) :- jmUnario(Un), !. jmDigitos(Bin,[]) :- jmBinario(Bin), !. jmDigitos([],[]). jmDigitos([X|Y], Lista) :- jmDigitos(X, Lista1), jmDigitos(Y, Lista2), jmAppend(Lista1, Lista2, Lista). %%-------------------------------------------------- %% Append nao esta' pre-definido nesta versao de %% Prolog. jmAppend([],L,L). jmAppend([X|L1], L2, [X|L3]) :- jmAppend(L1,L2,L3).