Aller au menu - Aller au contenu
Inscris-toi au e-camp "Héberge ton jeu Facebook sur Azure" de Microsoft vendredi 25 mai à 13h30 !

[Atelier] Prolog

Essayez un langage totalement différent !

Pour accéder à cette section
Connectez-vous !
connexion_rpx
Page 1  2  Suivante
Auteur Message
1 visiteur sur ce sujet (1 Anonyme)
Page 1  2  Suivante
Hors ligne bluestorm # Posté le 25/07/2010 à 02:37:04
dont ask to ask
Avatar
Groupe : Anciens
Flux RSS

Salut,

Vous avez envie de profiter de l'été en restant devant votre écran ? Voici un nouvel Atelier pour le forum Autres Langages. Le concept reste similaire aux ateliers précédents (dont Cod'Art, Compression, Brainfuck, Calculatrice(s) et P'tit Langage) : il s'agit de présenter son travail, de discuter, commenter et pourquoi pas retravailler/améliorer celui des autres.

Le sujet est par contre un peu différent. Plutôt qu'un problème donné à faire dans le langage de programmation de votre choix, il s'agit maintenant d'essayer, sous toutes ses coutures, un langage de programmation donné. Ça veut dire qu'il faut commencer par l'apprendre, essayer de comprendre ses spécificités, étudier ses points forts, ses applications privilégiées, et pourquoi pas essayer d'explorer un peu les coins plus sauvages.

Pour que ce soit amusant, j'ai essayé de choisir un langage radicalement différent de ce que vous connaissez déjà, qui demande une nouvelle façon de penser la programmation.

Le langage de cet atelier-ci est Prolog. Si ça marche, il y en aura d'autres, pour d'autres langages.

Qu'est-ce que Prolog ?



Prolog est un langage de programmation logique. De façon grossière, un programme Prolog est un ensemble de connaissances et de règle de déductions que le programmeur donne au langage. Le programmeur peut ensuite poser des questions, auxquels le programme essaiera de répondre en utilisant les informations fournies.

Plus exactement, on donne à Prolog des sortes de "règles de raisonnement", et on lui pose ensuite des questions. Il doit, en combinant les règles qu'on lui a donné, produire une réponse.

