et RPL : Extraire

Extraction d’éléments

En APL, il est facile d’extraire les éléments d’un vecteur en utilisant une liste de positions :

3 7 8 9 10 14 [2 4 1 1 2]
7 9 3 3 7     ⍝ Les 2e, 4e, 1er... éléments

Voyons comment obtenir un résultat équivalent en RPL, commençons en récupérant un seul élément :

2: {3 7 8 9 10 14}
1: 2
PRG LIST ELEM GET

1: 7.  # On a récupéré le 2e élément de la liste

On doit donc transformer (MAP ou DOLIST) la liste des positions en leurs valeurs correspondantes dans l’autre liste. Notons GL. (GETL dans le document d’origine) cette fonction :

« → v « « v SWAP GET » MAP » »
'GL. STO

ou encore :

« → v « 1 « v SWAP GET » DOLIST » »

Vérifions :

2: {2 4 1 1 2}
1: {3 7 8 9 10 14}
VAR GL.

1: {7. 9. 3. 3. 7.}

Créons séparément une version analogue nommée GLT. pour les chaines de caractères :

« SP. → v « « v SWAP GET » MAP ∑LIST » »
'GLT. STO

Rappelons que ∑LIST permet de concaténer les caractères et que SP. (Split) est la fonction que l’on a créée dans le chapitre sur les caractères permettant de transformer une chaine en liste :

« → s « « s j j SUB » 'j' 1 s SIZE 1 SEQ » »
'SP. STO

Vérifions que tout fonctionne :

2: {6 1 2 9 3 8 9 5 1 2}
1: "PLEURAIT "        # Avec une espace finale
VAR GLT.

1: "APL ET RPL"

Et en version APL :

'PLEURAIT '[6 1 2 9 3 8 9 5 1 2]
APL ET RPL

On peut alors se créer une fonction Get List Générale GLG., fonctionnant à la fois pour les listes et pour les chaines de caractères, pour cela il suffit de regarder le type de l’élément au niveau 1, sachant que les chaines sont du type 2. Le programme ci-dessous duplique le niveau 1 (qui peut être une liste ou une chaine), si son type est 2 alors on exécute GLT. sinon on exécute GL.

« DUP TYPE 2 == « GLT. » « GL. » IFTE »
'GLG. STO

Vérifions :

2: { 3 3 1 2 3 3 }
1: { 3 1 4 1 5 9 }
VAR GLG.

1: {4. 4. 3. 1. 4. 4.}

2: { 7 4 8 10 9 1 10 2 4 8 }
1: "TROPICALE "
VAR GLG.

1: "APL ET RPL"

Récupérer les positions

C’est l’opération inverse de GET, voyons déjà son fonctionnement en APL (symbole IOTA ⍳) :

'TROPICALE ' ⍳ 'APL ET RPL'
7 4 8 10 9 1 10 2 4 8

En français : Dans la chaine ‘TROPICALE’, quelles sont les positions des lettres ‘A’, ‘P’, ‘L’, ‘ ‘, ‘E’, ‘T’…

Lorsqu’une lettre n’est pas trouvée, APL renvoie comme valeur la longueur de la chaine de gauche + 1. Alors qu’en RPL, si l’élément n’est pas trouvé, c’est la valeur 0 qui est renvoyée :

2: { 1 2 3 }
1: 7
PRG LIST ELEM POS

1: 0

En APL :

1 2 3 ⍳ 7
4            ⍝ = longueur de la liste + 1

Finalement, il suffit de changer GET par POS dans nos programmes précédents :

« → v « « v SWAP POS » MAP » »
'PO. STO          # Version pour les listes
« → v « SP. « v SWAP POS » MAP » »
'POT. STO         # Version pour les chaines

« DUP TYPE 2 == « POT. » « PO. » IFTE »
'POG. STO         # Version générale

Vérifions :

2: "APL ET RPL"
1: "TROPICALE "
VAR POG.

1: { 7. 4. 8. 10. 9. 1. 10. 2. 4. 8. }
# "A" à la 7e position dans "TROPICALE ", etc. 

2: { 3 1 4 1 5 9 }
1: { 1 4 1 4 2 1 3 5 }
VAR POG.

1: { 7. 1. 2. 1. 8. 0. }
# Le 3 est à la 7e position, le 9 n'y est pas

Remarquez qu’en APL et RPL, POS renvoie la première position trouvée, par exemple 4 4 4 ⍳ 4 donne 1 (et non pas 2 ou 3).

Exercice – Nombre de voyelles

En vous inspirant des programmes précédents, créez une fonction VOY comptant le nombre de voyelles A, E, I, O, U dans une chaine de caractères.

1: "TROPICALE"
VAR VOY

1: 4       # Il y a 4 voyelles

Corrigé : On va chercher à quelles positions se trouvent les lettres du mot “TROPICALE” dans la chaine “AEIOU”. Nous aurons alors une liste avec des 0 (lettres non trouvées) ou un nombre strictement positif indiquant la position. En appliquant la fonction SIGN, les valeurs positives se transformeront en 1 (0 reste 0) et il suffira de faire une réduction par la somme (∑LIST). Ce qui donne :

« "AEIOU" POG. SIGN ∑LIST »
'VOY STO

En version “développée”, on aurait :

« → v « {"A" "E" "I" "O" "U" }
 « v SWAP POS SIGN » MAP
 ∑LIST »
»
'VOY STO

En APL, on utilisera  le symbole d’appartenance ∊ pour obtenir 0 ou 1 plutôt que la position :

+/ 'TROPICALE' ∊ 'AEIOU'
Output : 4

