IPB

Bienvenue invité ( Connexion | Inscription )

 
Reply to this topicStart new topic
> 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
Jaypee
posté 19 Dec 2020, 11:16
Message #2


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Dans le même rayon, un autre challenge Rosetta Code, l'intersection de deux droites dans le plan, définies chacune par deux points.
Données :
- Droite 1 passant par A=(4,0) et B=(6,10)
- Droite 2 passant par C=(0,3) et D=(10,7)

En fait, si on prend un poil de recul, on a besoin d'utiliser 3 fois la même fonction : Résoudre un système linéaire de n équations à n inconnues.
- Résoudre a et b dans y = ax + b pour la première droite
- Résoudre a' et b' dans y = a'x + b' pour la seconde droite
- Résoudre x et y dans les deux equations de droite pour trouver l'intersection

Il se trouve qu'en APL, il existe un opérateur le « domino dyadique » qui fait ce travail de résoudre un système d'équations. Domino parce qu'il résulte de la superposition du caractère "quad", la grosse boîte, et du signe de la division lui-même superposition du : et du -. Dyadique parce que les fonctions APL ont la particularité d'être "infixées":
- Dans 98% des langages de programmation, les fonctions sont préfixées : f(args) f apparaît devant.
- En Forth, utilisant la notion polonaise inversée, elles sont postfixées: 4 sqrt, sqrt apparaît derrière.
- En APL, la fonction apparaît au milieu: arg_gauche f arg_droit. Si la fonction n'utilise qu'un seul argument, c'est celui de droite, et la fonction redevient préfixée: f arg, on l'appelle alors "monadique", terme choisi bien avant que la programmation fonctionnelle s'en empare. Et si elle utilise deux arguments, elle est dite dyadique. Le domino monadique sert à calculer l'inverse d'une matrice. Mais revenons à sa version dyadique.
Pour la première droite on a:
0 = 4a + b
10 = 6a + b
Pour résoudre ce système en a et b, il faut passer les données suivantes au domino:
- à gauche (0, 10)
- à droite la matrice de 2 lignes x 2 colonnes :
( 4 1 )
( 6 1 )
On va définir une fonction "résoudre" à laquelle on passe les deux points (note: l'argument de gauche est noté alpha, et celui de droite oméga, ici, seulement oméga, c'est une fonction monadique):
( 4 0 )
( 6 10 )
et elle se débrouille pour sortir les y et faire entrer les 1 là où il faut, (noter que l'accent est interdit dans le nom resoudre), qui transforme l'argument avec des "prendre" la flèche vers le haut, 2 lignes 1 colonne prendre dans oméga => les x et 2 -1 pour les y:
Code
   A←4 0
   B←6 10
   C←0 3
   D←10 7
   resoudre ← {(,2 ¯1↑⍵)⌹(2 1↑⍵),1}

Mais au moment de résoudre l'intersection il faut bien réécrire le système dans le même format que dans les deux premiers cas:
y = ax + b
y = a'x + b'
On la transforme en:
b = -ax + y
b' = -a'x + y
Il faut changer le signe de a et a', donc multiplier les résultats de «résoudre» ( a b ) et (a' b') par (-1 1):
Code
   A←4 0
   B←6 10
   C←0 3
   D←10 7
   resoudre ← {(,2 ¯1↑⍵)⌹(2 1↑⍵),1}
   I ← resoudre 2 2⍴((¯1 1)× resoudre 2 2⍴A,B),(¯1 1)× resoudre 2 2⍴C,D
   I
5 5

Il a fallu beaucoup d'analyse, pour ce problème simple mais au moins, on comprend qu'il s'agit de trois fois le même problème, à de petits changements près. On doit aussi donner la bonne forme aux données : on construit une matrice de 2 lignes et 2 colonnes avec les (x, y, x', y') des deux points.
A essayer sur le portail tryAPL.org (auquel je ne suis absolument pas affilié)
J-P

Ce message a été modifié par Jaypee - 20 Dec 2020, 17:17.
Go to the top of the page
 
+Quote Post
Jaypee
posté 5 Apr 2021, 14:39
Message #3


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Continuons cette série, avec deux exemples simples de manipulations des chaines de caractères qui sont traitées directement comme des vecteurs de caractères.

Comme tout vecteur, on a vu qu'on pouvait les indicer pour en extraire les valeurs choisies, et que le résultat a la même dimension que les indices, qui commencent à 1:
Code
      'A'[1 1 1]
AAA
         'NICHE'[3 4 2 5 1]
CHIEN


Premier petit défi, extraire les 4 premiers mots d'un texte.
Code
      t ← 'Un texte pour illustrer le code'

On trouve l'emplacement des espaces avec un masque booléen. Espace égal t, la comparaison va être distribuée ou mappée à chaque caractère de t.
Code
       ' ' = t
0 0 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0

On a déjà vu le scan la barre oblique vers la gauche, qui fait des cumuls partiels. kms = kilomètres journaliers
Code
       kms ← 5 3 8 2 6 8 10
      + \ kms
5 8 16 18 24 32 42

On va l'appliquer au masque booléen pour compter le nombre d'espaces rencontrés :
Code
      + \ ' ' = t
0 0 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5

On va superposer le texte et le masque juste pour mieux visualiser, c'est juste un truc en passant ça ne fait pas partie de la résolution du défi. On voit que le nombre augmente juste à la rencontre de l'espace avant chaque mot,
Code
       t,[0.5]+\' ' = t
U n   t e x t e   p o u r   i l l u s t r e r   l e   c o d e
0 0 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5

Et pour ne retenir que 4 mots comme le défi le demande, il faut s'arrêter juste avant les 4. On va donc un fois de plus distribuer ou mapper la comparaison:
Code
      4 > +\ ' ' = t
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0

Ce masque booléen va servir pour sélectionner ou rejeter les caractères du texte original, et donc permet de résoudre ce défi, ce qui est fait avec l'opérateur barre oblique à droite /.
Code
       (4 > +\ ' ' = t) / t ← 'Un texte pour illustrer le code'
Un texte pour illustrer

Défi 1 relevé !

Maintenant, défi n°2, imaginons que nous sommes en train de construire une sorte de traitement de texte, et que pour avoir des colonnes de taille homogène de 40 colonnes, il faille élargir les espaces, rajouter du blanc au blanc.
On trouve facilement la taille du texte avec l'opérateur Rhô :
Code
      ⍴t
31

Il va donc falloir ajouter 9 espaces pour faire 40 caractères. Trouvons l'indice des espaces existants, grâce au même masque qui va sélectionner dans la liste des indices ceux en face des 1. On génère les indices de 1 à n avec l'opérateur iota. Et toujours l'astuce pour visualiser.
Code
      (' ' = t),[0.5]⍳⍴t
0 0 1 0 0 0 0 0 1  0  0  0  0  1  0  0  0  0  0  0  0  0  0  1  0  0  1  0  0  0  0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
      (' ' = t)/⍳⍴t
3 9 14 24 27

L'opérateur Rhô utilisé avec un argument à gauche, sert à "remplir" un vecteur. A gauche le nombre final, à droite le motif qui va âtre ré-utilisé autant de fois que nécessaire.
Code
      4 ⍴ 1 0
1 0 1 0

On va donc remplir un vecteur d'indices des espaces avec 9 valeurs, répétées si nécessaire:
Code
     (40 - ⍴t)⍴((' ' = t)/⍳⍴t)    
3 9 14 24 27 3 9 14 24

On va allonger la liste des indices originaux, avec ces indices supplémentaires, une simple virgule fait le collage de deux vecteurs. On va introduire la variable i pour le résultat:
Code
i ← (⍳⍴t),(40 - ⍴t)⍴(' ' = t)/⍳⍴t    
      i
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 3 9 14 24 27 3 9 14 24

L'idée est de trier le vecteur i aux valeurs croissantes. APL a un opérateur le triangle superposé à la barre verticale qui donne les indices qui reconstruisent l'ordre croissant. Il ne fait pas le tri, mais donne les clés du tri, donc il faut indicer le vecteur original pour le trier:
Code
      ⍋'CADB'
2 4 1 3
      'CADB'[⍋'CADB']
ABCD

Faisons le pour i.
Code
      i[⍋i]
1 2 3 3 3 4 5 6 7 8 9 9 9 10 11 12 13 14 14 14 15 16 17 18 19 20 21 22 23 24 24 24 25 26 27 27 28 29 30 31

Et finalement, on indexe le texte original avec ce vecteur d'indices enrichis de 9 espaces, là où il y en avait déjà un:
Code
      t[i[⍋i← (⍳⍴t),(40 - ⍴t)⍴(' ' = t)/⍳⍴t← 'Un texte pour illustrer le code']]
Un   texte   pour   illustrer   le  code
      t[i[⍋i]],[0.5]⍳40
U n       t e x t  e           p  o  u  r           i  l  l  u  s  t  r  e  r           l  e        c  o  d  e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
      (⍳40),[0.5]s[i[⍋i←(⍳⍴s),(40-⍴s)⍴(' '=s)/⍳⍴s←'et ça marche pour ce texte aussi']]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
e t       ç a         m  a  r  c  h  e        p  o  u  r        c  e        t  e  x  t  e        a  u  s  s  i
      t[i[⍋i←(⍳⍴t),(40-⍴t)⍴(' '=t)/⍳⍴t]],[0.5]s[i[⍋i←(⍳⍴s),(40-⍴s)⍴(' '=s)/⍳⍴s]]
Un   texte   pour   illustrer   le  code
et   ça   marche  pour  ce  texte  aussi

Défi n°2 résolu !

Une petite explication pour l'opérateur virgule :

Rappel: Un vecteur n'a qu'une seule dimension, sa longueur. On a vu ⍴t ne retourne que cette dimension.
Une matrice a deux dimensions le nombre de lignes et le nombre de colonnes.
Un nombre seul n'a pas de dimension du tout, c'est un «scalaire».

On a vu que la virgule servait à coller, deux vecteurs, on appelle cette opération une «caténation».
Lorsque qu'on spécifie la dimension qui augmente entre crochets, l'opération devient un laminage.

Par convention, lorsqu'on augmente le nombre de dimensions, passant de vecteur à matrice par exemple, on utilise ,[0.5]

Ce message a été modifié par Jaypee - 6 Apr 2021, 14:49.
Go to the top of the page
 
+Quote Post
Jaypee
posté 13 Apr 2021, 08:56
Message #4


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Petit exemple très facile, pour l'électronicien.

Résistances en série et en parallèle:

Supposons que l'on utilise un réseau de résistances en kΩ (Ω = alt-maj-Q smile.gif ):
Code
      R ← 10 33 47


Résistante équivalente en série Re = R1 + R2 + ... Rn

Code
       ⍝ L'ampoule sous un abat-jour est un commentaire éclairant le code
       ⍝ La réduction <opérateur / vecteur> consiste à intercaler l'opérateur entre tous les composantes du vecteur 10 + 33 + 47
       +/R
90


Résistance équivalente en parallèle 1/Re = 1/R1 + 1/R2 + ... 1/Rn
Code
      ⍝ Inversion de chaque composante du vecteur
      ÷R
0.1 0.0303030303 0.02127659574


Réduction par l'addition +/
Code
      +/÷R
0.151579626


Inversion du résultat
Code
      ÷+/÷R
6.597192684


Le réseau fait donc 6,6kΩ en parallèle !

Résumé, en 21 caractères
Code
      R←10 33 47
      +/R
90
      ÷+/÷R
6.597192684


JP

Ce message a été modifié par Jaypee - 13 Apr 2021, 10:49.
Go to the top of the page
 
+Quote Post
Jaypee
posté 13 Apr 2021, 10:29
Message #5


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Autre exemple simple, mais plus orienté "informatique".

Supposons que nous cherchions à vérifier si un texte représente une expression arithmétique bien équilibrée en terme de parenthèses, donc que le nombre de parenthèses ouvrantes égale celui des parenthèses fermantes.

Code
      E ← 'A+(B+(C/Y)+X/D)/(E+F)))'


Pour repérer les parenthèses ouvrantes et fermante on va utiliser un produit cartésien petit-rond point <opérateur>, celui qui sert pour créer par exemple un table de multiplication
Code
      (⍳10) ∘.× (⍳10)
1  2  3  4  5  6  7  8  9  10
2  4  6  8 10 12 14 16 18  20
3  6  9 12 15 18 21 24 27  30
4  8 12 16 20 24 28 32 36  40
5 10 15 20 25 30 35 40 45  50
6 12 18 24 30 36 42 48 54  60
7 14 21 28 35 42 49 56 63  70
8 16 24 32 40 48 56 64 72  80
9 18 27 36 45 54 63 72 81  90
10 20 30 40 50 60 70 80 90 100


Ici, on cherche l'égalité de soit '(', soit ')' avec chaque caractère de E, avec petit-rond point égal:
Code
      '()'∘.=E
0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1


Pour mieux visualiser, on va utiliser le truc du 'laminage' pour superposer E et les masques obtenus, voir en note l'explication pourquoi on a un [1], là où auparavant on avait [0.5]:
Code
      E,[1]'()'∘.=E
A + ( B + ( C / Y ) + X / D ) / ( E + F ) ) )
0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 1 1