Voici un exemple d'informations que l'on pourrait vouloir fournir à Prolog (ce n'est pas du vrai code prolog, juste une description en français) :

Citation
Une personne X (les majuscules désignent des variables) est frère ou sœur de Y si X est différent de Y, et s'il existe une personne Z qui est parent à la fois de X et de Y.

X est parent de Y si X est père de Y, ou si X est mère de Y.

Tania est la mère de Sophie.
Thomas est le père de Sophie.
Thomas est le père d'Émilie.
Tania est la mère de Camille.
Michel est le père de Thomas.


On peut alors poser la question suivante : Citation
Quels sont les frères et soeurs de Sophie ?


Et prolog fournira le bon résultat, à savoir tout ce qu'il peut déduire des informations qu'on lui a données : Citation
Émilie et Camille.


Voici le code Prolog correspondant : Code : Autre
1
2
3
4
5
6
7
8
frère_ou_sœur(X,Y) :- parent(Z,X), parent(Z,Y), X \= Y.
parent(X,Y) :- père(X,Y).
parent(X,Y) :- mère(X,Y).
mère(tania, sophie).
père(thomas, sophie).
père(thomas, émilie).
mère(tania, camille).
père(michel, thomas).


Et pour interroger Prolog (la première ligne est la question, avec la réponse ensuite) : Code : Autre
1
2
3
?- frère_ou_sœur(sophie, X).
X = émilie ;
X = camille .


Suivant ce principe, on peut en fait écrire des programmes relativement élaborés. Historiquement, la programmation logique est appréciée dans tous les domaines où il faut "faire des déductions" : certaines bases de données, de systèmes d'intelligence artificielle, etc.

Que faire pour cet atelier ?



La meilleure chose à faire est de commencer par trouver un tutoriel pour apprendre le langage Prolog. Cela nécessitera certainement d'installer une implémentation du langage sur votre machine, pour pouvoir tester du code.

Un bon début peut être l'article wikipédia Prolog, qui donne une présentation un peu moins superficielle du langage, et des liens vers des cours de Prolog. Prolog étant un langage créé en France, il devrait être plutôt facile de trouver de la documentation francophone sur le sujet avec un peu de recherche.

Une fois que vous commencez à comprendre un peu le langage, vous devriez essayer de coder de petits exercices dans ce langage. S'ils vous paraissent intéressant, n'hésitez pas à les poster ici. Les questions sur le langage sont bienvenues si vous butez sur un point particulier, mais essayez quand même de faire des efforts de compréhension de votre côté.

Et surtout, rappelez-vous : le but de cet atelier est de présenter quelque chose de très, très différent de ce que nous connaissons. Si vous vous sentez perdu au départ, c'est normal, et même plutôt bon signe : ça veut dire que vous apprendrez de nombreuses choses avec cet atelier.

J'essaierai de mettre à jour ce premier post de temps en temps pour y mettre des informations utiles, des liens pertinents, et pourquoi pas des idées de choses à essayer en Prolog. Mais je vous rappelle que ceci est un atelier, et tout le monde est invité à participer, proposer des solutions ou commenter celles des autres. Nous sommes tous à la fois organisateurs, juges, conseillers et participants !


Liens utiles



- une longue série d'exercices avec corrections
- quelques cours en français sur Developpez.com
Édité le 25/07/2010 à 16:00:05 par bluestorm
 
Publicité # Posté le 25/07/2010 à 02:37:04

Hors ligne EMC1 # Posté le 25/07/2010 à 12:27:17
Avatar

Une série d'exercices (avec correction), qui vont de basiques à élaborés pour ceux qui en chercheraient.

A propos de la ressource en Français, les quelques cours que j'ai lus étaient peu détaillés bien qu'intéressant.
Je vais sans doute m'y remettre à l'occasion de cet atelier, en cherchant en anglais cette fois.
Hors ligne gorgonite # Posté le 25/07/2010 à 13:10:23
Avatar

on avait tenté de faire une série de cours ici : http://prolog.developpez.com/cours/
Hors ligne bluestorm # Posté le 25/07/2010 à 15:57:35
dont ask to ask
Avatar
Groupe : Anciens
Flux RSS

EMC1 > merci pour les exercices, j'essaierai de regarder !


J'ai fait quelques expérimentations avec Prolog ce matin : j'ai joué avec les listes.

Listes en prolog



En prolog, une liste est :
  • soit la liste vide, []
  • soit une liste constituée d'un élément de tête H, et du reste de la liste T. Cette liste est notée [H|T].


Cette notation est pratique quand on accède seulement à un élément de la liste, mais un peu indigeste quand on veut des listes à plusieurs éléments. Par exemple, la liste dont les éléments sont 1 et 2 est [1 | [2|[]] ]. Il existe donc les sucres syntaxiques [H1,H2,..] (par exemple [1,2,3] pour la liste de ces trois éléments) et [H1,H2,..|T] (la liste commençant par les H1, etc., et dont le reste est la liste T). Cette syntaxe a d'ailleurs été reprise par le langage Erlang.

Quelques exemples de fonctions sur les listes



Les fonctions présentées ici sont juste des essais. Il y a sans doute moyen de faire mieux ou différemment, et tous les commentaires sont bienvenus.

Vérification des listes

On peut écrire des choses qui ressemblent à des listes mais n'en sont pas. Par exemple [1|2] n'est pas une liste, car la queue, 2, n'est pas une liste; attention, il ne s'agit pas de la liste [1,2], qui est représentée par [1| [2|[]] ].

J'ai donc écrit une fonction list pour vérifier qu'une valeur est bien une liste :

  • la liste vide est bien une liste
  • [H|T] est une liste si T est bien une liste


Code : Autre
1
2
list([]).
list([H|T]) :- list(T).


On peut l'interroger :

Code : Autre
1
2
3
4
5
6
7
8
?- list([]).
true.

?- list([1,2,3]).
true.

?- list([1|2]).
false.


On peut aussi lui poser la question qui tue : "Quelles sont les listes X ?"

Code : Autre
1
2
3
4
5
6
?- list(X).
X = [] ;
X = [_G266] ;
X = [_G266, _G269] ;
X = [_G266, _G269, _G272] ;
...


Prolog renvoie une suite de réponse, et à chaque fois le mode interactif me demande si je veux m'arrêter là, ou avoir la réponse suivante. Il commence par proposer la liste vide, puis la liste [_G266]. _G266 désigne en fait un nom de variable interne au mode interactif. Comme c'est une variable, cela veut dire que _G266 peut prendre n'importe quelle valeur : [1] est une liste, [[]] est une liste, etc. Il a donc désigné toutes les listes à un seul élément. Il continue ensuite à me proposer toutes les listes à deux éléments, à trois éléments, etc.

Appartenance à une liste

J'ai ensuite écrit la fonction qui teste si un élément appartient à une liste :

  • H appartient à [H|T] (comme la variable T peut prendre n'importe quelle valeur, ça désigne toutes les listes qui commencent par H)
  • sinon, X appartient à [H|T] si X appartient à T.


Code : Autre
1
2
mem(H, [H|T]).
mem(X, [H|T]) :- mem(X, T).


Questions classiques : Code : Autre
1
2
3
4
5
?- mem(2, [1,2,3]).
true .

?- mem(5, [1,2,3]).
false.


Questions plus originales :

  • à quelles listes appartient 2 ?


Code : Autre
1
2
3
4
5
6
7
?- mem(2, X).
  X = [2|_G269] ;
  X = [_G268, 2|_G272] ;
  X = [_G268, _G271, 2|_G275] ;
  X = [_G268, _G271, _G274, 2|_G278]...

(Il énumère toutes les listes qui contiennent 2 en première position, deuxième position..)


  • quels éléments appartiennent à [1,2,3] ?


Code : Autre
1
2
3
4
?- mem(X, [1,2,3]).
  X = 1 ;
  X = 2 ;
  X = 3 .


Autres fonctions sur les listes

J'ai aussi écrit la fonction qui concatène deux listes (les met bout à bout). Pour laisser un peu de suspense, et que les autres puissent jouer aussi (c'est moins drôle quand on a déjà vu la solution), je ne la posterai pas ici. Je vous invite à essayer aussi, et à regarder ce qu'on peut obtenir en lui posant différentes questions. On peut par exemple la forcer à répondre aux questions suivantes :

  • la liste X est-elle préfixe de la liste Y ?
  • enlever la liste X du début de Y (par exemple enlever [1,2] à [1,2,3,4] pour obtenir [3,4])
  • enlever la liste X de la fin de Y


On peut aussi s'en servir pour coder une fonction qui, par exemple, trouve toutes les positions où l'élément X apparaît dans la liste Y.
 
Hors ligne nax # Posté le 25/07/2010 à 16:13:16
Avatar

Ville : Brest
Pays : France métropolitaine

Tiens c'est marrant je me suis lancer ce matin et j'ai coder exactement la même chose que toi bluestorm :
Code : Console
find(X,[X|_]).
find(X,[_|L]) :- find(X,L).

Qui permet a la fois de savoir si un élément appartient à une liste ?- find(a,[a,b,c]).
Tt de lister tout les éléments d'une liste : ?- find(X,[a,b,c]).
(Voir de trouver les listes auquelles appartient un élément bien que ça semble moins intéressant.)
Mais par contre j'ai un warning que je ne comprend pas bien :
Citation
Clauses of find/2 are not together in the source-file


Juste un truc déroutant au début : pour tester un code il faut l'enregistrer dans un fichier et l'appeler avec par exemple
Code : Console
$ swipl -f fichier.pl

si l'on travaille avec SWI Prolog

ou bien
Code : Console
$ gprolog
| ?- ['fichier.pl']

Avec gprolog.

Je pensais au début pouvoir entrer les prédicats dans l'interpréteur directement à partir du shell mais on obtient ce genre d'erreur :
"uncaught exception: error(existence_error)"

Par contre j'ai plus de mal avec la fonction de concaténation. J'ai essayer quelque chose comme :

Code : Console
concat([H|T],L2,X) :- concat(T,L2,[H|X]).
concat([],L2,X) :- concat(L2,[],X).

Mais ça ne mène à rien.
Édité le 25/07/2010 à 17:18:21 par nax

Projets : SDZAPI | Github
Tuto : Les captchas
 
Hors ligne spider-mario # Posté le 25/07/2010 à 16:50:09
Avatar

Ville : Montigny-lès-cormeilles
Pays : France métropolitaine
Études : INSA Rouen

C'est possible, en les encadrant avec assert(...).

Note : consult('bla.pl'). permet aussi de les charger depuis un fichier.
Connecté rushia # Posté le 25/07/2010 à 17:31:04
Avatar

Études : Supélec

Pour la fonction de concaténation, j'ai fait ça :

Code : Autre
1
2
concat([],L,L).
concat([H|T],L,[H|Tc]) :- concat(T,L,Tc).


Ça a l'air de marcher :

Code : Autre
1
2
?- concat([1,2,3,4],[5,6,7,8,9],X).
X = [1, 2, 3, 4, 5, 6, 7, 8, 9].



Sinon, j'ai essayer de travailler un peu avec des arbres binaires :

Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
max(X,Y,M) :-
   X=<Y,
   M=Y.
max(X,Y,M) :-
   X>Y,
   M=X.
hauteur(0,0).
hauteur(1,0).
hauteur(n(X,Y),L) :-
   hauteur(X,Lx),
   hauteur(Y,Ly),
   max(Lx,Ly,M), L is M+1.

Résultat sur l'arbre :
Code : Autre
1
2
3
4
5
.    /   \
    /\   /\
   1 /\ 0  1
    /\ 0
   1  0

Code : Autre
1
2
?- hauteur(n(n(1,n(n(1,0),0)),n(0,1)),L).
L = 4 .


Par contre, je me suis limité à un arbre ne pouvant "stocker" que des 0 et des 1 parce que je n'ai pas trouvé de moyen de donner une longueur 0 à un arbre n'étant en réalité qu'une feuille pour n'importe quel nombre.
Edit : Après réflexion, je vois la solution suivante, un peu lourde :
On désigne une feuille par f(x) ou x est ce que l'on veut.
Secret (cliquez pour afficher)
Code : Autre
1
2
3
4
5
6
7
8
9
10
11
max(X,Y,M) :-
   X=<Y,
   M=Y.
max(X,Y,M) :-
   X>Y,
   M=X.
hauteur(f(_),0).
hauteur(n(X,Y),L) :-
   hauteur(X,Lx),
   hauteur(Y,Ly),
   max(Lx,Ly,M), L is M+1.

Code : Autre
1
2
?- hauteur(n(n(f(1),n(n(f(3.2),f(5)),f(4.1))),n(f(0.1),f(3))),L).
L = 4 .

Édité le 25/07/2010 à 18:39:55 par rushia
 
Hors ligne nax # Posté le 25/07/2010 à 18:21:08
Avatar

Ville : Brest
Pays : France métropolitaine

Oui ça marche mon erreur vient du fait qu'il faut réfléchir "à l'envers". On ne construit pas la liste, on doit en fait décrire ses propriétés.

Une chose amusante avec ce langage est qu'une fonction peut servir à plusieurs actions. Par exemple la fonction
Code : Console
remove_at(X,[X|R],1,R).
remove_at(X,[H|T],N,[H|R]) :- K is N - 1, remove_at(X,T,K,R).
?- remove_at(X,[a,b,c,d],2,R).
X = b,
R = [a, c, d] .

Permet d'insérer un élément dans une liste en changeant les paramètres :

Code : Console
remove_at(c,X,3,[a,b,d]).
X = [a, b, c, d] .


Et un peu de calcul avec une fonction factoriel :

Code : Console
fact(0,1).
fact(N,X) :- N > 0, K is N - 1, fact(K,X2), X is N*X2.
Édité le 25/07/2010 à 18:25:35 par nax

Projets : SDZAPI | Github
Tuto : Les captchas
 
Hors ligne Abronsius # Posté le 25/07/2010 à 19:41:36
Avatar

Salut,

Je vais me mettre au Prolog pour cet atelier et j'ai trouver un cours vraiment très bien fait:
http://perso.crans.org/martin/Doc/Cour [...] %20PROLOG.pdf

« Dis, Cortex, tu veux faire quoi cette nuit ?»
« La même chose que chaque nuit, Minus. Tenter de conquérir le monde »
 
Hors ligne gorgonite # Posté le 25/07/2010 à 20:14:06
Avatar

allez, je vais contribuer un peu... :)