Exercice – Conversion en chiffres romains

Je reprends l’exercice proposé par caloubugs sur le forum Silicium.org, à savoir transformer un nombre en chiffres romains.

Pour cela, vous devrez transposer en RPL cette solution en APL :

ROM ← {
 +/ (¯1 + 2 × z ≥ 1↓z,0) × 
 z ← (1 5 10 50 100 500 1000)['IVXLCDM' ⍳ ⍵]
}

ROM 'MDCCCLXXVI'
Output: 1876

Quelques explications :

  • ‘IVXLCDM’ ⍳ ⍵  donne les positions des lettres de ⍵ (un ‘X’ donne 3, un ‘M’ donne 7 etc.
  • (1 5 10 50 100 500 1000)[…] pour la récupération des valeurs
  • 1↓z,0 permet de supprimer la tête de la liste et ajout 0 en fin
  • z ≥  On cherche les endroits où il faudra soustraire
  • ¯1 + 2 × Les 0 deviennent -1 et les 1 des 1

Corrigé :

‘IVXLCDM’ ⍳ ⍵ va se traduire par POT. :

2: 'MDCCCLXXVI'
1: 'IVXLCDM'
VAR POT.

1: {7. 6. 5. 5. 5. 4. 3. 2. 1.}

La récupération des valeurs par GLT. :

2: {7. 6. 5. 5. 5. 4. 3. 2. 1.}
1: {1 5 10 50 100 500 1000}
VAR GLT.

1: {1000 500 100 100 100 50 10 5 1}

Pour 1↓z,0 on utilise TAIL et 0 + ajoutera un zéro à la fin de la liste.

1: {1000 500 100 100 100 50 10 5 1}
PRG LIST ELEM TAIL
0 +

1: {500 100 100 100 50 10 5 1 0}

On crée le vecteur logique avec ≥ puis il suffira de le multiplier par 2 et soustraire 1.
On termine en faisant la somme des produits des 2 listes : × ∑LIST. On ajoute un 0 à la liste car ∑LIST ne fonctionne pas avec une liste contenant un seul élément.

Programme final :

« "IVXLCDM" POT. { 1 5 10 50 100 500 1000 } GLT.
 DUP DUP TAIL 0 + ≥ 2 × 1 - × 0 + ∑LIST
»
'ROM STO

Vérifions :

1: "MMMCMXCIX"
VAR ROM

1: 3999

1: "MMCCCXLV"
VAR ROM

1: 2345

Exercice – Union

En APL, il existe une fonction qui permet de récupérer les éléments de façon unique :

∪ 1 2 3 2 1 2 2 3
1 2 3

Malheureusement il n’existe pas de fonction union sur les HP-48/49/50g (Même s’il existe des bibliothèques toutes prêtes comme GoferList dont le code source n’est hélas pas donné). Remarquons cependant que l’union
peut être vue comme une réduction :

{⍺ ∊ ⍵: ⍵ ⋄ ⍺,⍵} / 1 2 3 2 1 2 2 3
1 2 3

L’accolade utilise une notation assez classique :

{ test : valeur_si_vrai ⋄ valeur_si_faux }

  • Au premier tour, APL prend les 2 derniers éléments du vecteur (⍺ = 2 et ⍵ = 3) et fait le test 2 ∊ 3.
  • Comme celui-ci est faux, on obtient la concaténation de 2 et 3 (⍺,⍵) c’est-à-dire le vecteur 2 3
  • Au 2e tour, APL prend le ⍺ = 2 (le 3e en partant de la droite) puis fait le test 2 ∊ 2 3
  • Comme le test est vrai, on obtient ⍵ c’est-à-dire le vecteur précédent.
  • Donc à chaque étape, si le nouvel élément ⍺ appartient déjà à la liste précédente ⍵, on garde ⍵ sinon on ajoute ⍺ (au début du vecteur)

Voici une traduction possible en RPL (Inspiré de Gilles59) :

« {{}} SWAP + 
 « DUP2 POS { DROP } { + } IFTE » STREAM
»
'UN. STO

1: {1 2 3 2 1 2 2 3}
VAR UN.
1: {1 2 3}

Explications :

  • On veut créer une liste et ajouter les valeurs au fur et à mesure, l’idée est de partir de la liste vide {} que l’on insère en tête de celle fournie par l’utilisateur. Ainsi {1 2 3 2 1 2 2 3} va devenir
    {{} 1 2 3 2 1 2 2 3}.
  • Au premier tour, on a  {} au niveau 2 et la valeur 1 au niveau 1. On duplique ces 2 valeurs d’où 4 éléments dans la pile : {} 1 {} 1
  • On cherche la position de 1 dans {}, comme il n’y est pas, RPL renvoie 0.
  • On fait un If Then Else (IFTE) qui a également la structure :
    Test / Opération_si_vraie / Opération_si_faux. Dans notre cas, il applique “+” et donc ajoute le 1 à la liste {}, ce qui donne {1}.
  • Au second tour, on regarde si 2 existe dans {1}, comme ce n’est pas le cas on l’ajoute {1 2}, idem avec le 3 ce qui donne {1 2 3}
  • Au 4e tour, on teste si 2 existe dans {1 2 3} ce qui est le cas, on supprime (DROP) l’élément du niveau 1 et on récupère la liste précédente.

Remarque : Comme on l’a dit, APL travaille de la droite vers la gauche, on aura donc :

{⍺ ∊ ⍵: ⍵ ⋄ ⍺,⍵} / 4 5 4
5 4

Alors qu’en RPL :

{ 4 5 4 }
VAR UN.
{ 4 5 }

Lire la suite…