Et pour les compter, on va faire une réduction ligne par ligne, c'est-à-dire, les sommes ligne par ligne:
Code
      +/'()'∘.=E
3 2


Le résultat est clair, mais si on voulait une réponse par oui ou non, il faut réduire ce vecteur par l'égalité (sont-ils tous égaux entre eux ?)
Code
      =/+/'()'∘.=E
0


Si on veut transformer en fonction cette expression, il faut lui donner un nom, et encadrer sa définition par des accolades. E devient l'argument, et par convention, les arguments sont notés alpha pour celui de gauche, et oméga pour celui de droite. Ici, rien à gauche, donc pas d'alpha, juste oméga :
Code
      parensOK ← {=/+/'()'∘.=⍵}
      parensOK E
0
      E ← 'A+((B+(C/Y)+(X/D)/(E+F)))'
      parensOK E
1


Note sur le laminage:
En laminant, E au-dessus d'un tableau de 2 lignes par N colonnes, on aura un autre tableau de 3 lignes par N colonnes, dont grossit l'une des dimensions, mais on n'ajoute pas de nouvelle dimension, auquel cas on serait revienu à l'usage conventionnel de [0.5]
Les opérateurs comme, mais aussi / qui peuvent opérer sur n'importe quelle dimension (on peut réduire ligne par ligne ou colonne par colonne) sont accompagnés du numéro entre crochet de la dimension concernée. Implicitement, lorsqu'elle n'est pas indiquée, c'est la dernière dimension qui est sous-entendue.
Pour compliquer ou simplifier les choses, il y a une écriture simplifiée de <opérateur>[1], il s'agit du signe moins qui barre l'opérateur:
Code
      ⍝ ,[1] s'abrège en ⍪
      ⍝ /[1] s'abrège en ⌿
      '()'∘.=E