Le solveur Prolog consiste à parcourir l'arbre des solutions en profondeur d'abord, et en sélectionnant les clauses de gauche à droite

Ainsi, reprenons l'exemple de Bluestorm
Code : Autre
1
2
parent(X,Y) :- père(X,Y).
parent(X,Y) :- mère(X,Y).


Introduisons un peu de "récursivité" en cherchant des ancêtres
Code : Autre
1
2
ancetre(X,Z) :- ancetre(Y,Z),parent(X,Y).
ancetre(X,Z) :- parent(X,Y),ancetre(Y,Z).


La première ligne va partir envoyer le solveur standard dans une boucle infinie puisqu'il va chercher à résoudre ancetre:-ancetre avant de s'intéresser à parent qui est en seconde position

Donc gardez à l'esprit que l'ordre des termes peut grandement influer sur la vitesse de l'analyse (surtout si des ! (cut) sont introduits)

mais ne despérez pas, on peut s'en sortir si le problème ne correspond pas à une recherche en profondeur d'abord, via la méta-interprétation (changer la stratégie de l'évaluation) ou via la mémoïzation (se souvenir des cas déjà étudiés)
Hors ligne EMC1 # Posté le 25/07/2010 à 21:11:46
Avatar

Notons quand même qu'il n'y a pas de fonction en Prolog, on parle de prédicat, utilisés en essayant de prouver la véracité d'un but.

Par convention on donne l'usage d'un prédicat en écrivant ses termes + , - ou ? suivant qu'il doivent respectivement être donnés en entrée (une valeur), attendue en sortie (une variable libre) ou l'un des deux au choix.
Par exemple : between(+,+,?).
Hors ligne gnomnain # Posté le 25/07/2010 à 23:45:59
Blblbl !
Avatar
Groupe : Anciens

Citation : nax
Oui ça marche mon erreur vient du fait qu'il faut réfléchir "à l'envers". On ne construit pas la liste, on doit en fait décrire ses propriétés.

Une chose amusante avec ce langage est qu'une fonction peut servir à plusieurs actions. Par exemple la fonction [...]


Oui, j'ai utilisé ça et c'est marrant :
Code : Autre
1
2
3
4
5
6
7
8
9
10
11
prefix([X|XS],[X|YS]) :- prefix(XS,YS).
prefix(XS,[]).

length2([],0).
length2([X|Y],N) :- length(Y,M), N is M+1.

concat([],YS,YS).
concat([X|XS],YS,[X|ZS]) :- concat(XS,YS,ZS).

take(L,N,P) :- length2(P,N), prefix(L,P).
drop(L,N,S) :- length2(U,N), concat(U,S,L).


Je pourrais encore exprimer prefix avec concat, mais je l'ai pas fait (les prédicats take(L,N,P) et drop(L,N,S) veulent dire "P est la liste composée des N premiers éléments de N" et "S est ce qui reste quand on a enlevé N éléments". Par contre, je ne sais pas trop ce que ça fait niveau efficacité. J'imagine que comme je l'ai écrit, il crée un modèle pour la liste avec des trous partout (il connait son nombre d'éléménts), et ensuite il unifie avec le prédicat "prefix". Si c'est ça, ça fait du O(N) je crois.

