IPB

Bienvenue invité ( Connexion | Inscription )

> Un langage rare APL, supporté maintenant par l'éditeur ATOM
Options
Jaypee
posté 14 Dec 2020, 19:16
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.
Go to the top of the page
 
+Quote Post
 
Start new topic
Réponse(s)
Jaypee
posté 17 Apr 2021, 14:44
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.
Go to the top of the page
 
+Quote Post

Les messages de ce sujet


Reply to this topicStart new topic
1 utilisateur(s) sur ce sujet (1 invité(s) et 0 utilisateur(s) anonyme(s))
0 membre(s) :

 



Nous sommes le : 18th July 2025 - 09:14