0 0 1 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 1
      ⍝ Sommes colonne par colonne
      +⌿'()'∘.=E
0 0 1 1 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 1 1


Ce message a été modifié par Jaypee - 13 Apr 2021, 10:41.
Go to the top of the page
 
+Quote Post
Jaypee
posté 13 Apr 2021, 11:09
Message #6


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Petit retour sur l'exemple avec les parenthèses :
Code
parensOK ← {=/+/'()'∘.=⍵}


On pourrait la rendre plus utile en passant à gauche les séparateurs à rechercher :
Code
sepOK ← {=/+/⍺∘.=⍵}
      '()' sepOK E
1
      X ← '<xyz>un texte</xyz>'
      '<>' sepOK X
1


Maintenant, on pourrait dire 0 1 ce n'est pas encore assez clair. On peut alors remarquer que l'opérateur de rotation (un peu comme le shift des bits) le rond barré verticalement peut servir:
Code
      0 ⌽ 'KO'
KO
      1 ⌽ 'KO'
OK
      ('<>'sepOK X) ⌽ 'KO'
OK
      ('<>'sepOK '<x>abcd<Y') ⌽ 'KO'

KO

On peut donc redéfinir la fonction :
Code
      sepOK ← {(=/+/⍺∘.=⍵) ⌽ 'KO'}
      
      '<>' sepOK '<xyz>un texte</xyz>'