Image utilisateur
Haskell - Learn You a Haskell - Real World Haskell - xmonad - OCaml
Apprenez Haskell ! - #ircduzero
<colbseton> Serialk: tu cherches vraiment des liens logiques dans tout ce que je raconte ?
 
Hors ligne Cyprien_ # Posté le 25/07/2010 à 23:47:02
Avatar

Petite remarque en passant : non seulement l'ordre des prédicats peut compter, mais également l'ordre de propositions séparées par une virgule. J'ai été confronté à ça en voulant extraire le maximum d'une liste.
Je m'explique :

Code : Autre
1
2
3
4
5
6
7
mem(H, [H|_]).
mem(X, [_|T]) :- mem(X, T).

greater(_, []).
greater(X, [H|T]) :- X >= H, greater(X, T).

max(X, L) :- greater(X,L), mem(X, L).


Ce code refuse de fonctionner, on reçoit l'erreur :
Code : Console
?- max(X, [1,2,3]).
ERROR: >=/2: Arguments are not sufficiently instantiated


En revanche avec :
Code : Autre
1
max(X, L) :- mem(X, L), greater(X,L).


tout fonctionne bien :

Code : Console
?- max(X, [1,2,3]).
X = 3 ;


Quelqu'un pourrait-il m'expliquer cela ?
D'ailleurs, même avec le deuxième code, on retombe toujours sur la même erreur avec certaines questions :
Code : Console
?- max(3, L).
L = [3] ;
ERROR: >=/2: Arguments are not sufficiently instantiated
Hors ligne bluestorm # Posté le 25/07/2010 à 23:53:38
dont ask to ask
Avatar
Groupe : Anciens
Flux RSS

C'est un problème au niveau de l'opérateur >= qui n'est pas un opérateur logique. Il ne fonctionne que quand on lui donne deux valeurs, et pas des variables d'unification. Selon l'ordre dans lequel tu mets tes clauses et les arguments que tu donnes, si tu te retrouves au moment où tu testes >= à avoir déjà donné une valeur aux deux variables, tout va bien, par contre si ce n'est pas encore fait ça foire.

Les opérateurs arithmétiques primitifs sont aussi affectés par ce problème, ce qui les rend assez désagréables à utiliser. Je trouve plus amusant de recoder soit-même avec un encodage plus respectueux des aspects logique (mais moins performant).

D'ailleurs, si je me suis attaqué aux listes, c'est aussi parce que c'est une structure de donnée correspondant, d'une certaine manière, aux nombres en notation unaire. La fonction "somme" sur les nombres zero/succ est identique à la fonction "append" des listes.


Citation : gnomnain
Oui ça marche mon erreur vient du fait qu'il faut réfléchir "à l'envers". On ne construit pas la liste, on doit en fait décrire ses propriétés.

Une chose amusante avec ce langage est qu'une fonction peut servir à plusieurs actions.


Malheureusement, et c'est une des choses qui me déçoit avec prolog, ce n'est que très partiellement vrai. La programmation logique a une image de "programmation déclarative par excellence", on décrit les propriétés etc., mais en fait, des langages que je connais, c'est celui qui demande le plus d'avoir en tête les règles opérationnelles du langage. C'est un peu comme quand on essaie d'évaluer les performances d'un code Haskell, mais là ça joue sur la correction du code (si je m'en sers comme ça, est-ce qu'il va donner le résultat, ou boucler à l'infini ?).

Ceci dit, cette sensation est peut-être causée par mon manque d'habitude du paradigme.
 
Hors ligne Cyprien_ # Posté le 26/07/2010 à 00:15:08
Avatar

Citation : bluestorm
Je trouve plus amusant de recoder soit-même avec un encodage plus respectueux des aspects logique (mais moins performant).


Dans ce cas précis, tu recoderais ça comment ?

En attendant, j'ai tenté de recoder un tri par sélection, avant de passer à des tris plus compliqués... Je ne sais pas si ma solution est vraiment élégante, mais c'est celle qui m'est venue le plus naturellement.

Code : Autre
1
2
3
4
5
6
7
8
9
less(_, []).
less(X, [H|T]) :- X =< H, less(X, T).
min(X, L) :- mem(X, L), less(X, L).

remove(X, [X|T], T).
remove(X, [H|T], [H|S]) :- remove(X, T, S).

selectionSort([], []).
selectionSort(L, [X|S]) :- min(X, L), remove(X, L, T), selectionSort(T, S).


Exemple :

Code : Console
selectionSort([3,1,2], L).
L = [1, 2, 3] .

