![]() |
Bienvenue invité ( Connexion | Inscription )
![]() |
![]()
Message
#1
|
|
Macbidouilleur d'Or ! ![]() ![]() ![]() ![]() ![]() Groupe : Membres Messages : 2 486 Inscrit : 29 Aug 2002 Membre no 3 340 ![]() |
Le forum développeur en langages divers est plutôt calme et je me permets donc de faire un vagabondage avec un des langages "bizarres" que j'apprécie, le langage APL.
En effet, j'ai été récemment surpris que l'éditeur de programmation ATOM avait non seulement le support de la syntaxe APL mais que cela en faisait l'un des éditeurs APL les plus confortables que j'ai jamais pratiqué. Peut-être que dans ces conditions, de nouveaux programmeurs curieux aimeraient s'y frotter un peu ? APL est rare. Au temps des ordis 8-bit, seuls le Tandy TRS80 et une variété de Commodore, le super-PET 9000 de l'université canadienne de Waterloo en avaient une implémentation. Rodnay Zacks, l'auteur de livre sur la programmation du 6502, avait créé la sienne, mais je ne l'ai jamais vue. Avant cette époque, il équipait un IBM portable très rare, antérieur au PC DOS qu'on connait, l'IBM 5100 Modèle A, A comme APL. Après cette époque, un portable ultra-rare à base de 68000, l'Ampère WS-1, n'a jamais trouvé son public, vendu seulement en France, hors de son Japon d'origine, jamais validé par la FCC aux USA,... Avis aux les collectionneurs. APL est né chez IBM à la fin des années 1950, créé par Kenneth Iverson. De nos jours, il existe soit une version OpenSource GNU APL, soit une version commerciale gratuite Dyalog APL, qui est devenue l'état de l'art du langage. DyalogAPL propose aussi un portail très pratique en libre d'accès : http://TryAPL.org En France, le langage a été popularisé par Bernard Legrand http://www.afapl.asso.fr/AFAPL_2006_v2.pdf Pourquoi s'y intéresser aujourd'hui ? - Parce c'est un langage orienté calcul ou Data, avant le Big Data - Parce qu'il manipule directement des tableaux, et qu'il a inspiré des langages comme R. - Parce que l'environnement de développement est plus aisé d'approche que dans le passé, j'ai cité ATOM. Commençons par un exemple simple : Cette semaine, j'ai un peu marché et j'ai gardé le kilométrage journalier, et voici ma semaine : Lundi : 5 km Mardi : 3 km Mercredi : 8 km Jeudi : 2 km Vendredi : 6 km Samedi : 8 km Dimanche : 10 km Quelle a été ma moyenne ? Code kms ← 5 3 8 2 6 8 10 +/kms ÷ ⍴ kms 6 Essayez ça sur TryAPL.org +/kms c'est une réduction par l'addition du vecteur kms, s'il avait fallu tout multiplier on aurait pu écrire ×/kms ⍴ kms c'est la dimension de kms, 7 éléments Et la division n'est pas la barre utilisée pour la réduction, mais la superposition de : et -, qui donne le signe de division utilisé par les calculettes. A propos de la réduction, les habitués de Big Data connaissent les moteurs de Map Reduce, on parle de la même reduction dans les deux cas. Et si vous êtes plutôt Scala, il s'agit de l'opération «fold». Maintenant, avec ces kilomètres vous pouvez préférer des totaux partiels après chaque jour. Facile, c'est l'opérateur de scan qui le fait. Code +\kms 5 8 16 18 24 32 42 Le scan existe dans ReactJS ou dans Scala. Comme vous le voyez, avec ces caractères spéciaux, il n'était pas facile d'avoir un environnement de dév, il fallait des claviers spéciaux, des terminaux et des imprimantes spéciales. Plus aujourd'hui, les caractères APL sont dans l'UNICODE supporté par tous les ordis modernes. Voyons un autre problème simple, mais très matheux, l'intersection d'une droite et d'un plan dans l'espace. C'est l'un des exemples du portail Rosetta qui recense les résolutions de ce genre de problème dans différents langages. Mais pas en APL... du moins avant ce post. Défi Rosetta : Intersection droite et plan L'énoncé : - Soit une droite dans l'espace 3D, définie par un point D et un vecteur directeur V - Soit un plan dans l'espace 3D, défini par un point P et un vecteur normal N - Trouver l'intersection I de la droite et du plan - Données : D = (0, 0, 10), V = (0, -1, -1), P = (0, 0, 5), N = ( 0, 0, 1) On va commencer par raisonner géométriquement avec les vecteurs: Puisque le point I appartient à la droite, il existe un paramètre t ∊ R, tel que le vecteur DI = t×V Le vecteur DI c'est en fait I - D de coordonnées Ix - Dx, etc on ne va pas aller par là, on va rester au niveau Point et Vecteur. Donc on a I - D = t × V, ou encore I = D + t × V Comme I appartient aussi au plan, tout vecteur du plan est normal à N. Les maths nous disent si deux vecteurs sont normaux, leur produit scalaire est nul. Le produit scalaire de A par B est le nombre AxBx + AyBy + AzBz, et il est noté A.B, et là encore on va rester à ce haut niveau, sans descendre aux x, y, z. Les maths nous apprennent deux propriétés du produit scalaire qui vont nous être utiles: - Il est distributif par rapport à l'addition de vecteurs : ( A + B ).C = A.C + B.C - Il est associatif par rapport à la multiplication externe par un réel : (t × A).B = t × A.B Réf: Patrice Wira : Un rappel sur les matrices Ici, on veut exprimer que le vecteur IP est normal à N, et le vecteur IP c'est P - I, comme vu précédemment. Donc on a (P - I).N = 0, remplaçons I par sa valeur D + t × V. (P - D - t × V).N = 0 Utilisons la distributivité: (P - D).N - (t × V).N = 0 Puis l'associativité: (P - D).N - t × (V.N) = 0 D'où : t = (P - D).N ÷ V.N Et l'APL dans tous ça ? Le produit scalaire A.B se code +/A × B, donc on peut écrire : t ← (+/(P - D) × N) ÷ +/V × N Ce qui permet de résoudre I : I ← D + t × V A essayer sur TryAPL.org Code V ← 0 ¯1 ¯1 D ← 0 0 10 N ← 0 0 1 P ← 0 0 5 t ← (+/(P - D) × N) ÷ +/V × N I ← D + t × V Et ça donne I 0 ¯5 5 A comparer avec les autres langages ! J-P PS: Tant qu'à faire... j'ai ajouté l'exemple au portail Rosetta Portail Rosetta Ce message a été modifié par Jaypee - 17 Dec 2020, 08:16. |
|
|
![]() |
![]()
Message
#2
|
|
Macbidouilleur d'Or ! ![]() ![]() ![]() ![]() ![]() Groupe : Membres Messages : 2 486 Inscrit : 29 Aug 2002 Membre no 3 340 ![]() |
On va coder l'IA pour un Tic-Tac-Toe, le mini-morpion de 3 x 3.
On va représenter le jeu avec un simple vecteur de 1 à 9, qu'on va afficher comme un carré en utilisant : Code J←9⍴0 J 0 0 0 0 0 0 0 0 0 3 3⍴⍳9 1 2 3 4 5 6 7 8 9 Affichage et codage de l'état des cases : - Vide : '.' => codé 0 - Occupée par l'IA : 'O' => codé 1 - Occupée par le Joueur Humain : 'X' => codé 5 Tous les triplets possibles: 1 2 3 \ 4 5 6 horizontaux 7 8 9 / 1 4 7 \ 2 5 8 verticaux 3 6 9 / 1 5 9 descendant 7 5 3 ascendant Code T←8 3 ⍴1 2 3 4 5 6 7 8 9 1 4 7 2 5 8 3 6 9 1 5 9 7 5 3 T 1 2 3 4 5 6 7 8 9 1 4 7 2 5 8 3 6 9 1 5 9 7 5 3 8 en tout Les configurations de triplet avec au moins un vide, mais on peut rencontrer dans le cours du jeu les configs sans vide : ... Toutes vides O.. Ordi + 2 vides OO. 2xOrdi + 1 vide X.. Humain + 2 vides XO. Humain + Ordi + vide XOO Humain + 2xOrdi XX. 2xHumain . vide XXO 2xHumain + Ordi Pour afficher le jeu, on utilise les caractère suivants, là encore il faudra ajouter 1 au codage pour avoir des indices commençant à 1 : Code A←'.O???X' Les scores associés : De toute évidence, du point de vue de l'IA, XX. est la plus désirable pour bloquer une attaque, mais sans négliger la possibilité de gagner avec OO. ... vaudra 1 O.. vaudra 10 OO. vaudra 10000 X.. vaudra 3 XO. vaudra 0 XX. vaudra 1000 OOX vaudra 0 XXO vaudra 0 Mais comment facilement trouver la configuration et le score associé ? On somme l'état des cases pour trouver l'indice (à un +1 près) dans le vecteur des scores. ... = 0 + 0 + 0 = 0 => Indice 1 => score 1 O.. = 1 + 0 + 0 = 1 => Indice 2 => score 10 (aussi .O. ..O) OO. = 1 + 1 + 0 = 2 => Indice 3 => score 10000 (aussi O.O .OO) Rien pour 3, 4 => Indice 4, 5 => score 0 X.. = 5 + 0 + 0 = 5 => Indice 6 => score 3 (aussi .X. ..X) XO. = 5 + 1 + 0 = 6 => Indice 7 => score 0 (aussi OX. O.X X.O) OOX = 1 + 1 + 5 = 7 => Indice 8 => 0 (aussi XOO OXO) Rien pour 8, 9 => Indice 9, 10 => score 0 XX. = 5 + 5 + 0 = 10 => Indice 11 => score 1000 (aussi X.X .XX) XXO = 5 + 5 + 1 = 11 => Indice 12 => score 0 (aussi pour OXX XOX) On va donc utiliser un vecteur à 12 cases dont certaines ne serviront pas parce que les sommes 3, 4, 8, 9 sont impossibles à réaliser avec des 1 et des 5, il faudra ajouter 1 à l'indice calculé pour obtenir celui du score, Code confs ← 12 3⍴0 0 0 1 0 0 1 1 0 2 1 0 2 2 0 5 0 0 5 1 0 5 1 1 5 2 1 5 2 2 5 5 0 5 5 1 On utilisera le code invalide 2 pour garder une certaine cohérence dans les sommes impossibles. Code confs 0 0 0 1 0 0 1 1 0 2 1 0 2 2 0 5 0 0 5 1 0 5 1 1 5 2 1 5 2 2 5 5 0 5 5 1 +/confs 0 1 2 3 4 5 6 7 8 9 10 11 On utilise en parallèle un vecteur des scores : Code scores ← 1 10 10000 0 0 3 0 0 0 0 1000 0 scores, A[1 + confs] 1 ... 10 O.. 10000 OO. 0 ?O. 0 ??. 3 X.. 0 XO. 0 XOO 0 X?O 0 X?? 1000 XX. 0 XXO Supposons que je commence le jeu en jouant en 1: Code J[1] ← 5 A[1+3 3⍴J] X.. ... ... Plaçons nous du point de vue de l'IA, où jouer maintenant ? Pour chaque case ⍵, on va considérer le sous-ensemble des triplets, où elle apparaît, et pour chacun évaluer le score et garder le plus grand. T=⍵ donne un tableau booleen de la même forme que T, le OU logique ligne à ligne fournit un masque qui sert à sélectionner les lignes de T où ⍵ apparaît. Ce sous-ensemble de T, permet d'indicer le tableau de jeu, pour obtenir les configurations de triplet pour chacun d'eux. Depuis chaque configuration, la somme ligne par ligne, augmentée de 1, fournit les indices dans les scores, dont on ne retient que le plus grand d'entre eux, grâce à une réduction par l'opérateur max ⌈, et comme on va la réutiliser, l'expression est transformée en fonction : Code score_case ← {⌈/scores[1 + +/J[(∨/(T=⍵))⌿T]]} Lorsqu'on a une telle fonction est conçue pour un seul argument scalaire, elle risque de ne pas fonctionner si le scalaire est remplacé par un vecteur. Si malgré tout, on veut l'appliquer à un vecteur, il existe l'opérateur "pour chacun de", le tréma. Puis recherche de la meilleure case vide, par tirage au sort (opérateur ? entre 1 et la taille du vecteur, donnée par Rhô) parmi les meilleurs scores, c'est 9 qui sort de ce tirage : Code notes←(score_case¨⍳9)∧J=0 (⍳9),[0.5]notes 1 2 3 4 5 6 7 8 9 0 3 3 3 3 1 3 1 3 indices_meilleures_notes ←(notes ≥ ⌈/ notes)/⍳9 indices_meilleures_notes[?⍴ indices_meilleures_notes] 9 J[9]←1 A[1 + 3 3 ⍴J] X.. ... ..O Je joue en 3, puis l'ordi en 2 : Code J[3]←5 A[1 + 3 3⍴J] X.X ... ..O notes←(score_case¨⍳9)∧J=0 (⍳9),[0.5]notes 1 2 3 4 5 6 7 8 9 0 1000 0 3 3 1 10 10 0 indices_meilleures_notes ←(notes ≥ ⌈/ notes)/⍳9 indices_meilleures_notes[?⍴ indices_meilleures_notes] 2 J[2]←1 A[1 + 3 3⍴J] XOX ... ..O Je joue en 7, l'ordi en 5 : Code J[7]←5 A[1+3 3⍴J] XOX ... X.O notes←(score_case¨⍳9)∧J=0 (⍳9),[0.5]notes 1 2 3 4 5 6 7 8 9 0 0 0 1000 1000 1 0 10 0 indices_meilleures_notes ←(notes ≥ ⌈/ notes)/⍳9 indices_meilleures_notes[?⍴ indices_meilleures_notes] 5 J[5]←1 A[1 + 3 3⍴J] XOX .O. X.O Je joue en 4, l'ordi est battu : Code J[4]←5 A[1 + 3 3⍴J] XOX XO. X.O Voilà ! Ce message a été modifié par Jaypee - 18 Apr 2021, 13:33. |
|
|
![]() ![]() |
Nous sommes le : 18th July 2025 - 09:14 |