OK

Une petite variante pour obtenir une réponse encore plus claire, avec l'opérateur flèche en haut "prendre dans" :
Code
      sepOK ← {3 ↑ (3 × =/+/⍺∘.=⍵) ⌽ 'NONOUI'}
      '<>'sepOK'<xyz>un texte</xyz>'
OUI
      '<>'sepOK'<xyzun texte</xyz>'
NON

Analyse : Si le résultat est 1, multiplié par 3, la triple rotation donnera 'OUINON', dont on ne retient que les 3 premiers caractères.

JP

Ce message a été modifié par Jaypee - 13 Apr 2021, 15:55.
Go to the top of the page
 
+Quote Post
Jaypee
posté 13 Apr 2021, 15:26
Message #7


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Un exemple lié à la génétique, calcul du taux de GC d'une séquence ADN.

Les bases de l'ADN sont décrites par les lettres A, C, G, et T. Par exemple, les chaînes 'ACGTACGTACGT' ou 'ACGTAGTCGADTAGTA' représentent des séquences d'ADN. Il s'agit de compter les occurrences des 'CG' et de les rapporter au nombre total de bases dans la séquence.
Code
      ADN ← 'ACGTAGTCGADTAGTACGTAGTCGADTAGTA'

On peut trouver les occurrences de 'G' ou 'C' dans ADN soit par un produit cartésien comme dans l'exemple des parenthèses, puis faire un OU logique, ou plus simplement avec l'opérateur d'appartenance:
Code
      ADN,[0.5]ADN ∊ 'GC'
A C G T A G T C G A D T A G T A C G T A G T C G A D T A G T A
0 1 1 0 0 1 0 1 1 0 0 0 0 1 0 0 1 1 0 0 1 0 1 1 0 0 0 0 1 0 0

Puis une réduction par l'addition :
Code
      +/ADN ∊'GC'
12

On peut enfin calculer le rapport grâce à l'opérateur Rhô qui retourne le nombre de caractères (de bases) dans la chaîne ADN.
Code
       (+/ADN ∊'GC')÷⍴ADN
0.3870967742

On transforme l'expression en fonction:
Code
      tauxGC ← {(+/⍵ ∊'GC')÷⍴⍵}      
      tauxGC ADN
0.3870967742

Et finalement on peut créer une fonction qui vérifie si le taux de GC > 50%, qui est le test pour déterminer si la séquence est une bactérie !
Code
      bacterie ← {3 ↑ (3 × 0.5<tauxGC ⍵)  ⌽ 'NONOUI'}
      bacterie ADN
NON      
      bacterie 24 ⍴ 'CG'
OUI

JP

Ce message a été modifié par Jaypee - 14 Apr 2021, 06:12.
Go to the top of the page
 
+Quote Post
Jaypee
posté 14 Apr 2021, 08:53
Message #8


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Un nouvel exemple plus orienté vers les maths : Compter les nombres ayant un nombre pair de chiffres comme 12, 1206, 100000, mais pas comme 1, 100, 23548