?- selectionSort([3,1,2,17,2], L).
L = [1, 2, 2, 3, 17] .


EDIT : Désolé EMC1, ce n'était effectivement pas un tri à bulles comme je l'avais écrit à l'origine, mais je m'y attèle à l'heure actuelle ;) .
Édité le 26/07/2010 à 00:29:18 par Cyprien_
Hors ligne EMC1 # Posté le 26/07/2010 à 00:23:19
Avatar

Je ne pense pas que ce soit recodable en pur Prolog, comme toute manipulation de nombres.

Cyprien_, ton tri s'apparente plus selon moi à un tri par sélection.

Je vois que tu m'as grillé...
Édité le 26/07/2010 à 00:24:09 par EMC1
Hors ligne Cyprien_ # Posté le 26/07/2010 à 00:47:12
Avatar

Apparemment c'est faisable :

Code : Autre
1
2
3
4
5
bubble([X], [X]).
bubble([X ,Y | T], [Y|L]) :- X > Y, bubble([X | T], L).
bubble([X, Y | T], [X | L]) :- bubble([Y | T], L).

bubbleSort(L1, L) :- bubble(L1, L2), (L1 = L2, L2 = L ; bubbleSort(L2, L)), !.


Cette fois je ne crois pas me tromper en affirmant que c'est bien un tri à bulles :) .

Exemple :

Code : Console
?- bubbleSort([1,3,2], L).
L = [1, 2, 3].

?- bubbleSort([4, 2, 5, 3, 1], L).
L = [1, 2, 3, 4, 5].
Édité le 26/07/2010 à 00:48:09 par Cyprien_
Hors ligne Orwell # Posté le 26/07/2010 à 01:21:42
tahc nu sap tse'n icec
Avatar
Validateurs

Ville : Paris
Pays : France métropolitaine
Études : FSA ULB

Pour ceux que ça intéresse, j'ai retrouvé l'énoncé d'un gros exercice en Prolog (tiré de mon cours de logique et IA à la fac :-° ) :D

Voici les questions:
Secret (cliquez pour afficher)
On souhaite générer, en Prolog, la gamme d’assemblage d’un produit, constitué d’un ensemble de composants complexes. Chaque composant est modélisé comme une union de parallélépipèdes rectangles ayant les arrêtes parallèles aux axes (ces différentes parties de composants n'étant pas dissociables). Il s'agit donc de déterminer l'ordre (et les directions respectives) dans lequel on doit assembler chacun de ses composants complexes pour construire le produit complet.

L'ajout d'un composant complexe au produit à assembler se fait par translation latérale dans une des 6 directions de l'espace. (Note: En pratique, le robot manipulateur effectue une rotation du produit assemblé et présente toujours le nouveau composant dans la même direction). Il faut donc veiller à ne pas provoquer, au cours de la translation, de collision entre le composant à placer et les composants déjà assemblés.

La démarche adoptée pour générer la séquence d'assemblage est de partir du produit assemblé, et de déterminer l'ordre de désassemblage de chacun de ses composants. Il suffira ensuite d'inverser la séquence obtenue.

On demande:
  • 1.A de définir des prédicats qui permettent de représenter un produit et ses composants complexes.
  • 1.B de définir un prédicat audessus(A, B) qui détermine que le composant complexe B est au-dessus du composant complexe A (idem pour en-dessous, à gauche, à droite, devant, derrière).
  • 1.C de définir un prédicat searchobstacles(X, L, dir) qui donne la liste des composants qui empêchent le retrait du composant X dans la direction dir. Et, de générer pour tous les composants, X, la base de données des faits de type obstacles(X, dir , liste).
  • 1.D de générer un désassemblage possible.
  • 1.E de générer tous les désassemblages possible.
  • 1.F De choisir la meilleure gamme d’assemblage selon le critère de minimisation du nombre de changement de direction d’assemblage.

Et pour ceux qui ont besoin d'un coup de pouce, voici déjà des éléments de réponses aux 2 premières questions:
Secret (cliquez pour afficher)
Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
part(a, pa1, pointmin(pa1x1, pa1y1, pa1z1), pointmax(pa1x2, pa1y2, pa1z2)).
part(a, pa2, pointmin(pa2x1, pa2y1, pa2z1), pointmax(pa2x2, pa2y2, pa2z2)).
part(b, pb1, pointmin(pb1x1, pb1y1, pb1z1), pointmax(pb1x2, pb1y2, pb1z2)).

assemblage([a, b]).

existe(X) :- assemblage(L), membre(X, L).

auDessus(A, B) :- 
  part(A, PA, pointmin(Axmin, Aymin, Azmin), pointmax(Axmax, Aymax, Azmax)),
  part(B, PB, pointmin(Bxmin, Bymin, Bzmin), pointmax(Bxmax, Bymax, Bzmax)),
  A\=B, existe(A), existe(B),
  Axmin < Bxmax,
  Bxmin < Axmax,
  Aymin < Bymax,
  Bymin < Aymax,
  Bzmax =< Azmin.

Et un assemblage cadeau pour jouer avec et tenter de le désassembler: :D
Secret (cliquez pour afficher)
Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
part(a,pa1,pointmin(0,0,0),pointmax(1,1,1)).
part(a,pa2,pointmin(0,0,1),pointmax(1,1,2)).
part(b,pb1,pointmin(1,1,0),pointmax(2,2,1)).
part(c,pc1,pointmin(1,0,0),pointmax(2,1,1)).
part(d,pd1,pointmin(3,1,0),pointmax(4,2,1)).
part(d,pd2,pointmin(4,1,0),pointmax(5,2,1)).
part(e,pe1,pointmin(5,1,0),pointmax(6,2,1)).
part(e,pe2,pointmin(5,0,0),pointmax(6,1,1)).
part(e,pe3,pointmin(5,0,1),pointmax(6,1,2)).
part(f,pf1,pointmin(0,2,0),pointmax(1,3,1)).
part(f,pf2,pointmin(1,2,0),pointmax(2,3,1)).
part(f,pf3,pointmin(2,2,0),pointmax(3,3,1)).

assemblage([a,b,c,d,e,f]).

Amusez-vous bien :p
Édité le 26/07/2010 à 02:16:39 par Orwell

Tuto en beta-test : Entity Framework
Mon appli Windows Phone : Deezy
 
Hors ligne bluestorm # Posté le 26/07/2010 à 08:24:40
dont ask to ask
Avatar
Groupe : Anciens
Flux RSS

Citation : Cyprien_
Citation : bluestorm
Je trouve plus amusant de recoder soit-même avec un encodage plus respectueux des aspects logique (mais moins performant).


Dans ce cas précis, tu recoderais ça comment ?


Il s'agit d'utiliser des entiers logiques plutôt que des entiers machines. Tout encodage sous forme d'une structure de donnée supportant l'unification conviendrait, j'en utilise un particulièrement simple, et qui fera plaisir au prépiste qui sommeille en toi :

Code : Autre
1
2
3
4
5
isnat(z).
isnat(s(N)) :- isnat(N).

less(z, N).
less(s(M), s(N)) :- less(M, N).


Ensuite c'est facile :
Code : Autre
1
2
3
4
5
mem(H, [H|_]).
mem(X, [_|T]) :- mem(X, T).

greater(_, []).
greater(X, [H|T]) :- less(H, X), greater(X, T).


J'ai trois versions de "max". Elles fonctionnent toutes dans le sens max(X, [z, s(z), s(s(z))]), mais leur comportement sur max(s(s(z)), L) est intéressant.

Code : Autre
1
2
3
4
5
max1(X, L) :- greater(X,L), mem(X, L).

?- max1(s(s(z)), L).
Action (h for help) ? abort
% Execution Aborted

(Boucle à l'infini)


Code : Autre
1
2
3
4
5
6
7
8
max2(X, L) :- mem(X, L), greater(X,L).

?- max2(s(s(z)), L).
L = [s(s(z))] ;
L = [s(s(z)), z] ;
L = [s(s(z)), z, z] ;
L = [s(s(z)), z, z, z] ;
L = [s(s(z)), z, z, z, z] ...


Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
greater2(_, []).
greater2(X, [H|T]) :- greater2(X, T), less(H, X).
max3(X, L) :- mem(X, L), greater2(X,L).

?- max3(s(s(z)), L).
L = [s(s(z))] ;
L = [s(s(z)), z] ;
L = [s(s(z)), s(z)] ;
L = [s(s(z)), s(s(z))] ;
L = [s(s(z)), z, z] ;
L = [s(s(z)), s(z), z] ;
L = [s(s(z)), s(s(z)), z] ;
L = [s(s(z)), z, s(z)] ;
L = [s(s(z)), s(z), s(z)] ;
L = [s(s(z)), s(s(z)), s(z)] ...


Je n'ai pas encore vraiment réfléchi à pourquoi ces fonctions se comportent comme cela; il faudrait dessiner l'arbre de résolution, mais je n'ai pas beaucoup de temps en ce moment.
Édité le 26/07/2010 à 08:27:28 par bluestorm
 
Hors ligne Einstein++ # Posté le 26/07/2010 à 08:42:56
It's not a bug, it's a feature
Avatar

Études : TELECOM SudParis

Le langage est très intéressant, merci pour cet atelier.
Voici mon tri insertion :
Code : Autre
1
2
3
4
5
6
insert(X, [], [X|[]]).
insert(X, [H|T], [X|[H|T]]) :- X =< H.
insert(X, [H|T], [H|Z]) :- insert(X, T, Z).

isort([], []).
isort([H|T], L) :- isort(T, Y), insert(H, Y, L).


exemple :
Code : Autre
1
2
3
4
5
?- isort([5], L).
L = [5] .

?- isort([5, 4, 2, 3, 1, 5, 0], L).
L = [0, 1, 2, 3, 4, 5, 5] .
Édité le 26/07/2010 à 09:27:22 par Einstein++
 
Hors ligne Cygal # Posté le 26/07/2010 à 09:31:35
X-No-Archive: yes
Avatar
Flux RSS

Citation : nax
Mais par contre j'ai un warning que je ne comprend pas bien :
Citation
Clauses of find/2 are not together in the source-file


Ça veut dire que deux clauses d'une même fonction ne se suivent pas, par exemple si tu as laissé un "find" d'arité 2 traîner ailleurs dans ton fichier.
Hors ligne Cyprien_ # Posté le 26/07/2010 à 10:17:55
Avatar

J'ai dessiné la trace lors de l'appel de ta première fonction de maximum, bluestorm, et en fait, tout simplement, Prolog va d'abord tester toutes les possibilités avec des listes ne contenant que zéro, parce que zéro est inférieur à s(s(s(z))).
Tu peux le mettre en évidence par :

Code : Autre
1
2
3
4
5
6
?- greater(s(s(s(z))), L).
L = [] ;
L = [z] ;
L = [z, z] ;
L = [z, z, z] ;
...


EDIT : à noter que selon ce qu'on veut faire, certaines versions sont meilleures :

max1, ou bien max4 obtenu en inversant les prédicats mem et greater dans max3, bouclent à l'infini après la première solution.
Et max>4 est le "meilleur" si on veut obtenir toutes les listes dont N est le maximum : avec max3 on n'obtient pas toutes les permutations (ou plutôt on les obtiendra quand on aura fait toutes les listes commençant par N, donc jamais :p ).


EDIT² : je me suis un peu intéressé à la possibilité de faire des AST, déjà simplement pour des expressions numériques.
Le code que je montre s'appuie sur les entiers naturels redéfinis par bluestorm (et n'a que 2 opérations), mais avec des entiers naturels ça marche aussi bien, le seul souci étant de différencier une valeur numérique d'un arbre...

Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
isNat(z).
isNat(s(N)) :- isNat(N).

op(+).
op(*).

add(N, z, N).
add(N, s(X), s(Y)) :- add(N, X, Y).

mul(_, z, z).
mul(N, s(X), Y) :- add(N, Z, Y), mul(N, X, Z), !.

eval_op(A, +, B, X) :- add(A, B, X).
eval_op(A, *, B, X) :- mul(A, B, X).

ast(N) :- isNat(N).
ast((A, OP, B)) :- ast(A), ast(B), op(OP).

eval(N, N) :- isNat(N).
eval((A, OP, B), X) :-
    ast((A, OP, B)),
    eval(A, N),
    eval(B, M),
    eval_op(N, OP, M, X).


Exemple :

Code : Console
?-  eval(((s(z), +, s(s(z))), *, s(s(z))), N).
N = s(s(s(s(s(s(z)))))) ;
false.


Pour le fun, j'ai essayé de demander ceci à Prolog :

Code : Console
?- eval(A, s(s(z))).
A = s(s(z)) ;
A = (z, (+), s(s(z))) ;
(boucle infinie...)


Dommage que ça ne marche pas, quelqu'un saurait coder tout ça de meilleure manière, pour que Prolog arrive à répondre ? :p

EDIT3: pour plus de commodité lors de l'entrée :

Code : Autre
1
2
toNat(0, z).
toNat(X, s(N)) :- X > 0, Y is X-1, toNat(Y, N).


L'opération inverse ne marche pas par contre (ie. revenir à des valeurs numériques) :

Code : Console
toNat(1, U), toNat(2, D), toNat(3, T), eval(((U,+,D),*,T), X).
U = s(z),
D = s(s(z)),
T = s(s(s(z))),
X = s(s(s(s(s(s(s(s(s(z)))))))))
Édité le 26/07/2010 à 11:29:20 par Cyprien_
Hors ligne EMC1 # Posté le 26/07/2010 à 12:23:17
Avatar

bluestorm :

Tu peux utiliser le très utile prédicat trace pour suivre la résolution :

Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2 ?- 
|    trace.
Unknown message: query(yes)
[trace] 2 ?- max3(s(s(z)), L).
   Call: (7) max3(s(s(z)), _G3167) ? creep
   Call: (8) mem(s(s(z)), _G3167) ? creep
   Exit: (8) mem(s(s(z)), [s(s(z))|_G3236]) ? creep
   Call: (8) greater2(s(s(z)), [s(s(z))|_G3236]) ? creep
   Call: (9) greater2(s(s(z)), _G3236) ? creep
   Exit: (9) greater2(s(s(z)), []) ? creep
   Call: (9) less(s(s(z)), s(s(z))) ? creep
   Call: (10) less(s(z), s(z)) ? creep
   Call: (11) less(z, z) ? creep
   Exit: (11) less(z, z) ? creep
   Exit: (10) less(s(z), s(z)) ? creep
   Exit: (9) less(s(s(z)), s(s(z))) ? creep
   Exit: (8) greater2(s(s(z)), [s(s(z))]) ? creep
   Exit: (7) max3(s(s(z)), [s(s(z))]) ? creep
L = [s(s(z))]


Cyprien_ :

Pour revenir à des valeurs numériques :

Code : Autre
1
2
natTo(0,z).
natTo(X,s(L)):- natTo(Y,L), X is Y+1.
Édité le 26/07/2010 à 12:29:20 par EMC1
Hors ligne Orwell # Posté le 26/07/2010 à 13:42:20
tahc nu sap tse'n icec
Avatar
Validateurs

Ville : Paris
Pays : France métropolitaine
Études : FSA ULB

:euh: Je ne comprends pas trop ce que vous essayez de faire avec des requêtes comme celles-ci:

Citation : bluestorm
J'ai trois versions de "max". Elles fonctionnent toutes dans le sens max(X, [z, s(z), s(s(z))]), mais leur comportement sur max(s(s(z)), L) est intéressant.

Code : Console
max1(X, L) :- greater(X,L), mem(X, L).

?- max1(s(s(z)), L).
Action (h for help) ? abort
% Execution Aborted

(Boucle à l'infini)

et
Citation : Cyprien_
Pour le fun, j'ai essayé de demander ceci à Prolog :
Code : Console
?- eval(A, s(s(z))).
A = s(s(z)) ;
A = (z, (+), s(s(z))) ;
(boucle infinie...)

Dommage que ça ne marche pas, quelqu'un saurait coder tout ça de meilleure manière, pour que Prolog arrive à répondre ? :p


Dans le premier cas, la question posée à Prolog est "donne-moi toutes les listes possibles dont s(s(z)) est le maximum", et dans le second: "donne-moi tous les arbres possibles ayant s(s(z) comme évaluation". C'était juste pour voir que oui, Prolog donne une infinité de résultats, ou vous vous attendiez à autre chose? :euh:

Tuto en beta-test : Entity Framework
Mon appli Windows Phone : Deezy
 
Hors ligne Cyprien_ # Posté le 26/07/2010 à 13:45:37
Avatar

Non, l'intérêt aurait été que Prolog arrive effectivement à donner des résultats. Dans les deux exemples que tu as cités, "boucle infinie" signifie que Prolog tourne indéfiniment, sans donner de nouveaux résultats.

Dans le cas de max, en utilisant max4 (voir mon post précédent), Prolog arrive au contraire à générer une infinité de listes qui conviennent (et à chaque fois qu'on demande un résultat de plus, Prolog arrive à en donner un).
Et je me demandais s'il serait possible de faire pareil pour les arbres syntaxiques : programmer de tel sorte que Prolog puisse générer à volonté des arbres qui conviennent.
Édité le 26/07/2010 à 13:48:34 par Cyprien_
Hors ligne neo2500 # Posté le 26/07/2010 à 14:10:24

Études : Paris 6 - Université Pierre et Marie Curie (Jussieu)

Bonjour,

Je commance à faire moi aussi mes tests, mais je vois que vous étes plus rapide que moi...
Je vais essayer un résolveur de sudoku (cf le lien du début de topic).
Édité le 26/07/2010 à 14:11:31 par neo2500
Hors ligne Orwell # Posté le 26/07/2010 à 14:12:22
tahc nu sap tse'n icec
Avatar
Validateurs

Ville : Paris
Pays : France métropolitaine
Études : FSA ULB

OK, je comprends mieux ;)
A priori oui, c'est possible, mais ça dépend de l'ordre de définition des clauses et des prédicats qui les composent; Prolog va soit construire d'abord les solutions les plus complexes (-> boucle infinie), soit les plus simples (longue liste de solutions de complexité croissante).

Tuto en beta-test : Entity Framework
Mon appli Windows Phone : Deezy
 
Hors ligne bluestorm # Posté le 26/07/2010 à 22:23:44
dont ask to ask
Avatar
Groupe : Anciens
Flux RSS

Citation : Cyprien_
EDIT² : je me suis un peu intéressé à la possibilité de faire des AST, déjà simplement pour des expressions numériques.
Le code que je montre s'appuie sur les entiers naturels redéfinis par bluestorm (et n'a que 2 opérations), mais avec des entiers naturels ça marche aussi bien, le seul souci étant de différencier une valeur numérique d'un arbre...

Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
isNat(z).
isNat(s(N)) :- isNat(N).

op(+).
op(*).

add(N, z, N).
add(N, s(X), s(Y)) :- add(N, X, Y).

mul(_, z, z).
mul(N, s(X), Y) :- add(N, Z, Y), mul(N, X, Z), !.

eval_op(A, +, B, X) :- add(A, B, X).
eval_op(A, *, B, X) :- mul(A, B, X).

ast(N) :- isNat(N).
ast((A, OP, B)) :- ast(A), ast(B), op(OP).


Je n'aime pas la manière dont tu fais. Tu utilises le fait qu'il est possible de différencier les cas en inspectant les valeurs. En pratique, la vérification isNat étant linéaire, tu as des coûts de performance.

Comme pour un langage fonctionnel, la bonne solution me semble ici de faire une somme disjointe. On peut utiliser pour cela des symboles "nat" et "op" par exemple.

Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
is_nat(z).
is_nat(s(N)) :- is_nat(N).

is_op(+).
is_op(*).

add(N, z, N).
add(N, s(X), s(Y)) :- add(N, X, Y).

mul(_, z, z).
mul(N, s(X), Y) :- add(N, Z, Y), mul(N, X, Z), !.

eval_op(A, +, B, X) :- add(A, B, X).
eval_op(A, *, B, X) :- mul(A, B, X).

is_ast(nat(N)) :- is_nat(N).
is_ast(op(A, OP, B)) :- ast(A), ast(B), is_op(OP).

eval(nat(N), N).
eval(op(A, OP, B), X) :-
    eval(A, M),
    eval(B, N),
    eval_op(M, OP, N, X).


PS : si vous vous sentez de vous lancer dans un sous-projet un peu consistant pour l'atelier (non parce que là, hier soir je faisais gentiment des listes, aujourd'hui on fait des ASTs, demain on code un évaluateur lisp et après-demain un compilateur Haskell98 ?), n'hésitez pas à ouvrir un topic à part en mettant le tag [Atelier Prolog] dans le titre, et postant ici un petit message pour donner le lien.
Édité le 26/07/2010 à 22:24:27 par bluestorm
 