L'astuce mathématique est de passer par le logarithme à base 10 du nombre. Les nombres supérieurs à 10 à la puissance n, ont un logarithme compris dans l'intervalle [n, n + 1[, n + 1 est exclu.
Donc de 1 à 10, le logarithme est compris entre 0.0 et 1.0, de 10 à 100, entre 1.0 et 2.0, etc. On remarque alors que la partie entière du log est le nombre entier juste avant le nombre de chiffres.

De 1 à 10, log(10) : 0.xyz, partie entière: 0, nombre de chiffres : 1
De 10 à 100, log(10) : 1.xyz, partie entière : 1, nombre de chiffres : 2
etc.

Dans le cadre du problème, on va donc s'intéresser aux nombres donc la partie entière du logarithme à base 10 est impaire, car ainsi l'entier suivant, décrivant le nombre de chiffres, sera pair.
Code
      N ← 10 3 677 854 1000

Le logarithme est l'opérateur "orange", le rond superposé à l'astérisque, le tout ressemblant à l'empreinte de la tige sur la surface d'une orange. A gauche, la base du logarithme, rien si log Népérien, à droite le vecteur des nombres.
Code
      10 ⍟ N
1 0.4771212547 2.830588669 2.931457871 3

Opérateur plancher, le grand L élongué, pour retenir la partie entière:
Code
      ⌊ 10 ⍟ N
1 0 2 2 3

L'opérateur modulo est la barre verticale, diviseur à gauche, vecteur de nombres à droite, modulo = 1 pour les impairs, avec le truc habituel du laminage pour le confort :
Code
       N,[0.5] 2 | ⌊ 10 ⍟ N
10 3 677 854 1000
1 0   0   0    1

Et une désormais classique réduction par l'addition pour finir :
Code
      +/ 2 | ⌊ 10 ⍟ N
2

Juste un peu plus long, mais sans doute plus facile à suivre, sélection par compression logique des nombres grâce au masque des modulos :
Code
      (2 | ⌊ 10 ⍟ N) / N
10 1000

Et lecture de la longueur avec un Rhô :
Code
      ⍴(2 | ⌊ 10 ⍟ N) / N
2


Note sur la lecture de la longueur avec le Rhô: Le Rhô a un comportement un peu plus technique, car il va donner la dimension de l'objet passé à droite.
Et il ne répond rien si on l'applique à un scalaire, un nombre isolé, qui n'a pas de dimension du tout. Avec un vecteur rempli d'une seule valeur, Rhô répondra 1.
Il existe un opérateur plus intuitif, l'hamburger piqué superposition du égal à 3 barres (ou hamburger) et de la barre oblique /, qui va répondre 1 même pour un scalaire.
Code
      ⍝ Les vecteurs sont faciles, le même résultat est retourné.
      ⍴ 1 2 69
3
      ≢ 1 2 69
3
      ⍴0
<pas de réponse>
      ≢0
1      
      ⍝ Une ligne, une colonne, remplie de 1
      ≢1 1 ⍴ 1
1
      ⍴1 1 ⍴ 1
1 1


Ce message a été modifié par Jaypee - 14 Apr 2021, 09:31.
Go to the top of the page
 
+Quote Post
Jaypee
posté 17 Apr 2021, 14:44
Message #9


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
Jaypee
posté 18 Apr 2021, 10:28
Message #10


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Extension du Tic-Tac-Toe à 10 x 10, et à un morpion en vraie grandeur.

Le recensement des quintuplets est plus compliqué, donc on va le découper en horizontaux, verticaux, ascendants et descendants, puis on les rassemble dans un tableau unique Q (tout comme nous avions T pour les triplets)
Code
      QH ← ⍉⍪⍉(0,10 × ⍳9)∘.+(⍳5)⍪(⍳5)∘.+⍳5
      QV ← ⍉⍪⍉(0,10 × ⍳5)∘.+(⍳10)∘.+10 × 0,⍳4
      QA ← ⍉⍪⍉(0,10 × ⍳5)∘.+(0,⍳5)∘.+ (⌽10 × 0,⍳4) + ⍳5
      QD ← ⍉⍪⍉(0,10 × ⍳5)∘.+(0,⍳5)∘.++⌿(⍳5),[0.5](0,10×⍳4)
      Q←QH⍪QV⍪QA⍪QD

Le principe des configurations de quintuplets, leur codage, leur score, leur affichage restent les mêmes, en à peine plus complexe.
Code
      confs←22 5⍴0 0 0 0 0 1 0 0 0 0 1 1 0 0 0 1 1 1 0 0 1 1 1 1 0 5 0 0 0 0 1 5 0 0 0 1 1 5 0 0 1 1 1 5 0 1 1 1 1 5 5 5 0 0 0 1 5 5 0 0 1 1 5 5 0 1 1 1 5 5 5 5 2 2 0 5 5 5 0 0 1 5 5 5 0 1 1 5 5 5 5 5 5 2 1 5 5 5 2 2 5 5 5 5 0 5 5 5 5 1
      +/confs
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
      scores←1 3 50 1000 1000000 10 0 0 0 0 200 0 0 0 0 10000 0 0 0 0 10000000 0
      A←'.O???X'
      A[1+confs],scores
.....        1
O....        3
OO...       50
OOO..     1000
OOOO.  1000000
X....       10
OX...        0
OOX..        0
OOOX.        0
OOOOX        0
XX...      200
OXX..        0
OOXX.        0
OOOXX        0
XX??.        0
XXX..    10000
OXXX.        0
OOXXX        0
XXX?O        0
XXX??        0
XXXX. 10000000
XXXXO        0

La fonction pour calculer le score d'une case doit être mise à jour pour Q
Code
score_case ← {⌈/scores[1 + +/J[(∨/(Q=⍵))⌿Q]]}

Le jeu fait 100 cases, et l'affichage est un peu plus décoré
Code
      J←100 ⍴ 0
      ('\ ',⍳10)⍪(' ',0,⍳9),('+',10⍴'-')⍪'|',A[1+10 10⍴J]
\   1 2 3 4 5 6 7 8 9 10
  + - - - - - - - - -  -
0 | . . . . . . . . .  .
1 | . . . . . . . . .  .
2 | . . . . . . . . .  .
3 | . . . . . . . . .  .
4 | . . . . . . . . .  .
5 | . . . . . . . . .  .
6 | . . . . . . . . .  .
7 | . . . . . . . . .  .
8 | . . . . . . . . .  .
9 | . . . . . . . . .  .


Le jeu peut commencer, je commence en 45, l'ordi réplique en 75:
Code
      J[45]←5
      ('\ ',⍳10)⍪(' ',0,⍳9),('+',10⍴'-')⍪'|',A[1+10 10⍴J]
\   1 2 3 4 5 6 7 8 9 10
  + - - - - - - - - -  -
0 | . . . . . . . . .  .
1 | . . . . . . . . .  .
2 | . . . . . . . . .  .
3 | . . . . . . . . .  .
4 | . . . . X . . . .  .
5 | . . . . . . . . .  .
6 | . . . . . . . . .  .
7 | . . . . . . . . .  .
8 | . . . . . . . . .  .
9 | . . . . . . . . .  .
      notes←(J=0) ^ score_case¨⍳100
      indices_meilleures_notes ←(notes ≥ ⌈/ notes)/⍳100
      indices_meilleures_notes[?⍴ indices_meilleures_notes]
75
      J[75]←1
      ('\ ',⍳10)⍪(' ',0,⍳9),('+',10⍴'-')⍪'|',A[1+10 10⍴J]
\   1 2 3 4 5 6 7 8 9 10
  + - - - - - - - - -  -
0 | . . . . . . . . .  .
1 | . . . . . . . . .  .
2 | . . . . . . . . .  .
3 | . . . . . . . . .  .
4 | . . . . X . . . .  .
5 | . . . . . . . . .  .
6 | . . . . . . . . .  .
7 | . . . . O . . . .  .
8 | . . . . . . . . .  .
9 | . . . . . . . . .  .

La partie risque d'être longue, je ne vais pas tout montrer, mais juste le premier duel de l'ordi, je joue en 78, l'ordi en 89:
Code
      J[78]←5
      ('\ ',⍳10)⍪(' ',0,⍳9),('+',10⍴'-')⍪'|',A[1+10 10⍴J]
\   1 2 3 4 5 6 7 8 9 10
  + - - - - - - - - -  -
0 | . . . . . . . . .  .
1 | . . . . . . . . .  .
2 | . . . . . . . . .  .
3 | . . . . . . . . .  .
4 | . . . . X . . . .  .
5 | . . . . . . . . .  .
6 | . . . . . . . . .  .
7 | . . . . O . . X .  .
8 | . . . . . . . . .  .
9 | . . . . . . . . .  .
      notes←(J=0) ^ score_case¨⍳100
      indices_meilleures_notes ←(notes ≥ ⌈/ notes)/⍳100
      indices_meilleures_notes[?⍴ indices_meilleures_notes]
89
      J[89]←1
      ('\ ',⍳10)⍪(' ',0,⍳9),('+',10⍴'-')⍪'|',A[1+10 10⍴J]
\   1 2 3 4 5 6 7 8 9 10
  + - - - - - - - - -  -
0 | . . . . . . . . .  .
1 | . . . . . . . . .  .
2 | . . . . . . . . .  .
3 | . . . . . . . . .  .
4 | . . . . X . . . .  .
5 | . . . . . . . . .  .
6 | . . . . . . . . .  .
7 | . . . . O . . X .  .
8 | . . . . . . . . O  .
9 | . . . . . . . . .  .

On voit clairement mon attaque avec ce quintuplet descendant, que je continue en jouant 56, et l'ordi contre correctement en s'intercalant en 67:
Code
      J[56]←5
      ('\ ',⍳10)⍪(' ',0,⍳9),('+',10⍴'-')⍪'|',A[1+10 10⍴J]
\   1 2 3 4 5 6 7 8 9 10
  + - - - - - - - - -  -
0 | . . . . . . . . .  .
1 | . . . . . . . . .  .
2 | . . . . . . . . .  .
3 | . . . . . . . . .  .
4 | . . . . X . . . .  .
5 | . . . . . X . . .  .
6 | . . . . . . . . .  .
7 | . . . . O . . X .  .
8 | . . . . . . . . O  .
9 | . . . . . . . . .  .
      notes←(J=0) ^ score_case¨⍳100
      indices_meilleures_notes ←(notes ≥ ⌈/ notes)/⍳100
      indices_meilleures_notes[?⍴ indices_meilleures_notes]
67
      J[67]←1
      ('\ ',⍳10)⍪(' ',0,⍳9),('+',10⍴'-')⍪'|',A[1+10 10⍴J]
\   1 2 3 4 5 6 7 8 9 10
  + - - - - - - - - -  -
0 | . . . . . . . . .  .
1 | . . . . . . . . .  .
2 | . . . . . . . . .  .
3 | . . . . . . . . .  .
4 | . . . . X . . . .  .
5 | . . . . . X . . .  .
6 | . . . . . . O . .  .
7 | . . . . O . . X .  .
8 | . . . . . . . . O  .
9 | . . . . . . . . .  .

On va en rester là, mais ce morpion est opérationnel ! smile.gif
JP

Ce message a été modifié par Jaypee - 18 Apr 2021, 13:25.
Go to the top of the page
 
+Quote Post
baron
posté 5 Aug 2021, 00:27
Message #11


Macbidouilleur d'Or !
*****

Groupe : Modérateurs
Messages : 19 302
Inscrit : 22 Jul 2004
Lieu : Louvain-la-Neuve (Gaule Gelbique)
Membre no 21 291



Notons pour les curieux que la page Wikipédia consacrée à ce langage est particulièrement complète et instructive…
https://fr.wikipedia.org/wiki/APL_(langage)


--------------------
MacBook Pro 14’’ 2021, M1 Pro, 16 Go/1 To – macOS  12.6 “Monterey”  MacBook Pro 15’’ mi-2010 Core i5 2,53 GHz, 8 Go/SSD Samsung 860EVO 1 To – Mac OSX 10.6.8  Power Macintosh G3 beige de bureau, rev.1 @ 233MHz, 288 Mo/4Go – MacOS 9.1 — + carte PCI IDE/ATA Tempo 66 Acard 6260 avec HD interne Maxtor 80 Go + graveur interne CDRW/DVD LG GCC-4520B + tablette A4 Wacom UD-0608-A + LaCie ElectronBlueIV 19" + HP ScanJet 6100C   B-Box 3 + HP LaserJet 4000 N  
La recherche dans MacBidouille vous paraît obscure ? J'ai rédigé une proposition de FAQ. Le moteur logiciel a un peu changé depuis mais ça peut aider quand même.
Les corsaires mettent en berne…
Go to the top of the page
 
+Quote Post
Jaypee
posté 22 Sep 2021, 08:42
Message #12


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Citation (baron @ 5 Aug 2021, 01:27) *
Notons pour les curieux que la page Wikipédia consacrée à ce langage est particulièrement complète et instructive…
https://fr.wikipedia.org/wiki/APL_(langage)

Merci Baron, oui, j'y apprends quelques trucs !

Et curieusement, à me relire quelques mois plus tard, j'arrive à me surprendre moi-même !

Pour ceuss que l'anglais ne rebute pas, la chaine YT de code_report aborde régulièrement des challenges, la plupart du temps avec une solution compacte, mais difficile à digérer en APL.

@ciao,
JP
Go to the top of the page
 
+Quote Post
Jaypee
posté 26 Sep 2021, 09:32
Message #13


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Citation (Jaypee @ 13 Apr 2021, 16:26) *
Un exemple lié à la génétique, calcul du taux de GC d'une séquence ADN...

Le code est loin d'être correct :
On ne doit compter que les liaisons C-G, c-à-d les occurrences de C immédiatement suivi de G.
On peut visualiser cela en empilant l'ADN original, sur lui-même, décalé de 1 colonne, on note le C à la fin et le G au début:
Code
    ADN ← 'GACGTAGTCGADTAGTACGTAGTCGADTAGTAC'
      (ADN),[0.5](1 ⌽ ADN)
GACGTAGTCGADTAGTACGTAGTCGADTAGTAC
ACGTAGTCGADTAGTACGTAGTCGADTAGTACG

Mais ce faisant, on a créé artificiellement une liaison C-G en rapprochant les extrémités, donc une manipulation (génétique) simple, en ajoutant une base neutre à la fin évitera ce problème, sans modifier les autres liaisons.
Code
      (ADN, ' '),[0.5](1 ⌽ ADN, ' ')
GACGTAGTCGADTAGTACGTAGTCGADTAGTAC
ACGTAGTCGADTAGTACGTAGTCGADTAGTAC G

On va maintenant créer un masque avec des 1 à la place des C ou des G, c'est en fait un produit cartésien de couples (x='C', x = 'G') où x vient de l'ADN manipulé :
Code
      (ADN, ' ')∘.='CG'
0 1
0 0
1 0
0 1
0 0
0 0
0 1
0 0...

Ajoutons juste pour mieux visualiser, l'ADN en première colonne, les A et les T donnent (0, 0), C donne (1,0) et G donne (0, 1):
Code
      (ADN, ' '),(ADN, ' ')∘.='CG'
G 0 1
A 0 0
C 1 0
G 0 1
T 0 0
A 0 0
G 0 1
T 0 0
C 1 0
G 0 1
A 0 0

Et comme le résultat est tout en hauteur on va le "coucher" en transposant ligne -> colonne avec l'opérateur qui va bien.
Code
      ⍉(ADN, ' ')∘.='CG'
0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0
1 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0

Mais noter qu'on a perdu l'alignement du 1 du C au dessus du 1 du G, il faudrait ne rien toucher de la première ligne et shifter de 1 colonne la deuxième. Facile !
Code
     0 1⌽ ⍉(ADN, ' ')∘.='CG'
0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0
0 0 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 1

Maintenant on veut le masque résultat des 1 sur 1, pour ce faire on va comprimer (ou réduire) par le ET logique colonne par colonne:
Code
     ∧/[1]0 1⌽ ⍉(ADN, ' ')∘.='CG'
0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0

Le [1] exprime le "colonne par colonne", si on l'avait oublié, on aurait obtenu 0 0, le ET logique de la première ligne, puis le même ET logique pour la seconde ligne.
Et pour compter les 1, on comprime (ou réduit) le masque par l'addition:
Code
     +/∧/[1]0 1⌽ ⍉(ADN, ' ')∘.='CG'
4

Mais le taux de CG se calcule en divisant ce compte par la longueur totale :
Code
    (+/∧/[1]0 1⌽ ⍉(ADN, ' ')∘.='CG')÷⍴ADN
0.1212121212

Pour finir on va transformer cette expression en fonction qui accepte l'ADN comme unique argument à droite. Pour cela on remplace ADN par oméga, on enferme l'expression dans des accolades, et on lui donne un nom:
Code
    tauxCG ← {(+/∧/[1]0 1⌽ ⍉(⍵, ' ')∘.='CG')÷⍴⍵}

Et on l'applique aux données utilisées:
Code
      tauxCG ADN
0.1212121212

Ou à de nouvelles données:
Code
      tauxCG 'AGTCGATGGATC'
0.08333333333


@+JP

Ce message a été modifié par Jaypee - 26 Sep 2021, 09:43.
Go to the top of the page
 
+Quote Post
Jaypee
posté 27 Sep 2021, 08:05
Message #14


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Le langage APL n'a pas eu 36 versions. On y distingue essentiellement 2 grandes versions, APL et APL2.

APL2, poussé par IBM, a ajouté des fonctionnalités plus riches pour l'adressage des données, en liaison avec les bases de données, par exemple pour manipuler des tuples, que l'on peut assimiler à un record en Pascal ou à un struct en C. Mais APL est dynamique, et n'exige aucune déclaration préalable. Par exemple pour représenter un membre d'une association { nom, id, année d'inscription }. APL2 place les différents champs dans des boîtes.
Code
      membre ← 'Jean' 'M05436' 2010
      membre
┌────┬──────┬────┐
│Jean│M05436│2010│
└────┴──────┴────┘

On adresse chaque champ par un indice commençant à 1 par défaut (ou 0 par configuration):
Code
      membre[1]
┌────┐
│Jean│
└────┘

Mais comment manipuler cette "boîte" ? on peut la déballer avec l'opérateur d'inclusion inversé,
Code
      ⊃membre[1]
Jean

ou au contraire le sur-emballer, par l'opérateur d'inclusion.
Code
      ⊂membre[1]
┌──────┐
│┌────┐│
││Jean││
│└────┘│
└──────┘

Une autre manière de faire la sélection et le déballage en une seule opération est le mode dyadique de l'opérateur de déballage, mais aussi implicitement, le premier élément est déballé.
Code
      1 ⊃ membre
Jean
      ⊃ membre
Jean

Enfin, il existe un opérateur d'"aplatissement", qui transforme la boite en tableau simple, un caractère ou un nombre par cellule, il réutilise l'opérateur d'appartenance:
Code
      ∊membre
JeanM05436 2010

Et on accède à toutes les cellules par un seul indice (ou une liste/vecteur d'indices)
Code
      ⍳4
1 2 3 4
      (∊membre)[⍳4]
Jean
      4 + ⍳6
5 6 7 8 9 10
      (∊membre)[4 + ⍳6]
M05436
      (∊membre)[11]
2010

On se rappelle la facilité de l'usage des indices pour créer un nouveau tableau ou une nouvelle chaîne de caractères, de la même forme que les indices:
Code
      'CHIEN'[5 3 1 2 4]
NICHE
      'LE CHAT'[2 3 ⍴ 7 5 2 4 6 7]
THE
CAT

@+JP

Ce message a été modifié par Jaypee - 27 Sep 2021, 14:45.
Go to the top of the page
 
+Quote Post
Jaypee
posté 28 Sep 2021, 10:40
Message #15


Macbidouilleur d'Or !
*****

Groupe : Membres
Messages : 2 486
Inscrit : 29 Aug 2002
Membre no 3 340



Suite de l'exploration d'APL2

Soient 2 vecteurs de même longueur:
Code
      A ← 1 2 3      
      ⍴A
3
      B ← 10 20 30
      ⍴B
3

Examinons les manière de les assembler, par "caténation", par emballage, par laminage "horizontal", ou "vertical"
Code
      A,B
1 2 3 10 20 30
      ⍴A,B
6
      A B
┌─────┬────────┐
│1 2 3│10 20 30│
└─────┴────────┘
      ⍴A B
2
      ≡A B
2
      A,[0.5]B
1  2  3
10 20 30
      ⍴A,[0.5]B
2 3
      A,[1.5]B
1 10
2 20
3 30
      ⍴A,[1.5]B
3 2

      A,[0.12345]B
1  2  3
10 20 30
      ⍴A,[0.12345]B
2 3

Il existe un opérateur "each", le tréma, qui applique la fonction passée comme argument gauche au vecteur passé comme argument droit, on peut l'assimiler à la notion de map() en JS ou en Scala. On peut l'utiliser pour distribuer l'emballage de chaque élément de A avec un élément de B, le résultat est comparable à la fonction zip() présente en JS:
Code
      A,¨B
┌────┬────┬────┐
│1 10│2 20│3 30│
└────┴────┴────┘

Je n'ai pas trouvé de moyen de placer des boîtes verticalement, A emballé au dessus de B emballé.
En fait, oui j'ai trouvé, mais c'est explicite, pas d'opérateur magique smile.gif
Code
      2 1⍴(A B)
┌────────┐
│1 2 3   │
├────────┤
│10 20 30│
└────────┘

La notation rapide pour créer des données "emballées" est à base de parentèses:
Code
      2 1⍴((4 5 6)(40 50 60))
┌────────┐
│4 5 6   │
├────────┤
│40 50 60│
└────────┘

@+JP

Ce message a été modifié par Jaypee - 28 Sep 2021, 15:31.
Go to the top of the page
 
+Quote Post

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 : 19th March 2024 - 03:18