Hors ligne Cyprien_ # Posté le 26/07/2010 à 22:33:27
Avatar

Non seulement ton code est effectivement plus propre, mais en plus il répond à ma question précédente : cette fois, Prolog arrive sans soucis à générer des arbres dont le résultat est celui demandé :

Code : Console
?- eval(A, s(s(z))).
A = nat(s(s(z))) ;
A = op(nat(s(s(z))), +, nat(z)) ;
A = op(nat(s(z)), +, nat(s(z))) ;
A = op(nat(z), +, nat(s(s(z)))) ;
A = op(nat(s(s(z))), *, nat(s(z))) ;
A = op(nat(s(s(z))), +, op(nat(z), +, nat(z))) ;
...


Il n'y avait donc pas que des problèmes de performance, mais aussi de conception visiblement.
Hors ligne bluestorm # Posté le 26/07/2010 à 23:29:02
dont ask to ask
Avatar
Groupe : Anciens
Flux RSS

Code : Autre
1
2
3
4
5
int_to_nat(0, z).
int_to_nat(X, s(N)) :- X > 0, Y is X-1, int_to_nat(Y, N).

nat_to_int(z, 0).
nat_to_int(s(N), X):- nat_to_int(N, Y), X is Y+1.



En ayant envie de faire la conversion nat<->int en profondeur dans les arbres, je me suis rendu compte qu'on voulait une fonction 'map' et que je ne savais pas faire d'ordre supérieur en Prolog.

Code : Autre
1
2
3
4
5
6
7
8
map_ast(F, nat(N), R) :- apply(F, [nat(N), R]).
map_ast(F, op(A, OP, B), R) :-
        map_ast(F, A, A2),
        map_ast(F, B, B2),
        apply(F, [op(A2, OP, B2), R]).

on_leaf(F, nat(N), nat(R)) :- apply(F, [N, R]).
on_leaf(F, op(A, OP, B), op(A, OP, B)).


Exemples :
Code : Autre
1
2
3
4
5
?- T = op(nat(2), +, nat(3)), map_ast(on_leaf(int_to_nat), T, T2), eval(T2, T3), nat_to_int(T3, R).
T = op(nat(2), +, nat(3)),
T2 = op(nat(s(s(z))), +, nat(s(s(s(z))))),
T3 = s(s(s(s(s(z))))),
R = 5 .


Code : Autre
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
?- N = 4, int_to_nat(N, N2), eval(T, N2), map_ast(on_leaf(nat_to_int), T, R).
N = 4,
N2 = s(s(s(s(z)))),
T = nat(s(s(s(s(z))))),
R = nat(4) ;
N = 4,
N2 = s(s(s(s(z)))),
T = op(nat(s(s(s(s(z))))), +, nat(z)),
R = op(nat(4), +, nat(0)) ;
N = 4,
N2 = s(s(s(s(z)))),
T = op(nat(s(s(s(z)))), +, nat(s(z))),
R = op(nat(3), +, nat(1)) ;
N = 4,
N2 = s(s(s(s(z)))),
T = op(nat(s(s(z))), +, nat(s(s(z)))),
R = op(nat(2), +, nat(2)) ...


PS : dans ces exemples, j'ai une tripotée de variables intermédiaires (T2, T3, N2..) dans les tests. Comment ne pas afficher leur valeur ?
 

Retour au forum "Autres langages, outils et approches" ou à la liste des forums

Pour accéder à cette section
Connectez-vous !
connexion_rpx